Introduce with helper and use sipper in gallery example

This commit is contained in:
Héctor Ramón Jiménez 2025-02-11 03:39:42 +01:00
parent 9f21eae152
commit 0c528be2ea
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
8 changed files with 143 additions and 105 deletions

View file

@ -17,6 +17,7 @@ serde.features = ["derive"]
bytes.workspace = true
image.workspace = true
sipper.workspace = true
tokio.workspace = true
blurhash = "0.2.3"

View file

@ -1,5 +1,6 @@
use bytes::Bytes;
use serde::Deserialize;
use sipper::{sipper, Straw};
use tokio::task;
use std::fmt;
@ -45,58 +46,72 @@ impl Image {
self,
width: u32,
height: u32,
) -> Result<Rgba, Error> {
) -> Result<Blurhash, Error> {
task::spawn_blocking(move || {
let pixels = blurhash::decode(&self.hash, width, height, 1.0)?;
Ok::<_, Error>(Rgba {
width,
height,
pixels: Bytes::from(pixels),
Ok::<_, Error>(Blurhash {
rgba: Rgba {
width,
height,
pixels: Bytes::from(pixels),
},
})
})
.await?
}
pub async fn download(self, size: Size) -> Result<Rgba, Error> {
let client = reqwest::Client::new();
pub fn download(self, size: Size) -> impl Straw<Rgba, Blurhash, Error> {
sipper(move |mut sender| async move {
let client = reqwest::Client::new();
let bytes = client
.get(match size {
Size::Original => self.url,
Size::Thumbnail { width } => self
.url
.split("/")
.map(|part| {
if part.starts_with("width=") {
format!("width={}", width * 2) // High DPI
} else {
part.to_owned()
}
})
.collect::<Vec<_>>()
.join("/"),
if let Size::Thumbnail { width, height } = size {
let image = self.clone();
drop(task::spawn(async move {
if let Ok(blurhash) = image.blurhash(width, height).await {
sender.send(blurhash).await;
}
}));
}
let bytes = client
.get(match size {
Size::Original => self.url,
Size::Thumbnail { width, .. } => self
.url
.split("/")
.map(|part| {
if part.starts_with("width=") {
format!("width={}", width * 2) // High DPI
} else {
part.to_owned()
}
})
.collect::<Vec<_>>()
.join("/"),
})
.send()
.await?
.error_for_status()?
.bytes()
.await?;
let image = task::spawn_blocking(move || {
Ok::<_, Error>(
image::ImageReader::new(io::Cursor::new(bytes))
.with_guessed_format()?
.decode()?
.to_rgba8(),
)
})
.send()
.await?
.error_for_status()?
.bytes()
.await?;
.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(Rgba {
width: image.width(),
height: image.height(),
pixels: Bytes::from(image.into_raw()),
})
})
}
}
@ -106,6 +121,11 @@ impl Image {
)]
pub struct Id(u32);
#[derive(Debug, Clone)]
pub struct Blurhash {
pub rgba: Rgba,
}
#[derive(Clone)]
pub struct Rgba {
pub width: u32,
@ -125,7 +145,7 @@ impl fmt::Debug for Rgba {
#[derive(Debug, Clone, Copy)]
pub enum Size {
Original,
Thumbnail { width: u32 },
Thumbnail { width: u32, height: u32 },
}
#[derive(Debug, Clone)]

View file

@ -14,7 +14,8 @@ use iced::widget::{
};
use iced::window;
use iced::{
color, Animation, ContentFit, Element, Fill, Subscription, Task, Theme,
color, with, Animation, ContentFit, Element, Fill, Subscription, Task,
Theme,
};
use std::collections::HashMap;
@ -40,7 +41,7 @@ enum Message {
ImageDownloaded(Result<Rgba, Error>),
ThumbnailDownloaded(Id, Result<Rgba, Error>),
ThumbnailHovered(Id, bool),
BlurhashDecoded(Id, Result<Rgba, Error>),
BlurhashDecoded(Id, civitai::Blurhash),
Open(Id),
Close,
Animate(Instant),
@ -94,16 +95,14 @@ impl Gallery {
return Task::none();
};
Task::batch([
Task::future(
image.clone().blurhash(Preview::WIDTH, Preview::HEIGHT),
)
.map_with(id, Message::BlurhashDecoded),
Task::future(image.download(Size::Thumbnail {
Task::sip(
image.download(Size::Thumbnail {
width: Preview::WIDTH,
}))
.map_with(id, Message::ThumbnailDownloaded),
])
height: Preview::HEIGHT,
}),
with(Message::BlurhashDecoded, id),
with(Message::ThumbnailDownloaded, id),
)
}
Message::ImageDownloaded(Ok(rgba)) => {
self.viewer.show(rgba);
@ -129,9 +128,11 @@ impl Gallery {
Task::none()
}
Message::BlurhashDecoded(id, Ok(rgba)) => {
Message::BlurhashDecoded(id, blurhash) => {
if !self.previews.contains_key(&id) {
let _ = self.previews.insert(id, Preview::loading(rgba));
let _ = self
.previews
.insert(id, Preview::loading(blurhash.rgba));
}
Task::none()
@ -165,8 +166,7 @@ impl Gallery {
}
Message::ImagesListed(Err(error))
| Message::ImageDownloaded(Err(error))
| Message::ThumbnailDownloaded(_, Err(error))
| Message::BlurhashDecoded(_, Err(error)) => {
| Message::ThumbnailDownloaded(_, Err(error)) => {
dbg!(error);
Task::none()