fix: use image feature of jxl-oxide to fix image decoding error

This commit is contained in:
Michael Aaron Murphy 2025-08-18 16:11:13 +02:00
parent 1a37a81b66
commit fbe004b031
No known key found for this signature in database
GPG key ID: B2732D4240C9212C
3 changed files with 24 additions and 77 deletions

26
Cargo.lock generated
View file

@ -4282,9 +4282,9 @@ dependencies = [
[[package]]
name = "jxl-coding"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6748ba8af69b87c68f8dcdf992de959c207962689bc28ddb7906abf4a0b786c9"
checksum = "cd972bcd125e776f1eb241ac50e39f956095a1c2770c64736c968f8946bd9a3c"
dependencies = [
"jxl-bitstream",
"tracing",
@ -4307,9 +4307,9 @@ dependencies = [
[[package]]
name = "jxl-frame"
version = "0.13.2"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5b0b31f0de176f38c1b8da911f9cab337bf29e2f59bc38398fd96ef05357b95"
checksum = "2d967c6fd669c7c01060b5022d8835fa82fd46b06ffc98b549f17600a097c2b3"
dependencies = [
"jxl-bitstream",
"jxl-coding",
@ -4345,9 +4345,9 @@ dependencies = [
[[package]]
name = "jxl-jbr"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ba39b083a82788a17717edbcc4b08160b51fdffc9fec640deba9e8268da1a"
checksum = "e35d032bcec660647828527ff42c6f5776d2fd44b8357f9f6d9ac6dc07218e46"
dependencies = [
"brotli-decompressor",
"jxl-bitstream",
@ -4363,9 +4363,9 @@ dependencies = [
[[package]]
name = "jxl-modular"
version = "0.11.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa97a2e3752859d45506fa062e38a0768867beca5993b2ca20227d3aab5a1cc9"
checksum = "da758b2f989aafd9eeb39489fe43d7be5a3a0d2ad61cf1bad705eb6990a6053c"
dependencies = [
"jxl-bitstream",
"jxl-coding",
@ -4377,11 +4377,13 @@ dependencies = [
[[package]]
name = "jxl-oxide"
version = "0.12.3"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6644c2764ff41cd9b366804229e21a3cd7db45da4a436f9a21e15c0c26433651"
checksum = "fa635162d7d53c650ae9e429a4e354ac1d63f0d3b1bdd1991b400c22cd301a6d"
dependencies = [
"brotli-decompressor",
"bytemuck",
"image",
"jxl-bitstream",
"jxl-color",
"jxl-frame",
@ -4405,9 +4407,9 @@ dependencies = [
[[package]]
name = "jxl-render"
version = "0.12.2"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab17c55b16932f1e931c51f82f6b22dfaa0a70cfb257b66863a3f7d421a3681e"
checksum = "aa0c3100918bd3c41bb0f8ce1f4f1664e48f3032ff8eeab0d6a2cfc3276f462d"
dependencies = [
"bytemuck",
"jxl-bitstream",

View file

@ -21,7 +21,7 @@ futures-lite = "2.6.0"
futures-util = "0.3.31"
image = "0.25.6"
infer = "0.19.0"
jxl-oxide = "0.12.2"
jxl-oxide = { version = "0.12.4", features = ["image"] }
tokio = { version = "1.47.0", features = ["sync"] }
tracing = "0.1.41"
walkdir = "=2.5.0"

View file

@ -5,7 +5,8 @@ use futures_lite::Stream;
use futures_util::StreamExt;
use image::imageops::FilterType;
use image::{DynamicImage, ImageBuffer, Rgba, RgbaImage};
use jxl_oxide::{EnumColourEncoding, JxlImage, PixelFormat};
use jxl_oxide::integration::JxlDecoder;
use std::fs::File;
use std::os::unix::ffi::OsStrExt;
use std::{
borrow::Cow,
@ -120,6 +121,7 @@ pub async fn load_each_from_path(
false
}
} else {
eprintln!("is jxl");
true
};
@ -430,70 +432,13 @@ fn border_radius(
/// Decodes JPEG XL image files into `image::DynamicImage` via `jxl-oxide`.
pub fn decode_jpegxl(path: &std::path::Path) -> eyre::Result<DynamicImage> {
let mut image = JxlImage::builder()
.open(path)
.map_err(|why| eyre!("failed to read image header: {why}"))?;
image.request_color_encoding(EnumColourEncoding::srgb(
jxl_oxide::RenderingIntent::Relative,
));
let render = image
.render_frame(0)
.map_err(|why| eyre!("failed to render image frame: {why}"))?;
let file = File::open(path).map_err(|why| eyre!("failed to open jxl image file: {why}"))?;
let framebuffer = render.image_all_channels();
match image.pixel_format() {
PixelFormat::Graya => image::GrayAlphaImage::from_raw(
framebuffer.width() as u32,
framebuffer.height() as u32,
framebuffer
.buf()
.iter()
.map(|x| x * 255. + 0.5)
.map(|x| x as u8)
.collect::<Vec<_>>(),
)
.map(DynamicImage::ImageLumaA8)
.ok_or_eyre("Can't decode gray alpha buffer"),
PixelFormat::Gray => image::GrayImage::from_raw(
framebuffer.width() as u32,
framebuffer.height() as u32,
framebuffer
.buf()
.iter()
.map(|x| x * 255. + 0.5)
.map(|x| x as u8)
.collect::<Vec<_>>(),
)
.map(DynamicImage::ImageLuma8)
.ok_or_eyre("Can't decode gray buffer"),
PixelFormat::Rgba => image::RgbaImage::from_raw(
framebuffer.width() as u32,
framebuffer.height() as u32,
framebuffer
.buf()
.iter()
.map(|x| x * 255. + 0.5)
.map(|x| x as u8)
.collect::<Vec<_>>(),
)
.map(DynamicImage::ImageRgba8)
.ok_or_eyre("Can't decode rgba buffer"),
PixelFormat::Rgb => image::RgbImage::from_raw(
framebuffer.width() as u32,
framebuffer.height() as u32,
framebuffer
.buf()
.iter()
.map(|x| x * 255. + 0.5)
.map(|x| x as u8)
.collect::<Vec<_>>(),
)
.map(DynamicImage::ImageRgb8)
.ok_or_eyre("Can't decode rgb buffer"),
//TODO: handle this
PixelFormat::Cmyk => Err(eyre!("unsupported pixel format: CMYK")),
PixelFormat::Cmyka => Err(eyre!("unsupported pixel format: CMYKA")),
}
let decoder =
JxlDecoder::new(file).map_err(|why| eyre!("failed to read jxl image header: {why}"))?;
image::DynamicImage::from_decoder(decoder)
.map_err(|why| eyre!("failed to decode jxl image: {why}"))
}
/// Use `fast-image-resize` crate for faster thumbnail generation.