Skip to main content

Crate oxideav_jpegxl

Crate oxideav_jpegxl 

Source
Expand description

JPEG XL (JXL) codec — decoder-side header parsing.

JPEG XL is ISO/IEC 18181 (final specification 2022). It supersedes classic JPEG with a modal design that separates a “VarDCT” path (variable-size DCT + LF/HF subbands, quality-competitive with AVIF and modern JPEG) from a “Modular” path (grid-of-pixels predictor + MA-tree range coder, strong at lossless + non-photo material).

This crate currently ships:

  • Container + signature detection for both JXL wrappings: raw codestream (FF 0A) and ISOBMFF-wrapped (00 00 00 0C 4A 58 4C 20 0D 0A 87 0A), including extraction of the codestream from jxlc / jxlp boxes.
  • An LSB-first bitreader::BitReader matching the reference bit packing used by the codestream.
  • Parsing of the codestream preamble: metadata::SizeHeader and the metadata::ImageMetadata fields up to num_extra_channels (bit depth, orientation, preview/animation flags). Fuller ColorEncoding + ToneMapping decoding is deferred.
  • Modular sub-bitstream pixel decode (per the 2019 committee draft, Annexes C.9 + D.7), made of:
    • abrac::Abrac — the bit-level adaptive range coder (D.7);
    • begabrac::Begabrac — bounded-Exp-Golomb integer coder over a known signed range (D.7.1);
    • matree::MaTree — the meta-adaptive decision tree that picks a per-context BEGABRAC for each pixel (D.7.2 / D.7.3);
    • predictors — the five named pixel predictors (Zero, Average, Gradient, Left, Top) from C.9.3.1;
    • modular — the channel header parser plus the per-pixel property + predictor + entropy decode loop.

The integrated registered decoder is not yet wired: the registered make_decoder reports Error::Unsupported because the surrounding codestream framing (FrameHeader + TOC + frame-byte alignment) is not yet wired to the per-channel path. Programs that only need probe-level information (dimensions, bit depth) should call probe directly; programs that want to drive the per-channel Modular decode end-to-end should instantiate modular::decode_single_channel against a hand-built fixture (unit tests in modular show the expected wire format).

Follow-up work (tracked for the eventual landing PR):

  • GlobalModular wiring (C.4.8) so the FDIS path can actually drive the Modular sub-bitstream end-to-end.
  • Squeeze inverse transform (I.3) for multi-resolution Modular images.
  • VarDCT-path decoder (variable-size DCT + LF/HF, Chroma-from-Luma, Gaborish smoothing, EPF) — out of scope for this round.
  • MABrotli / MAANS entropy coders (the 2019 committee draft’s entropy_coder ∈ {1, 2}); only MABEGABRAC (entropy_coder == 0) is implemented today.

§FDIS 18181-1:2021 layer

In addition to the committee-draft pipeline above, the FDIS layer is being built up additively across rounds:

  • Round 1: ans — FDIS Annex D entropy decoder (prefix codes, ANS, distribution clustering, hybrid integer coding).
  • Round 2: extensions — A.5 Extensions; metadata_fdis — full A.6 ImageMetadata refresh including ColorEncoding, ToneMapping, ExtraChannelInfo, AnimationHeader, OpsinInverseMatrix, PreviewHeader; frame_header — C.2 FrameHeader bundle including Passes, BlendingInfo, RestorationFilter; toc — C.3 TOC with Lehmer-code permutation decoder driven by the round-1 ANS layer; ans::cluster::read_general_clustering — D.3.5 general path.
  • Round 3 (planned): GlobalModular wiring + cjxl fixture decode.

§Round-1 (2024-spec) status (this commit)

