From 06b56dc6862b815de998523d0708e62cec5dea67 Mon Sep 17 00:00:00 2001 From: Inflation <2375962+inflation@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:25:18 +0800 Subject: [PATCH] feat: Replace derive_builder with bon --- Cargo.lock | 118 +++++++++++++++-------------- jpegxl-rs/Cargo.toml | 2 +- jpegxl-rs/src/decode.rs | 116 ++++++++++++++--------------- jpegxl-rs/src/encode.rs | 124 +++++++++++++++++-------------- jpegxl-rs/src/encode/options.rs | 4 +- jpegxl-rs/src/errors.rs | 10 ++- jpegxl-rs/src/lib.rs | 3 - jpegxl-rs/src/tests/encode.rs | 3 +- jpegxl-sys/src/encoder/encode.rs | 3 +- 9 files changed, 204 insertions(+), 179 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff42605..d173e79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,31 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bon" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9276fe602371cd8a7f70fe68c4db55b2d3e92c570627d6ed0427646edfa5cf47" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94828b84b32b4f3ac3865f692fcdbc46c7d0dd87b29658a391d58a244e1ce45a" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -260,37 +285,6 @@ dependencies = [ "syn", ] -[[package]] -name = "derive_builder" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_builder_macro" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" -dependencies = [ - "derive_builder_core", - "syn", -] - [[package]] name = "diff" version = "0.1.13" @@ -436,9 +430,9 @@ dependencies = [ name = "jpegxl-rs" version = "0.11.2+libjxl-0.11.1" dependencies = [ + "bon", "byteorder", "criterion", - "derive_builder", "half", "image", "jpegxl-sys", @@ -499,9 +493,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "log" @@ -603,11 +597,21 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -670,6 +674,12 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + [[package]] name = "ryu" version = "1.0.18" @@ -875,9 +885,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -891,51 +901,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "yansi" diff --git a/jpegxl-rs/Cargo.toml b/jpegxl-rs/Cargo.toml index e721fda..83711c6 100644 --- a/jpegxl-rs/Cargo.toml +++ b/jpegxl-rs/Cargo.toml @@ -28,11 +28,11 @@ docs = ["jpegxl-sys/docs"] bench = [] [dependencies] -derive_builder = "0.20.2" image = { version = "0.25.5", optional = true, default-features = false } thiserror = "2.0.3" half = "2.4.1" byteorder = "1.5.0" +bon = "3.2.0" [dependencies.jpegxl-sys] version = "0.11.2" diff --git a/jpegxl-rs/src/decode.rs b/jpegxl-rs/src/decode.rs index aa15eba..88df729 100644 --- a/jpegxl-rs/src/decode.rs +++ b/jpegxl-rs/src/decode.rs @@ -19,6 +19,7 @@ use std::{mem::MaybeUninit, ptr::null}; +use bon::bon; #[allow(clippy::wildcard_imports)] use jpegxl_sys::{ common::types::{JxlDataType, JxlPixelFormat}, @@ -81,13 +82,9 @@ impl Default for PixelFormat { } /// JPEG XL Decoder -#[derive(Builder)] -#[builder(build_fn(skip, error = "None"))] -#[builder(setter(strip_option))] pub struct JxlDecoder<'pr, 'mm> { /// Opaque pointer to the underlying decoder - #[builder(setter(skip))] - ptr: *mut jpegxl_sys::decode::JxlDecoder, + dec: *mut jpegxl_sys::decode::JxlDecoder, /// Override desired pixel format pub pixel_format: Option, @@ -156,15 +153,29 @@ pub struct JxlDecoder<'pr, 'mm> { pub memory_manager: Option<&'mm dyn MemoryManager>, } -impl<'pr, 'mm> JxlDecoderBuilder<'pr, 'mm> { +#[bon] +impl<'pr, 'mm> JxlDecoder<'pr, 'mm> { /// Build a [`JxlDecoder`] /// /// # Errors /// Return [`DecodeError::CannotCreateDecoder`] if it fails to create the decoder. - pub fn build(&mut self) -> Result, DecodeError> { - let mm = self.memory_manager.flatten(); + #[builder] + pub fn new( + pixel_format: Option, + skip_reorientation: Option, + unpremul_alpha: Option, + render_spotcolors: Option, + coalescing: Option, + desired_intensity_target: Option, + decompress: Option, + progressive_detail: Option, + #[builder(default)] icc_profile: bool, + #[builder(default = 512 * 1024)] init_jpeg_buffer: usize, + parallel_runner: Option<&'pr dyn ParallelRunner>, + memory_manager: Option<&'mm dyn MemoryManager>, + ) -> Result { let dec = unsafe { - mm.map_or_else( + memory_manager.map_or_else( || JxlDecoderCreate(null()), |mm| JxlDecoderCreate(&mm.manager()), ) @@ -174,20 +185,20 @@ impl<'pr, 'mm> JxlDecoderBuilder<'pr, 'mm> { return Err(DecodeError::CannotCreateDecoder); } - Ok(JxlDecoder { - ptr: dec, - pixel_format: self.pixel_format.flatten(), - skip_reorientation: self.skip_reorientation.flatten(), - unpremul_alpha: self.unpremul_alpha.flatten(), - render_spotcolors: self.render_spotcolors.flatten(), - coalescing: self.coalescing.flatten(), - desired_intensity_target: self.desired_intensity_target.flatten(), - decompress: self.decompress.flatten(), - progressive_detail: self.progressive_detail.flatten(), - icc_profile: self.icc_profile.unwrap_or_default(), - init_jpeg_buffer: self.init_jpeg_buffer.unwrap_or(512 * 1024), - parallel_runner: self.parallel_runner.flatten(), - memory_manager: mm, + Ok(Self { + dec, + pixel_format, + skip_reorientation, + unpremul_alpha, + render_spotcolors, + coalescing, + desired_intensity_target, + decompress, + progressive_detail, + icc_profile, + init_jpeg_buffer, + parallel_runner, + memory_manager, }) } } @@ -217,14 +228,14 @@ impl JxlDecoder<'_, '_> { let next_in = data.as_ptr(); let avail_in = std::mem::size_of_val(data) as _; - check_dec_status(unsafe { JxlDecoderSetInput(self.ptr, next_in, avail_in) })?; - unsafe { JxlDecoderCloseInput(self.ptr) }; + check_dec_status(unsafe { JxlDecoderSetInput(self.dec, next_in, avail_in) })?; + unsafe { JxlDecoderCloseInput(self.dec) }; let mut status; loop { use JxlDecoderStatus as s; - status = unsafe { JxlDecoderProcessInput(self.ptr) }; + status = unsafe { JxlDecoderProcessInput(self.dec) }; match status { s::NeedMoreInput | s::Error => return Err(DecodeError::GenericError), @@ -232,7 +243,7 @@ impl JxlDecoder<'_, '_> { // Get the basic info s::BasicInfo => { check_dec_status(unsafe { - JxlDecoderGetBasicInfo(self.ptr, basic_info.as_mut_ptr()) + JxlDecoderGetBasicInfo(self.dec, basic_info.as_mut_ptr()) })?; if let Some(pr) = self.parallel_runner { @@ -252,7 +263,7 @@ impl JxlDecoder<'_, '_> { let buf = unsafe { reconstruct_jpeg_buffer.as_mut().unwrap_unchecked() }; buf.resize(self.init_jpeg_buffer, 0); check_dec_status(unsafe { - JxlDecoderSetJPEGBuffer(self.ptr, buf.as_mut_ptr(), buf.len()) + JxlDecoderSetJPEGBuffer(self.dec, buf.as_mut_ptr(), buf.len()) })?; } @@ -261,11 +272,11 @@ impl JxlDecoder<'_, '_> { // Safety: JpegNeedMoreOutput is only called when reconstruct_jpeg_buffer // is not None let buf = unsafe { reconstruct_jpeg_buffer.as_mut().unwrap_unchecked() }; - let need_to_write = unsafe { JxlDecoderReleaseJPEGBuffer(self.ptr) }; + let need_to_write = unsafe { JxlDecoderReleaseJPEGBuffer(self.dec) }; buf.resize(buf.len() + need_to_write, 0); check_dec_status(unsafe { - JxlDecoderSetJPEGBuffer(self.ptr, buf.as_mut_ptr(), buf.len()) + JxlDecoderSetJPEGBuffer(self.dec, buf.as_mut_ptr(), buf.len()) })?; } @@ -277,13 +288,13 @@ impl JxlDecoder<'_, '_> { s::FullImage => continue, s::Success => { if let Some(buf) = reconstruct_jpeg_buffer.as_mut() { - let remaining = unsafe { JxlDecoderReleaseJPEGBuffer(self.ptr) }; + let remaining = unsafe { JxlDecoderReleaseJPEGBuffer(self.dec) }; buf.truncate(buf.len() - remaining); buf.shrink_to_fit(); } - unsafe { JxlDecoderReset(self.ptr) }; + unsafe { JxlDecoderReset(self.dec) }; let info = unsafe { basic_info.assume_init() }; return Ok(Metadata { @@ -313,7 +324,7 @@ impl JxlDecoder<'_, '_> { fn setup_decoder(&self, icc: bool, reconstruct_jpeg: bool) -> Result<(), DecodeError> { if let Some(runner) = self.parallel_runner { check_dec_status(unsafe { - JxlDecoderSetParallelRunner(self.ptr, runner.runner(), runner.as_opaque_ptr()) + JxlDecoderSetParallelRunner(self.dec, runner.runner(), runner.as_opaque_ptr()) })?; } @@ -330,22 +341,22 @@ impl JxlDecoder<'_, '_> { events }; - check_dec_status(unsafe { JxlDecoderSubscribeEvents(self.ptr, events) })?; + check_dec_status(unsafe { JxlDecoderSubscribeEvents(self.dec, events) })?; if let Some(val) = self.skip_reorientation { - check_dec_status(unsafe { JxlDecoderSetKeepOrientation(self.ptr, val.into()) })?; + check_dec_status(unsafe { JxlDecoderSetKeepOrientation(self.dec, val.into()) })?; } if let Some(val) = self.unpremul_alpha { - check_dec_status(unsafe { JxlDecoderSetUnpremultiplyAlpha(self.ptr, val.into()) })?; + check_dec_status(unsafe { JxlDecoderSetUnpremultiplyAlpha(self.dec, val.into()) })?; } if let Some(val) = self.render_spotcolors { - check_dec_status(unsafe { JxlDecoderSetRenderSpotcolors(self.ptr, val.into()) })?; + check_dec_status(unsafe { JxlDecoderSetRenderSpotcolors(self.dec, val.into()) })?; } if let Some(val) = self.coalescing { - check_dec_status(unsafe { JxlDecoderSetCoalescing(self.ptr, val.into()) })?; + check_dec_status(unsafe { JxlDecoderSetCoalescing(self.dec, val.into()) })?; } if let Some(val) = self.desired_intensity_target { - check_dec_status(unsafe { JxlDecoderSetDesiredIntensityTarget(self.ptr, val) })?; + check_dec_status(unsafe { JxlDecoderSetDesiredIntensityTarget(self.dec, val) })?; } Ok(()) @@ -354,13 +365,13 @@ impl JxlDecoder<'_, '_> { fn get_icc_profile(&self, icc_profile: &mut Vec) -> Result<(), DecodeError> { let mut icc_size = 0; check_dec_status(unsafe { - JxlDecoderGetICCProfileSize(self.ptr, JxlColorProfileTarget::Data, &mut icc_size) + JxlDecoderGetICCProfileSize(self.dec, JxlColorProfileTarget::Data, &mut icc_size) })?; icc_profile.resize(icc_size, 0); check_dec_status(unsafe { JxlDecoderGetColorAsICCProfile( - self.ptr, + self.dec, JxlColorProfileTarget::Data, icc_profile.as_mut_ptr(), icc_size, @@ -402,12 +413,12 @@ impl JxlDecoder<'_, '_> { let mut size = 0; check_dec_status(unsafe { - JxlDecoderImageOutBufferSize(self.ptr, &pixel_format, &mut size) + JxlDecoderImageOutBufferSize(self.dec, &pixel_format, &mut size) })?; pixels.resize(size, 0); check_dec_status(unsafe { - JxlDecoderSetImageOutBuffer(self.ptr, &pixel_format, pixels.as_mut_ptr().cast(), size) + JxlDecoderSetImageOutBuffer(self.dec, &pixel_format, pixels.as_mut_ptr().cast(), size) })?; unsafe { *format = pixel_format }; @@ -497,26 +508,11 @@ impl JxlDecoder<'_, '_> { impl Drop for JxlDecoder<'_, '_> { fn drop(&mut self) { - unsafe { JxlDecoderDestroy(self.ptr) }; + unsafe { JxlDecoderDestroy(self.dec) }; } } /// Return a [`JxlDecoderBuilder`] with default settings -#[must_use] pub fn decoder_builder<'prl, 'mm>() -> JxlDecoderBuilder<'prl, 'mm> { - JxlDecoderBuilder::default() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[allow(clippy::clone_on_copy)] - fn test_derive() { - let e = PixelFormat::default().clone(); - println!("{e:?}"); - - _ = decoder_builder().clone(); - } + JxlDecoder::builder() } diff --git a/jpegxl-rs/src/encode.rs b/jpegxl-rs/src/encode.rs index deee13d..6c07b8e 100644 --- a/jpegxl-rs/src/encode.rs +++ b/jpegxl-rs/src/encode.rs @@ -19,6 +19,7 @@ along with jpegxl-rs. If not, see . use std::{marker::PhantomData, mem::MaybeUninit, ops::Deref, ptr::null}; +use bon::bon; #[allow(clippy::wildcard_imports)] use jpegxl_sys::encoder::encode::*; @@ -55,16 +56,11 @@ impl Deref for EncoderResult { // MARK: Encoder /// JPEG XL Encoder -#[derive(Builder)] -#[builder(build_fn(skip, error = "None"))] -#[builder(setter(strip_option))] #[allow(clippy::struct_excessive_bools)] pub struct JxlEncoder<'prl, 'mm> { /// Opaque pointer to the underlying encoder - #[builder(setter(skip))] enc: *mut jpegxl_sys::encoder::encode::JxlEncoder, /// Opaque pointer to the encoder options - #[builder(setter(skip))] options_ptr: *mut JxlEncoderFrameSettings, /// Set alpha channel @@ -74,7 +70,7 @@ pub struct JxlEncoder<'prl, 'mm> { /// Set lossless /// /// Default: false - pub lossless: bool, + pub lossless: Option, /// Set speed /// /// Default: `Squirrel` (7). @@ -115,8 +111,8 @@ pub struct JxlEncoder<'prl, 'mm> { /// Set color encoding /// - /// Default: SRGB - pub color_encoding: ColorEncoding, + /// Default: SRGB for uint, Linear SRGB for float + pub color_encoding: Option, /// Set parallel runner /// @@ -131,15 +127,29 @@ pub struct JxlEncoder<'prl, 'mm> { memory_manager: Option<&'mm dyn MemoryManager>, } -impl<'prl, 'mm> JxlEncoderBuilder<'prl, 'mm> { +#[bon] +impl<'prl, 'mm> JxlEncoder<'prl, 'mm> { /// Build a [`JxlEncoder`] /// /// # Errors /// Return [`EncodeError::CannotCreateEncoder`] if it fails to create the encoder - pub fn build(&self) -> Result, EncodeError> { - let mm = self.memory_manager.flatten(); + #[builder] + pub fn new( + memory_manager: Option<&'mm dyn MemoryManager>, + #[builder(default)] has_alpha: bool, + lossless: Option, + #[builder(default)] speed: EncoderSpeed, + #[builder(default = 1.0)] quality: f32, + #[builder(default)] use_container: bool, + #[builder(default)] uses_original_profile: bool, + #[builder(default)] decoding_speed: i64, + init_buffer_size: Option, + color_encoding: Option, + parallel_runner: Option<&'prl dyn ParallelRunner>, + #[builder(default)] use_box: bool, + ) -> Result { let enc = unsafe { - mm.map_or_else( + memory_manager.map_or_else( || JxlEncoderCreate(null()), |mm| JxlEncoderCreate(&mm.manager()), ) @@ -151,40 +161,44 @@ impl<'prl, 'mm> JxlEncoderBuilder<'prl, 'mm> { let options_ptr = unsafe { JxlEncoderFrameSettingsCreate(enc, null()) }; - let init_buffer_size = - self.init_buffer_size - .map_or(512 * 1024, |v| if v < 32 { 32 } else { v }); - - Ok(JxlEncoder { + Ok(Self { enc, options_ptr, - has_alpha: self.has_alpha.unwrap_or_default(), - lossless: self.lossless.unwrap_or_default(), - speed: self.speed.unwrap_or_default(), - quality: self.quality.unwrap_or(1.0), - use_container: self.use_container.unwrap_or_default(), - uses_original_profile: self.uses_original_profile.unwrap_or_default(), - decoding_speed: self.decoding_speed.unwrap_or_default(), - init_buffer_size, - color_encoding: self.color_encoding.unwrap_or(ColorEncoding::Srgb), - parallel_runner: self.parallel_runner.flatten(), - use_box: self.use_box.unwrap_or_default(), - memory_manager: mm, + has_alpha, + lossless, + speed, + quality, + use_container, + uses_original_profile, + decoding_speed, + init_buffer_size: init_buffer_size.map_or(512 * 1024, |v| if v < 32 { 32 } else { v }), + color_encoding, + parallel_runner, + use_box, + memory_manager, }) } +} + +use jxl_encoder_builder::{IsUnset, SetQuality, State}; +impl<'prl, 'mm, S: State> JxlEncoderBuilder<'prl, 'mm, S> { /// Set the `quality` parameter from a JPEG-style quality factor (0-100, higher is better /// quality). - pub fn jpeg_quality(&mut self, quality: f32) -> &mut Self { + #[allow(dead_code)] + pub fn jpeg_quality(self, quality: f32) -> JxlEncoderBuilder<'prl, 'mm, SetQuality> + where + S::Quality: IsUnset, + { // SAFETY: the C API has no safety requirements. - self.quality = Some(unsafe { JxlEncoderDistanceFromQuality(quality) }); - self + self.quality(unsafe { JxlEncoderDistanceFromQuality(quality) }) } } // MARK: Private helper functions impl JxlEncoder<'_, '_> { /// Error mapping from underlying C const to [`EncodeError`] enum + #[track_caller] #[cfg_attr(coverage_nightly, coverage(off))] fn check_enc_status(&self, status: JxlEncoderStatus) -> Result<(), EncodeError> { match status { @@ -204,10 +218,14 @@ impl JxlEncoder<'_, '_> { // Set options fn set_options(&self) -> Result<(), EncodeError> { - self.check_enc_status(unsafe { JxlEncoderUseContainer(self.enc, self.use_container) })?; self.check_enc_status(unsafe { - JxlEncoderSetFrameLossless(self.options_ptr, self.lossless) + JxlEncoderUseContainer(self.enc, self.use_container.into()) })?; + if let Some(lossless) = self.lossless { + self.check_enc_status(unsafe { + JxlEncoderSetFrameLossless(self.options_ptr, lossless.into()) + })?; + } self.check_enc_status(unsafe { JxlEncoderFrameSettingsSetOption( self.options_ptr, @@ -273,11 +291,8 @@ impl JxlEncoder<'_, '_> { basic_info.alpha_exponent_bits = 0; } - match self.color_encoding { - ColorEncoding::SrgbLuma | ColorEncoding::LinearSrgbLuma => { - basic_info.num_color_channels = 1; - } - _ => (), + if let Some(ColorEncoding::SrgbLuma | ColorEncoding::LinearSrgbLuma) = self.color_encoding { + basic_info.num_color_channels = 1; } if let Some(pr) = self.parallel_runner { @@ -286,9 +301,13 @@ impl JxlEncoder<'_, '_> { self.check_enc_status(unsafe { JxlEncoderSetBasicInfo(self.enc, &basic_info) })?; - self.check_enc_status(unsafe { - JxlEncoderSetColorEncoding(self.enc, &self.color_encoding.into()) - }) + if let Some(color_encoding) = self.color_encoding { + self.check_enc_status(unsafe { + JxlEncoderSetColorEncoding(self.enc, &color_encoding.into()) + })?; + } + + Ok(()) } // Add a frame @@ -432,7 +451,7 @@ impl<'prl, 'mm> JxlEncoder<'prl, 'mm> { self.set_options()?; // If using container format, store JPEG reconstruction metadata - self.check_enc_status(unsafe { JxlEncoderStoreJPEGMetadata(self.enc, true) })?; + self.check_enc_status(unsafe { JxlEncoderStoreJPEGMetadata(self.enc, true.into()) })?; self.add_jpeg_frame(data)?; self.start_encoding() @@ -480,9 +499,8 @@ impl Drop for JxlEncoder<'_, '_> { } /// Return a [`JxlEncoderBuilder`] with default settings -#[must_use] pub fn encoder_builder<'prl, 'mm>() -> JxlEncoderBuilder<'prl, 'mm> { - JxlEncoderBuilder::default() + JxlEncoder::builder() } // MARK: Tests @@ -492,15 +510,13 @@ mod tests { use testresult::TestResult; #[test] - #[allow(clippy::clone_on_copy)] - fn test_derive() { - let e = EncoderSpeed::default().clone(); - println!("{e:?}"); - - let e = ColorEncoding::Srgb.clone(); - println!("{e:?}"); - - _ = encoder_builder().clone(); + #[allow(clippy::float_cmp)] + fn test_jpeg_quality() -> TestResult { + let encoder = encoder_builder().jpeg_quality(100.0).build()?; + assert_eq!(encoder.quality, 0.0); + let encoder = encoder_builder().jpeg_quality(90.0).build()?; + assert_eq!(encoder.quality, 1.0); + Ok(()) } #[test] diff --git a/jpegxl-rs/src/encode/options.rs b/jpegxl-rs/src/encode/options.rs index 5470d70..d56138d 100644 --- a/jpegxl-rs/src/encode/options.rs +++ b/jpegxl-rs/src/encode/options.rs @@ -58,7 +58,9 @@ impl From for JxlColorEncoding { LinearSrgb => { api::JxlColorEncodingSetToLinearSRGB(color_encoding.as_mut_ptr(), false.into()); } - SrgbLuma => api::JxlColorEncodingSetToSRGB(color_encoding.as_mut_ptr(), true.into()), + SrgbLuma => { + api::JxlColorEncodingSetToSRGB(color_encoding.as_mut_ptr(), true.into()) + } LinearSrgbLuma => { api::JxlColorEncodingSetToLinearSRGB(color_encoding.as_mut_ptr(), true.into()); } diff --git a/jpegxl-rs/src/errors.rs b/jpegxl-rs/src/errors.rs index 2a37f0e..ec4743d 100644 --- a/jpegxl-rs/src/errors.rs +++ b/jpegxl-rs/src/errors.rs @@ -97,6 +97,8 @@ pub(crate) fn check_dec_status(status: JxlDecoderStatus) -> Result<(), DecodeErr mod tests { use testresult::TestResult; + use crate::encode::JxlEncoder; + use super::*; #[test] @@ -132,15 +134,17 @@ mod tests { #[test] #[cfg_attr(coverage_nightly, coverage(off))] fn encode_invalid_data() -> TestResult { - let mut encoder = crate::encoder_builder().has_alpha(true).build()?; + let mut encoder = JxlEncoder::builder().has_alpha(true).build()?; + + println!("{}", encoder.encode::(&[], 0, 0).err().unwrap()); assert!(matches!( encoder.encode::(&[], 0, 0), - Err(EncodeError::ApiUsage) + Err(EncodeError::ApiUsage { .. }) )); assert!(matches!( encoder.encode::(&[1.0, 1.0, 1.0, 0.5], 1, 1), - Err(EncodeError::ApiUsage) + Err(EncodeError::ApiUsage { .. }) )); println!( diff --git a/jpegxl-rs/src/lib.rs b/jpegxl-rs/src/lib.rs index 8da8e4a..26667c6 100644 --- a/jpegxl-rs/src/lib.rs +++ b/jpegxl-rs/src/lib.rs @@ -19,9 +19,6 @@ along with jpegxl-rs. If not, see . #![cfg_attr(coverage_nightly, feature(coverage_attribute))] #![doc = include_str!("../README.md")] -#[macro_use] -extern crate derive_builder; - mod common; pub mod decode; pub mod encode; diff --git a/jpegxl-rs/src/tests/encode.rs b/jpegxl-rs/src/tests/encode.rs index a32e1b7..7bf8622 100644 --- a/jpegxl-rs/src/tests/encode.rs +++ b/jpegxl-rs/src/tests/encode.rs @@ -54,7 +54,6 @@ fn jpeg() -> TestResult { .parallel_runner(&threads_runner) .use_container(true) .uses_original_profile(true) - .jpeg_quality(85.0) .build()?; let res = encoder.encode_jpeg(super::SAMPLE_JPEG)?; @@ -203,7 +202,7 @@ fn gray() -> TestResult { )?; _ = decoder.decode(&result)?; - encoder.color_encoding = ColorEncoding::LinearSrgbLuma; + encoder.color_encoding = Some(ColorEncoding::LinearSrgbLuma); let result: EncoderResult = encoder.encode_frame( &EncoderFrame::new(sample.as_raw()).num_channels(1), sample.width(), diff --git a/jpegxl-sys/src/encoder/encode.rs b/jpegxl-sys/src/encoder/encode.rs index 57182c3..76893d4 100644 --- a/jpegxl-sys/src/encoder/encode.rs +++ b/jpegxl-sys/src/encoder/encode.rs @@ -1342,7 +1342,8 @@ extern "C-unwind" { /// # Returns /// - [`JxlEncoderStatus::Success`] if the operation was successful. /// - [`JxlEncoderStatus::Error`] otherwise. - pub fn JxlEncoderUseContainer(enc: *mut JxlEncoder, use_container: JxlBool) -> JxlEncoderStatus; + pub fn JxlEncoderUseContainer(enc: *mut JxlEncoder, use_container: JxlBool) + -> JxlEncoderStatus; /// Configure the encoder to store JPEG reconstruction metadata in the JPEG XL /// container.