From c896cd8d31cbb8594595468f9509fde5b7a00ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 25 Oct 2025 00:56:01 +0200 Subject: [PATCH] Decode images in parallel in `gallery` example --- examples/gallery/src/civitai.rs | 6 +++ examples/gallery/src/main.rs | 66 +++++++++++++++++++++++---------- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/examples/gallery/src/civitai.rs b/examples/gallery/src/civitai.rs index 09fd0f91..322716f9 100644 --- a/examples/gallery/src/civitai.rs +++ b/examples/gallery/src/civitai.rs @@ -130,6 +130,12 @@ impl fmt::Debug for Rgba { #[derive(Clone)] pub struct Bytes(bytes::Bytes); +impl Bytes { + pub fn as_slice(&self) -> &[u8] { + &self.0 + } +} + impl From for bytes::Bytes { fn from(value: Bytes) -> Self { value.0 diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs index 87a8e811..ccd03589 100644 --- a/examples/gallery/src/main.rs +++ b/examples/gallery/src/main.rs @@ -111,16 +111,21 @@ impl Gallery { let _ = self.visible.insert(id); if self.downloaded.contains(&id) { - let Some(Preview::Ready { thumbnail, .. }) = - self.previews.get_mut(&id) + let Some(Preview::Ready { + thumbnail, + blurhash, + }) = self.previews.get_mut(&id) else { return Task::none(); }; - return image::allocate(image::Handle::from_bytes( - thumbnail.bytes.clone(), - )) - .map(Message::ThumbnailAllocated.with(id)); + if let Some(blurhash) = blurhash { + blurhash.show(now); + } + + return to_rgba(thumbnail.bytes.clone()) + .then(image::allocate) + .map(Message::ThumbnailAllocated.with(id)); } let _ = self.downloaded.insert(id); @@ -165,7 +170,8 @@ impl Gallery { let _ = self.previews.insert(id, preview); - image::allocate(image::Handle::from_bytes(bytes)) + to_rgba(bytes) + .then(image::allocate) .map(Message::ThumbnailAllocated.with(id)) } Message::ThumbnailAllocated(id, allocation) => { @@ -173,19 +179,12 @@ impl Gallery { return Task::none(); } - let Some(Preview::Ready { - thumbnail, - blurhash, - .. - }) = self.previews.get_mut(&id) + let Some(Preview::Ready { thumbnail, .. }) = + self.previews.get_mut(&id) else { return Task::none(); }; - if let Some(blurhash) = blurhash { - blurhash.show(now); - } - thumbnail.show(allocation, now); Task::none() @@ -369,7 +368,7 @@ impl Blurhash { pub fn reset(&mut self) { self.fade_in = Animation::new(false) .easing(animation::Easing::EaseIn) - .slow(); + .very_quick(); } } @@ -427,9 +426,15 @@ impl Preview { fn is_animating(&self, now: Instant) -> bool { match &self { Self::Loading { blurhash } => blurhash.fade_in.is_animating(now), - Self::Ready { thumbnail, .. } => { + Self::Ready { + thumbnail, + blurhash, + } => { thumbnail.fade_in.is_animating(now) || thumbnail.zoom.is_animating(now) + || blurhash.as_ref().is_some_and(|blurhash| { + blurhash.fade_in.is_animating(now) + }) } } } @@ -469,7 +474,7 @@ impl Thumbnail { self.allocation = None; self.fade_in = Animation::new(false) .easing(animation::Easing::EaseIn) - .slow(); + .quick(); } pub fn show(&mut self, allocation: image::Allocation, now: Instant) { @@ -547,3 +552,26 @@ impl Viewer { )) } } + +fn to_rgba(bytes: Bytes) -> Task { + use tokio::task; + + Task::future(async move { + task::spawn_blocking(move || { + match ::image::load_from_memory(bytes.as_slice()) { + Ok(image) => { + let rgba = image.to_rgba8(); + + image::Handle::from_rgba( + rgba.width(), + rgba.height(), + rgba.into_raw(), + ) + } + _ => image::Handle::from_bytes(bytes), + } + }) + .await + .unwrap() + }) +}