make_decoder returns a live decoder ([JxlDecoder]) that handles the simplest end-to-end Modular bitstreams:

  • Raw codestream OR ISOBMFF wrapping.
  • Grey (1 plane) OR RGB (3 planes), 8 bits per sample (integer).
  • Single-group, single-pass frame (num_groups == 1 && num_passes == 1).
  • nb_transforms arbitrary at the parse level (TransformInfo bundles per H.7 are decoded for any nb_transforms > 0); inverse application of Palette / Squeeze defers to round 2 with a clean Error::Unsupported exit point. RCT (no channel-list change) passes through the layout step.
  • Multi-leaf MA tree evaluated end-to-end (decision-node property[k] > value traversal per H.4.1).
  • use_global_tree is honoured.
  • No Patches / Splines / NoiseParameters — those are LfGlobal features round 2 will land alongside the VarDCT path.
  • No ICC profile (Annex E.4).
  • Predictor 6 (Annex H.5 Self-correcting) only resolved at the (0, 0) origin; full WP defers to round 2.

The acceptance fixture for round 1 is pixel-1x1.jxl (1×1 RGB lossless, 22 B): decodes to R=255 G=0 B=0 matching its expected.png.

Anything outside this envelope returns Error::Unsupported at the relevant gate point. Wider coverage (VarDCT, Squeeze inverse, Palette inverse, ICC, full WP predictor 6) lands in round 2+.

§Round-6 (2024-spec) additions

  • Annex E.4 ICC profile decode (icc): the 7-state-equivalent entropy-coded ICC byte stream (41 pre-clustered distributions + IccContext(i, b1, b2) 41-context function) is decoded into the final ICC profile bytes per E.4.3 (header), E.4.4 (tag list) and E.4.5 (main content). When metadata.colour_encoding.want_icc == true the bit-position is now correctly advanced past the ICC stream rather than failing with Error::Unsupported outright; the decoded bytes are validated for the “acsp” magic at offset 36 but are not yet propagated to oxideav_core::VideoFrame (which has no ICC slot in 0.1.x).
  • G.2 LfGroup / G.4 PassGroup type scaffolding (lf_group, pass_group): typed bundles + per-group rectangle geometry + (minshift, maxshift) computation per pass. Per-LfGroup and per-PassGroup decode itself is not yet wired (round-7 follow-up gated on the GlobalModular nb_meta_channels-aware refactor — see lf_group crate-level docs).
  • Multi-LfGroup / multi-group / multi-pass / VarDCT frames fail with precise round-7-targeting error messages instead of the round-3 generic “TOC with N entries” rejection.

§Round-7 (2024-spec) additions

Four-piece refactor coordinating the GlobalModular partial-decode path with per-PassGroup decode + post-PassGroup transforms (Annex G.1.3 last paragraph + G.4.2):

  • Partial GlobalModularglobal_modular::GlobalModular::read stops decoding at any non-meta channel exceeding group_dim (G.1.3 last paragraph). Such channels are zero-filled placeholders in image.channels until per-PassGroup decode fills them.
  • stream_index threadingmodular_fdis::decode_channels_at_stream takes the stream index from Table H.4: 0 for GlobalModular, 1 + 3*num_lf_groups + 17 + num_groups * pass_idx + group_idx for ModularGroup. Threaded through get_properties so the MA tree’s property[1] > value decisions select the correct per-section leaf.
  • TOC layout + empty entriestoc::Toc::read now accepts zero-size entries (e.g. an empty LfGroup or PassGroup section is legal when no channel matches that section’s filter). The decode_codestream consumer addresses sections by their TOC offsets (computed from the entry running sum), with permutation already handled in the round-2 TOC reader.
  • Post-PassGroup transformsglobal_modular::apply_inverse_transforms is invoked AFTER all PassGroups complete (G.4.2 last paragraph), not inside GlobalModular::read, so the inverse transform sees the fully-assembled image rather than a half-decoded one.

Per-PassGroup decode is in pass_group::decode_modular_group_into; the (minshift, maxshift) computation in pass_group::compute_pass_shift_range models an implicit n=num_ds final-resolution entry that the printed spec text omits but whose absence would make single-pass frames decode no modular data (documented SPECGAP).

Round-7 SPECGAP — cjxl 0.11.1 emits multi-group lossless modular fixtures where the per-cluster ANS distribution’s alphabet_size exceeds 1 << log_alphabet_size (specifically: alphabet_size=33 against table_size=32 when log_alphabet_size = 5 + u(2) = 5). The 2024 spec text in C.2.5 is silent on the cap (the introductory paragraph describes D as a 1 << log_alphabet_size-element array but the listing’s alphabet_size-iterating loop can exceed it).

