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 serde::Deserialize;
|
||||||
use sipper::{Straw, sipper};
|
use sipper::{Straw, sipper};
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
|
@ -54,14 +53,14 @@ impl Image {
|
||||||
rgba: Rgba {
|
rgba: Rgba {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
pixels: Bytes::from(pixels),
|
pixels: Bytes(pixels.into()),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.await?
|
.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| {
|
sipper(async move |mut sender| {
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
|
@ -97,21 +96,7 @@ impl Image {
|
||||||
.bytes()
|
.bytes()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let image = task::spawn_blocking(move || {
|
Ok(Bytes(bytes))
|
||||||
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()),
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Size {
|
pub enum Size {
|
||||||
Original,
|
Original,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
//! some smooth animations.
|
//! some smooth animations.
|
||||||
mod civitai;
|
mod civitai;
|
||||||
|
|
||||||
use crate::civitai::{Error, Id, Image, Rgba, Size};
|
use crate::civitai::{Bytes, Error, Id, Image, Rgba, Size};
|
||||||
|
|
||||||
use iced::animation;
|
use iced::animation;
|
||||||
use iced::time::{Instant, milliseconds};
|
use iced::time::{Instant, milliseconds};
|
||||||
|
|
@ -46,8 +46,8 @@ enum Message {
|
||||||
ImagesListed(Result<Vec<Image>, Error>),
|
ImagesListed(Result<Vec<Image>, Error>),
|
||||||
ImagePoppedIn(Id),
|
ImagePoppedIn(Id),
|
||||||
ImagePoppedOut(Id),
|
ImagePoppedOut(Id),
|
||||||
ImageDownloaded(Result<Rgba, Error>),
|
ImageDownloaded(Result<Bytes, Error>),
|
||||||
ThumbnailDownloaded(Id, Result<Rgba, Error>),
|
ThumbnailDownloaded(Id, Result<Bytes, Error>),
|
||||||
ThumbnailHovered(Id, bool),
|
ThumbnailHovered(Id, bool),
|
||||||
BlurhashDecoded(Id, civitai::Blurhash),
|
BlurhashDecoded(Id, civitai::Blurhash),
|
||||||
Open(Id),
|
Open(Id),
|
||||||
|
|
@ -110,6 +110,13 @@ impl Gallery {
|
||||||
let _ = self.visible.insert(id);
|
let _ = self.visible.insert(id);
|
||||||
|
|
||||||
if self.downloaded.contains(&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();
|
return Task::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,17 +136,17 @@ impl Gallery {
|
||||||
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::ImageDownloaded(Ok(rgba)) => {
|
Message::ImageDownloaded(Ok(bytes)) => {
|
||||||
self.viewer.show(rgba, self.now);
|
self.viewer.show(bytes, self.now);
|
||||||
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::ThumbnailDownloaded(id, Ok(rgba)) => {
|
Message::ThumbnailDownloaded(id, Ok(bytes)) => {
|
||||||
let thumbnail = if let Some(preview) = self.previews.remove(&id)
|
let thumbnail = if let Some(preview) = self.previews.remove(&id)
|
||||||
{
|
{
|
||||||
preview.load(rgba, self.now)
|
preview.load(bytes, self.now)
|
||||||
} else {
|
} else {
|
||||||
Preview::ready(rgba, self.now)
|
Preview::ready(bytes, self.now)
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = self.previews.insert(id, thumbnail);
|
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 {
|
Self::Ready {
|
||||||
blurhash: None,
|
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 {
|
let Self::Loading { blurhash } = self else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::Ready {
|
Self::Ready {
|
||||||
blurhash: Some(blurhash),
|
blurhash: Some(blurhash),
|
||||||
thumbnail: Thumbnail::new(rgba, now),
|
thumbnail: Thumbnail::new(bytes, now),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -387,13 +394,9 @@ impl Preview {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Thumbnail {
|
impl Thumbnail {
|
||||||
pub fn new(rgba: Rgba, now: Instant) -> Self {
|
pub fn new(bytes: Bytes, now: Instant) -> Self {
|
||||||
Self {
|
Self {
|
||||||
handle: image::Handle::from_rgba(
|
handle: image::Handle::from_bytes(bytes),
|
||||||
rgba.width,
|
|
||||||
rgba.height,
|
|
||||||
rgba.pixels,
|
|
||||||
),
|
|
||||||
fade_in: Animation::new(false).slow().go(true, now),
|
fade_in: Animation::new(false).slow().go(true, now),
|
||||||
zoom: Animation::new(false)
|
zoom: Animation::new(false)
|
||||||
.quick()
|
.quick()
|
||||||
|
|
@ -426,12 +429,8 @@ impl Viewer {
|
||||||
self.background_fade_in.go_mut(true, now);
|
self.background_fade_in.go_mut(true, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, rgba: Rgba, now: Instant) {
|
fn show(&mut self, bytes: Bytes, now: Instant) {
|
||||||
self.image = Some(image::Handle::from_rgba(
|
self.image = Some(image::Handle::from_bytes(bytes));
|
||||||
rgba.width,
|
|
||||||
rgba.height,
|
|
||||||
rgba.pixels,
|
|
||||||
));
|
|
||||||
self.background_fade_in.go_mut(true, now);
|
self.background_fade_in.go_mut(true, now);
|
||||||
self.image_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 {
|
} else if let core::image::Handle::Rgba { .. } = handle {
|
||||||
// Load RGBA handles synchronously, since it's very cheap
|
// Load RGBA handles synchronously, since it's very cheap
|
||||||
cache.insert(handle, Memory::load(handle));
|
cache.insert(handle, Memory::load(handle));
|
||||||
} else {
|
} else if !pending.contains(&handle.id()) {
|
||||||
let _ = jobs.send(Job::Load(handle.clone()));
|
let _ = jobs.send(Job::Load(handle.clone()));
|
||||||
let _ = pending.insert(handle.id());
|
let _ = pending.insert(handle.id());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue