Save thumbnails as handles to improve performance

This commit is contained in:
Jeremy Soller 2024-10-10 09:07:02 -06:00
parent a1c2828591
commit 3ea075b2c6
No known key found for this signature in database
GPG key ID: D02FD439211AF56F

View file

@ -42,7 +42,6 @@ use mime_guess::{mime, Mime};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
borrow::Cow,
cell::{Cell, RefCell}, cell::{Cell, RefCell},
cmp::Ordering, cmp::Ordering,
collections::HashMap, collections::HashMap,
@ -1053,8 +1052,8 @@ impl ItemMetadata {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum ItemThumbnail { pub enum ItemThumbnail {
NotImage, NotImage,
Rgba(image::RgbaImage, Option<(u32, u32)>), Image(widget::image::Handle, Option<(u32, u32)>),
Svg(Cow<'static, [u8]>), Svg(widget::svg::Handle),
} }
impl ItemThumbnail { impl ItemThumbnail {
@ -1065,7 +1064,7 @@ impl ItemThumbnail {
match fs::read(&path) { match fs::read(&path) {
Ok(data) => { Ok(data) => {
//TODO: validate SVG data //TODO: validate SVG data
return ItemThumbnail::Svg(data.into()); return ItemThumbnail::Svg(widget::svg::Handle::from_memory(data));
} }
Err(err) => { Err(err) => {
log::warn!("failed to read {:?}: {}", path, err); log::warn!("failed to read {:?}: {}", path, err);
@ -1076,9 +1075,14 @@ impl ItemThumbnail {
match image::io::Reader::open(&path).and_then(|img| img.with_guessed_format()) { match image::io::Reader::open(&path).and_then(|img| img.with_guessed_format()) {
Ok(reader) => match reader.decode() { Ok(reader) => match reader.decode() {
Ok(image) => { Ok(image) => {
let thumbnail = image.thumbnail(thumbnail_size, thumbnail_size); let thumbnail =
return ItemThumbnail::Rgba( image.thumbnail(thumbnail_size, thumbnail_size).into_rgba8();
thumbnail.to_rgba8(), return ItemThumbnail::Image(
widget::image::Handle::from_pixels(
thumbnail.width(),
thumbnail.height(),
thumbnail.into_raw(),
),
Some((image.width(), image.height())), Some((image.width(), image.height())),
); );
} }
@ -1121,9 +1125,16 @@ impl ItemThumbnail {
match image::io::Reader::open(file.path()) match image::io::Reader::open(file.path())
.and_then(|img| img.with_guessed_format()) .and_then(|img| img.with_guessed_format())
{ {
Ok(reader) => match reader.decode() { Ok(reader) => match reader.decode().map(|image| image.into_rgba8()) {
Ok(image) => { Ok(image) => {
return ItemThumbnail::Rgba(image.to_rgba8(), None); return ItemThumbnail::Image(
widget::image::Handle::from_pixels(
image.width(),
image.height(),
image.into_raw(),
),
None,
);
} }
Err(err) => { Err(err) => {
log::warn!("failed to decode {:?}: {}", path, err); log::warn!("failed to decode {:?}: {}", path, err);
@ -1193,22 +1204,15 @@ impl Item {
.unwrap_or(&ItemThumbnail::NotImage) .unwrap_or(&ItemThumbnail::NotImage)
{ {
ItemThumbnail::NotImage => icon, ItemThumbnail::NotImage => icon,
ItemThumbnail::Rgba(rgba, _) => { ItemThumbnail::Image(handle, _) => {
if let Some(path) = self.path_opt() { if let Some(path) = self.path_opt() {
if self.is_image() { if self.is_image() {
return widget::image(widget::image::Handle::from_path(path)).into(); return widget::image(widget::image::Handle::from_path(path)).into();
} }
} }
widget::image(widget::image::Handle::from_pixels( widget::image(handle.clone()).into()
rgba.width(),
rgba.height(),
rgba.as_raw().clone(),
))
.into()
}
ItemThumbnail::Svg(data) => {
widget::svg(widget::svg::Handle::from_memory(data.clone())).into()
} }
ItemThumbnail::Svg(handle) => widget::svg(handle.clone()).into(),
} }
} }
@ -1321,7 +1325,7 @@ impl Item {
.as_ref() .as_ref()
.unwrap_or(&ItemThumbnail::NotImage) .unwrap_or(&ItemThumbnail::NotImage)
{ {
ItemThumbnail::Rgba(_, Some((width, height))) => { ItemThumbnail::Image(_, Some((width, height))) => {
details = details.push(widget::text(format!("{}x{}", width, height))); details = details.push(widget::text(format!("{}x{}", width, height)));
} }
_ => {} _ => {}
@ -2558,25 +2562,21 @@ impl Tab {
let location = Location::Path(path); let location = Location::Path(path);
for item in items.iter_mut() { for item in items.iter_mut() {
if item.location_opt.as_ref() == Some(&location) { if item.location_opt.as_ref() == Some(&location) {
//TODO: pass handles already generated to avoid blocking main thread? let handle_opt = match &thumbnail {
match &thumbnail { ItemThumbnail::NotImage => None,
ItemThumbnail::NotImage => {} ItemThumbnail::Image(handle, _) => Some(widget::icon::Handle {
ItemThumbnail::Rgba(rgba, _) => { symbolic: false,
let handle = widget::icon::from_raster_pixels( data: widget::icon::Data::Image(handle.clone()),
rgba.width(), }),
rgba.height(), ItemThumbnail::Svg(handle) => Some(widget::icon::Handle {
rgba.as_raw().clone(), symbolic: false,
); data: widget::icon::Data::Svg(handle.clone()),
item.icon_handle_grid = handle.clone(); }),
item.icon_handle_list = handle.clone(); };
item.icon_handle_list_condensed = handle; if let Some(handle) = handle_opt {
} item.icon_handle_grid = handle.clone();
ItemThumbnail::Svg(data) => { item.icon_handle_list = handle.clone();
let handle = widget::icon::from_svg_bytes(data.clone()); item.icon_handle_list_condensed = handle;
item.icon_handle_grid = handle.clone();
item.icon_handle_list = handle.clone();
item.icon_handle_list_condensed = handle.clone();
}
} }
item.thumbnail_opt = Some(thumbnail); item.thumbnail_opt = Some(thumbnail);
break; break;
@ -2897,7 +2897,7 @@ impl Tab {
.unwrap_or(&ItemThumbnail::NotImage) .unwrap_or(&ItemThumbnail::NotImage)
{ {
ItemThumbnail::NotImage => {} ItemThumbnail::NotImage => {}
ItemThumbnail::Rgba(rgba, _) => { ItemThumbnail::Image(handle, _) => {
if let Some(path) = item.path_opt() { if let Some(path) = item.path_opt() {
image_opt = Some( image_opt = Some(
//TODO: use widget::image::viewer, when its zoom can be reset //TODO: use widget::image::viewer, when its zoom can be reset
@ -2909,20 +2909,16 @@ impl Tab {
} else { } else {
image_opt = Some( image_opt = Some(
//TODO: use widget::image::viewer, when its zoom can be reset //TODO: use widget::image::viewer, when its zoom can be reset
widget::image(widget::image::Handle::from_pixels( widget::image(handle.clone())
rgba.width(), .width(Length::Fill)
rgba.height(), .height(Length::Fill)
rgba.as_raw().clone(), .into(),
))
.width(Length::Fill)
.height(Length::Fill)
.into(),
); );
} }
} }
ItemThumbnail::Svg(data) => { ItemThumbnail::Svg(handle) => {
image_opt = Some( image_opt = Some(
widget::svg(widget::svg::Handle::from_memory(data.clone())) widget::svg(handle.clone())
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill) .height(Length::Fill)
.into(), .into(),