Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jxl-oxide: Add integration to image crate #368

Merged
merged 11 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- `jxl-oxide`: Accept `u8` and `u16` output buffers in `ImageStream::write_to_buffer` (#366).
- `jxl-oxide`: Add `image` integration under a feature flag (#368).

### Changed
- `jxl-color`: Use better PQ to HLG method (#348).
Expand Down
25 changes: 23 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
members = ["crates/*"]
resolver = "2"

[workspace.dependencies.bytemuck]
version = "1.19.0"

[workspace.dependencies.tracing]
version = "0.1.40"
default-features = false
Expand Down
8 changes: 7 additions & 1 deletion crates/jxl-oxide-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ tracing.workspace = true
version = "0.5.1"
optional = true

[dependencies.image]
version = "0.25.4"
default-features = false
optional = true

[dependencies.jxl-oxide]
version = "0.10.0"
path = "../jxl-oxide"
Expand Down Expand Up @@ -42,10 +47,11 @@ version = "0.13.0"
optional = true

[features]
default = ["net", "mimalloc", "rayon", "conformance", "crop", "decode", "bench"]
default = ["net", "mimalloc", "rayon", "image", "conformance", "crop", "decode", "bench"]
net = ["dep:reqwest"]
mimalloc = ["dep:mimalloc"]
rayon = ["jxl-oxide/rayon"]
image = ["dep:image", "jxl-oxide/image"]
conformance = []
crop = ["dep:rand"]
decode = ["dep:zstd"]
Expand Down
Binary file added crates/jxl-oxide-tests/tests/image/grayscale.icc
Binary file not shown.
88 changes: 88 additions & 0 deletions crates/jxl-oxide-tests/tests/image/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use std::fs::File;

use image::DynamicImage;
use jxl_oxide::integration::JxlDecoder;
use jxl_oxide_tests as util;

#[test]
fn decode_u8() {
let path = util::conformance_path("lz77_flower");
let file = File::open(path).unwrap();
let decoder = JxlDecoder::new(file).unwrap();

let image = DynamicImage::from_decoder(decoder).unwrap();
assert_eq!(image.color(), image::ColorType::Rgb8);
assert_eq!(image.width(), 834);
assert_eq!(image.height(), 244);
}

#[test]
fn decode_u16() {
let path = util::conformance_path("sunset_logo");
let file = File::open(path).unwrap();
let decoder = JxlDecoder::new(file).unwrap();

let image = DynamicImage::from_decoder(decoder).unwrap();
assert_eq!(image.color(), image::ColorType::Rgba16);
assert_eq!(image.width(), 924);
assert_eq!(image.height(), 1386);
}

#[test]
fn decode_f32() {
let path = util::conformance_path("lossless_pfm");
let file = File::open(path).unwrap();
let decoder = JxlDecoder::new(file).unwrap();

let image = DynamicImage::from_decoder(decoder).unwrap();
assert_eq!(image.color(), image::ColorType::Rgb32F);
assert_eq!(image.width(), 500);
assert_eq!(image.height(), 500);
}

#[test]
fn decode_gray_xyb() {
let path = util::conformance_path("grayscale");
let file = File::open(path).unwrap();
let decoder = JxlDecoder::new(file).unwrap();

let image = DynamicImage::from_decoder(decoder).unwrap();
assert_eq!(image.color(), image::ColorType::L8);
assert_eq!(image.width(), 200);
assert_eq!(image.height(), 200);
}

#[test]
fn decode_gray_modular() {
let path = util::conformance_path("grayscale_public_university");
let file = File::open(path).unwrap();
let decoder = JxlDecoder::new(file).unwrap();

let image = DynamicImage::from_decoder(decoder).unwrap();
assert_eq!(image.color(), image::ColorType::L8);
assert_eq!(image.width(), 2880);
assert_eq!(image.height(), 1620);
}

#[test]
fn decode_cmyk() {
let path = util::conformance_path("cmyk_layers");
let file = File::open(path).unwrap();
let decoder = JxlDecoder::new(file).unwrap();

let image = DynamicImage::from_decoder(decoder).unwrap();
assert_eq!(image.color(), image::ColorType::Rgba8);
assert_eq!(image.width(), 512);
assert_eq!(image.height(), 512);
}

#[test]
fn icc_profile() {
let path = util::conformance_path("grayscale");
let file = File::open(path).unwrap();
let mut decoder = JxlDecoder::new(file).unwrap();
let icc = image::ImageDecoder::icc_profile(&mut decoder)
.unwrap()
.unwrap();
assert_eq!(&icc, include_bytes!("./grayscale.icc"));
}
3 changes: 3 additions & 0 deletions crates/jxl-oxide-tests/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ mod crop;
#[cfg(feature = "decode")]
mod decode;

#[cfg(feature = "image")]
mod image;

mod fuzz_findings;
17 changes: 16 additions & 1 deletion crates/jxl-oxide/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ edition = "2021"
[dependencies]
tracing.workspace = true

[dependencies.bytemuck]
workspace = true
optional = true

[dependencies.image]
version = "0.25.4"
default-features = false
optional = true

[dependencies.jxl-bitstream]
version = "0.5.0-alpha.0"
path = "../jxl-bitstream"
Expand Down Expand Up @@ -52,5 +61,11 @@ optional = true

[features]
default = ["rayon"]
rayon = ["jxl-threadpool/rayon"]
image = ["dep:bytemuck", "dep:image"]
lcms2 = ["dep:lcms2"]
rayon = ["jxl-threadpool/rayon"]
__examples = ["image?/png"]

[[example]]
name = "image-integration"
required-features = ["image", "__examples"]
32 changes: 32 additions & 0 deletions crates/jxl-oxide/examples/image-integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use image::{DynamicImage, ImageDecoder};
use jxl_oxide::integration::JxlDecoder;

fn main() {
let mut args = std::env::args_os().skip(1);
let path = args
.next()
.expect("expected input filename as a command line argument");
let output_path = args
.next()
.expect("expected output filename as a command line argument");
assert!(args.next().is_none(), "extra command line argument found");

let file = std::fs::File::open(path).expect("cannot open file");
let mut decoder = JxlDecoder::new(file).expect("cannot decode image");

#[allow(unused)]
let icc = decoder.icc_profile().unwrap();
let image = DynamicImage::from_decoder(decoder).expect("cannot decode image");

let output_file = std::fs::File::create(output_path).expect("cannot open output file");
let encoder = image::codecs::png::PngEncoder::new(output_file);
// FIXME: PNG encoder of `image` doesn't support setting ICC profile for some reason
// use image::ImageEncoder;
// let mut encoder = encoder;
// if let Some(icc) = icc {
// encoder.set_icc_profile(icc).unwrap();
// }
image
.write_with_encoder(encoder)
.expect("cannot encode image");
}
7 changes: 7 additions & 0 deletions crates/jxl-oxide/src/fb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,11 +420,18 @@ mod private {
use jxl_image::BitDepth;
use jxl_render::ImageBuffer;

#[cfg(not(feature = "image"))]
pub trait Sealed: Sized + Default {
fn copy_from_grid(&mut self, grid: &ImageBuffer, x: usize, y: usize, bit_depth: BitDepth);
fn copy_from_f32(&mut self, val: f32);
}

#[cfg(feature = "image")]
pub trait Sealed: Sized + Default + bytemuck::NoUninit + bytemuck::AnyBitPattern {
fn copy_from_grid(&mut self, grid: &ImageBuffer, x: usize, y: usize, bit_depth: BitDepth);
fn copy_from_f32(&mut self, val: f32);
}

impl Sealed for f32 {
#[inline]
fn copy_from_grid(&mut self, grid: &ImageBuffer, x: usize, y: usize, bit_depth: BitDepth) {
Expand Down
4 changes: 4 additions & 0 deletions crates/jxl-oxide/src/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[cfg(feature = "image")]
mod image;

pub use image::*;
Loading