update image loading

This commit is contained in:
Daniel Bulant 2024-11-17 22:44:56 +01:00
parent d08dd6dcfc
commit f3760ec4e8
No known key found for this signature in database
2 changed files with 65 additions and 33 deletions

View file

@ -1,11 +1,15 @@
use std::sync::Arc;
use std::sync::{Arc, LazyLock};
use cushy::{kludgine::{AnyTexture, LazyTexture}, value::{CallbackDisconnected, CallbackHandle, Destination, Dynamic, Source, Value}, widgets::Image};
use cushy::{
kludgine::{AnyTexture, LazyTexture},
value::{CallbackDisconnected, CallbackHandle, Destination, Dynamic, Source, Value},
widgets::Image,
};
use futures_util::lock::Mutex;
use http_cache_reqwest::{CACacheManager, Cache, CacheMode, HttpCache, HttpCacheOptions};
use image::imageops::FilterType;
use reqwest::Client;
use reqwest_middleware::ClientBuilder;
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use tokio::task::JoinHandle;
use crate::rt::tokio_runtime;
@ -16,13 +20,24 @@ pub trait ImageExt {
fn load_url(&mut self, url: Dynamic<Option<String>>) -> CallbackHandle;
fn with_url(mut self, url: Dynamic<Option<String>>) -> Self
where Self: Sized
where
Self: Sized,
{
self.load_url(url).persist();
self
}
}
static CLIENT: LazyLock<ClientWithMiddleware> = LazyLock::new(|| {
ClientBuilder::new(Client::new())
.with(Cache(HttpCache {
mode: CacheMode::Default,
manager: CACacheManager::default(),
options: HttpCacheOptions::default(),
}))
.build()
});
impl ImageExt for Image {
fn new_empty() -> Self {
Image::new(Dynamic::new(get_empty_texture()))
@ -31,15 +46,6 @@ impl ImageExt for Image {
/// Makes the image connected to a URL
/// Calling this multiple times on a single image may cause memory leaks
fn load_url(&mut self, url: Dynamic<Option<String>>) -> CallbackHandle {
let client = ClientBuilder::new(Client::new())
.with(Cache(HttpCache {
mode: CacheMode::Default,
manager: CACacheManager::default(),
options: HttpCacheOptions::default(),
}))
.build();
// let texture = Dynamic::new(get_empty_texture());
match &mut self.contents {
Value::Constant(_) => self.contents = Value::Dynamic(Dynamic::new(get_empty_texture())),
@ -47,9 +53,9 @@ impl ImageExt for Image {
}
let texture = match &self.contents {
Value::Dynamic(dynamic) => dynamic,
_ => unreachable!()
_ => unreachable!(),
};
let prev_request_join = Arc::new(Mutex::new(None::<JoinHandle<()>>));
url.for_each_try({
let texture = texture.clone();
@ -58,16 +64,18 @@ impl ImageExt for Image {
if texture_count <= 1 {
return Err(CallbackDisconnected);
}
println!("loading url {:?}", url);
let guard = tokio_runtime().enter();
let url = url.clone();
let prev_request_join = prev_request_join.clone();
let texture = texture.clone();
let client = client.clone();
let client = CLIENT.clone();
tokio::spawn(async move {
let mut prev_request_join = prev_request_join.lock().await;
if let Some(prev_request_join) = prev_request_join.take() {
prev_request_join.abort();
}
println!("loading url {:?}", url);
if let Some(url) = url {
let texture = texture.clone();
let client = client.clone();
@ -75,8 +83,11 @@ impl ImageExt for Image {
let response = client.get(url).send().await.unwrap();
let bytes = response.bytes().await.unwrap();
let image = image::load_from_memory(&bytes).unwrap();
let image = image.resize(128, 128, FilterType::Lanczos3);
let image_texture = LazyTexture::from_image(image, cushy::kludgine::wgpu::FilterMode::Linear);
// let image = image.resize(128, 128, FilterType::Lanczos3);
let image_texture = LazyTexture::from_image(
image,
cushy::kludgine::wgpu::FilterMode::Linear,
);
let image_texture = AnyTexture::Lazy(image_texture);
texture.set(image_texture);
}));
@ -91,14 +102,9 @@ impl ImageExt for Image {
}
}
fn get_empty_texture() -> AnyTexture {
AnyTexture::Lazy(
LazyTexture::from_image(
image::DynamicImage::ImageRgba8(
image::ImageBuffer::new(1, 1)
),
cushy::kludgine::wgpu::FilterMode::Linear
)
)
}
AnyTexture::Lazy(LazyTexture::from_image(
image::DynamicImage::ImageRgba8(image::ImageBuffer::new(1, 1)),
cushy::kludgine::wgpu::FilterMode::Linear,
))
}

View file

@ -8,7 +8,10 @@ use cushy::{
styles::{Dimension, DimensionRange},
value::{Destination, Dynamic, Source},
widget::{MakeWidget, WidgetInstance},
widgets::{label::LabelOverflow, Image, Label, Space, VirtualList},
widgets::{
label::{Displayable, LabelOverflow},
Image, Label, Space, VirtualList,
},
};
use itertools::Itertools;
use rspotify::model::SavedTrack;
@ -35,7 +38,6 @@ fn get_or_create_track_image(
idx: usize,
create: impl FnOnce() -> WidgetInstance,
) -> WidgetInstance {
println!("Getting image");
let mut locked = track_images.lock().unwrap();
if let Some(image) = locked.get(&idx) {
image.clone()
@ -147,19 +149,43 @@ impl LikedSongsPage {
.and(track.map_each(|track| {
track
.as_ref()
.map(|track| track.track.album.name.clone().make_widget())
.map(|track| {
track
.track
.album
.name
.clone()
.into_label()
.overflow(LabelOverflow::Clip)
.make_widget()
})
.unwrap_or(Space::primary().make_widget())
}))
.and(track.map_each(|track| {
track
.as_ref()
.map(|track| track.added_at.to_string().make_widget())
.map(|track| {
track
.added_at
.to_string()
.into_label()
.overflow(LabelOverflow::Clip)
.make_widget()
})
.unwrap_or(Space::primary().make_widget())
}))
.and(track.map_each(|track| {
track
.as_ref()
.map(|track| track.track.duration.to_string().make_widget())
.map(|track| {
track
.track
.duration
.to_string()
.into_label()
.overflow(LabelOverflow::Clip)
.make_widget()
})
.unwrap_or(Space::primary().make_widget())
}))
.into_columns()