§Round-8 (2024-spec) additions

Two themes:

  1. C.2.5 SPECGAP partial resolution (ans::distribution): ans::distribution::read_distribution now returns (D, log_eff) where log_eff is the effective log_alphabet_size for downstream alias-table sizing. Round 8 picks “interpretation C”: iterate the logcounts loop for min(alphabet_size, table_size) entries, treating the bitstream’s signalled alphabet_size > table_size as a soft cap (the encoder advertises a wider alphabet but only serialises table_size per-symbol entries). Empirically validated by parsing the LfGlobal section of tests/fixtures/synth_320_grey/synth_320.jxl cleanly past the round-7 SPECGAP error. Interpretations A (grow D to accommodate alphabet_size) and B (drop writes at i >= table_size, accumulate total_count only over stored entries) were both tried and rejected — see ans::distribution crate docs for the comparison. The synth_320 fixture is still NOT decoded end-to-end: a separate post-LfGlobal blocker appears (cjxl emits a 0-byte PassGroup[0][0] slot which contradicts the spec’s “all groups carry data per pass” rule); that is round-9+ work.

  2. VarDCT scaffold (vardct): the FrameHeader’s encoding == kVarDCT path is now structurally recognised rather than rejected with a generic Error::Unsupported. The module exposes vardct::recognise_vardct_codestream which validates the round-8 envelope (single LF group, single pass, no extra channels, Grey or RGB colour space) and returns a vardct::VarDctScaffold geometry record. The IDCT-II primitive for the 8x8 block size (vardct::idct1d_8 + vardct::idct2d_8x8) is also wired with unit tests. End- to-end VarDCT pixel decode (LF subband, HF subband, dequant, inverse transform dispatch across block sizes 8x8 / 8x16 / 16x8 / 16x16 / 32x32 / 64x64 / DCT4 / IDENTITY / AFV, Chroma-from-Luma, Gaborish smoothing, EPF) is round-9+ work.

§Round-9 (2024-spec) additions

Three concurrent fixes that together unblock the synth_320 fixture (multi-group lossless grey, 320×320, num_groups=9):

  1. §F.3.1 HfGlobal slot is unconditional — the 2024 spec bullets list HfGlobal for every TOC, with NOTE 1 calling out that the slot is 0-byte for encoding == kModular. Round 8’s num_toc_entries / toc::Toc::read gated HfGlobal on encoding == kVarDCT, off-by-oning every PassGroup index in multi-group kModular frames. Also: HfPass[num_passes] is part of the HfGlobal section per Annex G.3 Table G.4 — round 8 had incorrectly counted it as separate TOC entries. With both off- by-ones fixed, synth_320’s TOC reads as 12 entries [33, 0, 0, 9, 20, 7, 20, 9, 24, 7, 23, 7] (slot 2 is the 0- byte HfGlobal, not PG[0][0]).

  2. §F.3 first-paragraph zero-padding — “When decoding a section, no more bits are read from the codestream than 8 times the byte size indicated in the TOC; if fewer bits are read, then the remaining bits of the section all have the value zero.” Round 8’s bitreader::BitReader errored on EOF for section sub-readers, breaking PassGroup ANS decodes whose modular sub-bitstreams consumed fewer real bits than the TOC-stated section size. Round 9 adds bitreader::BitReader::new_section which returns 0 for any read past the end of the section data; the legacy bitreader::BitReader::new preserves strict EOF for whole- codestream parsing.

  3. Per-PassGroup transforms (Annex H.6 inside G.4.2) — observed in cjxl 0.11.1’s synth_320 edge groups: the encoder emits a per-group Palette transform (begin_c=0, num_c=1, nb_colours=191) for the 64-pixel-wide column-2 / row-2 groups. pass_group::decode_modular_group_into now applies the transform layout adjustment to the per-group channel descs, decodes against the adjusted descs, and applies the inverse transforms LOCALLY before copying samples back into the parent image. global_modular::apply_transforms_to_channel_layout is now pub so the per-group reuse path doesn’t duplicate the table.

Round-9 status — synth_320 reaches end-of-frame without erroring and ~21k of 102400 pixels match the expected (y + x) & 0xFF gradient (the first 6 rows across the first two group columns); the remaining pixels drift mid-decode in the smaller edge groups. Full pixel-correctness is round-10 work (suspected residual: ANS state nuance specific to F.3 zero- padded tail OR per-group WP bookkeeping). All five small lossless fixtures still pixel-correct vs round 4’s expected.png.

§Round-10 (2024-spec) additions

Two themes:

  1. C.1 + C.3.3 lz_dist_ctx spec-conformance fix — [modular_fdis::decode_uint_in] and decode_uint_in_with_dist previously passed the per-symbol leaf context for both the literal token AND the LZ77 distance token, which contradicts the spec’s “the LZ77 distance token is read using D[clusters[lz_dist_ctx]]” with lz_dist_ctx = num_dist (the dedicated extra context the codestream reserves whenever lz77.enabled). When LZ77 fires, that bug would distort every copy. Fixed: derive lz_dist_ctx = cluster_map.len() - 1 from the post-prelude state of the EntropyStream and thread it to HybridUintState::decode’s ctx_lz argument. No-op for fixtures whose symbol stream has lz77.enabled = false (synth_320 included).

  2. synth_320 edge-group drift bisect — instrumented per- decode tracing pinpoints the first mismatch at PG[0][0] decode #3087 (frame coords y=24, x=14). State 0x9CA780 alias-maps to symbol 30 (cluster 0’s low-prob D[30] = 1 entry), forcing an ANS refill plus extra bits that over-consume 21 bits beyond the 9-byte slot. Bisect ruled out: per-PassGroup transform layout (PG[0][0] carries no transforms; only edge groups do); LZ77 path (off in the symbol stream); per-channel WP state reset (PG[0][0] is the first group); cluster_map / dist_multiplier derivation (matches H.3). Round-11+ work will need a finer state-by- state diff against djxl --debug (deferred to an Auditor round) since the implementer wall bars djxl source / the reference-decoder-trace doc.

Round-10 status — synth_320 still decodes ~21k of 102400 pixels matching the gradient (first 24 rows of PG[0][0] and PG[0][1] are pixel-correct; drift begins at y=24, x=14). All five small lossless fixtures still pixel-correct.

§Round-11 (2024-spec) additions

Three pieces wire LF subband decode (Annex G.2.2 / I.2):

  1. LfGlobal VarDCT bundles (lf_global): lf_global::Quantizer (FDIS C.4.3 — global_scale + quant_lf) drives LF dequant per Listing C.1. lf_global::LfChannelCorrelation (C.4.4) carries the CfL factors used by Annex G to reconstruct X/B from dY (default colour_factor=84, base_correlation_x=0.0, base_correlation_b=1.0). lf_global::HfBlockContext (C.8.4) implements only the u(1)==1 default-table fast path in round 11; the per-LF-threshold / qf-threshold / clustering- map branch returns Error::Unsupported. With these bundles wired, LfGlobal::read advances correctly past the VarDCT-only region of the LfGlobal slot rather than rejecting outright.

  2. GlobalModular zero-channel acceptance (global_modular): GlobalModular::read previously rejected any frame where derive_channel_descs returned 0 channels (the common VarDCT-without-extras case). Round 11 accepts the empty case: the inner ModularHeader (use_global_tree, WPHeader, nb_transforms) is still consumed, but the MA-tree and per- cluster distribution decode are skipped per FDIS C.9.1 last sentence (“In the trivial case where N is zero, the decoder takes no action.”).

  3. LfGroup + LfCoefficients (lf_group): lf_group::LfCoefficients::read reads extra_precision = u(2) per FDIS C.5.3, builds the per-LfGroup channel layout (3 LF channels of ceil(group_w/8) × ceil(group_h/8) samples, optionally further right-shifted by frame_header.jpeg_upsampling on chroma planes), then drives a Modular sub-bitstream with stream_index = 1 + lf_group_index per Table H.4. lf_group::LfGroup::read composes ModularLfGroup (G.2.3 — round-11 only handles the empty-channel-list case for now) with LfCoefficients. HfMetadata (G.2.4) is round-12+ work.

Acceptance fixture: hand-built minimal VarDCT bitstream — no cjxl dependency, encoded directly from spec listings — covering an 8×8 frame with 1×1 LF coefficient channels, MA tree of one Zero-predictor leaf, and prefix-code symbol stream with alphabet_size=1 (so every decoded LF coefficient is 0). The fixture parses through LfGlobal::readLfGroup::readLfCoefficients::read end-to-end. Test: lf_group::tests::round11_lfgroup_minimal_vardct_one_block_parses.

All five small lossless fixtures stay pixel-correct (see tests/round11_lf_subband.rs).

§Round-13 (2024-spec) additions

Three pieces tighten the VarDCT pipeline so round-12’s unit-tested F.1 / F.2 work actually runs on real codestreams:

  1. DctSelect / HfMul derivation from BlockInfo (dct_select): walks each column of the per-LfGroup BlockInfo channel decoded in round 12, looks up the transform type in Table C.16, and places the varblock at the next-empty 8×8 cell of the LfGroup’s block grid (raster order). HfMul is computed as 1 + mul. The 27-entry transform-type table is committed verbatim with per-entry (block_cols, block_rows) from the FDIS spec.

  2. HfGlobal C.6 default-fast-path (hf_global): reads the u(1) dequant-default flag and the num_hf_presets - 1 = u(ceil(log2(num_groups))) field. The non-default-encoding branch (per-matrix encoding_mode = u(3) + Listing C.7 ReadDctParams()) returns Error::Unsupported until round 14+ wires the full table.

  3. VarDCT pipeline wiring ([decode_vardct_round13]): the top-level decode_one_frame no longer rejects VarDCT codestreams at the round-8 scaffold gate. Instead, for num_lf_groups == 1 && num_passes == 1, it now drives: LfGlobal → LfGroup (LfCoefficients + HfMetadata) → DctSelect derivation → HfGlobal → F.1 LF dequantisation → F.2 adaptive smoothing (when not skipped). The round-13 pipeline returns Error::Unsupported with a “round 14+: HF subband decode + IDCT not yet wired” message AFTER all round-12 work has run on the real input.

Round-13 status — five small lossless Modular fixtures stay pixel-correct; both VarDCT fixtures (vardct_256x256_d1.jxl and vardct_256x256_d3.jxl, copied from docs/image/jpegxl/fixtures/) reach the round-13 pipeline (no longer hit the round-8 scaffold gate).

Round-14 candidates (in dependency order):

  • HfBlockContext non-default-table branch (per-LF thresholds + qf thresholds + clustering map), required for any cjxl-encoded VarDCT fixture that doesn’t take the u(1)=1 default-table fast path.
  • HfGlobal C.6.2 dequant-matrix encoding modes (Listing C.7) + Listing C.10 GetDCTQuantWeights for per-DctSelect dequant matrices.
  • HfPass C.7.1 coefficient orders (used_orders 13-bit mask, DecodePermutation) + C.7.2 histograms (495 × num_hf_presets × nb_block_ctx clustered distributions).
  • PassGroup HF coefficients C.8.3: per-block hfp = u(ceil(log2(num_hf_presets))) + clustered ANS coeff decode + F.3 HF dequantisation (Listing F.2 + per-channel scale + per-DctSelect dequant matrix multiply).
  • Inverse DCT dispatch across block sizes (8×8 IDCT wired round 8; 8×16 / 16×8 / 16×16 / 32×32 / 64×64 / DCT4 / DCT4×8 / DCT8×4 / IDENTITY / AFV remain).
  • Listing I.5 LLF-from-downsampled-LF composition (the bridge from F.2-smoothed LF samples to varblock LF coefficients) — pure-math step landed round 121 as llf_from_lf (FDIS Listings I.15 + I.16). Still pending: per-LfGroup wiring that drives the per-varblock invocation from the pass_group_hf coefficient buffer.
  • Chroma-from-Luma (Annex G), Gaborish (Annex J?), EPF.

§Round-16 (2024-spec) additions

lf_group::HfMetadata::read now wires nested transforms (FDIS §C.5.4 + §C.9.4): the four-channel HfMetadata sub-bitstream parses nb_transforms + TransformInfo[] exactly like the GlobalModular section, applies the transform-rewritten channel layout via global_modular::apply_transforms_to_channel_layout before the inner per-channel decode, then walks global_modular::apply_inverse_transforms in reverse bitstream order to recover the four-channel base layout [XFromY, BFromY, BlockInfo, Sharpness].

Round-15 left the d1 fixture stuck on the round-12 deferral inside HfMetadata::read (nb_transforms > 0 errored with “transforms inside HF metadata sub-bitstream not yet supported”). With round 16 the parse succeeds; the d1 fixture surfaces a strictly-later blocker — its HfMetadata Squeeze step references channels beyond the four-channel baseline (begin_c=39 on step 0), tripping the apply_transforms_to_channel_layout channel-count invariant. That’s the round-17 candidate (suspected upstream bit-position drift in LfGlobal or LfCoefficients).

HfMetadata::read and LfGroup::read now both take a &ImageMetadataFdis argument so the inverse Palette transform can read bit_depth.bits_per_sample for delta-palette prediction.

§Round-26 (2024-spec) — Annex L colour transforms

Parent-dispatch “r11”. New xyb module transcribes FDIS §L.2.2 inverse XYB → linear RGB and §L.3 inverse YCbCr → RGB verbatim from the ISO/IEC 18181-1:2024 PDF. Three public entry points: xyb::inverse_xyb_to_rgb, xyb::inverse_ycbcr_to_rgb, and the convenience composite xyb::modular_xyb_to_linear_rgb (§L.2.2 preamble + inverse XYB in one call).

Wired into [decode_codestream] modular output stage: when metadata.xyb_encoded == true and colour_encoding.colour_space is Rgb, the per-channel pass-through is replaced with [build_rgb_planes_from_xyb]; symmetric branch for frame_header.do_ycbcr == true. Pre-round-26 pass-through path preserved for xyb_encoded == false && do_ycbcr == false modular frames so the five small lossless fixtures stay pixel-correct.

Round-26 SPECGAP: §L.2.2 NOTE describes the output as linear-domain RGB but doesn’t prescribe a gamma encoding step before display. xyb::linear_rgb_to_u8 emits linear bytes (clamp + scale by 255 + round); callers that need sRGB-encoded bytes apply the sRGB transfer function downstream.

§Round-27 (2024-spec) — IDCT dispatch

Parent-dispatch “r12” item (5). New idct module wires the spec-conformant 1-D inverse DCT for power-of-two sizes s ∈ {1, 2, 4, 8, 16, 32, 64, 128, 256} (FDIS Annex I.2.1) and the 2-D inverse DCT (Annex I.2.2 Listing I.4) for rectangular R × C blocks. Three public entry points: idct::idct_1d, idct::idct_2d (taking coefficients in spec (short × long) row-major natural-ordering layout per Annex I.2.4 and returning (R × C) row-major samples), and idct::idct_for_transform which dispatches on a dct_select::TransformType to the 2-D IDCT for the 18 plain-DCT block sizes in Table C.16.

The 9 non-DCT transforms (Hornuss, DCT2x2, DCT4x4, DCT4x8, DCT8x4, AFV0..AFV3) — Listings I.7..I.13 — return Err(Unsupported) from idct::idct_for_transform and are deferred to round 28+ alongside HF coefficient decode + F.3 dequantisation. The legacy vardct::idct1d_8 / vardct::idct2d_8x8 (round-8 scaffold, scaled-orthonormal IDCT) are retained for backward compatibility but are NOT spec-conformant; new HF-decode wiring will call through idct::idct_for_transform exclusively.

Re-exports§

pub use container::detect;
pub use container::extract_codestream;
pub use container::Signature;
pub use metadata::parse_headers;
pub use metadata::BitDepth;
pub use metadata::Headers;
pub use metadata::ImageMetadata;
pub use metadata::SizeHeader;

Modules§

