Pure-Rust JPEG XL (JXL, ISO/IEC 18181-1) decoder for the
oxideav framework. Built
clean-room from the published core specification and the conformance /
behavioural-trace fixtures committed under docs/image/jpegxl/ only —
no external codec source is consulted. Zero C dependencies, zero FFI,
zero *-sys.
This crate is a decoder under active construction. The Modular path
decodes end to end (grey / RGB / RGBA, 1–16-bit integer, XYB / YCbCr
inverse colour) for the small lossless fixtures; the VarDCT path
decodes on the public path (round 389): the full chain — §C.8.3
per-(pass, group) HF-entropy decode → F.3 dequant → Annex G
coefficient-domain chroma-from-luma → §I.2.3.2 IDCT → §C.2 group
assembly → §6.2 crop → §J restoration filters → §L.2.2 XYB→RGB →
Table A.10 transfer encoding — is validated by direct sRGB byte
comparison against four reference decodes. Round 393's §F.3 HfMul
erratum fix (HfMul is the per-varblock quantisation-precision
multiplier and divides on dequant, arbitrated externally on the
purpose-built flat-content-lf-smoothing fixture) collapsed every
VarDCT baseline: vardct-256x256-d1 per-channel sRGB MAD
0.66 / 0.47 / 0.61 (was ≈ 3.4 — the round-385 "d1 HF accuracy tail"
is closed), vardct-256x256-d3 0.76 / 0.51 / 0.81, large-1024x768-d2
(12-group) 0.44 / 0.37 / 0.32, flat-content 0.20 / 0.20 / 0.20. The
same fixture resolved §F.2 erratum candidate 4: the corrected
clamp(4·gap − 3, 0, 1) smoothing ramp is the conformant reading
(CI-gated arbitration). Single-LfGroup frames of any group count
decode; §C.5 multi-LfGroup (> 2048 px) framing + LZ77 TOC permutations
landed round 393 and are pinned on large-3072x2048-multigroup.
Round 437 resolved the §C.7.1 used_orders != 0 boundary for
single-preset single-pass frames (the Listing C.12 per-channel
permutation layout erratum — see below); multi-preset / multi-pass
§C.7 slices still refuse loudly. Multi-frame
codestreams compose per §C.2 (Reference slots + Table C.8 blending,
incl. round-393 kBlend / kAlphaWeightedAdd alpha modes) in
decode_all_frames. Programs that only need probe-level information
should call probe(...) directly.
What is implemented and tested today:
- Containers + signature detection — both JXL wrappings: raw
codestream (
FF 0A) and the ISOBMFF box form (00 00 00 0C 4A 58 4C 20 0D 0A 87 0A), including extraction of the codestream fromjxlc/jxlpboxes. - Codestream preamble — an LSB-first bit reader (with the JXL
U32selector encoding),SizeHeader(all four dimension encodings), andImageMetadataup tonum_extra_channels(bit depth, orientation, preview / animation presence flags). - Modular path primitives — the adaptive range coder, the
bounded-Exp-Golomb integer coder, the meta-adaptive decision tree, the
named pixel predictors (including the Weighted predictor), and the
per-channel decode loop.
modular::decode_single_channeldrives a single channel against a hand-built fixture, and individual stages decode pixel- / byte-exact against the staged behavioural traces. - VarDCT path primitives — the LfGlobal bundles (Quantizer,
HfBlockContext, LfChannelCorrelation), the LfCoefficients
sub-bitstream, the spec-conformant 1-D / 2-D IDCT dispatch across the
plain-DCT block sizes and the non-DCT helpers, per-block dequant +
residual assembly (including the §I.2.4 LLF-coefficient placement that
folds the LF-derived DC block into the natural-order low-frequency
prefix before the §I.2.3.2 inverse DCT), the per-LfGroup three-channel
residual-plane reconstruction, the §6.2 right/bottom crop that turns
the padded block-grid reconstruction into the logical channel extent
(
ResidualPlane::crop_to/ChannelResidualPlanes::crop_to), and the inverse XYB / YCbCr colour transforms. The non-square transform families (DCT8×16 / DCT16×8 / DCT32×8 / DCT8×32 / DCT32×16 / DCT16×32 and their larger relatives) reconstruct to spatial samples through the same walk — the IDCT carries the Listing I.4 pre/post-transpose forR != C, the LLF extraction reads acy × cxsub-block, and the dequant matrix is the widebwidth × bheightlayout. - §C.8.3 cross-pass HF accumulation — the multi-pass coefficient
stack the per-pass decode driver yields (
out[p][i]) is folded into a single accumulated quantised grid per varblock (cross_pass): each pass's HF coefficients are left-shifted by the Table C.6shift[i](last pass behaves as shift 0) and summed cell-wise, uniform across every transform family.vardct_reconstruct::reconstruct_lf_group_cross_passis the one-call per-LfGroup driver tying it together — cross-pass accumulate → LF→LLF seed (Listing I.16) → F.3 dequant → §I.2.4 LLF merge → §I.2.3.2 IDCT → §C.5.4 placement → Annex G CfL — driving any mix of square / non-square / non-DCT varblocks (single- or multi-pass) to the three XYB residual planes.vardct_reconstruct::reconstruct_lf_group_from_entropyfuses that reconstruction with the live §C.8.3 multi-pass entropy decode (multi_pass_decode::decode_multi_pass_three_channels_with_resolver) in a single per-LfGroup call: it walks the DctSelect grid once per pass against the caller's entropy closures (qdc_at/read_non_zeros/decode_symbol), producing the per-passDecodedHfBlockstack from the stream itself, then runs the cross-pass reconstruction on it — closing the "feed the live per-pass stack rather than a caller-supplied one" wiring step. It is bit-for-bit identical to the explicit decode-then-reconstruct two-call path. The histogram-backed sibling (vardct_reconstruct::reconstruct_lf_group_from_histogramoverHfHistogramDecodeContext::decode_lf_group_multi_pass_three_channels) goes one step further: it owns the §C.7.2 entropy-stream routing itself — the per-passhistogram_offsetselection, the per-pass per-channelPredictedNonZerosread +NonZeros(x, y)writeback — so the only entropy input the caller supplies is the storage-onlyqdc_atquantised-LF lookup (noread_non_zeros/decode_symbolclosures). It is bit-for-bit identical to the closure path wired to the same histogram context over the same stream. - §C.7 HfGlobal-section assembly —
hf_global_section::HfGlobalSectionreads the HfGlobal TOC slot as the three contiguous pieces the spec lays out on one bit cursor with no byte alignment between them: §I.2.4 dequant matrices + §I.2.6num_hf_presets(HfGlobal::read) → §C.7.1num_hf_presetsHfPasscoefficient-order bundles (read_hf_pass_sequence) → §C.7.2495 × num_hf_presets × nb_block_ctxHF-coefficient histograms (HfCoefficientHistograms) + the §C.3.2 ANS-state init (u(32), a no-op for prefix streams).nb_block_ctxis threaded in from the LfGlobalHfBlockContext(§I.2.2).HfGlobalSection::decode_contextbinds the parsed §C.7.2 histograms to a per-frame §C.8.3PerPassHfHeadersto produce theHfHistogramDecodeContext(cross-validating every per-passhfpagainst the section's authoritativenum_hf_presets) — the bridge the per-LfGroupreconstruct_lf_group_from_histogramdecode walks against. The integrated VarDCT decode path now parses through this full §C.7 section on a real codestream (vardct_256x256_d1.jxl). - §J.3 restoration filters — the Gabor-like 3×3 convolution
(
gaborish::apply_xyb_planes_in_place) and the edge-preserving filter, both as pure XYB-plane math. The §J.3.1 three-step EPF iteration driver (epf::apply_epf_iterations) composes the up-to-three passes perepf_iters, feeding each step's output into the next (§J.3.4), for the constant-sigma (Modular,epf_sigma_for_modular) case. The §J.3.3 VarDCT per-block-sigma driver (epf::apply_epf_iterations_per_block_sigma) generalises it: each 8×8 block carries its own Listing J.3 sigma (packed intoepf::SigmaGrid, looked up per reference pixel) and thesigma < 0.3block-skip (epf::EPF_SKIP_SIGMA) passes a block's pixels through unchanged. A uniform grid reduces bit-exactly to the constant-sigma path. - §C.4.6 + §K.3 Splines image feature — the self-contained
splinesmodule decodes and renders centripetal Catmull-Rom splines. The §C.4.6 parse (decode_splines/decode_splines_raw) reads Listing C.3 (num_splines, delta-coded start coords, thenquant_adjust— the round-441 field-order erratum, see below) + per-spline control points (Listing C.4DecodeDoubleDelta) and 4×32 DCT coefficients over the §D.3 six-distribution entropy stream, dequantizes (dequant_dct32,kChannelWeight) and recorrelates (recorrelate_xb,Y × base_correlation_{x,b}). The §K.3 render (Spline::render/render_splines) upsamples control points (upsample_control_points), resamples by unit arc length (resample_by_arclength), and additively splats anerf-based Gaussian brush (s2s = √2·σ,maximum_distance = -2·ln(0.1)·σ²) onto the XYB planes, evaluating each channel viacontinuous_idct. A suspected FDIS typo in the Listing K.1 arc parameter is corrected (see below). Wired into the registered decode path since round 441 (VarDCT and Modular-XYB frames, drawn after patches and before noise per §K.1) and pinned wire-level on a hand-assembled 43-byte codestream the reference decoder accepts — our render lands within ±1/255 of the black-box reference decode.
- Multi-group VarDCT framing (§C.3.1 / §C.8.1): one PassGroup
section per
(pass, group)off the pass-major TOC slot map; per group the §C.8.1 group-local views (group_rectmodule: sub-grid slice with the §C.5.4 no-straddle invariant, LF rect, 64×64-aligned CfL tiles), the section's ownhfpheader + D.3.3 ANS state re-init, group-local NonZeros grids, pasted at the group offset. Landed reference-exact on first measure: per-pixel XYB MAD 7e-5 / 1.4e-3 / 9e-4 on the 12-group fixture. - Multi-pass framing (Table C.1
hf_pass[num_passes]): the HfGlobal slot reads one §C.7.1-orders + §C.7.2-histograms slice per pass; each(pass, group)section decodes as its own entropy stream and the per-pass stacks fold through the §C.8.3 cross-pass accumulator. (No multi-pass fixture is staged yet — unit-pinned.) - Table A.10 transfer encoding (
xyb::TransferEncoder): the §L.2.2 linear RGB is encoded with the signalled transfer function (sRGB / BT.709 / gamma / linear; PQ/DCI/HLG rejected precisely) before 8-bit quantisation — the rounds-11–385 linear-bytes SPECGAP left every XYB fixture uniformly dark (MAD ≈ 70/255). - FrameHeader
save_before_ctpresence sharessave_as_reference's!is_lastgate (fixture-measured against the 2021 FDIS text; unblockedvardct-256x256-d3). - §C.2 frame composition (
frame_compose): Reference[1..=3] recording,Reference[source]lookup (zeros when unstored), crop-rect blending (kReplace / kAdd / kMul; alpha modes + pre-CT recording surface precisely), zero-duration frames composed but not presented. - D.3.5 clustering fix: dropped the
num_distributions ≤ bits_remainingheuristic (ANS cluster indices cost ≪ 1 bit amortised; 2475 contexts in a 33-byte section are valid). - Real §C.8.3
qdc[3]: the Listing C.13lf_thresholdsladder reads the actual quantised-LF samples; the non-empty-lf_thresholdsreject gate is gone.
- §F.3 HfMul erratum (the "d1 HF accuracy tail" closed). The FDIS
prose says the bias-adjusted quant "is then multiplied by … the
value of HfMul" — but HfMul is the per-varblock
quantisation-precision multiplier (§C.8.3
qf), so the decoder must DIVIDE. Arbitrated externally on theflat-content-lf-smoothingfixture (uniform HfMul = 13, near-empty HF band: the literal multiply produced ±30-code low-frequency garbage, MAD 2.67 → 0.20 with the division) and confirmed on every staged VarDCT fixture (d1 3.42/1.99/2.10 → 0.66/0.47/0.61). Same divisor-vs-multiplier shape as the round-385 Listing C.1 erratum. Ratchets tightened (round362…4.5 → 1.0/255; new flat-content 0.35/255 bound). The earlier round-385 corrections (Listing C.1 multipliers, Annex G CfL branch split +-128bias, Listing I.16 LLF normalisation) stand. - §F.2 erratum candidate 4 RESOLVED. On the flat fixture (674/900
interior LF samples at the
gap = 0.5floor, where the two candidate ramps take opposite values) the correctedclamp(4·gap − 3, 0, 1)ramp beats the literalmax(0, 3 − 4·gap)on every channel and matches ~740 more reference pixels exactly. CI-gated; the literal ramp stays reachable only through the per-thread arbitration hook. - Crate-side instrumentation (the #168 fixture-notes deliverables):
per-sample §F.2 LF trace (
lf_dequant::LF_SMOOTH_TRACE— pre/post planes + per-sample gap/factor), the literal-ramp override, and the per-varblock decoded quantised HF-coefficient capture (VARDCT_HF_COEFF_CAPTURE). - §C.2 alpha blending: kBlend (premultiplied + straight branches,
0-alpha guard) and kAlphaWeightedAdd (post-blend alpha per the §C.2
definitions paragraph) land in the composer; the alpha plane itself
blends
oa + na·(1 − oa); Reference slots store the full plane stack; the multi-frame walk threadsalpha_plane/alpha_associated/ec_blending_info. - §C.5 multi-LfGroup framing + §C.3.2 LZ77 TOC permutations:
permuted TOCs decode over the shared full-D.3 reader (cjxl
large-image TOC permutations are LZ77-enabled) with §C.3.3
permutation-aware offsets; §D.3.5 clustering accepts LZ77 nested
sub-streams (depth-capped); per-LfGroup structures assemble into
frame-level canvases and §F.2 smoothing runs frame-level. Pinned on
large-3072x2048-multigroup(2×1 LF groups, 96 groups, permuted 100-entry TOC) up to the §C.7.1 boundary below.
Four of the six committed Part 3 conformance streams now decode
end-to-end, validated against black-box reference decodes
(round406_conformance_composition):
alpha_nonpremultiplied(12-bit) andalpha_triangles(9-bit): bit-exact on all four channels.blendmodes— a five-frameReference[1]chain exercising every Table C.8 blend mode (kReplace → kBlend → kAdd → kMul → kAlphaWeightedAdd) — within ±1/4095 (alpha exact at native depth).sunset_logo(RCT, 10-bit, orientation 7, two kBlend layers with out-of-canvas signed crops): bit-exact on all four channels at the correct transposed 924×1386 extent.
The fixes behind them (each an FDIS-text divergence pinned on the
official corpus): float-domain unclamped §C.2 composition
(DecodedFrame::raw_f32 carries out-of-range Modular samples;
quantisation only at presentation), 9–16-bit plane composition,
kAlphaWeightedAdd weighting by the frame's own alpha with the alpha
channel left unchanged, signed (UnpackSigned) crop offsets with
§3.5.1 clipping, Table C.7 alpha_channel/clamp presence gated on
the blend mode alone (not multi_extra), §A.6 Table A.17 orientation
(all eight transforms, new orientation module), and the §5.2 Idiv
semantics (round toward zero) in the Listing C.16 averaging predictors
and the Listing I.21 Squeeze tendency function.
- Squeeze decodes end-to-end (second block): the Listing I.19
default-parameter sequence (derived at transform-application time;
the printed
countformula has a sign typo), the Listing I.21 tendency erratum (refined round 420, below), and the Listing D.8rleft = 0column-0 rule. Single-group Squeeze is bit-exact (round408_squeeze_multilf). - §C.5.2 ModularLfGroup: multi-LfGroup Modular frames decode — the
grayscale_public_universityconformance stream (2880×1620, 2 LF groups, Squeeze) went from hard-Unsupportedto a full decode.
-
Coded-domain forward-Squeeze oracle (
round420_squeeze_residual_oracle): the inverse Squeeze is a bijection, so forward-transforming a reference decode reconstructs the exact coded channel pyramid; comparing it sample-by-sample against the decoder's assembled pre-inverse Modular image pins every GlobalModular / per-LfGroup / per-PassGroup residual slice exactly. -
Listing I.21 tendency half-tie erratum (refines the round-408 floor reading): the division rounds half-away-from-zero —
x = sign(m) × ((|m| + 6) Idiv 12)form = 4A - 3C - B. The two readings differ ONLY on exact negative half-ties (m ≡ 6 mod 12, ascending), which is precisely where every multi-group Squeeze stream diverged.sq_512is now bit-exact (was MAD 0.27); the round-408 "sporadic multi-group residual tail" was never a group-boundary issue. -
Multi-LfGroup + weighted-predictor Squeeze pinned bit-exact: new fixture
sq_2880x320_wp(2 LfGroups, 12 PassGroups, > 1024-node all-predictor-6 global tree) decodes coded-domain and output bit-exact — WP state, group-seam borders and the §C.5.2 walk all verified in the coded domain. The D.4.2 tree-size cap now matches the spec bound ((1 << 26); the old 1024 working cap rejected this real encoder output). -
Listing I.18 in-place inverse-Squeeze pairing fix:
rstays constant through the c-loop (each merge removeschannel[r]). The oldr + (c - begin)mis-paired every in-place step withnum_c > 1— invisible on grey pyramids, a hard error on the 3-channel XYB default sequence. Lossy-modular XYB streams now run the full inverse (their RGB output mapping for Grey colour-space frames is still pending). -
§J restoration filters wired into the registered Modular path: Gabor-like transform + EPF now run on Modular frames that signal them. The
grayscale_public_universitystream (lossy Squeeze, gab=1, epf_iters=3) drops from MAD 1.68 to 1.00; its Modular pyramid decode is verified fully in sync (all 87 modular sub-bitstreams end on the D.3.3 ANS final-state invariant0x130000— the residual is filter accuracy, not entropy or Squeeze). -
§J.2 EPF weight-sign erratum: the printed
4 × (sqrt(0.5) - 1) / sigmais negative, makingWeight()INCREASE with distance (the most dissimilar neighbours would get the largest weights) — contradicting J.3.1's normative "decreasing function" prose. The magnitude4 × (1 - sqrt(0.5))is the conformant reading (literal sign: MAD 6.3 on the same stream). -
Docs-gap (filed): the §J.3 EPF sample-domain / channel-scale semantics for kModular non-XYB frames are underdetermined — under the literal signalled
epf_sigma_for_modular = 20the EPF is a near-identity on 8-bit-scaled samples, while an (unjustified) sigma ≈ ×32 would minimise the residual at ≈ 0.42. The literal reading is shipped pending a behavioural trace. -
The ImageMetadata-tail SPECGAP is resolved (Table A.16): the
default_transformBool() is unconditional — present even whenall_default— and its printed gating is inverted (bit set = "defaults, nothing follows"; bit clear readsopsin_inverse_matrix/cw_mask/ the custom upsampling-weight arrays). A per-field metadata-tail gating trace (metadata_fdis::METADATA_TAIL_TRACE) pins the layout on every staged fixture. -
ICC-bearing streams decode.
enc_size = U64()follows the end of ImageMetadata at the very next bit (no ZeroPadToByte(), despite the §B.2 "byte aligned" opener); Listing B.1'sIccContextgained its missing ASCII-digit class; and the D.2.1 simple prefix code assigns the short code to the FIRST symbol as transmitted (only equal-length symbols sort). The 18181-3grayscalestream's embedded 912-byte GRAY/ADBE profile decodes with every header field matching the reference tooling, and a synthesised digit-heavy profile embedded by a real encoder round-trips byte-exactly (round408_icc_grayscale). -
§C.7.1 half-resolved: the §C.3.2
endfield is a Lehmer-entry count, not an endpoint (pinned: the grayscale stream codesend = 0, skip = 4, impossible under the endpoint reading). The decode advances pastDecodePermutation()and now stops loudly in §C.7.2 — the permutation stream's exact end position is still underdetermined (the ANS final-state invariant fails on locally generatedused_ordersstreams), so the grayscale frame itself remains refused, one boundary later than round 393.
- §C.7.1
used_orders != 0streams DECODE — the Listing C.12 per-channel permutation layout erratum. The printed listing reads ONEDecodePermutation()per setused_ordersbit; the wire carries THREE — one per colour channel, in the §C.8.3 decode sequence Y, X, B. Pinned by two independent oracles: the stagedpatches-256x256clean-room decode trace (under one-per-bit the §C.7.2 read starts 281 bits early and misparses; under one-per-channel it begins at the recorded position, parses to the recorded shape and the section lands on the trace'sAC_GLOBAL_ENDto the bit) and the D.3.3 ANS final-state closure on ANS-coded specimens (fails under one-per-bit for every fdis-errata.md Part 8.3 context/count grid combination — the prescribed six-way bisection was run to exhaustion first — and closes under one-per-channel). The 18181-3grayscaleconformance stream decodes past its round-393 boundary, and the frame-level multi-pass gate is lifted (pass-major §C.3.1 TOC walk + §C.8.3 cross-pass accumulation run end to end). Multi-preset / multi-pass §C.7 slices (the stagedprogressive-ac-multipassfixture, 3 passes × 2 presets) still refuse loudly one boundary later. - Known limitation (ratcheted): synthetic-edge content decodes
structurally exactly (closure invariant; flat saturated regions
byte-exact) but carries a high-detail VarDCT accuracy deficiency
(MAD ≈ 20 on the
custom_orders_t256_e1fixture vs sub-1 on photo content) that is INDEPENDENT of the permutation content — an open follow-up, bounded byround437_custom_orders_decode. - §K.4 kNoise decodes end to end (
noisemodule): §C.4.7 LUT parse in LfGlobal + per-group XorShift128Plus/SplitMix64 pseudorandom channels + frame-level 5×5 convolution (§6.5 mirroring) + Listing K.5 injection. Stagednoise-feature-256x256fixture: sRGB MAD 0.92 / 0.79 / 0.88, max ≤ 7 — the same sub-1/255 band as the noise-free VarDCT fixtures. - The §J.3-for-kModular SPECGAP is RESOLVED by the in-crate grid
bisection fdis-errata.md Part 9 prescribes: samples normalised to
[0, 1](reading N1) with a 1-channel Grey frame replicated into all three Annex J planes.grayscale_public_universityMAD 1.00 → 0.2909 (max 21 → 8); the previously reported "sigma ≈ ×32 best fit at ≈ 0.42" is reproduced exactly by the normalised-domain grey-in-c0 grid cell — that fit was the missing domain normalisation. CI-gated arbitration (round437_modular_epf_posture).
- §C.4.5 + §K.2 kPatches decodes and renders end to end (
patchesmodule): the Listing C.2 dictionary parse (10-distribution §D.3 stream, D.3.3 final-state guard) and the Table K.1 blending (kNone / kReplace / kAdd / kMul; alpha modes and extra-channel blending refuse precisely — no specimen exercises them). The §C.2 plumbing that feeds it: Table C.3 kReferenceOnly frames decode and are skipped by the multi-frame walk ("not itself part of the image"), andsave_before_ctrecordings land in a walk-level pre-CTReference[0..4]store (float-XYB for xyb frames, normalised samples for integer Modular frames; a pre-CT slot named as a §C.2 blending source refuses precisely). Pinned on three locally generated fixtures: two lossless Modular patch streams (60 single-position dot patches; 9×5-dict multi-position non-square glyph patches) decode bit-exact against black-box reference decodes, and a VarDCT+XYB sibling (Modular-XYB dictionary consumed in the pre-CT float-XYB domain) sits at MAD 1.7/0.8/0.7 — its residual is the round-437 impulse deficiency below, not patch error. The Listing C.2mode-context question (printed ctx 5 vs the unused ctx 6) is unarbitrable on available wire evidence — every specimen's cluster map merges contexts 5 and 6 — so the printed reading ships, with a CI equivalence pin and a per-thread override. - §C.4.6 + §K.3 kSplines wired and wire-validated. No encoder
emits spline streams, so round 441 hand-assembles one bit-by-bit
from the FDIS bundle tables (43 bytes; the builder lives in the
round-441 test and must reproduce the committed fixture
byte-for-byte). The reference decoder accepts it, and arbitrated a
NEW FDIS erratum — the Listing C.3 field order: on the wire
quant_adjustfollows the starting-coordinate loop, notnum_splinesas printed. Under the printed order the reference decode places the spline aty = sp_x, starts x at 0, and scales the brush by exactly1 + sp_y/8(our second token consumed as a start coordinate, our fourth asquant_adjust); three independent geometry/σ probes all fit the corrected order. With it, our §K.3 render (Catmull-Rom upsampling → arc-length resampling → erf brush, incl. the Part 3 K.1 arc-parameter correction) matches the reference decode to max ±1/255. - §L.2 kModular XYB rescale erratum — the ×
mproduct divides by 128. The FDIS prose readsX = X' × m_x_lf_unscaledwith no further scale; on real streams the literal reading saturates every sample (≈128× too large). Per-channel linear regression of the wire integers against black-box reference decodes of three independent lossy-Modular-XYB streams fits slopem / 128on every channel (±0.02 %, zero intercept). The Modular-XYB output path — previously never pixel-validated — now lands max ±1/255 on all three fixtures; the same /128 makes the VarDCT patches fixture's XYB dictionary land correctly. - The round-437 "synthetic-content VarDCT accuracy deficiency" is
sharply characterised (root-caused round 444; the final residual
closed round 451 with the §C.8.3 errata): isolated impulse content
(single-pixel dots) vanishes entirely on VarDCT frames with or
without features. The dot blocks are Hornuss / DCT2×2 varblocks
whose declared NonZeros exceeds the decoded nonzero count (e.g.
raw 20, decoded 9,
remaining_non_zeros = 11after the full k-walk — a silent D.3.3-class violation the §C.8.3 loop currently does not reject). The dedicated fixture generated this round reproduces it standalone; follow-up round material.
Round 444 — the §C.8.3 entropy layer root-caused: impulse deficiency FIXED, two new FDIS errata (§F.3 2^16/global_scale, Listing I.4 orientation)
Round 444 took the round-441 impulse reproducer (Hornuss / DCT2×2 varblocks decoding fewer nonzeros than declared, `remaining_non_zeros
0
silently accepted) to root cause and found SIX distinct defects stacked across the §C.8.3 entropy layer and the reconstruction chain, each arbitrated black-box on purpose-built single-basis-function / impulse probe streams (committed as ther444_*` fixtures with their reference decodes):
- §C.8.3 reads are D.3.6 hybrid-integer reads — the
histogram-backed path returned raw entropy tokens, truncating every
value ≥ the cluster's
splitand skipping its raw completion bits. Invisible while photo-content coefficients stayed below the split; a desyncing misparse on impulse content. This was the round-437/441 deficiency's primary cause. - Per-section entropy-stream lifecycle (D.3.3) — each PassGroup
section is its own stream: the per-section
u(32)ANS init was silently skipped for sections after the first (idempotency guard), and no terminal-state check existed. Sections now re-init, tear down, and checkstate == 0x130000, with two public per-thread diagnostics (hf_coefficient_histograms::section_closure_failures,pass_group_hf::walk_underruns) that CI pins per fixture — the desync states that rounds 437/441 accepted invisibly can never go silent again. - Listing I.4 IDCT orientation (FDIS erratum) — the inverse-DCT
pre-transpose belongs to the
C > Rbranch only; running it for every shape (rounds 12..441) transposed the coefficient interpretation of square and tall blocks. Masked inside the photo sub-1/255 band, fatal on basis/impulse content: the reference decoder reproduces the encoded orientation, the pre-transposed reading its transpose. The forwardDCT_2Dhelpers were re-derived to Listing I.3 literally. - §F.3 / §C.6.2 omit the global quantization scale (NEW FDIS
erratum #5) — the "final multiplier defined by the channel, the
transform type and the coefficient index" also carries
2^16 / global_scale(the §C.4.3 Quantizer field; the LF sibling is explicit in Listing C.1). Fit on five independently generated probe streams spanningglobal_scale1022..10223 and HfMul 7/11: reference amplitude ratio ≡65536 / global_scaleon every stream (−2..−9 %, the sign and size of the Listing F.2 bias adjustment); independent ofquant_lf(varied 15..23) and of HfMul beyond the round-393 §F.3 division, which the same data re-confirms. - Listing I.16 LLF normalisation (round-385 erratum refined) —
over the §I.2.1-normalised forward DCT, each LLF axis carries
exactly the Listing I.15
C(c, 8c, u)boundary term (measured 0.7871 / 0.9018 at u = 3 / 2 on Dct32x32 probes — the cosine products to four decimals). The round-385 "no factor at all" measurement was taken atop the transposed IDCT and the missing global scale, which masked it. - §C.7.1 per-channel permutation assignment is channel-index order
X, Y, B (round-437 erratum refined) — the assignment is invisible
to every bit-position oracle (the three permutations are decoded
back-to-back either way), so round 437's Y-X-B reading was never
actually arbitrated; a 171-byte custom-orders impulse specimen
(
r444_minidots) decides it: under Y-first the Y-channel Hornuss corner coefficient lands on the wrong cell and the dots vanish, under index order the decode is reference-band exact.
Measured: the round-441 standalone impulse reproducer class decodes
at max ±1/255 (r444_onedot, r444_minidots, r444_basis32,
r444_basis64 — including a Dct64x64 walk with 569 declared nonzeros
and |q| ≈ 500), flat-content-lf-smoothing tightens to max 1,
vardct-256x256-d3 0.47/0.31/0.55 (was 0.76/0.51/0.81),
large-1024x768-d2 0.39/0.33/0.30, noise-feature 0.69/0.67/0.70
max 4 (was max 7), patches_vardct MAD 1.91/0.85/0.91.
Round 448 — ISO/IEC 18181-2 file format + byte-exact JPEG reconstruction; the round-444 desync class CLOSED
- The full 18181-2:2024 box layer (
container): the Clause 8 box walk (LBox/XLBox/last-box), byte-exact Signature and File Type validation,jxll(third-box constraint), thejxliFrame Index parse (Table 9, E.4.2 Varint), strictjxlcXORjxlpcodestream population with the §9.10 index sequence, orderedExif/xml/jumbmetadata capture includingbrob-wrapped equivalents (Brotli viacompcol; the §9.7 forbidden-payload types reject), andjbrdcapture. - §9.11 JPEG Bitstream Reconstruction Data (
jpeg_bitstream): the full Tables 11–18 bundle parse plus the trailing Brotli data streams. Two wire-arbitrated corrections to the printed tables: the per-code counts field has 17 slots (code lengths 0..16 — with 16 the first table's Kraft sum is 1/2 and every later table misparses) and the values selector reaches 256, the sentinel symbol the A.3 rule drops when serializing DHT. - Annex A JPEG reconstruction (
jpeg_reconstruct::reconstruct_jpeg): a lossless JPEG→JXL transcode reproduces the ORIGINAL JPEG file byte-exactly — coefficient-level codestream decode (§I.2.4 RAW dequant matrices carrying the JPEG quant tables, §C.8.3 entropy, §C.5.3 quantized LF as JPEG DC), the A.1–A.11 segment loop, and the 10918-1 sequential entropy re-encode. Ten committed transcode pairs pin byte-exactness: 4:4:4 / 4:2:0 / 4:2:2, custom coefficient orders, COM markers, DRI/restart markers, a 512×320 multi-group frame, and the staged docsjpeg-transcodefixture (256×256 noisy photo). Wire-arbitrated findings: the JXL cell layout is the TRANSPOSE of the JPEG raster (coefficients and RAW tables); stored chroma re-correlates in the integer domain asc += round(k·y·qY/qC)withk = base_correlation + tile/colour_factor(no Listing F.2 bias, no qm-scale factor); RAW slots carry NO ZeroPadToByte before their modular sub-bitstream (the 2021 FDIS prints one; the 2024 listing and the wire agree); A.6 entropy padding defaults to ONE bits (the text prints "zero"). - The round-444 open §C.8.3 desync class is CLOSED — a 2021/2024
edition divergence. At a block's first HF coefficient read the
2021 FDIS Listing C.14 prints
non_zeros > size/16 → prev = 1; the 2024 IS (I.4) prints→ prev = 0. This crate implemented the 2021 text; blocks opening with1 ≤ non_zeros ≤ size/16routed their first read to the wrong cluster whenever the stream's cluster map splits the two contexts — exactly the near-uniform non-dyadic histograms of the r444 reproducers. Pinned byte-exactly by the JPEG-oracle fixtures;r444_wave64now closes cleanly at MAD < 1.0 / max 2 (was one failed section, MAD < 6 / max 40), and the committed photovardct-256x256-d1— the crate's longest-standing accuracy tail — collapses from MAD 0.66 / 0.47 / 0.61 to 0.25 / 0.25 / 0.26 (max ≤ 3) with clean closure (ratchet tightened to 0.35). The two round-448 residuals — the r437custom_orders_t256_e1walk underruns and ther448_noise444desync — were closed in round 451 (see below). - F.2 channel-scaling correction (first reachable this round): a
jpeg_upsamplingvalue names that channel's sampling FACTORS and the channels stored subsampled are those BELOW the frame-wide maximum — for{Cb, Y, Cr} = {0, 1, 0}the chroma planes halve. The I.4 subsampled varblock walk decodes chroma only on its lattice with NonZeros bookkeeping on the channel's own grid; §I.6 chroma-from-luma is skipped entirely for subsampled frames. probe_fdison box-structured files now skips the extracted codestream'sFF 0Asignature (previously the two signature bytes parsed as SizeHeader bits and coincidentally yielded a plausible header for 256-px-tall images).
Round 451 — the noisy-content §C.8.3 desync class CLOSED (two errata) + Annex A reconstruction complete across greyscale / ICC / MCU-padded / progressive JPEGs
Round 451 root-caused the desync class that had stalked every noisy 4:4:4 stream since round 444 to two independent errata, each bisected on an 18-stream noise ladder against a test-side 10918-1 sequential decode of the original JPEG bytes:
CoeffNumNonzeroContext[21]transcription error. Listing C.13 prints eight 152 entries (non_zeros13..=20) and twelve 180 entries (21..=32); the in-crate table (rounds 159–450) carried a ninth 152 at index 21. Invisible until a stream's cluster map splits the 152-family coefficient contexts from the 180-family AND a block's remaining count walks through exactly 21 — the near-uniform histograms of noisy content — where the one misrouted read desynced the shared ANS state. Bisected on a 48×48 stream whose first luma block decodes k=1..11 exactly and diverges at precisely the first read that consultsCNNC[21].- Alias-map exactly-full buckets (2024 IS C.2.6 arbitrating FDIS
Listing D.1): a bucket with
cutoffs[i] == bucket_sizegoes on NEITHER Vose worklist. The FDIS's plainelsequeues it as underfull; popping it is a mass-free no-op that still consumes a pump step and re-queues the overfull bucket, permuting every later (overfull, underfull) pairing — a different alias table whose redirected slices decode wrong symbols.
With both landed: r448_noise444 (256×256 noisy photo)
reconstructs byte-exact (its loud-refusal pin flipped); all 18
noise-ladder streams byte-exact; the r437 custom_orders_t256_e1
"synthetic-content accuracy deficiency" (MAD ≈ 20/13/8) collapses to
0.55 / 0.46 / 0.76 with clean closure/underrun diagnostics
(ratchet now 1.0).
The same round found the CfL tie-rounding erratum: the integer
chroma re-correlation c += round(tile × y × qY / (cf × qC)) is an
exact rational whose half-way values round TOWARD ZERO (13 tie
specimens; rounds 448–450 used f64 round() — half-away — and
emitted a silently wrong JPEG, ±1 on one chroma coefficient, on
every tie). The inversion is now pure i64 arithmetic.
Annex A reconstruction now covers, each pinned byte-exact on locally
generated cjxl transcode pairs:
- greyscale JPEGs (
is_grey— single luma component mapping to JXL channel 1, gradient + noisy specimens); - ICC-carrying JPEGs (A.9 kind 1 — the Annex B / E.4 encoded ICC
stream now decodes on the transcode path and re-chunks into
ICC_PROFILEAPP2 segments); - MCU-padded dimensions (the §F.2 grid rule pads the full-resolution channels' block grid to the MCU boundary — a 100×60 4:2:0 transcode stores a 14×8-block luma grid whose padded blocks carry the JPEG dummy blocks' real coefficients; non-interleaved scans walk the TRUE 10918-1 A.1.1 grid);
- progressive (SOF2) JPEGs (the 10918-1 Annex G re-encode with
the A.6 amendments: DC first/refinement scans, AC first/refinement
scans with the shared EOB run and buffered correction bits,
reset_points-forcedEncode_EOBRUN, restart-interval flushes — pinned on 4:4:4, 4:2:0 MCU-padded, greyscale andjpegtran -restartspecimens). Arithmetic-coded JPEGs (SOF9/10) refuse precisely.
-
Multi-preset / multi-pass §C.7 slices with
used_orders != 0(the round-437 residual): after preset 0's per-channel Listing C.12 bundles, the next preset's fields still misparse on the stagedprogressive-ac-multipassfixture — the per-preset repetition or per-pass slice layout hides one more wire divergence (round437_custom_orders_boundarypins the loud refusal). -
A rare self-consistent §C.8.3 chroma mis-parse (round 451): ~0.03 % of chroma coefficients on some noisy 4:4:4 content decode off by one (14 cells across a 39-stream corpus; ~2 per affected stream) while every closure invariant passes — the decode is a VALID ANS parse that differs from the encoder's, so some context-model input still diverges on a rare block shape. The CfL integer rule itself is CONFIRMED at ~47 000 datapoints (nearest, exact half-way ties toward zero: 413/413 near-half specimens away, 163/163 ties toward zero); the mismatching cells are decode-side. Both a sequential and a progressive transcode of the same content deviate identically (the model is stream-independent). Pinned loudly (
cfl_near_half_corner_still_open) until a reproducer isolates the input. -
The residual sub-1/255 VarDCT accuracy tail (float rounding + §J filter differences) and
save_before_ctpre-CT reference recording in the §C.2 composer. (The stagedprogressive-ac-multipassfixture now exists and reaches the multi-preset §C.7 boundary above; the end-to-end multi-pass pixel pin lands when that boundary closes.) -
ColorEncoding / ToneMapping fuller decode, preview / animation / intrinsic-size sub-bundles (parsing stops cleanly at the
have_*flags). -
The AFV non-DCT IDCT variants (parsed and dispatched; accuracy unvalidated — no staged fixture reaches them with a pixel oracle).
-
Floating-point samples and
bps > 16; high-bit-depth XYB / YCbCr. -
Surfacing the decoded ICC profile to callers (the Annex B decode runs and validates, but
oxideav_core::VideoFramehas no ICC slot) and applying an embedded profile's transfer curve to the decoded samples (thegrayscalestream's image output currently uses the signalled/default transfer, sRGB, rather than the profile's gamma-2.2-classkTRCcurve). -
Patch alpha blend modes (kBlendAbove/Below, kAlphaWeightedAdd Above/Below) and extra-channel patch blending — parsed exactly, refused precisely at render (no specimen exercises them; the decode paths carry no extra planes there yet). Splines on non-XYB Modular frames (the §K.3 coefficients are XYB-domain quantities; domain undetermined) and kNoise on Modular frames stay refused.
-
Arithmetic-coded JPEG (SOF9/SOF10) reconstruction (refuses precisely; no staged specimen —
cjxldeclines arithmetic input). Pixel-domain decode of YCbCr transcode frames (the registered decoder still refuses them; reconstruction is coefficient-level). -
The LfFrame (
lf_level > 0) dimension scalingprogressive-dcneeds. -
The encoder (not registered).
Unsupported inputs surface as Error::Unsupported rather than a silent
misparse.
Earlier decoder and encoder work was reset off master in 2026-05 when
the behavioural-trace document it had been authored against was
withdrawn from docs/ under fruits-of-the-poisonous-tree (the writeup
could not be guaranteed free of structural narrative carried from a
third-party implementation). Decoder work resumed against the published
core specification PDF, the conformance corpus, and the small lossless
fixtures committed under docs/image/jpegxl/fixtures/. Workspace policy
forbids consulting any third-party implementation source as a
substitute.
[dependencies]
oxideav-core = "0.1"
oxideav-codec = "0.1"
oxideav-jpegxl = "0.0"use oxideav_jpegxl::{probe, Signature};
let bytes = std::fs::read("input.jxl")?;
let headers = probe(&bytes)?;
match headers.signature {
Signature::RawCodestream => println!("raw .jxl codestream"),
Signature::Isobmff => println!("ISOBMFF-wrapped .jxl"),
}
println!("{}x{}", headers.size.width, headers.size.height);
println!("{} bits/sample, float={}",
headers.metadata.bit_depth.bits_per_sample,
headers.metadata.bit_depth.floating_point);
# Ok::<(), Box<dyn std::error::Error>>(())- Codec
"jpegxl"— decoder slot registered; no encoder slot. The registered decoder handles the Modular path (grey / RGB / RGBA, 1–16-bit integer) and the VarDCT path (single-LfGroup frames of any group count, reference-validated; see Status). - No demuxer is registered: a JXL file is treated as a single
codestream buffer fed directly to
probe(...).
oxideav_core::VideoPlane carries (stride, data) only — there is no
per-plane bit-depth field in core 0.1.x. The decoder packs samples into
data: Vec<u8> according to the codestream's bits_per_sample
(Annex A.6 + Table A.22):
bits_per_sample (bps) |
Bytes / sample | Plane stride | Layout |
|---|---|---|---|
1 ..= 8 |
1 | width |
sample clamped to [0, 2^bps - 1] |
9 ..= 16 |
2 | width × 2 |
little-endian u16 per sample |
Floating-point samples and bps > 16 are not yet supported and surface
as Error::Unsupported. The little-endian 16-bit convention lets a
little-endian host take a zero-cost u16 view of the plane:
let samples: Vec<u16> = plane
.data
.chunks_exact(2)
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect();MIT — see LICENSE.