From 6be355ad155f2a06b1d0fa3eae52cee1d72dc9d4 Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Sun, 23 Jul 2023 03:22:57 +0200 Subject: [PATCH] refactor: migrate to jxl-oxide (#28) * refactor: migrate to jxl-oxide * refactor: trim needless `&` * refactor: reduce use of `std::mem::transmute()` * chore: add `#![allow]` to suppress warnings from library uses * chore(ci): skip extra toolchain installation * docs(README): remove build environment requirements * fix: proper size after applying orientation --- .github/workflows/ci.yml | 28 +-- Cargo.lock | 363 +++++++++---------------------- Cargo.toml | 2 +- README.md | 18 +- src/dll.rs | 2 +- src/lib.rs | 228 +++++++++++++++---- src/properties.rs | 29 +-- src/registry.rs | 4 +- src/registry/property_handler.rs | 2 +- 9 files changed, 308 insertions(+), 368 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ddcc31..cf0bbf1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,9 +10,7 @@ jobs: runs-on: windows-latest steps: - name: Checkout sources - uses: actions/checkout@v2 - with: - submodules: recursive + uses: actions/checkout@v3 - name: Install stable toolchain uses: actions-rs/toolchain@v1 @@ -21,28 +19,6 @@ jobs: toolchain: stable override: true - - name: Install Ninja - uses: seanmiddleditch/gha-setup-ninja@master - - - name: Cache LLVM and Clang - id: cache-llvm - uses: actions/cache@v2 - with: - path: ${{ runner.temp }}/llvm - key: llvm-14.0 - - name: Install LLVM and Clang - uses: KyleMayes/install-llvm-action@v1 - with: - version: "14.0" - directory: ${{ runner.temp }}/llvm - cached: ${{ steps.cache-llvm.outputs.cache-hit }} - - # See: - # https://github.com/rust-lang/rust-bindgen/issues/1797 - # https://github.com/KyleMayes/clang-sys/issues/121 - - name: Set LIBCLANG_PATH - run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV - - name: Run cargo build uses: actions-rs/cargo@v1 with: @@ -53,7 +29,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install stable toolchain uses: actions-rs/toolchain@v1 diff --git a/Cargo.lock b/Cargo.lock index 7fc88be..acdbcfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,248 +3,153 @@ version = 3 [[package]] -name = "aho-corasick" -version = "0.7.18" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "atty" -version = "0.2.14" +name = "jxl-bitstream" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "76c3205d18cf6229b3f694de66e592886ff7afb0740bc0e85594e3b28d6f6622" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "tracing", ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "jxl-coding" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bindgen" -version = "0.60.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" +checksum = "7075600c762c1ce9e2dd1fbc3fa8ded2af1401fe2221449eca943977742dd0b4" dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "clap", - "env_logger", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "which", + "jxl-bitstream", ] [[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "cc" -version = "1.0.73" +name = "jxl-color" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +checksum = "9084bc33b6d01e26b1db6c187514a51a57f4e3780335f3120ab55ee0b08f6e73" dependencies = [ - "nom", + "jxl-bitstream", + "jxl-coding", + "jxl-grid", ] [[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clang-sys" -version = "1.3.3" +name = "jxl-frame" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" +checksum = "e2c4e276ea56d8bc4961f13501d565cc6b665f0da76e0e5f90aa44a1475eb409" dependencies = [ - "glob", - "libc", - "libloading", + "jxl-bitstream", + "jxl-coding", + "jxl-grid", + "jxl-image", + "jxl-modular", + "jxl-vardct", + "tracing", ] [[package]] -name = "clap" -version = "3.1.18" +name = "jxl-grid" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" -dependencies = [ - "atty", - "bitflags", - "clap_lex", - "indexmap", - "strsim", - "termcolor", - "textwrap", -] +checksum = "48800b21ed6bb3bbc2f818ae9cd40530bdfb1a211f57d5a7a49b8b10f62145e8" [[package]] -name = "clap_lex" -version = "0.2.0" +name = "jxl-image" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +checksum = "28d10c717baa0dd19c25b37b6baebb5f07d7efdaa6225a65aa98dcaf1d40a3e5" dependencies = [ - "os_str_bytes", + "jxl-bitstream", + "jxl-color", + "jxl-grid", + "tracing", ] [[package]] -name = "cmake" -version = "0.1.48" +name = "jxl-modular" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" +checksum = "a4fea731da48d358a60d6185c58ac897f22895ea0e3380df04c106c87e192a7d" dependencies = [ - "cc", + "jxl-bitstream", + "jxl-coding", + "jxl-grid", + "tracing", ] [[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "env_logger" -version = "0.9.0" +name = "jxl-oxide" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "7d90d7ccb9a843e69563abdab2cd362702668f23e39e7fa3dcdf2594b1d29042" dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", + "jxl-bitstream", + "jxl-color", + "jxl-frame", + "jxl-grid", + "jxl-image", + "jxl-render", + "tracing", ] [[package]] -name = "glob" +name = "jxl-render" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "774684715db3b6e60a13059ad746829c9f9b39b7865ba0dc7ea9fd93469d9297" dependencies = [ - "libc", + "jxl-bitstream", + "jxl-coding", + "jxl-color", + "jxl-frame", + "jxl-grid", + "jxl-image", + "jxl-modular", + "jxl-vardct", + "tracing", ] [[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "indexmap" -version = "1.8.2" +name = "jxl-vardct" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" +checksum = "1abaffccbc217e48cb45b9aca639c18a873b66200fc5a3695fb98333e0b1fead" dependencies = [ - "autocfg", - "hashbrown", + "jxl-bitstream", + "jxl-coding", + "jxl-grid", + "jxl-modular", + "tracing", ] [[package]] name = "jxl-winthumb" version = "0.1.19" dependencies = [ - "kagamijxl", + "jxl-oxide", "log", "simple-logging", "windows", "winreg", ] -[[package]] -name = "kagamijxl" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94575f15548df13eeaa090f47ab4ef4a790b0c05a29e80bfd029911233f1c8fa" -dependencies = [ - "libjxl-sys", -] - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" -[[package]] -name = "libjxl-src" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6e9340063b5b46e97791f9a82db58d0d1d112de4561dc7534074838d311f75" -dependencies = [ - "cmake", -] - -[[package]] -name = "libjxl-sys" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356d4745c8eaebf32ae2fdc0b7c06f78ae1bd64863a61e593e504f9c1adda888" -dependencies = [ - "bindgen", - "libjxl-src", -] - -[[package]] -name = "libloading" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" -dependencies = [ - "cfg-if", - "winapi", -] - [[package]] name = "log" version = "0.4.17" @@ -255,38 +160,16 @@ dependencies = [ ] [[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "nom" -version = "7.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "os_str_bytes" -version = "6.1.0" +name = "once_cell" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] -name = "peeking_take_while" -version = "0.1.2" +name = "pin-project-lite" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "proc-macro2" @@ -312,35 +195,6 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" -[[package]] -name = "regex" -version = "1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "shlex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" - [[package]] name = "simple-logging" version = "2.0.2" @@ -352,12 +206,6 @@ dependencies = [ "thread-id", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "syn" version = "1.0.96" @@ -369,21 +217,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" - [[package]] name = "thread-id" version = "3.3.0" @@ -396,22 +229,31 @@ dependencies = [ ] [[package]] -name = "unicode-ident" -version = "1.0.0" +name = "tracing" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] [[package]] -name = "which" -version = "4.2.5" +name = "tracing-core" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ - "either", - "lazy_static", - "libc", + "once_cell", ] +[[package]] +name = "unicode-ident" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" + [[package]] name = "winapi" version = "0.3.9" @@ -428,15 +270,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 827a0c2..1fd16e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,8 @@ crate-type = ["cdylib"] [dependencies] simple-logging = "2.0.2" log = "0.4.17" -kagamijxl = "0.3.3" winreg = "0.10.1" +jxl-oxide = "0.3.0" [dependencies.windows] version = "0.37.0" diff --git a/README.md b/README.md index 2f76d34..50198bc 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,10 @@ # jxl-winthumb -A JPEG XL (*.jxl) thumbnail handler for Windows File Explorer. - -Now with WIC decoding support, which means you can use Windows Photo Viewer or any WIC-capable image viewers to view JXL files. - -## How to install - -0. Install [Microsoft Visual C++ Redistributable for Visual Studio 2022](https://visualstudio.microsoft.com/downloads/#microsoft-visual-c-redistributable-for-visual-studio-2022) if it's not already installed. -1. Download the latest dll file from the [releases](https://github.com/saschanaz/jxl-winthumb/releases) page. -2. Open a terminal window as administrator -3. Move to your download directory -4. `regsvr32 jxl_winthumb.dll`, or to uninstall, `regsvr32 /u jxl_winthumb.dll`. +A JPEG XL (*.jxl) WIC decoder to render thumbnails on Windows File Explorer or view images on any WIC-capable image viewers. ## Build environment -Please read [the requirements](https://github.com/saschanaz/jxl-rs/tree/main/libjxl-src) to build the libjxl dependency, or take a look at [the CI configuration](https://github.com/saschanaz/jxl-winthumb/blob/main/.github/workflows/ci.yml). - -### Rust Nightly - -Currently Rust Nightly toolchain is required because of [windows-rs dependency](https://github.com/microsoft/windows-rs/issues/1408#issuecomment-1021692345). +Use the stable Rust toolchain. Current toolchain as of 23th July 2023 is 1.71.0. ## Helpful resources diff --git a/src/dll.rs b/src/dll.rs index c372de6..258c957 100644 --- a/src/dll.rs +++ b/src/dll.rs @@ -121,7 +121,7 @@ pub unsafe extern "system" fn DllGetClassObject( { // Set up logging to the project directory. simple_logging::log_to_file( - &format!("{}\\debug.log", env!("CARGO_MANIFEST_DIR")), + format!("{}\\debug.log", env!("CARGO_MANIFEST_DIR")), log::LevelFilter::Trace, ) .unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 90ab498..743ebcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,11 @@ #![crate_type = "dylib"] // https://github.com/microsoft/windows-rs/issues/1506 #![allow(clippy::not_unsafe_ptr_arg_deref)] +// TODO: Update windows-rs +#![allow(unused_must_use)] +#![allow(non_snake_case)] -use kagamijxl::{DecodeProgress, Decoder}; +use jxl_oxide::{FrameBuffer, JxlImage, PixelFormat}; use std::{cell::RefCell, io::BufReader, rc::Rc}; use windows::core::{implement, Interface, GUID}; @@ -22,10 +25,19 @@ mod guid; mod properties; +type JxlImageFromWinStream = JxlImage>; + +pub struct DecodedResult { + frames: Vec>, + pixel_format: PixelFormat, + width: u32, + height: u32, +} + #[implement(Windows::Win32::Graphics::Imaging::IWICBitmapDecoder)] #[derive(Default)] pub struct JXLWICBitmapDecoder { - decoded: RefCell>>>, + decoded: RefCell>, } impl JXLWICBitmapDecoder { @@ -50,12 +62,49 @@ impl IWICBitmapDecoder_Impl for JXLWICBitmapDecoder { let stream = WinStream::from(pistream.to_owned().unwrap()); let reader = BufReader::new(stream); - let decoder = Decoder::new(); - - let result = decoder.decode_buffer(reader).map_err(|err| { + let mut image = JxlImageFromWinStream::from_reader(reader).map_err(|err| { windows::core::Error::new(WINCODEC_ERR_BADIMAGE, format!("{:?}", err).as_str().into()) })?; - self.decoded.replace(Some(Rc::new(RefCell::new(result)))); + let mut renderer = image.renderer(); + + let mut frames: Vec> = Vec::new(); + + let mut load_all = || -> jxl_oxide::Result { + loop { + let load_result = renderer.render_next_frame()?; + match load_result { + jxl_oxide::RenderResult::NoMoreFrames => { + return Ok(renderer.num_loaded_keyframes()) + } + jxl_oxide::RenderResult::NeedMoreData => { + return Err(Box::new(std::io::Error::from( + std::io::ErrorKind::UnexpectedEof, + ))) + } + jxl_oxide::RenderResult::Done(render) => { + frames.push(Rc::new(render.image())); + } + } + } + }; + load_all().map_err(|err| { + windows::core::Error::new(WINCODEC_ERR_BADIMAGE, format!("{:?}", err).as_str().into()) + })?; + + let (width, height, _left, _top) = renderer.image_header().metadata.apply_orientation( + renderer.image_header().size.width, + renderer.image_header().size.height, + 0, + 0, + false, + ); + + self.decoded.replace(Some(DecodedResult { + frames, + pixel_format: renderer.pixel_format(), + width, + height, + })); Ok(()) } @@ -78,6 +127,7 @@ impl IWICBitmapDecoder_Impl for JXLWICBitmapDecoder { fn CopyPalette(&self, _pipalette: &Option) -> windows::core::Result<()> { log::trace!("JXLWICBitmapDecoder::CopyPalette"); + // TODO WINCODEC_ERR_PALETTEUNAVAILABLE.ok() } @@ -93,12 +143,26 @@ impl IWICBitmapDecoder_Impl for JXLWICBitmapDecoder { fn GetColorContexts( &self, - _ccount: u32, - _ppicolorcontexts: *mut Option, - _pcactualcount: *mut u32, + ccount: u32, + ppicolorcontexts: *mut Option, + pcactualcount: *mut u32, ) -> windows::core::Result<()> { - log::trace!("JXLWICBitmapDecoder::GetColorContexts"); - WINCODEC_ERR_UNSUPPORTEDOPERATION.ok() + log::trace!( + "JXLWICBitmapDecoder::GetColorContexts {} {:?} {:?}", + ccount, + ppicolorcontexts, + pcactualcount + ); + // TODO: Proper color context + unsafe { + if !ppicolorcontexts.is_null() && ccount == 1 { + *ppicolorcontexts = Some(JXLWICColorContext {}.into()); + } + if !pcactualcount.is_null() { + *pcactualcount = 1; + } + } + Ok(()) } fn GetThumbnail(&self) -> windows::core::Result { @@ -107,45 +171,55 @@ impl IWICBitmapDecoder_Impl for JXLWICBitmapDecoder { } fn GetFrameCount(&self) -> windows::core::Result { - if self.decoded.borrow().is_none() { + let decoded_ref = self.decoded.borrow(); + if decoded_ref.is_none() { return Err(WINCODEC_ERR_NOTINITIALIZED.ok().unwrap_err()); } + let frame_count = decoded_ref.as_ref().unwrap().frames.len(); - let frame_count = self - .decoded - .borrow() - .as_ref() - .unwrap() - .borrow() - .frames - .len(); log::trace!("JXLWICBitmapDecoder::GetFrameCount: {}", frame_count); Ok(frame_count as u32) } fn GetFrame(&self, index: u32) -> windows::core::Result { - if self.decoded.borrow().is_none() { + let decoded_ref = self.decoded.borrow(); + if decoded_ref.is_none() { return Err(WINCODEC_ERR_NOTINITIALIZED.ok().unwrap_err()); } - let basic_info = self.decoded.borrow().as_ref().unwrap().borrow().basic_info; - log::trace!("[{}]: {:?}", index, basic_info); + let decoded = decoded_ref.as_ref().unwrap(); + log::trace!("[{}/{}]", index, decoded.frames.len()); + + if index >= decoded.frames.len() as u32 { + return Err(WINCODEC_ERR_FRAMEMISSING.ok().unwrap_err()); + } - let frame_decode = - JXLWICBitmapFrameDecode::new(self.decoded.borrow().to_owned().unwrap(), index as usize); + let frame_decode = JXLWICBitmapFrameDecode::new( + decoded.frames[index as usize].clone(), + decoded.pixel_format, + decoded.width, + decoded.height, + ); Ok(frame_decode.into()) } } #[implement(Windows::Win32::Graphics::Imaging::IWICBitmapFrameDecode)] pub struct JXLWICBitmapFrameDecode { - decoded: Rc>, - index: usize, + frame: Rc, + pixel_format: PixelFormat, + width: u32, + height: u32, } impl JXLWICBitmapFrameDecode { - pub fn new(decoded: Rc>, index: usize) -> Self { - Self { decoded, index } + pub fn new(frame: Rc, pixel_format: PixelFormat, width: u32, height: u32) -> Self { + Self { + frame, + pixel_format, + width, + height, + } } } @@ -155,16 +229,30 @@ impl IWICBitmapSource_Impl for JXLWICBitmapFrameDecode { fn GetSize(&self, puiwidth: *mut u32, puiheight: *mut u32) -> windows::core::Result<()> { log::trace!("JXLWICBitmapFrameDecode::GetSize"); unsafe { - *puiwidth = self.decoded.borrow().basic_info.xsize; - *puiheight = self.decoded.borrow().basic_info.ysize; + *puiwidth = self.width; + *puiheight = self.height; } Ok(()) } fn GetPixelFormat(&self) -> windows::core::Result { log::trace!("JXLWICBitmapFrameDecode::GetPixelFormat"); - // TODO: Support HDR - Ok(GUID_WICPixelFormat32bppRGBA) + // TODO: Support all formats + match self.pixel_format { + PixelFormat::Gray => Ok(GUID_WICPixelFormat32bppGrayFloat), + PixelFormat::Graya => Err(windows::core::Error::new( + WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT, + "Gray alpha image is currently not supported".into(), + )), + PixelFormat::Rgb => Ok(GUID_WICPixelFormat96bppRGBFloat), + PixelFormat::Rgba => Ok(GUID_WICPixelFormat128bppRGBAFloat), + jxl_oxide::PixelFormat::Cmyk | jxl_oxide::PixelFormat::Cmyka => { + Err(windows::core::Error::new( + WINCODEC_ERR_BADIMAGE, + "Cmyk is currently not supported".into(), + )) + } + } } fn GetResolution(&self, pdpix: *mut f64, pdpiy: *mut f64) -> windows::core::Result<()> { @@ -198,17 +286,19 @@ impl IWICBitmapSource_Impl for JXLWICBitmapFrameDecode { let prc = unsafe { prc.as_ref().unwrap() }; log::trace!("JXLWICBitmapFrameDecode::CopyPixels::WICRect {:?}", prc); - let basic_info = &self.decoded.borrow().basic_info; - let data = &self.decoded.borrow().frames[self.index].data; + let channels = self.frame.channels(); for y in prc.Y..(prc.Y + prc.Height) { - let src_offset = basic_info.xsize as i32 * 4 * y; + let src_offset = self.width as i32 * channels as i32 * y; let dst_offset = prc.Width * 4 * (y - prc.Y); unsafe { std::ptr::copy_nonoverlapping( - data.as_ptr().offset((src_offset + prc.X) as isize), - pbbuffer.offset(dst_offset as isize), - (prc.Width as usize) * 4, + self.frame + .buf() + .as_ptr() + .offset((src_offset + prc.X) as isize), + (pbbuffer as *mut f32).offset(dst_offset as isize), + (prc.Width as usize) * channels, ); } } @@ -225,13 +315,23 @@ impl IWICBitmapFrameDecode_Impl for JXLWICBitmapFrameDecode { fn GetColorContexts( &self, - _ccount: u32, - _ppicolorcontexts: *mut Option, + ccount: u32, + ppicolorcontexts: *mut Option, pcactualcount: *mut u32, ) -> windows::core::Result<()> { - log::trace!("JXLWICBitmapFrameDecode::GetColorContexts"); + log::trace!( + "JXLWICBitmapFrameDecode::GetColorContexts {} {:?} {:?}", + ccount, + ppicolorcontexts, + pcactualcount + ); unsafe { - *pcactualcount = 0; + if !ppicolorcontexts.is_null() && ccount == 1 { + *ppicolorcontexts = Some(JXLWICColorContext {}.into()); + } + if !pcactualcount.is_null() { + *pcactualcount = 1; + } } Ok(()) } @@ -241,3 +341,45 @@ impl IWICBitmapFrameDecode_Impl for JXLWICBitmapFrameDecode { Err(WINCODEC_ERR_CODECNOTHUMBNAIL.ok().unwrap_err()) } } + +#[implement(Windows::Win32::Graphics::Imaging::IWICColorContext)] +pub struct JXLWICColorContext {} + +impl IWICColorContext_Impl for JXLWICColorContext { + fn InitializeFromFilename( + &self, + _wzfilename: &::windows::core::PCWSTR, + ) -> ::windows::core::Result<()> { + WINCODEC_ERR_UNSUPPORTEDOPERATION.ok() + } + + fn InitializeFromMemory( + &self, + _pbbuffer: *const u8, + _cbbuffersize: u32, + ) -> ::windows::core::Result<()> { + WINCODEC_ERR_UNSUPPORTEDOPERATION.ok() + } + + fn InitializeFromExifColorSpace(&self, _value: u32) -> ::windows::core::Result<()> { + WINCODEC_ERR_UNSUPPORTEDOPERATION.ok() + } + + fn GetType(&self) -> ::windows::core::Result { + Ok(WICColorContextExifColorSpace) + } + + fn GetProfileBytes( + &self, + _cbbuffer: u32, + _pbbuffer: *mut u8, + _pcbactual: *mut u32, + ) -> ::windows::core::Result<()> { + // TODO: Implement this for proper ICC profile support + WINCODEC_ERR_UNSUPPORTEDOPERATION.ok() + } + + fn GetExifColorSpace(&self) -> ::windows::core::Result { + Ok(1) // sRGB + } +} diff --git a/src/properties.rs b/src/properties.rs index 0fd5184..0652f13 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -14,7 +14,7 @@ use windows::Win32::{ }; use crate::winstream::WinStream; -use kagamijxl::Decoder; +use jxl_oxide::JxlImage; #[implement( Windows::Win32::UI::Shell::PropertiesSystem::IInitializeWithStream, @@ -46,16 +46,23 @@ impl IInitializeWithStream_Impl for JXLPropertyStore { let stream = WinStream::from(pstream.to_owned().unwrap()); let reader = BufReader::new(stream); - let mut decoder = Decoder::new(); - decoder.no_full_image = true; - let result = decoder.decode_buffer(reader).map_err(|err| { + let mut image = JxlImage::from_reader(reader).map_err(|err| { windows::core::Error::new(WINCODEC_ERR_BADIMAGE, format!("{:?}", err).as_str().into()) })?; + let renderer = image.renderer(); + + let (width, height, _left, _top) = renderer.image_header().metadata.apply_orientation( + renderer.image_header().size.width, + renderer.image_header().size.height, + 0, + 0, + false, + ); unsafe { PSCreateMemoryPropertyStore( &IPropertyStoreCache::IID, - std::mem::transmute(&self.props), + &self.props as *const _ as *mut *mut std::ffi::c_void, )? }; @@ -67,14 +74,14 @@ impl IInitializeWithStream_Impl for JXLPropertyStore { let PSGUID_IMAGESUMMARYINFORMATION = GUID::from_u128(0x6444048F_4C8B_11D1_8B70_080036B11A03); - let variant = unsafe { InitPropVariantFromUInt32Vector(&[result.basic_info.xsize])? }; + let variant = unsafe { InitPropVariantFromUInt32Vector(&[width])? }; let propkey = PROPERTYKEY { fmtid: PSGUID_IMAGESUMMARYINFORMATION, pid: 3, }; unsafe { props.SetValueAndState(&propkey, &variant, PSC_READONLY)? }; - let variant = unsafe { InitPropVariantFromUInt32Vector(&[result.basic_info.ysize])? }; + let variant = unsafe { InitPropVariantFromUInt32Vector(&[height])? }; let propkey = PROPERTYKEY { fmtid: PSGUID_IMAGESUMMARYINFORMATION, pid: 4, @@ -92,12 +99,8 @@ impl IInitializeWithStream_Impl for JXLPropertyStore { let pcwstr = pcwstr.into_param().abi(); InitPropVariantFromStringVector(&[PWSTR(pcwstr.0 as *mut _)]) } - let variant = unsafe { - InitPropVariantFromStringVectorWrapped(format!( - "{} x {}", - result.basic_info.xsize, result.basic_info.ysize - ))? - }; + let variant = + unsafe { InitPropVariantFromStringVectorWrapped(format!("{} x {}", width, height))? }; let propkey = PROPERTYKEY { fmtid: PSGUID_IMAGESUMMARYINFORMATION, pid: 13, diff --git a/src/registry.rs b/src/registry.rs index 774e6f6..0eaf1de 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -21,7 +21,7 @@ const PERCEIVED_TYPE_VALUE: &str = "image"; fn register_clsid_base(module_path: &str, clsid: &windows::core::GUID) -> std::io::Result { let hkcr = RegKey::predef(HKEY_CLASSES_ROOT); let clsid_key = hkcr.open_subkey("CLSID")?; - let (key, _) = clsid_key.create_subkey(&guid_to_string(clsid))?; + let (key, _) = clsid_key.create_subkey(guid_to_string(clsid))?; key.set_value("", &"jxl-winthumb")?; let (inproc, _) = key.create_subkey("InProcServer32")?; @@ -167,7 +167,7 @@ fn register_provider() -> std::io::Result<()> { // https://docs.microsoft.com/en-us/windows/win32/wic/-wic-integrationregentries#integration-with-the-windows-thumbnail-cache let (system_shell_ex, _) = system_ext_key.create_subkey("ShellEx")?; system_shell_ex - .create_subkey(&guid_to_string( + .create_subkey(guid_to_string( &windows::Win32::UI::Shell::IThumbnailProvider::IID, ))? .0 diff --git a/src/registry/property_handler.rs b/src/registry/property_handler.rs index 9912b94..6436b16 100644 --- a/src/registry/property_handler.rs +++ b/src/registry/property_handler.rs @@ -29,7 +29,7 @@ pub fn unregister_property_handler() -> std::io::Result<()> { let clsid_key = hkcr.open_subkey("CLSID")?; clsid_key - .delete_subkey_all(&guid_to_string(&JXLPropertyStore::CLSID)) + .delete_subkey_all(guid_to_string(&JXLPropertyStore::CLSID)) .ok(); let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);