diff --git a/crates/zune-capi/src/imread.rs b/crates/zune-capi/src/imread.rs index 26e16a92..05234490 100644 --- a/crates/zune-capi/src/imread.rs +++ b/crates/zune-capi/src/imread.rs @@ -8,7 +8,6 @@ use zune_image::codecs::bmp::BmpDecoder; use zune_image::codecs::farbfeld::FarbFeldDecoder; use zune_image::codecs::hdr::HdrDecoder; use zune_image::codecs::jpeg::JpegDecoder; -use zune_image::codecs::jpeg_xl::jxl_oxide::RenderResult; use zune_image::codecs::png::PngDecoder; use zune_image::codecs::ppm::PPMDecoder; use zune_image::codecs::psd::PSDDecoder; @@ -498,36 +497,30 @@ where decoder.decode_into(output)?; } ImageFormat::JPEG_XL => { - let mut decoder = zune_image::codecs::jpeg_xl::jxl_oxide::JxlImage::from_reader( - ZReader::new(data) - ) - .map_err(|x| ImageErrors::GenericString(x.to_string()))?; + let decoder = zune_image::codecs::jpeg_xl::jxl_oxide::JxlImage::builder() + .read(ZReader::new(data)) + .map_err(|x| ImageErrors::GenericString(x.to_string()))?; - let result = decoder - .render_next_frame() + let render = decoder + .render_frame(0) .map_err(|x| ImageErrors::GenericString(x.to_string()))?; - match result { - RenderResult::Done(render) => { - let (a, f32_buf, c) = unsafe { output.align_to_mut() }; + let (a, f32_buf, c) = unsafe { output.align_to_mut() }; - if !(a.is_empty() && c.is_empty()) { - // misalignment - return Err(ImageErrors::GenericStr("Buffer misalignment")); - } + if !(a.is_empty() && c.is_empty()) { + // misalignment + return Err(ImageErrors::GenericStr("Buffer misalignment")); + } - let im_plannar = render.image(); - let buf_len = im_plannar.buf().len(); + let im_plannar = render.image(); + let buf_len = im_plannar.buf().len(); - if buf_len > f32_buf.len() { - return Err(ImageErrors::GenericStr( - "Too small of a buffer for jxl output" - )); - } - f32_buf[..buf_len].copy_from_slice(im_plannar.buf()) - } - _ => return Err(ImageErrors::GenericStr("Cannot handle jxl status")) + if buf_len > f32_buf.len() { + return Err(ImageErrors::GenericStr( + "Too small of a buffer for jxl output" + )); } + f32_buf[..buf_len].copy_from_slice(im_plannar.buf()) } ImageFormat::HDR => { let mut decoder = HdrDecoder::new(data); diff --git a/crates/zune-image/Cargo.toml b/crates/zune-image/Cargo.toml index 416c9c64..a2233e35 100644 --- a/crates/zune-image/Cargo.toml +++ b/crates/zune-image/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" authors = ["caleb "] repository = "https://github.com/etemesi254/zune-image/tree/dev/crates/zune-image" license = "MIT OR Apache-2.0 OR Zlib" -keywords = ["image", "decoder", "encoder","image-processing"] +keywords = ["image", "decoder", "encoder", "image-processing"] categories = ["multimedia::images"] description = "An image library, contiaining necessary capabilities to decode, manipulate and encode images" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -41,22 +41,22 @@ all = ["image_formats", "serde-support", "metadata", "threads", "simd", "log"] # Core primitives zune-core = { path = "../zune-core", version = "^0.4.12" } # Images -zune-png = { path = "../zune-png",version = "0.4", optional = true } -zune-jpeg = { path = "../zune-jpeg",version = "0.4", optional = true } -zune-ppm = { path = "../zune-ppm",version = "0.4", optional = true } -zune-psd = { path = "../zune-psd",version = "0.4", optional = true } -zune-farbfeld = { path = "../zune-farbfeld",version = "0.4", optional = true } -zune-qoi = { path = "../zune-qoi",version = "0.4", optional = true } -zune-jpegxl = { path = "../zune-jpegxl",version = "0.4", optional = true } -zune-hdr = { path = "../zune-hdr",version = "0.4", optional = true } -zune-bmp = { path = "../zune-bmp",version = "0.4", optional = true } +zune-png = { path = "../zune-png", version = "0.4", optional = true } +zune-jpeg = { path = "../zune-jpeg", version = "0.4", optional = true } +zune-ppm = { path = "../zune-ppm", version = "0.4", optional = true } +zune-psd = { path = "../zune-psd", version = "0.4", optional = true } +zune-farbfeld = { path = "../zune-farbfeld", version = "0.4", optional = true } +zune-qoi = { path = "../zune-qoi", version = "0.4", optional = true } +zune-jpegxl = { path = "../zune-jpegxl", version = "0.4", optional = true } +zune-hdr = { path = "../zune-hdr", version = "0.4", optional = true } +zune-bmp = { path = "../zune-bmp", version = "0.4", optional = true } # Channel conversions in a safe way bytemuck = { version = "1.13", default-features = false } # Serializing info serde = { version = "1.0.152", optional = true } # External image APIs -jpeg-encoder = { version = "0.5.1", optional = true, features = ["simd", "std"] } -jxl-oxide = { version = "0.4.0", optional = true } +jpeg-encoder = { version = "0.6.0", optional = true, features = ["simd", "std"] } +jxl-oxide = { version = "0.8.0", optional = true } # metadata kamadak-exif = { version = "0.5.5", optional = true } diff --git a/crates/zune-image/src/codecs/jpeg_xl.rs b/crates/zune-image/src/codecs/jpeg_xl.rs index 8f903fe0..b7c9e19a 100644 --- a/crates/zune-image/src/codecs/jpeg_xl.rs +++ b/crates/zune-image/src/codecs/jpeg_xl.rs @@ -22,7 +22,7 @@ use std::thread::sleep; use std::time::Duration; pub use jxl_oxide; -use jxl_oxide::{PixelFormat, RenderResult}; +use jxl_oxide::{JxlImage, PixelFormat, RenderResult}; use zune_core::bit_depth::{BitDepth, BitType}; use zune_core::bytestream::ZByteWriterTrait; use zune_core::colorspace::ColorSpace; @@ -117,14 +117,15 @@ impl From for ImgEncodeErrors { } } -pub struct JxlDecoder { - inner: jxl_oxide::JxlImage, +pub struct JxlDecoder { + inner: jxl_oxide::JxlImage, options: DecoderOptions } -impl JxlDecoder { - pub fn try_new(source: R, options: DecoderOptions) -> Result, ImageErrors> { - let parser = jxl_oxide::JxlImage::from_reader(source) +impl JxlDecoder { + pub fn try_new(source: R, options: DecoderOptions) -> Result { + let parser = jxl_oxide::JxlImage::builder() + .read(source) .map_err(|x| ImageErrors::ImageDecodeErrors(format!("{:?}", x)))?; let decoder = JxlDecoder { @@ -135,15 +136,12 @@ impl JxlDecoder { } } -impl DecoderTrait for JxlDecoder -where - R: Read -{ +impl DecoderTrait for JxlDecoder { fn decode(&mut self) -> Result { // by now headers have been decoded, so we can fetch these let metadata = self.read_headers()?; - let (w, h) = as DecoderTrait>::dimensions(self).unwrap(); - let color = as DecoderTrait>::out_colorspace(self); + let (w, h) = ::dimensions(self).unwrap(); + let color = ::out_colorspace(self); let mut total_frames = vec![]; @@ -173,44 +171,37 @@ where return Err(ImageErrors::ImageDecodeErrors(msg)); } - loop { - let result = self + let taken = if self.options.jxl_decode_animated() { + self.inner.num_loaded_frames() + } else { + 1 + }; + + for frame in 0..taken { + let render = self .inner - .render_next_frame() + .render_frame(frame) .map_err(|x| ImageErrors::ImageDecodeErrors(format!("{}", x)))?; - match result { - RenderResult::Done(render) => { - // get the images - let duration = render.duration(); - - let im_plannar = render.image_planar(); - let mut frame_v = vec![]; - - for channel in im_plannar { - let mut chan = Channel::new_with_bit_type( - channel.width() * channel.height() * size_of::(), - BitType::F32 - ); - // copy the channel as plannar - let c = chan.reinterpret_as_mut()?; - c.copy_from_slice(channel.buf()); - // then store it in frame_v - frame_v.push(chan); - } - let frame = Frame::new(frame_v); - total_frames.push(frame); - } - RenderResult::NeedMoreData => { - sleep(Duration::new(1, 0)); - // return to the loop - } - RenderResult::NoMoreFrames => break - } - if !self.options.jxl_decode_animated() { - // we won't be decoding animated, so don't decode the next frame - break; + // get the images + let duration = render.duration(); + + let im_plannar = render.image_planar(); + let mut frame_v = vec![]; + + for channel in im_plannar { + let mut chan = Channel::new_with_bit_type( + channel.width() * channel.height() * size_of::(), + BitType::F32 + ); + // copy the channel as plannar + let c = chan.reinterpret_as_mut()?; + c.copy_from_slice(channel.buf()); + // then store it in frame_v + frame_v.push(chan); } + let frame = Frame::new(frame_v); + total_frames.push(frame); } // then create a new image let mut image = Image::new_frames(total_frames, BitDepth::Float32, w, h, color); @@ -242,8 +233,8 @@ where } fn read_headers(&mut self) -> Result, ImageErrors> { - let (w, h) = as DecoderTrait>::dimensions(self).unwrap(); - let color = as DecoderTrait>::out_colorspace(self); + let (w, h) = ::dimensions(self).unwrap(); + let color = ::out_colorspace(self); let (width, height) = self.dimensions().unwrap(); diff --git a/crates/zune-python/src/py_functions.rs b/crates/zune-python/src/py_functions.rs index be8c5939..928f2067 100644 --- a/crates/zune-python/src/py_functions.rs +++ b/crates/zune-python/src/py_functions.rs @@ -19,7 +19,6 @@ use zune_image::codecs::bmp::BmpDecoder; use zune_image::codecs::farbfeld::FarbFeldDecoder; use zune_image::codecs::hdr::HdrDecoder; use zune_image::codecs::jpeg::JpegDecoder; -use zune_image::codecs::jpeg_xl::jxl_oxide::RenderResult; use zune_image::codecs::png::PngDecoder; use zune_image::codecs::ppm::PPMDecoder; use zune_image::codecs::psd::PSDDecoder; @@ -339,39 +338,34 @@ pub fn imread(py: Python<'_>, file: String) -> PyResult<&PyUntypedArray> { ImageFormat::JPEG_XL => { let c = ZReader::new(ZCursor::new(&bytes)); - let mut decoder = - zune_image::codecs::jpeg_xl::jxl_oxide::JxlImage::from_reader(c) - .map_err(|x| PyErr::new::(format!("{x:?}")))?; + let decoder = zune_image::codecs::jpeg_xl::jxl_oxide::JxlImage::builder() + .read(c) + .map_err(|x| PyErr::new::(format!("{x:?}")))?; let (w, h) = (decoder.width() as usize, decoder.height() as usize); let color = decoder.pixel_format(); - let result = decoder - .render_next_frame() + let render = decoder + .render_frame(0) .map_err(|x| PyErr::new::(format!("{x}")))?; - match result { - RenderResult::Done(render) => { - // get the images - let im_plannar = render.image(); - - if color.channels() == 1 { - let arr = PyArray2::zeros(py, [h, w], false); - arr.try_readwrite()? - .as_slice_mut()? - .copy_from_slice(im_plannar.buf()); - - Ok(arr.as_untyped()) - } else { - let arr = PyArray3::zeros(py, [h, w, color.channels()], false); - arr.try_readwrite()? - .as_slice_mut()? - .copy_from_slice(im_plannar.buf()); - - Ok(arr.as_untyped()) - } - } - _ => return Err(PyErr::new::("Cannot process jxl frame")) + // get the images + let im_plannar = render.image(); + + if color.channels() == 1 { + let arr = PyArray2::zeros(py, [h, w], false); + arr.try_readwrite()? + .as_slice_mut()? + .copy_from_slice(im_plannar.buf()); + + Ok(arr.as_untyped()) + } else { + let arr = PyArray3::zeros(py, [h, w, color.channels()], false); + arr.try_readwrite()? + .as_slice_mut()? + .copy_from_slice(im_plannar.buf()); + + Ok(arr.as_untyped()) } } d @ ImageFormat::Unknown => Err(PyErr::new::(format!(