Decode on the fly in gallery example

Use `release` mode. Image decoding is terribly slow
in `debug` mode!
This commit is contained in:
Héctor Ramón Jiménez 2025-10-24 20:06:26 +02:00
parent 1b51364154
commit 6fa54f7f6b
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
3 changed files with 44 additions and 43 deletions

View file

@ -1,4 +1,3 @@
use bytes::Bytes;
use serde::Deserialize;
use sipper::{Straw, sipper};
use tokio::task;
@ -54,14 +53,14 @@ impl Image {
rgba: Rgba {
width,
height,
pixels: Bytes::from(pixels),
pixels: Bytes(pixels.into()),
},
})
})
.await?
}
pub fn download(self, size: Size) -> impl Straw<Rgba, Blurhash, Error> {
pub fn download(self, size: Size) -> impl Straw<Bytes, Blurhash, Error> {
sipper(async move |mut sender| {
let client = reqwest::Client::new();
@ -97,21 +96,7 @@ impl Image {
.bytes()
.await?;
let image = task::spawn_blocking(move || {
Ok::<_, Error>(
image::ImageReader::new(io::Cursor::new(bytes))
.with_guessed_format()?
.decode()?
.to_rgba8(),
)
})
.await??;
Ok(Rgba {
width: image.width(),
height: image.height(),
pixels: Bytes::from(image.into_raw()),
})
Ok(Bytes(bytes))
})
}
}
@ -142,6 +127,23 @@ impl fmt::Debug for Rgba {
}
}
#[derive(Clone)]
pub struct Bytes(bytes::Bytes);
impl From<Bytes> for bytes::Bytes {
fn from(value: Bytes) -> Self {
value.0
}
}
impl fmt::Debug for Bytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Compressed")
.field("bytes", &self.0.len())
.finish()
}
}
#[derive(Debug, Clone, Copy)]
pub enum Size {
Original,

View file

@ -4,7 +4,7 @@
//! some smooth animations.
mod civitai;
use crate::civitai::{Error, Id, Image, Rgba, Size};
use crate::civitai::{Bytes, Error, Id, Image, Rgba, Size};
use iced::animation;
use iced::time::{Instant, milliseconds};
@ -46,8 +46,8 @@ enum Message {
ImagesListed(Result<Vec<Image>, Error>),
ImagePoppedIn(Id),
ImagePoppedOut(Id),
ImageDownloaded(Result<Rgba, Error>),
ThumbnailDownloaded(Id, Result<Rgba, Error>),
ImageDownloaded(Result<Bytes, Error>),
ThumbnailDownloaded(Id, Result<Bytes, Error>),
ThumbnailHovered(Id, bool),
BlurhashDecoded(Id, civitai::Blurhash),
Open(Id),
@ -110,6 +110,13 @@ impl Gallery {
let _ = self.visible.insert(id);
if self.downloaded.contains(&id) {
if let Some(Preview::Ready { thumbnail, .. }) =
self.previews.get_mut(&id)
{
thumbnail.fade_in =
Animation::new(false).slow().go(true, now);
}
return Task::none();
}
@ -129,17 +136,17 @@ impl Gallery {
Task::none()
}
Message::ImageDownloaded(Ok(rgba)) => {
self.viewer.show(rgba, self.now);
Message::ImageDownloaded(Ok(bytes)) => {
self.viewer.show(bytes, self.now);
Task::none()
}
Message::ThumbnailDownloaded(id, Ok(rgba)) => {
Message::ThumbnailDownloaded(id, Ok(bytes)) => {
let thumbnail = if let Some(preview) = self.previews.remove(&id)
{
preview.load(rgba, self.now)
preview.load(bytes, self.now)
} else {
Preview::ready(rgba, self.now)
Preview::ready(bytes, self.now)
};
let _ = self.previews.insert(id, thumbnail);
@ -339,21 +346,21 @@ impl Preview {
}
}
fn ready(rgba: Rgba, now: Instant) -> Self {
fn ready(bytes: Bytes, now: Instant) -> Self {
Self::Ready {
blurhash: None,
thumbnail: Thumbnail::new(rgba, now),
thumbnail: Thumbnail::new(bytes, now),
}
}
fn load(self, rgba: Rgba, now: Instant) -> Self {
fn load(self, bytes: Bytes, now: Instant) -> Self {
let Self::Loading { blurhash } = self else {
return self;
};
Self::Ready {
blurhash: Some(blurhash),
thumbnail: Thumbnail::new(rgba, now),
thumbnail: Thumbnail::new(bytes, now),
}
}
@ -387,13 +394,9 @@ impl Preview {
}
impl Thumbnail {
pub fn new(rgba: Rgba, now: Instant) -> Self {
pub fn new(bytes: Bytes, now: Instant) -> Self {
Self {
handle: image::Handle::from_rgba(
rgba.width,
rgba.height,
rgba.pixels,
),
handle: image::Handle::from_bytes(bytes),
fade_in: Animation::new(false).slow().go(true, now),
zoom: Animation::new(false)
.quick()
@ -426,12 +429,8 @@ impl Viewer {
self.background_fade_in.go_mut(true, now);
}
fn show(&mut self, rgba: Rgba, now: Instant) {
self.image = Some(image::Handle::from_rgba(
rgba.width,
rgba.height,
rgba.pixels,
));
fn show(&mut self, bytes: Bytes, now: Instant) {
self.image = Some(image::Handle::from_bytes(bytes));
self.background_fade_in.go_mut(true, now);
self.image_fade_in.go_mut(true, now);
}

View file

@ -248,7 +248,7 @@ fn load_image<'a>(
} else if let core::image::Handle::Rgba { .. } = handle {
// Load RGBA handles synchronously, since it's very cheap
cache.insert(handle, Memory::load(handle));
} else {
} else if !pending.contains(&handle.id()) {
let _ = jobs.send(Job::Load(handle.clone()));
let _ = pending.insert(handle.id());
}