From 5550f15264994b92050508df22819e414e8ea043 Mon Sep 17 00:00:00 2001 From: Isotr0py Date: Tue, 2 Jul 2024 16:25:41 +0800 Subject: [PATCH] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20(deps):=20Update=20jpegxl-?= =?UTF-8?q?rs=20to=200.10.4=20(#50)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update jpegxl-rs v0.10.4 * Fix linux CI * Fix linux CI --- .github/workflows/release.yml | 12 ++++++----- .gitmodules | 4 ---- Cargo.toml | 2 +- jpegxl-rs | 1 - pillow_jxl/JpegXLImagePlugin.py | 15 +------------- src/decode.rs | 36 +++++++++++++++++++++++++++++---- src/encode.rs | 29 +++++++++++++++----------- 7 files changed, 58 insertions(+), 41 deletions(-) delete mode 100644 .gitmodules delete mode 160000 jpegxl-rs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4fe9829..6ee1057 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,18 +49,20 @@ jobs: manylinux: auto docker-options: | -e RUST_BACKTRACE=1 - -e DEP_JXL_LIB=${{ github.workspace }}/jpegxl-rs/jpegxl-src/libjxl/build/lib - -e DEP_BROTLI_LIB=${{ github.workspace }}/jpegxl-rs/jpegxl-src/libjxl/build/third_party/brotli - -e DEP_HWY_LIB=${{ github.workspace }}/jpegxl-rs/jpegxl-src/libjxl/build/third_party/highway + -e DEP_JXL_LIB=${{ github.workspace }}/libjxl/build/lib + -e DEP_BROTLI_LIB=${{ github.workspace }}/libjxl/build/third_party/brotli + -e DEP_HWY_LIB=${{ github.workspace }}/libjxl/build/third_party/highway before-script-linux: | - cd jpegxl-rs/jpegxl-src/libjxl + git clone --recurse-submodules --depth 1 -b v0.10.3 \ + https://github.com/libjxl/libjxl.git + cd libjxl cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF \ -DJPEGXL_ENABLE_TOOLS=OFF -DJPEGXL_ENABLE_DOXYGEN=OFF -DJPEGXL_ENABLE_MANPAGES=OFF \ -DJPEGXL_ENABLE_BENCHMARKS=OFF -DJPEGXL_ENABLE_EXAMPLES=OFF -DJPEGXL_ENABLE_JNI=OFF \ -DJPEGXL_ENABLE_SJPEG=OFF -DJPEGXL_ENABLE_OPENEXR=OFF cmake --build build cmake --install build - cd ../../../ + cd .. - name: Upload wheels uses: actions/upload-artifact@v4 diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index b0bb3c8..0000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "jpegxl-rs"] - path = jpegxl-rs - url = https://github.com/Isotr0py/jpegxl-rs.git - branch = plugin diff --git a/Cargo.toml b/Cargo.toml index 057bc23..f168b54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["cdylib"] [dependencies] pyo3 = { version="0.22.0", features = ["extension-module"] } -jpegxl-rs = { path="./jpegxl-rs/jpegxl-rs", default-features = false, features = ["threads"] } +jpegxl-rs = { version="0.10.4", features = ["threads"] } [features] # Enables parallel processing support by enabling the "rayon" feature of jpeg-decoder. diff --git a/jpegxl-rs b/jpegxl-rs deleted file mode 160000 index 108024f..0000000 --- a/jpegxl-rs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 108024f036dd46f9c5513868975310de7fa77206 diff --git a/pillow_jxl/JpegXLImagePlugin.py b/pillow_jxl/JpegXLImagePlugin.py index 8960928..2365742 100644 --- a/pillow_jxl/JpegXLImagePlugin.py +++ b/pillow_jxl/JpegXLImagePlugin.py @@ -80,19 +80,6 @@ def tell(self): return self.__frame -def _quality_to_frame_distance(q): - # Translate quality to frame distance using the same mapping as - # libjxl's JxlEncoderDistanceFromQuality, which roughly maps to - # jpeg's quality values. - if q >= 100: - fd = 0 - elif q >= 30: - fd = 0.1 + (100 - q) * 0.09 - else: - fd = 53.0 / 3000.0 * q * q - 23.0 / 20.0 * q + 25.0 - return fd - - def _save(im, fp, filename, save_all=False): if im.mode not in _VALID_JXL_MODES: raise NotImplementedError("Only RGB, RGBA, L, LA are supported.") @@ -101,7 +88,7 @@ def _save(im, fp, filename, save_all=False): # default quality is 90 lossless = info.get("lossless", False) - quality = 0 if lossless else _quality_to_frame_distance(info.get("quality", 90)) + quality = 0 if lossless else info.get("quality", 90) decoding_speed = info.get("decoding_speed", 0) effort = info.get("effort", 7) diff --git a/src/decode.rs b/src/decode.rs index 033a027..f39d501 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -5,6 +5,7 @@ use pyo3::prelude::*; use jpegxl_rs::decode::{Data, Metadata, Pixels}; use jpegxl_rs::decoder_builder; use jpegxl_rs::parallel::threads_runner::ThreadsRunner; + // it works even if the item is not documented: #[pyclass(module = "pillow_jxl")] @@ -43,6 +44,30 @@ impl ImageInfo { } } +pub fn convert_pixels(pixels: Pixels) -> Vec { + let mut result = Vec::new(); + match pixels { + Pixels::Uint8(pixels) => { + for pixel in pixels { + result.push(pixel); + } + } + Pixels::Uint16(pixels) => { + for pixel in pixels { + result.push((pixel >> 8) as u8); + result.push(pixel as u8); + } + } + Pixels::Float(pixels) => { + for pixel in pixels { + result.push((pixel * 255.0) as u8); + } + } + Pixels::Float16(_) => panic!("Float16 is not supported yet"), + } + result +} + #[pyclass(module = "pillow_jxl")] pub struct Decoder { parallel: bool, @@ -57,7 +82,11 @@ impl Decoder { } #[pyo3(signature = (data))] - fn __call__(&self, _py: Python, data: &[u8]) -> (bool, ImageInfo, Cow<'_, [u8]>, Cow<'_, [u8]>) { + fn __call__( + &self, + _py: Python, + data: &[u8], + ) -> (bool, ImageInfo, Cow<'_, [u8]>, Cow<'_, [u8]>) { let parallel_runner: ThreadsRunner; let decoder = match self.parallel { true => { @@ -70,11 +99,10 @@ impl Decoder { } false => decoder_builder().icc_profile(true).build().unwrap(), }; - let (info, img) = decoder.reconstruct_with::(&data).unwrap(); + let (info, img) = decoder.reconstruct(&data).unwrap(); let (jpeg, img) = match img { Data::Jpeg(x) => (true, x), - Data::Pixels(Pixels::Uint8(x)) => (false, x), - _ => panic!("Unsupported dtype for decoding"), + Data::Pixels(x) => (false, convert_pixels(x)), }; let icc_profile: Vec = match &info.icc_profile { Some(x) => x.to_vec(), diff --git a/src/encode.rs b/src/encode.rs index 0fab0cd..2f7777a 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -2,9 +2,7 @@ use std::borrow::Cow; use pyo3::prelude::*; -use jpegxl_rs::encode::{ - ColorEncoding, EncoderFrame, EncoderResult, EncoderSpeed, Metadata as EncoderMetadata, -}; +use jpegxl_rs::encode::{ColorEncoding, EncoderFrame, EncoderResult, EncoderSpeed, Metadata}; use jpegxl_rs::encoder_builder; use jpegxl_rs::parallel::threads_runner::ThreadsRunner; @@ -79,20 +77,24 @@ impl Encoder { xmp: Option<&[u8]>, ) -> Cow<'_, [u8]> { let parallel_runner: ThreadsRunner; + let mut encoder_builder = encoder_builder(); let mut encoder = match self.parallel { true => { parallel_runner = ThreadsRunner::default(); - encoder_builder() + encoder_builder.set_jpeg_quality(self.quality); + encoder_builder .parallel_runner(¶llel_runner) .build() .unwrap() } - false => encoder_builder().build().unwrap(), + false => { + encoder_builder.set_jpeg_quality(self.quality); + encoder_builder.build().unwrap() + } }; encoder.uses_original_profile = self.use_original_profile; encoder.has_alpha = self.has_alpha; encoder.lossless = self.lossless; - encoder.quality = self.quality; encoder.use_container = self.use_container; encoder.decoding_speed = self.decoding_speed; encoder.color_encoding = match self.num_channels { @@ -116,13 +118,16 @@ impl Encoder { true => encoder.encode_jpeg(&data).unwrap(), false => { let frame = EncoderFrame::new(data).num_channels(self.num_channels); - let metadata = EncoderMetadata::new() - .exif(exif.unwrap()) - .jumb(jumb.unwrap()) - .xmp(xmp.unwrap()); encoder - .encode_frame_with_metadata(&frame, width, height, metadata) - .unwrap() + .add_metadata(&Metadata::Exif(exif.unwrap()), true) + .unwrap(); + encoder + .add_metadata(&Metadata::Xmp(xmp.unwrap()), true) + .unwrap(); + encoder + .add_metadata(&Metadata::Jumb(jumb.unwrap()), true) + .unwrap(); + encoder.encode_frame(&frame, width, height).unwrap() } }; Cow::Owned(buffer.data)