From ddd9322d431b725936acdf2208c04ad0fcb24a3b Mon Sep 17 00:00:00 2001 From: Mitchel Stewart Date: Fri, 1 Aug 2025 01:35:40 -0300 Subject: [PATCH 1/2] feat: jxl support for gallery and preview --- src/tab.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/tab.rs b/src/tab.rs index edbd2b53..09264445 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -1776,6 +1776,7 @@ impl ItemThumbnail { tried_supported_file = true; let dyn_img: Option = match mime.subtype().as_str() { "jxl" => match File::open(path) { + //TODO: working Ok(file) => match JxlDecoder::new(file) { Ok(mut decoder) => { let mut limits = image::Limits::default(); @@ -2056,7 +2057,29 @@ impl Item { ItemThumbnail::Image(handle, _) => { if let Some(path) = self.path_opt() { if self.mime.type_() == mime::IMAGE { - return widget::image(widget::image::Handle::from_path(path)).into(); + //TODO: working + match self.mime.subtype().as_str() { + "jxl" => { + let jxl_buffer = (|| { + let file = File::open(path).ok()?; + let jxl = JxlDecoder::new(file).ok()?; + let image = image::DynamicImage::from_decoder(jxl).ok()?; + Some(image.into_rgba8()) + })(); + //TODO: This will be unwrap_or(generic image) + let imagebuffer = jxl_buffer.unwrap(); + return widget::image(widget::image::Handle::from_rgba( + imagebuffer.width(), + imagebuffer.height(), + imagebuffer.into_raw(), + )) + .into(); + } + _ => { + //TODO: add thumbnailer/generic image + return widget::image(widget::image::Handle::from_path(path)).into() + } + } } } widget::image(handle.clone()).into() @@ -4197,12 +4220,34 @@ impl Tab { element_opt = Some( widget::container( //TODO: use widget::image::viewer, when its zoom can be reset - widget::image(widget::image::Handle::from_path(path)), + //TODO: working + match item.mime.subtype().as_str() { + "jxl" => { + let jxl_buffer = (|| { + let file = File::open(path).ok()?; + let jxl = JxlDecoder::new(file).ok()?; + let image = + image::DynamicImage::from_decoder(jxl) + .ok()?; + Some(image.into_rgba8()) + })( + ); + //TODO: This will be unwrap_or(generic image) + let imagebuffer = jxl_buffer.unwrap(); + widget::image(widget::image::Handle::from_rgba( + imagebuffer.width(), + imagebuffer.height(), + imagebuffer.into_raw(), + )) + } //TODO: Add a generic image or don't don't display + _ => { widget::image(widget::image::Handle::from_path(path)) } + }, ) .center(Length::Fill) .into(), ); } else { + log::warn!("gallery view is handle?"); element_opt = Some( widget::container( //TODO: use widget::image::viewer, when its zoom can be reset From 3a9e3ba789a8a10a99ed5e9ea45e83ca9b1d41fd Mon Sep 17 00:00:00 2001 From: Mitchel Stewart Date: Sat, 16 Aug 2025 12:46:27 -0300 Subject: [PATCH 2/2] use closure for thumbnail --- Cargo.lock | 30 ++++++++++++++++++++ Cargo.toml | 2 +- src/tab.rs | 80 ++++++++++++++++++++++++++++++++++-------------------- 3 files changed, 82 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec623a4c..b5178b97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2091,6 +2091,12 @@ dependencies = [ "linux-raw-sys 0.6.5", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -4322,6 +4328,7 @@ dependencies = [ "jxl-oxide-common", "jxl-render", "jxl-threadpool", + "lcms2", "tracing", ] @@ -4468,6 +4475,29 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lcms2" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680ec3fa42c36e0af9ca02f20a3742a82229c7f1ee0e6754294de46a80be6f74" +dependencies = [ + "bytemuck", + "foreign-types", + "lcms2-sys", +] + +[[package]] +name = "lcms2-sys" +version = "4.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "593265f9a3172180024fb62580ee31348f31be924b19416da174ebb7fb623d2e" +dependencies = [ + "cc", + "dunce", + "libc", + "pkg-config", +] + [[package]] name = "lebe" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index 4b6b56a9..a09d03a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,8 +70,8 @@ zip = "2.2.2" uzers = "0.12.1" md-5 = "0.10.6" png = "0.17.16" -jxl-oxide = { version = "0.12.2", features = ["image"] } num_cpus = "1.17.0" +jxl-oxide = { version = "0.12.2", features = ["image", "lcms2", "rayon"] } # Completion-based IO runtime to enable io_uring / IOCP file IO support. [dependencies.compio] diff --git a/src/tab.rs b/src/tab.rs index 09264445..b222c5ea 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -1775,32 +1775,52 @@ impl ItemThumbnail { if mime.type_() == mime::IMAGE && check_size("image", max_size_mb * 1000 * 1000) { tried_supported_file = true; let dyn_img: Option = match mime.subtype().as_str() { - "jxl" => match File::open(path) { - //TODO: working - Ok(file) => match JxlDecoder::new(file) { - Ok(mut decoder) => { - let mut limits = image::Limits::default(); - let max_ram = max_mem * 1000 * 1000 / jobs as u64; - limits.max_alloc = Some(max_ram); - let _ = decoder.set_limits(limits); - match image::DynamicImage::from_decoder(decoder) { - Ok(img) => Some(img), - Err(err) => { - log::warn!("failed to decode jxl {:?}: {}", path, err); - None - } - } - } - Err(err) => { - log::warn!("failed to create jxl decoder {:?}: {}", path, err); - None - } - }, - Err(err) => { - log::warn!("failed to open path {:?}: {}", path, err); - None - } - }, + "jxl" => { + let jxldynimg = (|| { + let file = File::open(path).map_err(|err| { + log::warn!("failed to open file {:?}: {}", path, err); + }).ok()?; + let mut limits = image::Limits::default(); + let max_ram = max_mem * 1000 * 1000 / jobs as u64; + limits.max_alloc = Some(max_ram); + let mut jxl = JxlDecoder::new(file).map_err(|err| { + log::warn!("to create JxlDecoder for {:?}: {}", path, err); + }).ok()?; + let _ = jxl.set_limits(limits); + Some(image::DynamicImage::from_decoder(jxl).map_err(|err| { + log::warn!("failed to decode the file {:?}: {}", path, err); + }).ok()? + ) + })(); + //TODO: This will be unwrap_or(generic image) + jxldynimg + } + //match File::open(path) { + // //TODO: working + // Ok(file) => match JxlDecoder::new(file) { + // Ok(mut decoder) => { + // let mut limits = image::Limits::default(); + // let max_ram = max_mem * 1000 * 1000 / jobs as u64; + // limits.max_alloc = Some(max_ram); + // let _ = decoder.set_limits(limits); + // match image::DynamicImage::from_decoder(decoder) { + // Ok(img) => Some(img), + // Err(err) => { + // log::warn!("failed to decode jxl {:?}: {}", path, err); + // None + // } + // } + // } + // Err(err) => { + // log::warn!("failed to create jxl decoder {:?}: {}", path, err); + // None + // } + // }, + // Err(err) => { + // log::warn!("failed to open path {:?}: {}", path, err); + // None + // } + //}, _ => { match image::ImageReader::open(path).and_then(|img| img.with_guessed_format()) { Ok(mut reader) => { @@ -2077,7 +2097,7 @@ impl Item { } _ => { //TODO: add thumbnailer/generic image - return widget::image(widget::image::Handle::from_path(path)).into() + return widget::image(widget::image::Handle::from_path(path)).into(); } } } @@ -4239,8 +4259,10 @@ impl Tab { imagebuffer.height(), imagebuffer.into_raw(), )) - } //TODO: Add a generic image or don't don't display - _ => { widget::image(widget::image::Handle::from_path(path)) } + } //TODO: Add a generic image or don't don't display + _ => widget::image(widget::image::Handle::from_path( + path, + )), }, ) .center(Length::Fill)