From 3fea82934055d38f8eda7aa7605cc4c0b209c3e8 Mon Sep 17 00:00:00 2001 From: bjoern Date: Thu, 24 Apr 2025 20:44:23 +0200 Subject: [PATCH] feat: better avatar quality (#6822) this PR scaled avatars using the Triangle-filter, resulting in often better image quality and smaller files (5%). it comes at high costs, therefore, we do not do that unconditionally for each image sent, see comment in the code and https://github.com/chatmail/core/pull/6815 --------- Co-authored-by: iequidoo <117991069+iequidoo@users.noreply.github.com> --- src/blob.rs | 15 ++++++++++++++- src/blob/blob_tests.rs | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/blob.rs b/src/blob.rs index f2622420a..9b53fbcf5 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -432,7 +432,20 @@ impl<'a> BlobObject<'a> { if mem::take(&mut add_white_bg) { self::add_white_bg(&mut img); } - let new_img = img.thumbnail(img_wh, img_wh); + + // resize() results in often slightly better quality, + // however, comes at high price of being 4+ times slower than thumbnail(). + // for a typical camera image that is sent, this may be a change from "instant" (500ms) to "long time waiting" (3s). + // as we do not have recoding in background while chat has already a preview, + // we vote for speed. + // exception is the avatar image: this is far more often sent than recoded, + // usually has less pixels by cropping, UI that needs to wait anyways, + // and also benefits from slightly better (5%) encoding of Triangle-filtered images. + let new_img = if is_avatar { + img.resize(img_wh, img_wh, image::imageops::FilterType::Triangle) + } else { + img.thumbnail(img_wh, img_wh) + }; if encoded_img_exceeds_bytes( context, diff --git a/src/blob/blob_tests.rs b/src/blob/blob_tests.rs index 57fe798e3..47132baf2 100644 --- a/src/blob/blob_tests.rs +++ b/src/blob/blob_tests.rs @@ -174,7 +174,7 @@ async fn test_selfavatar_outside_blobdir() { let avatar_blob = t.get_config(Config::Selfavatar).await.unwrap().unwrap(); let avatar_path = Path::new(&avatar_blob); assert!( - avatar_blob.ends_with("1e08d1c9398297c21dd3820f7db2324.jpg"), + avatar_blob.ends_with("7dde69e06b5ae6c27520a436bbfd65b.jpg"), "The avatar filename should be its hash, put instead it's {avatar_blob}" ); let scaled_avatar_size = file_size(avatar_path).await; @@ -226,7 +226,7 @@ async fn test_selfavatar_in_blobdir() { .unwrap(); let avatar_cfg = t.get_config(Config::Selfavatar).await.unwrap().unwrap(); assert!( - avatar_cfg.ends_with("ec054c444a5755adf2b0aaea40209f2.png"), + avatar_cfg.ends_with("d57cb5ce5f371531b6e1fb17b6dd1af.png"), "Avatar file name {avatar_cfg} should end with its hash" );