Images: count a JPEG cached at the reduced levels it is drawn at as cached

A JPEG drawn small is decoded at a reduced level and has no full-size entry, so every later render queued it again and started decode threads that found it cached.
This commit is contained in:
dotCooCoo committed 2026-11-12 15:31:18 -08:00
1 parent dbb334c529
commit 37a7076daf
1 file changed
+28 -2
+28 -2
View File
@@ -282,6 +282,18 @@ pub(crate) fn warm(wants: Vec<Want>) -> usize {
}
}
}
// The reduced JPEG level [`reduced`] takes for each drawing of an asset, when every drawing
// of it takes one.
let targets: HashMap<(usize, u32), Vec<u8>> = assets
.iter()
.filter_map(|(key, wanted)| {
let levels = wanted
.iter()
.map(|w| reduced_sizes(&w.bytes, w.page, w.width_pt, w.on_screen).and_then(|s| u8::try_from(s.len() - 1).ok()).filter(|l| *l > 0))
.collect::<Option<Vec<u8>>>()?;
Some((*key, levels))
})
.collect();
// The bytes each asset in view holds in the cache, when it is cached or failed, and the bytes
// of everything else cached. Its levels become the most recently used, so the stores below
// evict other images first.
@@ -295,8 +307,11 @@ pub(crate) fn warm(wants: Vec<Want>) -> usize {
*held.entry((k.0, k.1)).or_default() += size_of(&e.pm);
}
}
// Smaller levels alone don't make an asset cached.
held.retain(|k, _| c.map.contains_key(&(k.0, k.1, 0)));
// Smaller levels alone don't make an asset cached, unless they are the reduced JPEG levels
// every drawing of it takes.
held.retain(|k, _| {
c.map.contains_key(&(k.0, k.1, 0)) || targets.get(k).is_some_and(|ls| ls.iter().all(|l| c.map.contains_key(&(k.0, k.1, *l))))
});
let in_view = held.values().sum::<usize>();
for k in index.keys() {
if c.bad.contains_key(k) {
@@ -616,6 +631,17 @@ mod tests {
Arc::new(out)
}
#[test]
fn warm_counts_cached_reduced_jpeg_levels_as_cached() {
// Drawn at a tenth of their pixels, two JPEGs decode at a reduced level only; once that
// level is cached, a later warm has nothing to decode.
let (a, b) = (jpeg(1024, 768), jpeg(1024, 768));
let small = |bytes: &Arc<Vec<u8>>| Want { bytes: bytes.clone(), page: 0, width_pt: 1024.0, on_screen: 0.1 };
assert!(warm(vec![small(&a), small(&b)]) > 0, "decoded ahead");
assert!(lookup((Arc::as_ptr(&a) as usize, 0, 0)).is_none(), "no full-size decode is kept");
assert_eq!(warm(vec![small(&a), small(&b)]), 0, "nothing left to decode");
}
#[test]
fn screen_levels_of_a_jpeg_need_no_full_size_decode() {
let bytes = jpeg(1024, 768);