feat: Optimize avatar size multiplier for 2 << n px avatars

Instead of 2/3 which is not optimal for 512 px avatars usually passed to Core, use the sequence 3/4,
5/8, 4/8, 3/8... to do "smaller" downscaling steps and reduce aliasing effects.

Before, it was discussed that just 3/4 can be used. However, if we repeat the reduction step, we get
`3 << n` as a numerator and this way increase aliasing effects on each step. Better limit the
numerator to 5.
This commit is contained in:
iequidoo
2026-02-02 01:09:32 -03:00
parent af16fc9038
commit 11a8cd03af
3 changed files with 26 additions and 3 deletions

View File

@@ -435,6 +435,8 @@ impl<'a> BlobObject<'a> {
});
if do_scale {
let (mut m, mut d) = (3, 4);
let wh = target_wh;
loop {
if mem::take(&mut add_white_bg) {
self::add_white_bg(&mut img);
@@ -467,8 +469,11 @@ impl<'a> BlobObject<'a> {
"Failed to scale image to below {max_bytes}B.",
));
}
target_wh = target_wh * 2 / 3;
target_wh = wh * m / d;
(m, d) = match m > 3 {
true => (m - 1, d),
false => (5, d * 2),
};
} else {
info!(
context,

View File

@@ -4,7 +4,9 @@ use super::*;
use crate::message::{Message, Viewtype};
use crate::param::Param;
use crate::sql;
use crate::test_utils::{self, AVATAR_64x64_BYTES, AVATAR_64x64_DEDUPLICATED, TestContext};
use crate::test_utils::{
self, AVATAR_64x64_BYTES, AVATAR_64x64_DEDUPLICATED, TestContext, TestContextManager,
};
use crate::tools::SystemTime;
fn check_image_size(path: impl AsRef<Path>, width: u32, height: u32) -> image::DynamicImage {
@@ -239,6 +241,22 @@ async fn test_selfavatar_in_blobdir() {
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_huge_selfavatar() -> Result<()> {
let mut tcm = TestContextManager::new();
let t = &tcm.unconfigured().await;
let avatar_src = t.get_blobdir().join("avatar.png");
let bytes = include_bytes!("../../test-data/image/noise400x400.png");
fs::write(&avatar_src, bytes).await?;
t.set_config(Config::Selfavatar, Some(avatar_src.to_str().unwrap()))
.await?;
let avatar_cfg = t.get_config(Config::Selfavatar).await?.unwrap();
// At 3/4 the avatar is still huge, so it's downscaled to 5/8.
check_image_size(avatar_cfg, 250, 250);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_selfavatar_copy_without_recode() {
let t = TestContext::new().await;

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB