diff --git a/src/blob.rs b/src/blob.rs index bd426a35c0..ad2777de7b 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -12,7 +12,7 @@ use futures::StreamExt; use image::ImageReader; use image::codecs::jpeg::JpegEncoder; use image::{DynamicImage, GenericImage, GenericImageView, ImageFormat, Pixel, Rgba}; -use num_traits::FromPrimitive; +use num_traits::{FromPrimitive, cast}; use tokio::{fs, task}; use tokio_stream::wrappers::ReadDirStream; @@ -388,14 +388,9 @@ impl<'a> BlobObject<'a> { _ => img, }; - // max_wh is the maximum image width and height, i.e. the resolution-limit. - // target_wh target-resolution for resizing the image. + // max_wh is the maximum image width and height, i.e. the resolution-limit, + // as set by `Config::MediaQuality`. let exceeds_wh = img.width() > max_wh || img.height() > max_wh; - let mut target_wh = if exceeds_wh { - max_wh - } else { - max(img.width(), img.height()) - }; let exceeds_max_bytes = nr_bytes > max_bytes as u64; let jpeg_quality = 75; @@ -434,6 +429,35 @@ impl<'a> BlobObject<'a> { }); if do_scale { + let n_px_longest_side = max(img.width(), img.height()); + + // target_wh will be used as the target-resolution for resizing the image, + // so that the longest sides of the image match the target-resolution. + let mut target_wh = if !is_avatar { + let n_all_px_sqrt = f64::from(img.width() * img.height()).sqrt(); + // Limit resolution to the number of pixels that fit within max_wh * max_wh, + // so that the image-quality does not depend on the aspect-ratio. + let limit: Option = cast( + (f64::from(n_px_longest_side) * (f64::from(max_wh) / n_all_px_sqrt)) + .floor(), + ); + let mut resolution_limit = limit.context("resolution_limit is out of range")?; + // Align at least one dimension of the resampled image to a multiple of 8 pixels, + // to have fewer partially used JPEG-blocks (which represent 8x8 pixels each). + if resolution_limit < n_px_longest_side { + while !resolution_limit.is_multiple_of(8) { + resolution_limit -= 1 + } + } + resolution_limit + } else { + max_wh + }; + + if target_wh > n_px_longest_side { + target_wh = n_px_longest_side; + }; + loop { if mem::take(&mut add_white_bg) { self::add_white_bg(&mut img); diff --git a/src/blob/blob_tests.rs b/src/blob/blob_tests.rs index ed5a2b0017..63763f54a6 100644 --- a/src/blob/blob_tests.rs +++ b/src/blob/blob_tests.rs @@ -384,8 +384,8 @@ async fn test_recode_image_balanced_png() { extension: "png", original_width: 1920, original_height: 1080, - compressed_width: constants::WORSE_IMAGE_SIZE, - compressed_height: constants::WORSE_IMAGE_SIZE * 1080 / 1920, + compressed_width: 848, + compressed_height: 477, ..Default::default() } .test() @@ -475,8 +475,8 @@ async fn test_recode_image_rgba_png_to_jpeg() { extension: "png", original_width: 1920, original_height: 1080, - compressed_width: constants::WORSE_IMAGE_SIZE, - compressed_height: constants::WORSE_IMAGE_SIZE * 1080 / 1920, + compressed_width: 848, + compressed_height: 477, ..Default::default() } .test() @@ -495,8 +495,8 @@ async fn test_recode_image_huge_jpg() { has_exif: true, original_width: 1920, original_height: 1080, - compressed_width: constants::BALANCED_IMAGE_SIZE, - compressed_height: constants::BALANCED_IMAGE_SIZE * 1080 / 1920, + compressed_width: 1704, + compressed_height: 959, ..Default::default() } .test() diff --git a/src/tests/pre_messages/additional_text.rs b/src/tests/pre_messages/additional_text.rs index 0af33ec8c6..f37baa4e4e 100644 --- a/src/tests/pre_messages/additional_text.rs +++ b/src/tests/pre_messages/additional_text.rs @@ -34,7 +34,7 @@ async fn test_additional_text_on_different_viewtypes() -> Result<()> { let (pre_message, _, _) = send_large_image_message(alice, a_group_id).await?; let msg = bob.recv_msg(&pre_message).await; assert_eq!(msg.text, "test".to_owned()); - assert_eq!(msg.get_text(), "test [Image – 146.12 KiB]".to_owned()); + assert_eq!(msg.get_text(), "test [Image – 228.45 KiB]".to_owned()); Ok(()) } diff --git a/src/tests/pre_messages/receiving.rs b/src/tests/pre_messages/receiving.rs index 0657f523fd..2cd28ede45 100644 --- a/src/tests/pre_messages/receiving.rs +++ b/src/tests/pre_messages/receiving.rs @@ -323,9 +323,9 @@ async fn test_receive_pre_message_image() -> Result<()> { // test that metadata is correctly returned by methods assert_eq!(msg.get_post_message_viewtype(), Some(Viewtype::Image)); // recoded image dimensions - assert_eq!(msg.get_filebytes(bob).await?, Some(149632)); - assert_eq!(msg.get_height(), 1280); - assert_eq!(msg.get_width(), 720); + assert_eq!(msg.get_filebytes(bob).await?, Some(233935)); + assert_eq!(msg.get_height(), 1704); + assert_eq!(msg.get_width(), 959); Ok(()) }