17 releases (9 breaking)

new 0.10.2 Dec 7, 2024
0.10.1 Nov 10, 2024
0.9.1 Oct 12, 2024
0.8.1 Jul 30, 2024
0.3.0 Jun 16, 2023

#68 in Images

Download history 275/week @ 2024-08-20 412/week @ 2024-08-27 371/week @ 2024-09-03 364/week @ 2024-09-10 527/week @ 2024-09-17 550/week @ 2024-09-24 1408/week @ 2024-10-01 2314/week @ 2024-10-08 1320/week @ 2024-10-15 1309/week @ 2024-10-22 1942/week @ 2024-10-29 2026/week @ 2024-11-05 6392/week @ 2024-11-12 4511/week @ 2024-11-19 5393/week @ 2024-11-26 4366/week @ 2024-12-03

21,271 downloads per month
Used in 14 crates (7 directly)

MIT/Apache

1MB
22K SLoC

jxl-oxide

jxl-oxide is a JPEG XL decoder written in pure Rust. It's internally organized into a few small crates. This crate acts as a blanket and provides a simple interface made from those crates to decode the actual image.

See the crate-level docs for usage.


lib.rs:

jxl-oxide is a JPEG XL decoder written in pure Rust. It's internally organized into a few small crates. This crate acts as a blanket and provides a simple interface made from those crates to decode the actual image.

Decoding an image

Decoding a JPEG XL image starts with constructing JxlImage. First create a builder using JxlImage::builder, and use open to read a file:

let image = JxlImage::builder().open("input.jxl").expect("Failed to read image header");
println!("{:?}", image.image_header()); // Prints the image header

Or, if you're reading from a reader that implements Read, you can use read:

let image = JxlImage::builder().read(reader).expect("Failed to read image header");
println!("{:?}", image.image_header()); // Prints the image header

In async context, you'll probably want to feed byte buffers directly. In this case, create an image struct with uninitialized state using build_uninit, and call feed_bytes and try_init:

#
let mut uninit_image = JxlImage::builder().build_uninit();
let image = loop {
    uninit_image.feed_bytes(reader.read().await?);
    match uninit_image.try_init()? {
        InitializeResult::NeedMoreData(uninit) => {
            uninit_image = uninit;
        }
        InitializeResult::Initialized(image) => {
            break image;
        }
    }
};
println!("{:?}", image.image_header()); // Prints the image header

JxlImage parses the image header and embedded ICC profile (if there's any). Use JxlImage::render_frame to render the image.

use jxl_oxide::{JxlImage, RenderResult};

for keyframe_idx in 0..image.num_loaded_keyframes() {
    let render = image.render_frame(keyframe_idx)?;
    present_image(render);
}

Color management

jxl-oxide has basic color management support, which enables color transformation between well-known color encodings and parsing simple, matrix-based ICC profiles. However, jxl-oxide alone does not support conversion to and from arbitrary ICC profiles, notably CMYK profiles. This includes converting from embedded ICC profiles.

Use JxlImage::request_color_encoding or JxlImage::request_icc to set color encoding of rendered images. Conversion to and/or from ICC profiles may occur if you do this; in that case, external CMS need to be set using JxlImage::set_cms.

let mut image = JxlImage::builder().read(reader).expect("Failed to read image header");
image.set_cms(MyCustomCms);

let color_encoding = EnumColourEncoding::display_p3(RenderingIntent::Perceptual);
image.request_color_encoding(color_encoding);

External CMS is set to Little CMS 2 by default if lcms2 feature is enabled. You can explicitly disable this by setting CMS to NullCms.

let mut image = JxlImage::builder().read(reader).expect("Failed to read image header");
image.set_cms(NullCms);

Not using set_cms for color management

If implementing ColorManagementSystem is difficult for your use case, color management can be done separately using ICC profile of rendered images. JxlImage::rendered_icc returns ICC profile for further processing.

use jxl_oxide::{JxlImage, RenderResult};

let icc_profile = image.rendered_icc();
for keyframe_idx in 0..image.num_loaded_keyframes() {
    let render = image.render_frame(keyframe_idx)?;
    present_image_with_cms(render, &icc_profile);
}

Feature flags

  • rayon: Enable multithreading with Rayon. (default)
  • image: Enable integration with image crate.
  • lcms2: Enable integration with Little CMS 2.

Dependencies

~0.6–1.8MB
~35K SLoC