Decode on the fly in gallery example
Use `release` mode. Image decoding is terribly slow in `debug` mode!
This commit is contained in:
parent
1b51364154
commit
6fa54f7f6b
3 changed files with 44 additions and 43 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue