thumbnail: Support jxl and plumbing for future formats. (#1058)
* add plumbing for additional thumbnailers * remove bad logging and fmt * fix bad logging message * add decoding ram limits * add configuration for thumbs * cleanups * fix rebase fails
This commit is contained in:
parent
edca40058b
commit
293350092c
6 changed files with 338 additions and 40 deletions
206
Cargo.lock
generated
206
Cargo.lock
generated
|
|
@ -170,6 +170,21 @@ dependencies = [
|
||||||
"equator",
|
"equator",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "alloc-no-stdlib"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "alloc-stdlib"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
|
||||||
|
dependencies = [
|
||||||
|
"alloc-no-stdlib",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "almost"
|
name = "almost"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
@ -796,6 +811,16 @@ dependencies = [
|
||||||
"piper",
|
"piper",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "brotli-decompressor"
|
||||||
|
version = "5.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03"
|
||||||
|
dependencies = [
|
||||||
|
"alloc-no-stdlib",
|
||||||
|
"alloc-stdlib",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
|
|
@ -1504,6 +1529,7 @@ dependencies = [
|
||||||
"ignore",
|
"ignore",
|
||||||
"image",
|
"image",
|
||||||
"io-uring",
|
"io-uring",
|
||||||
|
"jxl-oxide",
|
||||||
"libc",
|
"libc",
|
||||||
"libcosmic",
|
"libcosmic",
|
||||||
"log",
|
"log",
|
||||||
|
|
@ -4173,6 +4199,186 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-bitstream"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eda699770a7f4ea38f8eb21d91b545eb6448be28e540acc7ce84498bcead4903"
|
||||||
|
dependencies = [
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-coding"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6748ba8af69b87c68f8dcdf992de959c207962689bc28ddb7906abf4a0b786c9"
|
||||||
|
dependencies = [
|
||||||
|
"jxl-bitstream",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-color"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f316b1358c1711755b3ee8e8cb5c4a1dad12e796233088a7a513440782de80b2"
|
||||||
|
dependencies = [
|
||||||
|
"jxl-bitstream",
|
||||||
|
"jxl-coding",
|
||||||
|
"jxl-grid",
|
||||||
|
"jxl-image",
|
||||||
|
"jxl-oxide-common",
|
||||||
|
"jxl-threadpool",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-frame"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f30587a9687223a602a408555db47803c907ea47700e1f28eb14cdb3bf1527a9"
|
||||||
|
dependencies = [
|
||||||
|
"jxl-bitstream",
|
||||||
|
"jxl-coding",
|
||||||
|
"jxl-grid",
|
||||||
|
"jxl-image",
|
||||||
|
"jxl-modular",
|
||||||
|
"jxl-oxide-common",
|
||||||
|
"jxl-threadpool",
|
||||||
|
"jxl-vardct",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-grid"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "335e4371396c5729ba80a42798746d198897d3b854ba4f3684efac5f4025d84f"
|
||||||
|
dependencies = [
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-image"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c5f752d62577c702a94dbbce4045caf08cb58639e8a4d56464b40ecf33ffe565"
|
||||||
|
dependencies = [
|
||||||
|
"jxl-bitstream",
|
||||||
|
"jxl-grid",
|
||||||
|
"jxl-oxide-common",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-jbr"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d91ba39b083a82788a17717edbcc4b08160b51fdffc9fec640deba9e8268da1a"
|
||||||
|
dependencies = [
|
||||||
|
"brotli-decompressor",
|
||||||
|
"jxl-bitstream",
|
||||||
|
"jxl-frame",
|
||||||
|
"jxl-grid",
|
||||||
|
"jxl-image",
|
||||||
|
"jxl-modular",
|
||||||
|
"jxl-oxide-common",
|
||||||
|
"jxl-threadpool",
|
||||||
|
"jxl-vardct",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-modular"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f526ad8af8daea0d1cccce945f18c241f95b391d34443be018de2efbf28b44e"
|
||||||
|
dependencies = [
|
||||||
|
"jxl-bitstream",
|
||||||
|
"jxl-coding",
|
||||||
|
"jxl-grid",
|
||||||
|
"jxl-oxide-common",
|
||||||
|
"jxl-threadpool",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-oxide"
|
||||||
|
version = "0.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e45ccb25d698cdcad3a5573a7181835842711fd951c98fe38986e3cb721e775"
|
||||||
|
dependencies = [
|
||||||
|
"brotli-decompressor",
|
||||||
|
"bytemuck",
|
||||||
|
"image",
|
||||||
|
"jxl-bitstream",
|
||||||
|
"jxl-color",
|
||||||
|
"jxl-frame",
|
||||||
|
"jxl-grid",
|
||||||
|
"jxl-image",
|
||||||
|
"jxl-jbr",
|
||||||
|
"jxl-oxide-common",
|
||||||
|
"jxl-render",
|
||||||
|
"jxl-threadpool",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-oxide-common"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b62394c5021b3a9e7e0dbb2d639d555d019090c9946c39f6d3b09d390db4157b"
|
||||||
|
dependencies = [
|
||||||
|
"jxl-bitstream",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-render"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3f3fece78b2104450bd6d1bdbc48e3b6ef7442ef276be2a08e35b229eeff1a4"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"jxl-bitstream",
|
||||||
|
"jxl-coding",
|
||||||
|
"jxl-color",
|
||||||
|
"jxl-frame",
|
||||||
|
"jxl-grid",
|
||||||
|
"jxl-image",
|
||||||
|
"jxl-modular",
|
||||||
|
"jxl-oxide-common",
|
||||||
|
"jxl-threadpool",
|
||||||
|
"jxl-vardct",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-threadpool"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25f15eb830aa77a7f21148d72e153562a26bfe570139bd4922eab1908dd499d3"
|
||||||
|
dependencies = [
|
||||||
|
"rayon",
|
||||||
|
"rayon-core",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jxl-vardct"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d48ad406543de5d6cd50aaaa8b87534f82991d684d848b3190228e8fa690fff"
|
||||||
|
dependencies = [
|
||||||
|
"jxl-bitstream",
|
||||||
|
"jxl-coding",
|
||||||
|
"jxl-grid",
|
||||||
|
"jxl-modular",
|
||||||
|
"jxl-oxide-common",
|
||||||
|
"jxl-threadpool",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kamadak-exif"
|
name = "kamadak-exif"
|
||||||
version = "0.5.5"
|
version = "0.5.5"
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ zip = "2.2.2"
|
||||||
uzers = "0.12.1"
|
uzers = "0.12.1"
|
||||||
md-5 = "0.10.6"
|
md-5 = "0.10.6"
|
||||||
png = "0.17.16"
|
png = "0.17.16"
|
||||||
|
jxl-oxide = { version = "0.12.2", features = ["image"] }
|
||||||
|
|
||||||
# Completion-based IO runtime to enable io_uring / IOCP file IO support.
|
# Completion-based IO runtime to enable io_uring / IOCP file IO support.
|
||||||
[dependencies.compio]
|
[dependencies.compio]
|
||||||
|
|
|
||||||
|
|
@ -1002,6 +1002,7 @@ impl App {
|
||||||
let mut tab = Tab::new(
|
let mut tab = Tab::new(
|
||||||
location.clone(),
|
location.clone(),
|
||||||
self.config.tab,
|
self.config.tab,
|
||||||
|
self.config.thumb_cfg,
|
||||||
Some(&self.state.sort_names),
|
Some(&self.state.sort_names),
|
||||||
window_id,
|
window_id,
|
||||||
);
|
);
|
||||||
|
|
@ -6208,7 +6209,7 @@ pub(crate) mod test_utils {
|
||||||
use tempfile::{tempdir, TempDir};
|
use tempfile::{tempdir, TempDir};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{IconSizes, TabConfig},
|
config::{IconSizes, TabConfig, ThumbCfg},
|
||||||
tab::Item,
|
tab::Item,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -6372,7 +6373,7 @@ pub(crate) mod test_utils {
|
||||||
// New tab with items
|
// New tab with items
|
||||||
let location = Location::Path(path.to_owned());
|
let location = Location::Path(path.to_owned());
|
||||||
let (parent_item_opt, items) = location.scan(IconSizes::default());
|
let (parent_item_opt, items) = location.scan(IconSizes::default());
|
||||||
let mut tab = Tab::new(location, TabConfig::default(), None);
|
let mut tab = Tab::new(location, TabConfig::default(), ThumbCfg::default(), None);
|
||||||
tab.parent_item_opt = parent_item_opt;
|
tab.parent_item_opt = parent_item_opt;
|
||||||
tab.set_items(items);
|
tab.set_items(items);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,7 @@ pub struct Config {
|
||||||
pub app_theme: AppTheme,
|
pub app_theme: AppTheme,
|
||||||
pub dialog: DialogConfig,
|
pub dialog: DialogConfig,
|
||||||
pub desktop: DesktopConfig,
|
pub desktop: DesktopConfig,
|
||||||
|
pub thumb_cfg: ThumbCfg,
|
||||||
pub favorites: Vec<Favorite>,
|
pub favorites: Vec<Favorite>,
|
||||||
pub show_details: bool,
|
pub show_details: bool,
|
||||||
pub tab: TabConfig,
|
pub tab: TabConfig,
|
||||||
|
|
@ -220,6 +221,7 @@ impl Default for Config {
|
||||||
app_theme: AppTheme::System,
|
app_theme: AppTheme::System,
|
||||||
desktop: DesktopConfig::default(),
|
desktop: DesktopConfig::default(),
|
||||||
dialog: DialogConfig::default(),
|
dialog: DialogConfig::default(),
|
||||||
|
thumb_cfg: ThumbCfg::default(),
|
||||||
favorites: vec![
|
favorites: vec![
|
||||||
Favorite::Home,
|
Favorite::Home,
|
||||||
Favorite::Documents,
|
Favorite::Documents,
|
||||||
|
|
@ -289,6 +291,23 @@ impl Default for DialogConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, CosmicConfigEntry, Deserialize, Serialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct ThumbCfg {
|
||||||
|
pub jobs: NonZeroU16,
|
||||||
|
pub max_mem_mb: NonZeroU16,
|
||||||
|
pub max_size_mb: NonZeroU16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ThumbCfg {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
jobs: 4.try_into().unwrap(),
|
||||||
|
max_mem_mb: 2000.try_into().unwrap(),
|
||||||
|
max_size_mb: 64.try_into().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Global and local [`crate::tab::Tab`] config.
|
/// Global and local [`crate::tab::Tab`] config.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ use std::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{Action, ContextPage, Message as AppMessage, PreviewItem, PreviewKind},
|
app::{Action, ContextPage, Message as AppMessage, PreviewItem, PreviewKind},
|
||||||
config::{Config, DialogConfig, Favorite, TimeConfig, TIME_CONFIG_ID},
|
config::{Config, DialogConfig, Favorite, TabConfig, ThumbCfg, TimeConfig, TIME_CONFIG_ID},
|
||||||
fl, home_dir,
|
fl, home_dir,
|
||||||
key_bind::key_binds,
|
key_bind::key_binds,
|
||||||
localize::LANGUAGE_SORTER,
|
localize::LANGUAGE_SORTER,
|
||||||
|
|
@ -945,7 +945,13 @@ impl Application for App {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut tab = Tab::new(location, flags.config.dialog_tab(), None, None);
|
let mut tab = Tab::new(
|
||||||
|
location,
|
||||||
|
flags.config.dialog_tab(),
|
||||||
|
ThumbCfg::default(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
tab.mode = tab::Mode::Dialog(flags.kind.clone());
|
tab.mode = tab::Mode::Dialog(flags.kind.clone());
|
||||||
tab.sort_name = tab::HeadingOptions::Modified;
|
tab.sort_name = tab::HeadingOptions::Modified;
|
||||||
tab.sort_direction = false;
|
tab.sort_direction = false;
|
||||||
|
|
|
||||||
137
src/tab.rs
137
src/tab.rs
|
|
@ -44,6 +44,8 @@ use icu::datetime::{
|
||||||
options::{components, preferences},
|
options::{components, preferences},
|
||||||
DateTimeFormatter, DateTimeFormatterOptions,
|
DateTimeFormatter, DateTimeFormatterOptions,
|
||||||
};
|
};
|
||||||
|
use image::ImageDecoder;
|
||||||
|
use jxl_oxide::integration::JxlDecoder;
|
||||||
use mime_guess::{mime, Mime};
|
use mime_guess::{mime, Mime};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use ordermap::OrderMap;
|
use ordermap::OrderMap;
|
||||||
|
|
@ -71,7 +73,7 @@ use walkdir::WalkDir;
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{Action, PreviewItem, PreviewKind},
|
app::{Action, PreviewItem, PreviewKind},
|
||||||
clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste},
|
clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste},
|
||||||
config::{DesktopConfig, IconSizes, TabConfig, ICON_SCALE_MAX, ICON_SIZE_GRID},
|
config::{DesktopConfig, IconSizes, TabConfig, ThumbCfg, ICON_SCALE_MAX, ICON_SIZE_GRID},
|
||||||
dialog::DialogKind,
|
dialog::DialogKind,
|
||||||
fl,
|
fl,
|
||||||
localize::{LANGUAGE_SORTER, LOCALE},
|
localize::{LANGUAGE_SORTER, LOCALE},
|
||||||
|
|
@ -1721,6 +1723,9 @@ impl ItemThumbnail {
|
||||||
metadata: ItemMetadata,
|
metadata: ItemMetadata,
|
||||||
mime: mime::Mime,
|
mime: mime::Mime,
|
||||||
mut thumbnail_size: u32,
|
mut thumbnail_size: u32,
|
||||||
|
max_mem: u64,
|
||||||
|
jobs: usize,
|
||||||
|
max_size_mb: u64,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let thumbnail_cacher =
|
let thumbnail_cacher =
|
||||||
ThumbnailCacher::new(path, ThumbnailSize::from_pixel_size(thumbnail_size));
|
ThumbnailCacher::new(path, ThumbnailSize::from_pixel_size(thumbnail_size));
|
||||||
|
|
@ -1763,49 +1768,92 @@ impl ItemThumbnail {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut tried_supported_file = false;
|
let mut tried_supported_file = false;
|
||||||
|
if !check_size("image", max_size_mb * 1000 * 1000) {
|
||||||
if !check_size("image", 64 * 1000 * 1000) {
|
|
||||||
return ItemThumbnail::NotImage;
|
return ItemThumbnail::NotImage;
|
||||||
}
|
}
|
||||||
// First try built-in image thumbnailer
|
// First try built-in image thumbnailer
|
||||||
if mime.type_() == mime::IMAGE {
|
if mime.type_() == mime::IMAGE {
|
||||||
|
log::warn!("mime is {}", mime.subtype().as_str());
|
||||||
tried_supported_file = true;
|
tried_supported_file = true;
|
||||||
match image::ImageReader::open(path).and_then(|img| img.with_guessed_format()) {
|
let dyn_img: Option<image::DynamicImage> = match mime.subtype().as_str() {
|
||||||
Ok(reader) => match reader.decode() {
|
"jxl" => match File::open(path) {
|
||||||
Ok(image) => {
|
Ok(file) => match JxlDecoder::new(file) {
|
||||||
if let Ok(cacher) = thumbnail_cacher.as_ref() {
|
Ok(mut decoder) => {
|
||||||
match cacher.update_with_image(image) {
|
let mut limits = image::Limits::default();
|
||||||
Ok(path) => {
|
let max_ram = max_mem * 1000 * 1000 / jobs as u64;
|
||||||
return ItemThumbnail::Image(
|
limits.max_alloc = Some(max_ram);
|
||||||
widget::image::Handle::from_path(path),
|
let _ = decoder.set_limits(limits);
|
||||||
None,
|
match image::DynamicImage::from_decoder(decoder) {
|
||||||
);
|
Ok(img) => Some(img),
|
||||||
}
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::warn!("failed to decode {:?}: {}", path, err);
|
log::warn!("failed to decode jxl {:?}: {}", path, err);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Fallback for when thumbnail cacher isn't available.
|
|
||||||
let thumbnail =
|
|
||||||
image.thumbnail(thumbnail_size, thumbnail_size).into_rgba8();
|
|
||||||
return ItemThumbnail::Image(
|
|
||||||
widget::image::Handle::from_rgba(
|
|
||||||
thumbnail.width(),
|
|
||||||
thumbnail.height(),
|
|
||||||
thumbnail.into_raw(),
|
|
||||||
),
|
|
||||||
Some((image.width(), image.height())),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
Err(err) => {
|
||||||
|
log::warn!("failed to create jxl decoder {:?}: {}", path, err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::warn!("failed to decode {:?}: {}", path, err);
|
log::warn!("failed to open path {:?}: {}", path, err);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => {
|
_ => {
|
||||||
log::warn!("failed to read {:?}: {}", path, err);
|
match image::ImageReader::open(path).and_then(|img| img.with_guessed_format()) {
|
||||||
|
Ok(mut reader) => {
|
||||||
|
let mut limits = image::Limits::default();
|
||||||
|
let max_ram = max_mem * 1000 * 1000 / jobs as u64;
|
||||||
|
limits.max_alloc = Some(max_ram);
|
||||||
|
reader.limits(limits);
|
||||||
|
match reader.decode() {
|
||||||
|
Ok(reader) => Some(reader),
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("failed to decode {:?}: {}", path, err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("failed to read {:?}: {}", path, err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match dyn_img {
|
||||||
|
Some(dyn_img) => {
|
||||||
|
if let Ok(cacher) = thumbnail_cacher.as_ref() {
|
||||||
|
match cacher.update_with_image(dyn_img) {
|
||||||
|
Ok(path) => {
|
||||||
|
return ItemThumbnail::Image(
|
||||||
|
widget::image::Handle::from_path(path),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("cacher failed to decode {:?}: {}", path, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback for when thumbnail cacher isn't available.
|
||||||
|
let thumbnail = dyn_img
|
||||||
|
.thumbnail(thumbnail_size, thumbnail_size)
|
||||||
|
.into_rgba8();
|
||||||
|
return ItemThumbnail::Image(
|
||||||
|
widget::image::Handle::from_rgba(
|
||||||
|
thumbnail.width(),
|
||||||
|
thumbnail.height(),
|
||||||
|
thumbnail.into_raw(),
|
||||||
|
),
|
||||||
|
Some((dyn_img.width(), dyn_img.height())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2401,6 +2449,7 @@ pub struct Tab {
|
||||||
pub history_i: usize,
|
pub history_i: usize,
|
||||||
pub history: Vec<Location>,
|
pub history: Vec<Location>,
|
||||||
pub config: TabConfig,
|
pub config: TabConfig,
|
||||||
|
pub thumb_config: ThumbCfg,
|
||||||
pub sort_name: HeadingOptions,
|
pub sort_name: HeadingOptions,
|
||||||
pub sort_direction: bool,
|
pub sort_direction: bool,
|
||||||
pub gallery: bool,
|
pub gallery: bool,
|
||||||
|
|
@ -2485,6 +2534,7 @@ impl Tab {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
location: Location,
|
location: Location,
|
||||||
config: TabConfig,
|
config: TabConfig,
|
||||||
|
thumb_config: ThumbCfg,
|
||||||
sorting_options: Option<&OrderMap<String, (HeadingOptions, bool)>>,
|
sorting_options: Option<&OrderMap<String, (HeadingOptions, bool)>>,
|
||||||
window_id: Option<window::Id>,
|
window_id: Option<window::Id>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
@ -2515,6 +2565,7 @@ impl Tab {
|
||||||
history_i: 0,
|
history_i: 0,
|
||||||
history,
|
history,
|
||||||
config,
|
config,
|
||||||
|
thumb_config,
|
||||||
sort_name,
|
sort_name,
|
||||||
sort_direction,
|
sort_direction,
|
||||||
gallery: false,
|
gallery: false,
|
||||||
|
|
@ -5524,7 +5575,7 @@ impl Tab {
|
||||||
|
|
||||||
pub fn subscription(&self, preview: bool) -> Subscription<Message> {
|
pub fn subscription(&self, preview: bool) -> Subscription<Message> {
|
||||||
//TODO: how many thumbnail loads should be in flight at once?
|
//TODO: how many thumbnail loads should be in flight at once?
|
||||||
let jobs = 8;
|
let jobs = self.thumb_config.jobs.get().clone() as usize;
|
||||||
let mut subscriptions = Vec::with_capacity(jobs + 3);
|
let mut subscriptions = Vec::with_capacity(jobs + 3);
|
||||||
|
|
||||||
if let Some(items) = &self.items_opt {
|
if let Some(items) = &self.items_opt {
|
||||||
|
|
@ -5570,16 +5621,26 @@ impl Tab {
|
||||||
};
|
};
|
||||||
if can_thumbnail {
|
if can_thumbnail {
|
||||||
let mime = item.mime.clone();
|
let mime = item.mime.clone();
|
||||||
|
let max_jobs = jobs.clone();
|
||||||
|
let max_mb = self.thumb_config.max_mem_mb.get().clone() as u64;
|
||||||
|
let max_size = self.thumb_config.max_size_mb.get().clone() as u64;
|
||||||
subscriptions.push(Subscription::run_with_id(
|
subscriptions.push(Subscription::run_with_id(
|
||||||
("thumbnail", path.clone()),
|
("thumbnail", path.clone()),
|
||||||
stream::channel(1, |mut output| async move {
|
stream::channel(1, move |mut output| async move {
|
||||||
let message = {
|
let message = {
|
||||||
let path = path.clone();
|
let path = path.clone();
|
||||||
|
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let thumbnail =
|
let thumbnail = ItemThumbnail::new(
|
||||||
ItemThumbnail::new(&path, metadata, mime, THUMBNAIL_SIZE);
|
&path,
|
||||||
|
metadata,
|
||||||
|
mime,
|
||||||
|
THUMBNAIL_SIZE,
|
||||||
|
max_mb,
|
||||||
|
max_jobs,
|
||||||
|
max_size,
|
||||||
|
);
|
||||||
log::debug!("thumbnailed {:?} in {:?}", path, start.elapsed());
|
log::debug!("thumbnailed {:?} in {:?}", path, start.elapsed());
|
||||||
Message::Thumbnail(path.clone(), thumbnail)
|
Message::Thumbnail(path.clone(), thumbnail)
|
||||||
})
|
})
|
||||||
|
|
@ -6081,6 +6142,7 @@ mod tests {
|
||||||
Location::Path(path.into()),
|
Location::Path(path.into()),
|
||||||
TabConfig::default(),
|
TabConfig::default(),
|
||||||
None,
|
None,
|
||||||
|
ThumbCfg::default(),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -6183,6 +6245,7 @@ mod tests {
|
||||||
Location::Path(path.to_owned()),
|
Location::Path(path.to_owned()),
|
||||||
TabConfig::default(),
|
TabConfig::default(),
|
||||||
None,
|
None,
|
||||||
|
ThumbCfg::default(),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
debug!(
|
debug!(
|
||||||
|
|
@ -6320,6 +6383,7 @@ mod tests {
|
||||||
Location::Path(path.into()),
|
Location::Path(path.into()),
|
||||||
TabConfig::default(),
|
TabConfig::default(),
|
||||||
None,
|
None,
|
||||||
|
ThumbCfg::default(),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -6347,6 +6411,7 @@ mod tests {
|
||||||
Location::Path(next_dir.clone()),
|
Location::Path(next_dir.clone()),
|
||||||
TabConfig::default(),
|
TabConfig::default(),
|
||||||
None,
|
None,
|
||||||
|
ThumbCfg::default(),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
// This will eventually yield false once root is hit
|
// This will eventually yield false once root is hit
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue