Releases: OxideAV/oxideav-jpegxl
v0.0.11
Other
- round-309 (parent-dispatch r309) against ISO/IEC FDIS 18181-1:2021 — per-LfGroup VarDCT three-channel residual-plane assembly + Annex G chroma-from-luma
- round-306 (parent-dispatch r306) against ISO/IEC FDIS 18181-1:2021 — per-LfGroup VarDCT residual-plane assembly (§C.5.4 placement + §C.8.3 + Table I.4/§I.2.3 pixel-dims)
- cover non-DCT transforms (Hornuss/DCT2x2/DCT4x4/DCT4x8/DCT8x4/AFV) — round 300
- round-293 (parent-dispatch r293) against ISO/IEC FDIS 18181-1:2021 — extend the per-block VarDCT decode walk to every plain separable-DCT transform (rectangular + DCT64..256 family), lifting the round-286 square-only orientation deferral
- Round 286 — per-block VarDCT decode walk to spatial samples (square DCTs)
- round-281 (parent-dispatch r281) against ISO/IEC FDIS 18181-1:2021 — two §C.8.3 decode-walk prose-conformance fixes: per-varblock channel decode order is Y, X, then B (rounds 221..264 advanced the entropy stream X-first; Listing C.13's (c < 2 ? c ^ 1 : 2) mapping independently corroborates Y-first) + NonZeros(x, y) writeback covers every block of the varblock footprint per the 'for each block in the current varblock' prose (rounds 177..264 wrote only the top-left cell, corrupting PredictedNonZeros reads against continuation cells of multi-cell transforms)
- round-278 (parent-dispatch r278) against ISO/IEC FDIS 18181-1:2021 — FIX the rounds-31..272 noise-64x64-lossless WP pixel divergence: Listing E.2 error2weight Idiv-first operand order + true_errNW column-0 N-fallback, both pinned by the staged wp-trace-sample-194.md; noise-64x64-lossless now byte-exact on all three planes and synth_320 pixel-exact (102400/102400)
- name + pin the WP sub_err reading choice (Annex E.1)
- round-264 (parent-dispatch r264) against ISO/IEC FDIS 18181-1:2021 — HfHistogramDecodeContext::decode_lf_group_three_channels_for_pass bundled per-LfGroup raster-walk three-channel decode driver for one pass
- round-260 (parent-dispatch r260) against ISO/IEC FDIS 18181-1:2021 — HfHistogramDecodeContext::decode_three_channel_varblock_for_pass bundled three-channel per-varblock walk composing the round-255 single-channel decode method three times against the round-214 BlockContextResolver per-channel block_ctx derivation
- round-255 (parent-dispatch r255) against ISO/IEC FDIS 18181-1:2021 — HfHistogramDecodeContext::decode_block_for_pass_transform bundled per-varblock decode method composing the round-90 Listing C.14 state machine with the round-252 per-pass histogram routing
- round-252 (parent-dispatch r252) against ISO/IEC FDIS 18181-1:2021 — multi_pass_hf_histogram_decoder::HfHistogramDecodeContext typed bridge wiring the §C.7.2 entropy stream to the §C.8.3 per-pass histogram_offset routing
- drop release-plz.toml — use release-plz defaults across the workspace
- round-247 (parent-dispatch r247) against ISO/IEC FDIS 18181-1:2021 — §C.7.2 HfCoefficientHistograms wrapper performing the actual EntropyStream::read of the 495 × num_hf_presets × nb_block_ctx clustered-distributions block
- round-238 (parent-dispatch r238) against ISO/IEC FDIS 18181-1:2021 — hf_coeff_histogram_size typed sizing primitive for §C.7.2 + §C.8.3 routing offset
- round-232 (parent-dispatch r232) against ISO/IEC FDIS 18181-1:2021 — per-LfGroup multi-pass HF-header + per-pass histogram_offset routing driver (§C.8.3 first paragraph)
- round-228 (parent-dispatch r228) against ISO/IEC FDIS 18181-1:2021 — per-LfGroup multi-pass three-channel varblock decode driver (§C.8.3 + Table C.6 Passes outer pass loop)
- round-221 (parent-dispatch r221) against ISO/IEC FDIS 18181-1:2021 — three-channel per-LfGroup varblock decode driver (§C.8.3 outer-varblock × inner-X/Y/B sweep)
- round-214 (parent-dispatch r214) against ISO/IEC FDIS 18181-1:2021 — per-LfGroup BlockContext() resolver (§C.8.3 Listing C.13 + §I.2.2 HfBlockContext bundle)
- round-208 (parent-dispatch r208) against ISO/IEC FDIS 18181-1:2021 — per-LfGroup varblock-walk driver (§C.5.4 + §C.8.3)
- round-202 (parent-dispatch r202) against ISO/IEC FDIS 18181-1:2021 — full-row WP state-evolution chain validation across noise-64x64-lossless samples 192..=200
- Hat-2 scrub: replace 'libjxl' decorative-attribution lines with neutral terms
- r195 fix — add serial_test for r195 WP trace tests
Added
-
Round 309 — per-LfGroup VarDCT three-channel spatial-reconstruction
layer (src/residual_plane.rs), lifting the round-306 single-channel
assemble_channel_planeto the X / Y / B level and applying Annex G
chroma-from-luma. New public API:ChannelResidualPlanes(the three XYB
residual planes of one LfGroup, all on the shared padded block grid,
channel order 0 = X / 1 = Y / 2 = B per Listing C.13, withx()/y()
/b()/dims()accessors);assemble_three_channel_planes(grid, residual_at)(walks the shareddct_select::DctSelectGridonce per
channel viaassemble_channel_plane, invoking the caller's
residual_at(channel, &vb)decode closure — in VarDCT mode all three
channels share one DctSelect grid per §C.5.4, and Annex G CfL "is skipped
if any channel is subsampled," so the three planes are geometrically
identical);apply_chroma_from_luma(planes, x_from_y, b_from_y, cfl)
(applies Annex G Listing G.1 in place via the round-138
chroma_from_luma::apply_hf_plane_inplace:X = dX + kX·Y,
B = dB + kB·Y, with(kX, kB)looked up per the 64×64 tile containing
the sample; after the call X-plane holds finalX, B-plane finalB, Y
unchanged); and the one-call driverreconstruct_three_channel_planes( grid, x_from_y, b_from_y, cfl, residual_at)(= assemble + CfL). 9 unit +
5 integration (round309_three_channel_residual_plane, composing the
real F.3-dequant + I.2.3-IDCT walk across all three channels then the
real Annex G CfL end-to-end) tests. Lib tests 788 → 797 (+9).
Pure-control-flow composition primitive — no bit reads, no spec
re-derivation, no histogram materialisation. Gaborish (Annex J.2) + EPF
(Annex J.3) run on the returned planes and remain caller-side concerns.
Source of truth: ISO/IEC FDIS 18181-1:2021 §C.5.4 (DctSelect placement) +
Annex G (chroma-from-luma, Listing G.1) + §F.3 / §I.2.3 (dequant + IDCT). -
Round 306 — per-LfGroup VarDCT residual-plane assembly
(src/residual_plane.rs), the spatial-placement layer directly above
the round-286/293/300block_dequantper-block decode walk. Walks a
dct_select::DctSelectGridviavarblock_walk::VarblockWalkand
writes each varblock'sR × Crow-major residual block (the
block_dequant::decode_block_to_residualoutput) into a single-channel
spatial plane at the varblock's pixel origin(bx · 8, by · 8). New
public API:ResidualPlane(row-majorf32plane sized to the padded
block gridwidth_blocks·8 × height_blocks·8,for_grid/get);
block_pixel_dims(t)(the(R, C)pixel shape from
idct::dct_pixel_dims∪non_dct_pixel_dims, covering every
TransformType);place_block(plane, vb, block)(verbatim copy with
length-mismatch + footprint-spill rejection); and
assemble_channel_plane(grid, residual_at)(raster-order grid walk
invoking the caller's per-varblock decode closure once per top-left
cell, continuation cells skipped, residual-Emptycell rejected). The
plane is the padded block grid (no per-edge clamping; caller crops to
lf_w × lf_h). The geometry invariantC == block_dims().0 · 8/
R == block_dims().1 · 8is pinned for every transform. The IDCT
output already carries the LLF/DC contribution (§I.2.5) so no separate
DC add at placement; chroma-from-luma / Gaborish / EPF run on the
assembled plane and remain caller-side concerns. 14 unit + 5
integration (round306_residual_plane, composing the real F.3-dequant- I.2.3-IDCT walk end-to-end) tests. Lib tests 774 → 788 (+14).
Pure-control-flow geometry primitive — no bit reads, no spec
re-derivation, no histogram materialisation. Source of truth:
ISO/IEC FDIS 18181-1:2021 §C.5.4 (DctSelect placement) + §C.8.3 +
Table I.4 / §I.2.3 (pixel-dims).
- I.2.3-IDCT walk end-to-end) tests. Lib tests 774 → 788 (+14).
-
Round 300 — extend the per-block VarDCT decode walk
(src/block_dequant.rs) to the non-DCT transforms: Hornuss,
DCT2×2, DCT4×4, DCT4×8, DCT8×4, and AFV0..AFV3 — i.e. exactly the set
for whichidct::non_dct_pixel_dimsreturnsSome(all8 × 8).
This lifts the round-293 deferral. The deferral worried that the
AFV / DCT2×2 sub-block coefficient extraction "does not reduce to a
flat identity over an8 × 8grid", but per ISO/IEC FDIS 18181-1:2021
the §I.2.3 sub-block re-mapping happens inside the inverse-transform
dispatch (idct_afv,idct_dct2x2, …), which the spec applies
after the Annex F.3 dequant. The §F.3 dequant stage is uniform: it
multiplies each stored coefficient by a multiplier keyed on "the
channel, the transform type and the coefficient index inside the
varblock". For every non-DCT transform the varblock is the8 × 8
OrderId-1 grid (coeff_order::varblock_size_for_order→(8, 8)),
the dequant matrix is the8 × 8slot matrix
(weights_matrix_dims_for_slot→(8, 8)for slots 1 / 2 / 3 / 9 /
10), and the decoded block is already in raster index space
(coeffs[natural_order[k]],natural_order[k] = y·bwidth + x), so
the per-cell dequant is the identity raster map — exactly as for the
square / rectangular DCT family, with no orientation subtlety
(bwidth == bheight == 8).covered_grid_dimsnow returnsSomefor
everyTransformType;require_covered'sUnsupportedpath now only
guards a hypothetical future variant lacking a pixel-dims mapping.
+3 lib tests (non-DCT all-zero residual census; non-DCT single-coeff
per-sample-formula identity; AFV0..AFV3 shared-slot/grid dequant
equality; chained == manual dequant-then-IDCT for the non-DCT path).
Lib tests 771 → 774. -
Round 293 — extend the per-block VarDCT decode walk
(src/block_dequant.rs) from the three square plain-DCT transforms
to **every ...
v0.0.10
Other
- round-191 (parent-dispatch r191) against ISO/IEC FDIS 18181-1:2021 — Annex E / §H.5.2 Weighted-Predictor oracle test driven by clean-room behavioural trace at noise-64x64-lossless sample 194
- round-190 (parent-dispatch r190) against ISO/IEC FDIS 18181-1:2021 — typed per-pass NonZeros(x, y) grid container above the round-183 per-channel primitive
- round-183 (parent-dispatch r183) against ISO/IEC FDIS 18181-1:2021 — typed per-channel NonZeros(x, y) grid container layered above round-177 single-channel primitive
- round-177 (parent-dispatch r177) against ISO/IEC FDIS 18181-1:2021 — typed NonZeros(x, y) grid bookkeeping + per-varblock decode driver
- round-164 (parent-dispatch r164) against ISO/IEC FDIS 18181-1:2021 — TransformType-driven entry points for the §C.8.3 per-block HF coefficient decode loop
- round-159 (parent-dispatch r159) against ISO/IEC FDIS 18181-1:2021 — §C.8.3 per-block HF coefficient decode loop scaffolding (Listings C.13 + C.14)
- round-150 (parent-dispatch r150) against ISO/IEC FDIS 18181-1:2021 — Annex I.2.3.8 Listing I.13 Inverse AFV transform wired into idct dispatch
- round-147 (parent-dispatch r147) against ISO/IEC FDIS 18181-1:2021 — Annex I.2.2 AFV basis + AFV_IDCT pure-math primitive (Listings I.5 + I.6)
- round-144 (parent-dispatch r144) against ISO/IEC FDIS 18181-1:2021 — Annex J.3 edge-preserving-filter pure-math primitive
- round-141 (parent-dispatch r141) against ISO/IEC FDIS 18181-1:2021 — Annex J.2 Gabor-like-transform pure-math primitive
- round-138 (parent-dispatch r138) against ISO/IEC FDIS 18181-1:2021 — Annex G Chroma-from-Luma pure-math primitive (Listing G.1)
- round-133 (parent-dispatch r133) against ISO/IEC FDIS 18181-1:2021 — §C.7.1 DecodePermutation() for used_orders != 0
- Round 129: per-varblock LF→LLF composition glue (§I.2.5 plumbing)
- Round 126: WP deep-trace plumbing + sample-194 hand-derivation
- round-121 (parent-dispatch r121) against ISO/IEC FDIS 18181-1:2021 — §I.2.5 LLF-from-LF pure-math step (Listings I.15 + I.16)
- round-95 (parent-dispatch r95) against ISO/IEC FDIS 18181-1:2021 — §F.3 HF dequantisation pure-math step
- round-90 (parent-dispatch r90) against ISO/IEC 18181-1:2021 FDIS — HfPass + PassGroup HF structural parsers
- round-89 (parent-dispatch r89) against ISO/IEC 18181-1:2024 — GetDCTQuantWeights + Table I.6 default dequantization-matrix materialisation
- rewrite lf_dequant comment to remove libjxl numeric-defaults citation
- round-77 fixup — inline animation-3frame fixture under crate-local tests/fixtures/
- round-77 (parent-dispatch r17) against ISO/IEC 18181-1:2024 — animation-3frame SPECDIFF audit harness
- round-32 (parent-dispatch r17) against ISO/IEC 18181-1:2024 — noise-64x64-lossless pixel-divergence bisected to WP at first predictor=6 sample with WW/NN both in-image; fix deferred pending libjxl-WP behavioural trace
- round-31 (parent-dispatch r16) against ISO/IEC 18181-1:2024 — §F.3 zero-pad uniformly applied to single-TOC-entry LfGlobal fast path
- round-30 (parent-dispatch r15) against ISO/IEC 18181-1:2024 — bit-depth-16 RGB pixel-correct + 16-bit LE plane-pack convention
- round-29 (parent-dispatch r14) against ISO/IEC 18181-1:2024 — alpha-64x64 RGBA pixel-correct + ISOBMFF FF 0A strip
- round-28 (parent-dispatch r13) against ISO/IEC 18181-1:2024 — non-DCT IDCT helpers (Annex I.9.3..I.9.7)
- round-27 (parent-dispatch r12) against ISO/IEC 18181-1:2024 — IDCT dispatch (Annex I.2.1 + I.2.2 Listing I.4)
- round-26 (parent-dispatch r11) against ISO/IEC 18181-1:2024 — Annex L colour transforms (XYB inverse + YCbCr inverse)
- round-25 (Auditor mode) against ISO/IEC 18181-1:2024 — d1 LfCoefficients per-sample rich-state range dump 22..=79
- round-24 (Auditor mode) against ISO/IEC 18181-1:2024 — d1 per-cluster D[] byte trace + per-call alias-mapping invariant audit
- round-23 (Auditor mode) against ISO/IEC 18181-1:2024 — d1 leaf-pick property dump at Y' sample 22 + WP y=0 boundary audit
- round-22 (Auditor mode) against ISO/IEC 18181-1:2024 — d1 lf_quant sample dump + WP rounding bias toggle
- round-21 (Auditor mode) against ISO/IEC 18181-1:2024 — d1 per-cluster distribution + alias-table self-map audit
- round-20 followup — refresh round-19 trace eprintln with corrected DC_GROUP budget
- round-20 (Auditor pivot) against ISO/IEC 18181-1:2024 — DC_GROUP boundary recount + ANS-final-state oracle
- round-19 (Auditor mode) — d1 cluster + ANS state evolution audit
- round-18 (Auditor mode) against ISO/IEC 18181-1:2024 — per-token bit accounting trace + drift narrowed
Added
-
Round 191 (2021-FDIS) — Annex E / §H.5.2 Weighted-Predictor
oracle test driven by clean-room behavioural trace at sample 194 of
noise-64x64-lossless. Newtests/r191_wp_trace_oracle.rs(5
tests) and newpub fn modular_fdis::wp_predict_pubtest wrapper
around the productionwp_predict. The oracle consumes the
docs/image/jpegxl/fixtures/noise-64x64-lossless/wp-trace-sample-194.md
trace (provenance recorded alongside aswp-trace-provenance.md),
which records the FDIS-conformant per-listing intermediates an
instrumented reference decoder produces at the
(channel 0, x=2, y=3)divergence point bisected in rounds 31..126:r191_wp_predict_matches_trace_at_sample_194— drives the
productionwp_predictwith the trace'sWpState/Neighbours
inputs; asserts the four sub-predictions[1248, 747, 420, 559],
the final pre-round prediction709, andmax_error = 737all
reproduce exactly. Result: PASS — proves Annex E.2 Listings
E.1 (sub-predictions), E.2 (err_sum_i+error2weight), E.3
(weighted sum + same-sign clamp), and E.4 (max_error) are
spec-correct inwp_predict, isolating the still-unfixed
sample-194 wp_pred8 = 717 vs trace 709 off-by-8 divergence to
upstream state evolution (set_true_err/set_sub_err
calls fired across samples 0..193) rather than the predictor
arithmetic itself.r191_trace_err_sum_self_consistency— pure-arithmetic sanity
check on the trace'ssub_err_{i,N/NE/NW}table summing to the
reportederr_sum_i([438, 330, 416, 240]).r191_trace_weights_match_error2weight— hand-derives the
trace'sweight_i = [495694, 599189, 474830, 825112]from
FDIS-literalerror2weight(err_sum_i, wp_w_i); documents a
1-unit inner-Idiv-vs-multiplication-first discrepancy with the
production reading that does NOT affect sample 194's shifted
weights (both readings give[3, 4, 3, 6]after the Listing E.3
>> shstep).r191_trace_prediction_matches_listing_e3— independent
hand-derivation ofprediction = 709from Listing E.3 inputs,
including verification that the same-sign clamp predicate fires
but is a no-op (pre-clamp 709 ∈ [min(W,N,NE)=584, max(W,N,NE)=
1232]).r191_pin_state_evolution_gap— pins the production-vs-trace
delta as a roadmap for the next round's bisect: Δ te_w = +21,
Δ te_nw = -21 (symmetric pair → likely a single upstream
defect), Δ wp_pred8 = +8 in 8x scale = +1 in un-shifted pixel
space (matchesr126_first_divergence_scandec=35 vs exp=34).
Spec citations and provenance attestation embedded in the test
module docstring; references the in-repo FDIS §E.1-E.4 line
numbers and the trace doc's statedprediction − true_value
sign convention. Trace doc is the newly-staged
docs/image/jpegxl/fixtures/noise-64x64-lossless/wp-trace-*.md
pair landed alongside this round (tasks #820 + #1077). Issues #6,
#64, #799.
-
Round 190 (2021-FDIS) — typed per-pass
NonZeros(x, y)grid
container (FDIS §C.8.3 + Listing C.13 per-pass keying). New
per_pass_non_zerosmodule that owns one
per_channel_non_zeros::PerChannelNonZerosGridsper pass index
p ∈ [0, num_passes), layered above the round-183 per-channel
container. A VarDCT frame is decoded innum_passesordered passes
(declared inFrameHeader.passes.num_passes); each pass scans every
PassGrouponce and §C.8.3 specifies that within a pass each
channel of each varblock maintains its ownNonZeros(x, y)state.
Between passes the per-channel bookkeeping is reset because the
per-pass histogram is selected byhfpfrom the per-passHfPass
array — a different pass uses a different histogram and the
prediction recurrence is keyed against the current pass's own
coefficient counts. The new module captures the per-pass routing
layer above round 183's per-channel routing layer:PerPassNonZerosGrids::new(pass_dims: &[&[(u32, u32)]]) -> Result<Self>
— per-pass per-channel(width, height)slice, validated
entry-by-entry viaPerChannelNonZerosGrids::new(zero / oversize
dims rejected per channel; empty pass-list rejected).PerPassNonZerosGrids::new_uniform(num_passes, num_channels, width, height) -> Result<Self>— convenience builder for the
uniform-per-pass case.PerPassNonZerosGrids::{num_passes, pass, pass_mut, predicted, get, set, update_after_block, update_after_block_for_transform}—
per-pass routing accessors; out-of-rangeperrors cleanly.PerPassNonZerosGrids::decode_block_at_for_pass_channel(p, c, x, y, t, block_ctx, nb_block_ctx, read_non_zeros, decode_symbol) -> Result<(DecodedHfBlock, u32)>— typed per-pass per-channel
driver that wraps the round-183
PerChannelNonZerosGrids::decode_block_at_for_channelwith pass
routing. Caller pre-computesblock_ctxvia
pass_group_hf::block_contextwith the matchingc; the
container is a pure storage + routing primitive and does not
re-derivepass_group_hf::block_contextnor materialise the
per-pass histogram.- Per-pass per-channel shapes are independent — ragged per-pass
channel counts are tolerated.
41 new tests (28 unit in
per_pass_non_zeros::tests+ 13 integration
intests/round190_per_pass_non_zeros.rs) pin: empty-pass-list /
zero-channel-pass / zero-dim rejection; two-pass chroma...
v0.0.9
Other
- round-17 (Auditor mode) against ISO/IEC 18181-1:2024 — d1 bit-position-drift bisect
- round-16 against ISO/IEC 18181-1:2024 — HfMetadata nested transforms (FDIS §C.5.4 + §C.9.4)
- round-15 against ISO/IEC 18181-1:2024 — GlobalModular zero-channel ModularHeader gating + single-TOC-entry section chaining (unblocks d1 past LfGlobal)
- round-14 against ISO/IEC 18181-1:2024 — HfBlockContext custom branch + HfGlobal §I.2.4 dequant-matrix encoding-modes parse
- round-13 against ISO/IEC 18181-1:2024 — DctSelect derivation + HfGlobal + VarDCT pipeline wiring
- round-12 against ISO/IEC 18181-1:2024 — F.1 LF dequant + F.2 adaptive smoothing + G.2.4 HfMetadata
- round-11 against ISO/IEC 18181-1:2024 — LF subband decode (Annex G.2.2 / I.2 / FDIS C.5.3)
- round-10 against ISO/IEC 18181-1:2024 — synth_320 drift bisected to PG[0][0] decode #3087 + C.3.3 lz_dist_ctx spec fix
- round-9 against ISO/IEC 18181-1:2024 — synth_320 0-byte PassGroup blocker resolved via three concurrent fixes
- round-8 against ISO/IEC 18181-1:2024 — C.2.5 SPECGAP partial resolution + VarDCT scaffold
- round-7 against ISO/IEC 18181-1:2024 — four-piece refactor wiring multi-group decode infrastructure (Annex G.1.3 + G.4.2)
- round-6 against ISO/IEC 18181-1:2024 — Annex E.4 ICC profile decode + LfGroup/PassGroup type scaffolding
- round-5 against ISO/IEC 18181-1:2024 — RFC 7932 §3.5 Kraft early-stop fix; grey_8x8_lossless pixel-correct
- round-4 against ISO/IEC 18181-1:2024 — three independent decoder bugs fixed; gradient + palette + gray pixel-correct vs expected.png
- round-3 against ISO/IEC 18181-1:2024 — bit-alignment + alias-mapping fixes
- copy docs fixtures into tests/fixtures/ for CI self-containment
- round-2 against ISO/IEC 18181-1:2024 — inverse transforms + WP predictor
- round-1 against ISO/IEC 18181-1:2024 — Modular pixel decode end-to-end
- clippy 1.95: unusual_byte_groupings + vec_init_then_push fixes
Added
-
Round 17 (2024-spec, Auditor mode) — d1 bit-position-drift bisect.
Round 16 left the d1 fixture surfacing
InvalidData("JXL Modular Squeeze: end 40 >= channel count 4")
and hypothesised an upstream bit-position drift in LfGlobal or
LfCoefficients. Round 17 confirms the drift via a step-by-step
bit-cursor walk through the LfGlobal/LfGroup decode, captured by the
newtests/round17_d1_bit_trace.rsdiagnostic test.Findings (full analysis in
round17-d1-bisect.md):- Our
LfGlobal::readends at codestream-relative bit 1026, which
matches the cjxl ground-truth trace at
docs/image/jpegxl/fixtures/vardct-256x256-d1/trace.txt
(DC_GLOBAL_END=1026) exactly. LfGlobal is NOT the drift site. - Our
LfCoefficients::readconsumes 11995 bits for 3072 LF
samples — but the cjxl trace says the entire LfGroup bundle (=
LfCoefficients + ModularLfGroup + HfMetadata) is 11728 bits
(DC_GROUP_END=12754). LfCoefficients alone is 267 bits over the
whole LfGroup budget, which means the per-channel decode is reading
~2.3 bits more per sample than the spec demands. - The decoded LF coefficient values look plausible (smooth gradient
in ch0, small chroma variation in ch1/ch2), suggesting the entropy
decoder produces "real" tokens but consumes too many trailing
extra bits per token. - Round-16 hypothesis ranked HfBlockContext custom branch HIGH; round
17 RULES THAT OUT (HfBlockContext consumed 87 bits for the smallest
legal custom path, and LfGlobal ended at the cjxl-expected bit
boundary).
Round-18 candidate (deferred, not landed in r17):
crates/oxideav-jpegxl/src/modular_fdis.rs::decode_uint_in_with_dist
hybrid-uint extra-bits accounting on the global-tree-reused leaf
entropy stream. EitherHybridUintConfigis mis-read in
EntropyStream::read(prelude bug) or a stray post-token
u(extra_bits)is being read on the wrong gate
(per-token bug).No code-path fix landed in round 17 (Auditor mode: ship diagnostic
evidence + r18 candidate only). Test count: 328 → 329 (+1: new
d1 bit-trace diagnostic). Five small lossless fixtures + round-11..16
sentinels remain green. - Our
-
Round 16 (2024-spec) — HfMetadata nested transforms (FDIS §C.5.4
- §C.9.4) — the four-channel HfMetadata sub-bitstream now parses
nb_transforms+TransformInfo[]and applies the inverse
transforms in reverse bitstream order to recover the four-channel
base layout[XFromY, BFromY, BlockInfo, Sharpness].
Round 15 closed two stacked bugs (GlobalModular ModularHeader N=0
gate + single-TOC-entry section chaining), exposing the round-12
HfMetadata deferral on the d1 fixture:nb_transforms > 0errored
out as"transforms inside HF metadata sub-bitstream not yet supported (round 13+)". Round 16 wires the parse:HfMetadata::readnow takes themetadata: &ImageMetadataFdis
bundle (forwarded fromLfGroup::read) so the inverse Palette
transform can readbit_depth.bits_per_samplefor delta-palette
prediction.- The four-channel HfMetadata baseline is fed through
apply_transforms_to_channel_layout(mirroring
GlobalModular::read) so the inner per-channel decode operates on
the post-transform list. - After
decode_channels_at_stream,apply_inverse_transformsis
invoked with the sametransformslist so RCT / Palette / Squeeze
are undone and the four-channel baseline is recovered. The decoded
nb_blocksand per-channel widths/heights are validated against
the §C.5.4 baseline before being returned.
Acceptance: the d1 (
vardct_256x256_d1.jxl) fixture now reaches a
strictly-later blocker — its HfMetadata sub-bitstream emits an
explicit Squeeze whoseSqueezeParam.begin_creferences channels
beyond the four-channel baseline (begin_c=39on the very first
step), andapply_transforms_to_channel_layout's
begin_c + num_c <= channel_countinvariant fires with
Error::InvalidData("JXL Modular Squeeze: end 40 >= channel count 4"). That's the round-17 candidate to investigate (suspected
upstream bit-position drift in LfGlobal or LfCoefficients). Round-16
sentinel test (round16_hfmeta_transforms.rs) asserts the d1
progression and the five small lossless fixtures stay
regression-free. - §C.9.4) — the four-channel HfMetadata sub-bitstream now parses
-
Round 15 (2024-spec) — GlobalModular zero-channel ModularHeader
gating (FDIS §C.9.1 last sentence) + single-TOC-entry section chaining
for the VarDCT pipeline. Unblocks the d1 fixture past the LfGlobal
boundary.Round-14 left the d1 (
vardct_256x256_d1.jxl) fixture stuck on
JXL TransformId: invalid value 3. Round-15 root-causes + fixes two
consecutive bugs:-
GlobalModular ModularHeader gating (
global_modularmodule) —
GlobalModular::readwas unconditionally reading the inner
ModularHeader (use_global_tree,WPHeader,nb_transforms,
TransformInfo[]) even when the channel count was zero.
Bit-position trace of d1 confirmed the libjxl reference decoder
ends LfGlobal at the bit where our code starts reading
inner_use_global_tree— i.e. the entire ModularHeader is gated
byN > 0per FDIS §C.9.1 ("In the trivial case where N is zero,
the decoder takes no action."). Fix: skip the inner ModularHeader
whenderive_channel_descsreturns an empty list (the typical
VarDCT-without-extras case). -
Single-TOC-entry section chaining (
decode_vardct_round13) —
whennum_groups == 1 && num_passes == 1, F.3.1 says the TOC has
a single entry containing all sections concatenated bit-aligned
without byte alignment between them.decode_vardct_round13was
slicing each TOC slot into its own byte range, which only works
for multi-entry TOCs. Fix: whentoc.entries.len() == 1, chain
LfGlobal::read→LfGroup::read→HfGlobal::readon a
sharedBitReader.
Acceptance:
vardct_256x256_d1.jxlnow reaches the HfMetadata
transforms-inside-HF-metadata round-13+ deferral message instead of
failing in LfGlobal. Round-15 sentinel test
(round15_d1_past_global_modular.rs) asserts the d1 progression and
the five small lossless fixtures stay regression-free. -
-
Round 14 (2024-spec) — HfBlockContext non-default-table branch
(§I.2.2 custom encoding) + HfGlobal §I.2.4 dequant-matrix
encoding_modeparse (Listing C.10 / Table I.5).Two pre-flight pieces for round-15+ HF coefficient decode:
-
HfBlockContext non-default branch (
lf_globalmodule) —
u(1) == 0now drives:- per-channel
nb_lf_thr[i] = u(4)followed by
nb_lf_thr[i]thresholds via
t = UnpackSigned(ReadThreshold())where
ReadThreshold = U32(u(4), 16+u(8), 272+u(16), 65808+u(32)), nb_qf_thr = u(4)followed byqf_thresholds[i] = 1 + U32(u(2), 4+u(3), 12+u(5), 44+u(8)),bsize = 39 * (nb_qf_thr+1) * Π (nb_lf_thr[i]+1)with the
spec invariantbsize ≤ 39 * 64,block_ctx_map = ReadBlockCtxMap()— re-uses the existing
C.2.2 clustering decoder withnum_dist = bsize;bsize == 1
short-circuits to[0](no bits read) per C.2.2'snum_dist == 1
skip rule.num_clusters ≤ 16invariant enforced.
Thevardct_256x256_d1.jxlfixture progresses past LfGlobal as
a result.
- per-channel
-
HfGlobal C.6.2 dequant-matrix non-default-encoding parse
(hf_globalmodule) —u(1) == 0now drives 17 sets of:
encoding_mode = u(3)validated against Table I.5's per-slot
valid-index list, then per-mode parameters per Listing C.10:- Library (0) — no params.
- Hornuss (1) — 3×3 F16 matrix, all elements ×64.
- DCT2 (2) — 3×6 F16 matrix, all elements ×64.
- DCT4 (3) — 3×2 F16 matrix (col 0 ×64) +
ReadDctParams(). - DCT4x8 (4) — 3×1 F16 matrix +
ReadDctParams(). - AFV (5) — 3×9 F16 matrix (cols 0..5 ×64) + 2×
ReadDctParams()
(the second is thedct4x4_params). - DCT (6) —
ReadDctParams()only.
...
-
v0.0.8
Other
- drop oxideav-codec/oxideav-container shims, import from oxideav-core
- drop Cargo.lock — this crate is a library
- bump oxideav-core / oxideav-codec dep examples to "0.1"
- bump to oxideav-core 0.1.1 + codec 0.1.1
- migrate register() to CodecInfo builder
- bump oxideav-core + oxideav-codec deps to "0.1"
v0.0.4
Other
- drop oxideav-codec/oxideav-container shims, import from oxideav-core
- drop Cargo.lock — this crate is a library
- bump oxideav-core / oxideav-codec dep examples to "0.1"
- bump to oxideav-core 0.1.1 + codec 0.1.1
- migrate register() to CodecInfo builder
- bump oxideav-core + oxideav-codec deps to "0.1"
v0.0.3
chore: Release package oxideav-jpegxl version 0.0.3