11 releases (7 breaking)

0.8.0 Mar 26, 2024
0.7.1 Feb 28, 2024
0.5.2 Dec 23, 2023
0.5.1 Nov 21, 2023
0.3.0 Jun 16, 2023

#128 in Images

Download history 362/week @ 2024-01-03 207/week @ 2024-01-10 269/week @ 2024-01-17 134/week @ 2024-01-24 55/week @ 2024-01-31 137/week @ 2024-02-07 128/week @ 2024-02-14 431/week @ 2024-02-21 537/week @ 2024-02-28 247/week @ 2024-03-06 265/week @ 2024-03-13 299/week @ 2024-03-20 344/week @ 2024-03-27 335/week @ 2024-04-03 116/week @ 2024-04-10 215/week @ 2024-04-17

1,136 downloads per month
Used in 4 crates

MIT/Apache

1MB
20K 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)
  • lcms2: Enable integration with Little CMS 2.

Dependencies

~0.6–1.3MB
~25K SLoC