zenjxl is a JPEG XL encoding and decoding library combining zenjxl-decoder and jxl-encoder with resource limits, cancellation, and gain map support.
zenjxl-decoder is Imazen's fork of jxl-rs with additional metadata extraction, gain map parsing, and resource limiting. jxl-encoder is a pure Rust JPEG XL encoder supporting both lossless (modular) and lossy (VarDCT) modes. zenjxl wraps both behind a unified API and provides zencodec integration for use in zenpipe pipelines.
#![forbid(unsafe_code)], no_std + alloc, edition 2024.
use zenjxl::{decode, probe, JxlLimits};
use zenpixels::PixelDescriptor;
let jxl_bytes: &[u8] = &std::fs::read("photo.jxl").unwrap();
// Metadata-only probe (no pixel decode).
let info = probe(jxl_bytes).unwrap();
println!("{}x{}, alpha={}, gray={}", info.width, info.height, info.has_alpha, info.is_gray);
// Full decode with resource limits. The 3rd arg is a pixel-format preference
// list (`&[zenpixels::PixelDescriptor]`); `&[]` lets the decoder pick natively.
let limits = JxlLimits {
max_pixels: Some(120_000_000), // 108 MP photos are common
max_memory_bytes: Some(2 * 1024 * 1024 * 1024),
};
let output = decode(jxl_bytes, Some(&limits), &[]).unwrap();
// `output.pixels` is a `zenpixels::PixelBuffer` in the image's NATIVE format
// (opaque → RGB8, with alpha → RGBA8). Normalize to packed RGBA8 bytes with the
// `zenpixels-convert` extension trait:
use zenpixels_convert::PixelBufferConvertTypedExt;
let rgba: Vec<u8> = output.pixels.to_rgba8().copy_to_contiguous_bytes(); // w*h*4, R,G,B,ADependencies & errors. Besides zenjxl, add zenpixels (PixelBuffer/
PixelDescriptor), zenpixels-convert (the .to_rgba8() trait), and enough
(cancellation). decode/probe/encode_* return Result<_, whereat::At<E>>
(At<JxlError>): the At<…> adds a build-time source location for logs —
get the underlying error with err.error() (borrow) or err.decompose().0
(owned), then match the [JxlError] enum (it is #[non_exhaustive], so keep a
wildcard arm).
use zenjxl::{encode_rgb8, encode_rgb8_lossless, calibrated_jxl_quality};
let rgb: &[u8] = &[0u8; 256 * 256 * 3]; // RGB pixels
// Lossy encode -- calibrated_jxl_quality maps 0..=100 to JXL distance.
let distance = calibrated_jxl_quality(85);
let lossy = encode_rgb8(rgb, 256, 256, distance).unwrap();
// Lossless encode.
let lossless = encode_rgb8_lossless(rgb, 256, 256).unwrap();Decode -- probe() returns JxlInfo with dimensions, bit depth, ICC profile, CICP signaling, EXIF orientation, raw EXIF/XMP bytes, extra channel metadata, HDR tone mapping fields, preview size, and gain map bundles. decode() returns a PixelBuffer with automatic format negotiation. decode_with_parallel() enables multithreaded decoding; decode_with_options() adds cancellation via enough::Stop.
Encode -- Convenience functions for RGB, RGBA, BGRA, and grayscale u8 data in both lossy and lossless modes. calibrated_jxl_quality() maps a 0--100 quality scale to JXL distance. Container utilities (append_gain_map_box, is_bare_codestream) for gain map authoring.
Gain maps -- Decode extracts GainMapBundle from jhgm container boxes (ISO 21496-1). Encode can append gain map boxes to existing codestreams.
HDR metadata -- JxlInfo exposes intensity_target, min_nits, relative_to_max_display, and linear_below from the JXL tone mapping header.
| Flag | Default | Description |
|---|---|---|
decode |
yes | JPEG XL decoding via zenjxl-decoder |
encode |
yes | JPEG XL encoding via jxl-encoder |
threads |
no | Multithreaded decoding via rayon (requires decode) |
parallel |
no | Per-frame parallelism inside the encoder via rayon (requires encode) |
butteraugli-loop |
no | Perceptual quality tuning (requires encode) |
zencodec |
no | Config/Job/Executor trait integration for zen codec pipelines |
- Not published to crates.io. Depend on it via git or path.
- Encoder is u8-only for the convenience API. The zencodec path supports wider bit depths.
- zenjxl-decoder does not yet support all JPEG XL features (e.g., some edge cases in progressive decoding).
| State of the art codecs* | zenjpeg · zenpng · zenwebp · zengif · zenavif (rav1d-safe · zenrav1e · zenavif-parse · zenavif-serialize) · zenjxl (jxl-encoder · zenjxl-decoder) · zentiff · zenbitmaps · heic · zenraw · zenpdf · ultrahdr · mozjpeg-rs · webpx |
| Compression | zenflate · zenzop |
| Processing | zenresize · zenfilters · zenquant · zenblend |
| Metrics | zensim · fast-ssim2 · butteraugli · resamplescope-rs · codec-eval · codec-corpus |
| Pixel types & color | zenpixels · zenpixels-convert · linear-srgb · garb |
| Pipeline | zenpipe · zencodec · zencodecs · zenlayout · zennode |
| ImageResizer | ImageResizer (C#) — 24M+ NuGet downloads across all packages |
| Imageflow | Image optimization engine (Rust) — .NET · node · go — 9M+ NuGet downloads across all packages |
| Imageflow Server | The fast, safe image server (Rust+C#) — 552K+ NuGet downloads, deployed by Fortune 500s and major brands |
* as of 2026
archmage · magetypes · enough · whereat · zenbench · cargo-copter
And other projects · GitHub @imazen · GitHub @lilith · lib.rs/~lilith · NuGet (over 30 million downloads / 87 packages)
Dual-licensed: AGPL-3.0 or commercial.
I've maintained and developed open-source image server software -- and the 40+ library ecosystem it depends on -- full-time since 2011. Fifteen years of continual maintenance, backwards compatibility, support, and the (very rare) security patch. That kind of stability requires sustainable funding, and dual-licensing is how we make it work without venture capital or rug-pulls. Support sustainable and secure software; swap patch tuesday for patch leap-year.
Your options:
- Startup license -- $1 if your company has under $1M revenue and fewer than 5 employees. Get a key
- Commercial subscription -- Governed by the Imazen Site-wide Subscription License v1.1 or later. Apache 2.0-like terms, no source-sharing requirement. Sliding scale by company size. Pricing & 60-day free trial
- AGPL v3 -- Free and open. Share your source if you distribute.
See LICENSE-COMMERCIAL for details.
Upstream code from libjxl/libjxl is licensed under BSD-3-Clause. Our additions and improvements are dual-licensed (AGPL-3.0 or commercial) as above.
We are willing to release our improvements under the original BSD-3-Clause license if upstream takes over maintenance of those improvements. We'd rather contribute back than maintain a parallel codebase. Open an issue or reach out.