fix resolutions for exif rotated images
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				test / cargo test (push) Failing after 1m25s
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	test / cargo test (push) Failing after 1m25s
				
			This commit is contained in:
		| @@ -19,7 +19,7 @@ tower-http = { version = "0.6.0", features = ["trace", "fs"] } | ||||
| tower-livereload = "0.9.2" | ||||
| tracing = "0.1" | ||||
| tracing-subscriber = { version = "0.3", features = ["env-filter"] } | ||||
| image = "0.25.2" | ||||
| image = "0.25.6" | ||||
| anyhow = "1.0.86" | ||||
| rayon = "1.10.0" | ||||
| syntect = "5.2.0" | ||||
|   | ||||
| @@ -1,14 +1,15 @@ | ||||
| use core::fmt; | ||||
| use std::path::Path; | ||||
|  | ||||
| use image::image_dimensions; | ||||
| use image::{image_dimensions, ImageReader}; | ||||
| use indoc::formatdoc; | ||||
| use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd}; | ||||
| use syntect::{highlighting::ThemeSet, html::highlighted_html_for_string, parsing::SyntaxSet}; | ||||
| use tracing::{debug, error}; | ||||
|  | ||||
| use crate::picture_generator::{ | ||||
|     picture_markup_generator::generate_picture_markup, resolutions::get_max_resolution, | ||||
|     picture_markup_generator::{generate_picture_markup, should_swap_dimensions}, | ||||
|     resolutions::get_max_resolution, | ||||
| }; | ||||
|  | ||||
| pub const MAX_BLOG_IMAGE_RESOLUTION: (u32, u32) = (1280, 860); | ||||
| @@ -65,8 +66,16 @@ pub fn parse_markdown<T: fmt::Display>( | ||||
|  | ||||
|             let dev_only_img_path = | ||||
|                 Path::new("static/").join(dest_url.strip_prefix("/").unwrap_or(&dest_url)); | ||||
|             let img_dimensions = image_dimensions(&dev_only_img_path).unwrap(); | ||||
|  | ||||
|             // We need to take the exif rotation into consideration here | ||||
|             let img_dimensions = { | ||||
|                 let orig_img_dimensions = image_dimensions(&dev_only_img_path).unwrap(); | ||||
|                 if should_swap_dimensions(&dev_only_img_path) { | ||||
|                     (orig_img_dimensions.1, orig_img_dimensions.0) | ||||
|                 } else { | ||||
|                     orig_img_dimensions | ||||
|                 } | ||||
|             }; | ||||
|             let (max_width, max_height) = get_max_resolution( | ||||
|                 img_dimensions, | ||||
|                 MAX_BLOG_IMAGE_RESOLUTION.0, | ||||
|   | ||||
| @@ -16,7 +16,6 @@ pub fn generate_images( | ||||
|     formats.par_iter().for_each(|format| { | ||||
|         resolutions.par_iter().for_each(|resolution| { | ||||
|             let (width, height, _) = *resolution; | ||||
|             // let image = image.clone(); | ||||
|             let resized = image.resize_to_fill(width, height, FilterType::Triangle); | ||||
|             let file_name = path_to_generated.file_name().unwrap().to_str().unwrap(); | ||||
|             let save_path = Path::new("./") | ||||
|   | ||||
| @@ -8,6 +8,7 @@ use super::{ | ||||
|     picture_markup_generator::{get_export_formats, get_generated_file_name, get_image_path}, | ||||
| }; | ||||
|  | ||||
| /// Used directly in templates | ||||
| pub fn generate_image_with_src( | ||||
|     orig_img_path: &str, | ||||
|     width: u32, | ||||
|   | ||||
| @@ -4,7 +4,7 @@ use std::{ | ||||
| }; | ||||
|  | ||||
| use anyhow::Context; | ||||
| use image::{image_dimensions, ImageReader}; | ||||
| use image::{image_dimensions, ImageDecoder, ImageReader}; | ||||
| use indoc::formatdoc; | ||||
|  | ||||
| use super::{ | ||||
| @@ -14,6 +14,7 @@ use super::{ | ||||
|  | ||||
| pub const PIXEL_DENSITIES: [f32; 5] = [1., 1.5, 2., 3., 4.]; | ||||
|  | ||||
| /// Used by markdown generator | ||||
| pub fn generate_picture_markup( | ||||
|     orig_img_path: &str, | ||||
|     width: u32, | ||||
| @@ -29,6 +30,7 @@ pub fn generate_picture_markup( | ||||
|         "".to_string() | ||||
|     }; | ||||
|  | ||||
|     // Here the resolution is already correct | ||||
|     if exported_formats.is_empty() { | ||||
|         return Ok(formatdoc!( | ||||
|             r#"<img | ||||
| @@ -45,8 +47,14 @@ pub fn generate_picture_markup( | ||||
|     let disk_img_path = | ||||
|         Path::new("static/").join(orig_img_path.strip_prefix("/").unwrap_or(orig_img_path)); | ||||
|  | ||||
|     // Here we have a problem. The resolution is swapped but we want to generate images with original dimensions which are not here. | ||||
|     let orig_img_dimensions = image_dimensions(&disk_img_path)?; | ||||
|     let resolutions = get_resolutions(orig_img_dimensions, width, height); | ||||
|     let resolutions = get_resolutions( | ||||
|         orig_img_dimensions, | ||||
|         width, | ||||
|         height, | ||||
|         should_swap_dimensions(&disk_img_path), | ||||
|     ); | ||||
|     let path_to_generated_arc = Arc::new(path_to_generated); | ||||
|     let path_to_generated_clone = Arc::clone(&path_to_generated_arc); | ||||
|     let resolutions_arc = Arc::new(resolutions); | ||||
| @@ -54,23 +62,19 @@ pub fn generate_picture_markup( | ||||
|     let exported_formats_arc = Arc::new(exported_formats); | ||||
|     let exported_formats_clone = Arc::clone(&exported_formats_arc); | ||||
|  | ||||
|     // AI? Which data escapes? | ||||
|     rayon::spawn(move || { | ||||
|         let orig_img = ImageReader::open(&disk_img_path) | ||||
|             .with_context(|| format!("Failed to read instrs from {:?}", &disk_img_path)) | ||||
|             .unwrap() | ||||
|             .decode() | ||||
|             .unwrap(); | ||||
|         let path_to_generated = path_to_generated_clone.as_ref(); | ||||
|         let resolutions = resolutions_clone.as_ref(); | ||||
|         let exported_formats = exported_formats_clone.as_ref(); | ||||
|  | ||||
|         let result = generate_images( | ||||
|             &orig_img, | ||||
|             &disk_img_path, | ||||
|             path_to_generated, | ||||
|             resolutions, | ||||
|             exported_formats, | ||||
|             &path_to_generated_clone, | ||||
|             &resolutions_clone, | ||||
|             &exported_formats_clone, | ||||
|         ) | ||||
|         .with_context(|| "Failed to generate images".to_string()); | ||||
|         if let Err(e) = result { | ||||
| @@ -130,11 +134,19 @@ pub fn get_image_path(path: &Path, resolution: &(u32, u32, f32), format: &Export | ||||
|     format!("{path_name}_{width}x{height}.{extension}") | ||||
| } | ||||
|  | ||||
| /// Take original resolution of photo and | ||||
| /// exif data is not taken into consideration therefore we don't need to do anything here regarding to swapping width-height | ||||
| fn get_resolutions( | ||||
|     (orig_width, orig_height): (u32, u32), | ||||
|     width: u32, | ||||
|     height: u32, | ||||
|     swap_dimensions: bool, | ||||
| ) -> Vec<(u32, u32, f32)> { | ||||
|     let (width, height) = if swap_dimensions { | ||||
|         (height, width) | ||||
|     } else { | ||||
|         (width, height) | ||||
|     }; | ||||
|     let mut resolutions: Vec<(u32, u32, f32)> = vec![]; | ||||
|     for pixel_density in PIXEL_DENSITIES { | ||||
|         let (density_width, density_height) = ( | ||||
| @@ -159,22 +171,22 @@ fn get_resolutions( | ||||
| #[test] | ||||
| fn test_get_resolutions() { | ||||
|     assert_eq!( | ||||
|         get_resolutions((320, 200), 320, 200), | ||||
|         get_resolutions((320, 200), 320, 200, false), | ||||
|         vec![(320, 200, 1.)], | ||||
|         "Only original size fits" | ||||
|     ); | ||||
|     assert_eq!( | ||||
|         get_resolutions((500, 400), 320, 200), | ||||
|         get_resolutions((500, 400), 320, 200, false), | ||||
|         vec![(320, 200, 1.), (480, 300, 1.5), (500, 312, 2.)], | ||||
|         "Should only create sizes that fits and fill the max possible for the last one - width" | ||||
|     ); | ||||
|     assert_eq!( | ||||
|         get_resolutions((400, 600), 300, 200), | ||||
|         get_resolutions((400, 600), 300, 200, false), | ||||
|         vec![(300, 200, 1.), (400, 266, 1.5)], | ||||
|         "Should only create sizes that fits and fill the max possible for the last one - height" | ||||
|     ); | ||||
|     assert_eq!( | ||||
|         get_resolutions((1200, 900), 320, 200), | ||||
|         get_resolutions((1200, 900), 320, 200, false), | ||||
|         vec![ | ||||
|             (320, 200, 1.), | ||||
|             (480, 300, 1.5), | ||||
| @@ -184,6 +196,7 @@ fn test_get_resolutions() { | ||||
|         ], | ||||
|         "Should create all possible sizes, with the last one maxed" | ||||
|     ); | ||||
|     // TODO add test for swapping | ||||
| } | ||||
|  | ||||
| fn strip_prefixes(path: &Path) -> &Path { | ||||
| @@ -257,6 +270,23 @@ pub fn get_export_formats(orig_img_path: &Path) -> Vec<ExportFormat> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn should_swap_dimensions(img_path: &Path) -> bool { | ||||
|     let orientation = ImageReader::open(img_path) | ||||
|         .unwrap() | ||||
|         .into_decoder() | ||||
|         .unwrap() | ||||
|         .orientation() | ||||
|         .unwrap(); | ||||
|  | ||||
|     matches!( | ||||
|         orientation, | ||||
|         image::metadata::Orientation::Rotate90 | ||||
|             | image::metadata::Orientation::Rotate270 | ||||
|             | image::metadata::Orientation::Rotate90FlipH | ||||
|             | image::metadata::Orientation::Rotate270FlipH | ||||
|     ) | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_get_export_formats() { | ||||
|     assert_eq!( | ||||
|   | ||||
		Reference in New Issue
	
	Block a user