abrac
Adaptive binary range coder (ABRAC), per ISO/IEC 18181-1 committee draft (2019-08-05) Annex D.7. ABRAC is the binary building block from which the MABEGABRAC context model used by the Modular sub-bitstream is constructed.
afv
AFV (Asymmetric “FREE”) variable-block helpers — ISO/IEC FDIS 18181-1:2021 Annex I.2.2 Listings I.5 and I.6.
ans
ANS entropy coder + companion lookup tables for the FDIS-2021 revision of JPEG XL (ISO/IEC FDIS 18181-1:2021, Annex D).
begabrac
Bounded-Exp-Golomb ABRAC (BEGABRAC), per ISO/IEC 18181-1 committee draft (2019-08-05) Annex D.7.1.
bitreader
LSB-first bit reader used by the JPEG XL codestream.
block_context_resolver
Per-LfGroup BlockContext() resolver — ISO/IEC FDIS 18181-1:2021 §C.8.3 + §I.2.2 (was Listing C.13 + Listing C.15).
block_dequant
Per-block VarDCT decode walk — chains the §C.8.3 decoded quantised-coefficient block through Annex F.3 HF dequantisation and the Annex I.2 inverse DCT to produce a block of spatial residual samples.
chroma_from_luma
Chroma-from-Luma (CfL) — ISO/IEC FDIS 18181-1:2021 Annex G (“Chroma from luma”, page 73) and ISO/IEC 18181-1:2024 §I.2.6.
coeff_order
Natural ordering of the DCT coefficients — ISO/IEC FDIS 18181-1:2021 §I.2.4 + Table I.1.
container
JPEG XL container detection.
dct_quant_weights
GetDCTQuantWeights + per-DctSelect dequantization-matrix materialisation — ISO/IEC 18181-1:2024 §I.2.4 / §I.2.5 + Table I.4
dct_select
DctSelect / HfMul derivation from BlockInfo — ISO/IEC 18181-1:2024 Annex C.5.4 prose + Table C.16.
epf
Edge-preserving restoration filter — ISO/IEC FDIS 18181-1:2021 Annex J.3 (“Edge-preserving filter”, pages 85–87).
extensions
Extensions bundle — FDIS 18181-1 §A.5 (Table A.14).
frame_header
FrameHeader bundle — FDIS 18181-1 §C.2 (Table C.2).
gaborish
Gabor-like restoration filter — ISO/IEC FDIS 18181-1:2021 Annex J.2 (“Gabor-like transform”, page 85).
global_modular
GlobalModular bundle — FDIS 18181-1 §C.4.8.
hf_coeff_histogram_size
HfCoefficientHistogramSize — typed sizing primitive for the §C.7.2 HF coefficient histogram block.
hf_coefficient_histograms
HfCoefficientHistograms — ISO/IEC FDIS 18181-1:2021 §C.7.2 HF coefficient histograms read.
hf_dequant
HF coefficient dequantisation — ISO/IEC FDIS 18181-1:2021 Annex F.3 (page 72 of the FDIS PDF) and ISO/IEC 18181-1:2024 Annex F.3.
hf_global
HfGlobal bundle — ISO/IEC 18181-1:2024 Annex I.2.4 + I.2.6.
hf_pass
HfPass bundle — ISO/IEC FDIS 18181-1:2021 §C.7.
icc
ICC profile decoder — ISO/IEC 18181-1:2024 Annex E.4.
idct
Inverse DCT dispatch — ISO/IEC 18181-1:2024 Annex I.7 + I.9.
lf_dequant
LF dequantization + adaptive LF smoothing — ISO/IEC 18181-1:2024 Annex F.1 (Listing F.1) and Annex F.2 (the prose immediately following Listing F.1).
lf_global
LfGlobal bundle — FDIS 18181-1 §C.4 (Table C.10).
lf_group
LfGroup bundle — ISO/IEC 18181-1:2024 Annex G.2 (= 2021 FDIS C.5).
llf_from_lf
LLF coefficients from downsampled image — ISO/IEC FDIS 18181-1:2021 Annex I.2.5 (Listings I.15 + I.16) and ISO/IEC 18181-1:2024 Annex I.2.5.
matree
Meta-Adaptive Bounded-Exp-Golomb ABRAC (MABEGABRAC) decision tree, per ISO/IEC 18181-1 committee draft (2019-08-05) Annex D.7.2 and D.7.3.
metadata
JPEG XL codestream header parsing.
metadata_fdis
ImageMetadata (full FDIS A.6 form), ColourEncoding (A.4), ToneMapping (A.6 Table A.18), ExtraChannelInfo (A.9), AnimationHeader (A.7), OpsinInverseMatrix (A.8), and the SizeHeader re-decoded against the FDIS published in 2021.
modular
Modular sub-bitstream channel decoding (per ISO/IEC 18181-1 committee draft, 2019-08-05, Annex C.9).
modular_fdis
Modular image sub-bitstream — ISO/IEC 18181-1:2024 Annex H.
multi_pass_decode
Per-LfGroup multi-pass varblock decode driver — ISO/IEC FDIS 18181-1:2021 §C.8.3 + Table C.6 Passes.
multi_pass_hf_header
Per-LfGroup multi-pass driver with per-pass HF-header hfp reads — ISO/IEC FDIS 18181-1:2021 §C.8.3 first paragraph.
multi_pass_hf_histogram_decoder
Per-pass HF histogram decode context — ISO/IEC FDIS 18181-1:2021 §C.7.2 (entropy histograms) + §C.8.3 (per-pass histogram_offset routing) bridge.
non_zeros_grid
Per-pass / per-channel NonZeros(x, y) grid bookkeeping — ISO/IEC FDIS 18181-1:2021 §C.8.3 + Listing C.13 + Listing C.14.
pass_group
PassGroup bundle — ISO/IEC 18181-1:2024 Annex G.4.
pass_group_hf
PassGroup HF coefficients — ISO/IEC FDIS 18181-1:2021 §C.8.3 + §C.8.4 supporting machinery.
per_channel_non_zeros
Per-channel NonZeros(x, y) grid container — ISO/IEC FDIS 18181-1:2021 §C.8.3 + Listing C.13 prelude.
per_pass_non_zeros
Per-pass NonZeros(x, y) grid container — ISO/IEC FDIS 18181-1:2021 §C.8.3 + Listing C.13.
predictors
Modular sub-bitstream pixel predictors, per ISO/IEC 18181-1 committee draft (2019-08-05) Annex C.9.3.1.
residual_plane
Per-LfGroup VarDCT residual-plane assembly — places each varblock’s R × C spatial residual block (the output of the Annex F.3 dequant + Annex I.2.3 inverse-DCT stage) into a single-channel spatial plane at the varblock’s pixel origin.
toc
TOC (Table of Contents) — FDIS 18181-1 §C.3.
varblock_walk
Per-LfGroup varblock-walk driver — ISO/IEC FDIS 18181-1:2021 §C.5.4 (DctSelect placement prose) + §C.8.3 (per-pass per-channel varblock decode loop).
vardct
VarDCT decode path — ISO/IEC 18181-1:2024 Annex I.
xyb
Annex L colour transforms — ISO/IEC 18181-1:2024.

Structs§

HeadersFdis
FDIS-side Headers returned by probe_fdis. Mirrors the committee-draft Headers but uses the FDIS bundle types.

Constants§

CODEC_ID_STR
Public codec id string. Matches the aggregator feature name jpegxl.

Functions§

decode_one_frame
Decode the entire JXL packet (raw codestream OR ISOBMFF-wrapped) and return the first frame as a VideoFrame. Round-3 envelope.
make_encoder
Encoder slot, always rejected. Exposed for completeness so callers that wire an Encoder factory by codec id get a clean Unsupported error instead of CodecNotFound.
probe
Inspect a JXL file (raw codestream or ISOBMFF-wrapped) and return the signature type + parsed SizeHeader + ImageMetadata preamble.
probe_fdis
FDIS-side probe: parse SizeHeader + full A.6 ImageMetadata. Falls back to the committee-draft probe if the FDIS path errors (so that container detection still works on edge cases the committee-draft path tolerates).
register
Unified entry point: install the JPEG XL codec into a RuntimeContext.
register_codecs
Register the JPEG XL decoder stub into the supplied CodecRegistry. The encoder slot is intentionally left unregistered: the crate is decoder-side only and currently retired-pending-cleanroom (see crate-level docs).