Skip to content

Releases: OxideAV/oxideav-jpegxl

v0.0.11

15 Jun 05:08
8136f5b

Choose a tag to compare

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_plane to 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, with x() / y()
    / b() / dims() accessors); assemble_three_channel_planes(grid, residual_at) (walks the shared dct_select::DctSelectGrid once per
    channel via assemble_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 final X, B-plane final B, Y
    unchanged); and the one-call driver reconstruct_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/300 block_dequant per-block decode walk. Walks a
    dct_select::DctSelectGrid via varblock_walk::VarblockWalk and
    writes each varblock's R × C row-major residual block (the
    block_dequant::decode_block_to_residual output) into a single-channel
    spatial plane at the varblock's pixel origin (bx · 8, by · 8). New
    public API: ResidualPlane (row-major f32 plane sized to the padded
    block grid width_blocks·8 × height_blocks·8, for_grid / get);
    block_pixel_dims(t) (the (R, C) pixel shape from
    idct::dct_pixel_dimsnon_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-Empty cell rejected). The
    plane is the padded block grid (no per-edge clamping; caller crops to
    lf_w × lf_h). The geometry invariant C == block_dims().0 · 8 /
    R == block_dims().1 · 8 is 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).
  • 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 which idct::non_dct_pixel_dims returns Some (all 8 × 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 an 8 × 8 grid", 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 the 8 × 8
    OrderId-1 grid (coeff_order::varblock_size_for_order(8, 8)),
    the dequant matrix is the 8 × 8 slot 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_dims now returns Some for
    every TransformType; require_covered's Unsupported path 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 ...

Read more

v0.0.10

31 May 10:22

Choose a tag to compare

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.
    New tests/r191_wp_trace_oracle.rs (5
    tests) and new pub fn modular_fdis::wp_predict_pub test wrapper
    around the production wp_predict. The oracle consumes the
    docs/image/jpegxl/fixtures/noise-64x64-lossless/wp-trace-sample-194.md
    trace (provenance recorded alongside as wp-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
      production wp_predict with the trace's WpState/Neighbours
      inputs; asserts the four sub-predictions [1248, 747, 420, 559],
      the final pre-round prediction 709, and max_error = 737 all
      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 in wp_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's sub_err_{i,N/NE/NW} table summing to the
      reported err_sum_i ([438, 330, 416, 240]).
    • r191_trace_weights_match_error2weight — hand-derives the
      trace's weight_i = [495694, 599189, 474830, 825112] from
      FDIS-literal error2weight(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
      >> sh step).
    • r191_trace_prediction_matches_listing_e3 — independent
      hand-derivation of prediction = 709 from 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 (matches r126_first_divergence_scan dec=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 stated prediction − 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_zeros module that owns one
    per_channel_non_zeros::PerChannelNonZerosGrids per pass index
    p ∈ [0, num_passes), layered above the round-183 per-channel
    container. A VarDCT frame is decoded in num_passes ordered passes
    (declared in FrameHeader.passes.num_passes); each pass scans every
    PassGroup once and §C.8.3 specifies that within a pass each
    channel of each varblock maintains its own NonZeros(x, y) state.
    Between passes the per-channel bookkeeping is reset because the
    per-pass histogram is selected by hfp from the per-pass HfPass
    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 via PerChannelNonZerosGrids::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-range p errors 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_channel with pass
      routing. Caller pre-computes block_ctx via
      pass_group_hf::block_context with the matching c; the
      container is a pure storage + routing primitive and does not
      re-derive pass_group_hf::block_context nor 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
    in tests/round190_per_pass_non_zeros.rs) pin: empty-pass-list /
    zero-channel-pass / zero-dim rejection; two-pass chroma...

Read more

v0.0.9

08 May 22:39
e643947

Choose a tag to compare

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
    new tests/round17_d1_bit_trace.rs diagnostic test.

    Findings (full analysis in round17-d1-bisect.md):

    • Our LfGlobal::read ends 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::read consumes 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. Either HybridUintConfig is 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.

  • 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 > 0 errored
    out as "transforms inside HF metadata sub-bitstream not yet supported (round 13+)". Round 16 wires the parse:

    • HfMetadata::read now takes the metadata: &ImageMetadataFdis
      bundle (forwarded from LfGroup::read) so the inverse Palette
      transform can read bit_depth.bits_per_sample for 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_transforms is
      invoked with the same transforms list so RCT / Palette / Squeeze
      are undone and the four-channel baseline is recovered. The decoded
      nb_blocks and 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 whose SqueezeParam.begin_c references channels
    beyond the four-channel baseline (begin_c=39 on the very first
    step), and apply_transforms_to_channel_layout's
    begin_c + num_c <= channel_count invariant 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.

  • 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:

    1. GlobalModular ModularHeader gating (global_modular module) —
      GlobalModular::read was 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
      by N > 0 per FDIS §C.9.1 ("In the trivial case where N is zero,
      the decoder takes no action."). Fix: skip the inner ModularHeader
      when derive_channel_descs returns an empty list (the typical
      VarDCT-without-extras case).

    2. Single-TOC-entry section chaining (decode_vardct_round13) —
      when num_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_round13 was
      slicing each TOC slot into its own byte range, which only works
      for multi-entry TOCs. Fix: when toc.entries.len() == 1, chain
      LfGlobal::readLfGroup::readHfGlobal::read on a
      shared BitReader.

    Acceptance: vardct_256x256_d1.jxl now 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_mode parse (Listing C.10 / Table I.5).

    Two pre-flight pieces for round-15+ HF coefficient decode:

    1. HfBlockContext non-default branch (lf_global module) —
      u(1) == 0 now 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 by qf_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 invariant bsize ≤ 39 * 64,
      • block_ctx_map = ReadBlockCtxMap() — re-uses the existing
        C.2.2 clustering decoder with num_dist = bsize; bsize == 1
        short-circuits to [0] (no bits read) per C.2.2's num_dist == 1
        skip rule. num_clusters ≤ 16 invariant enforced.
        The vardct_256x256_d1.jxl fixture progresses past LfGlobal as
        a result.
    2. HfGlobal C.6.2 dequant-matrix non-default-encoding parse
      (hf_global module) — u(1) == 0 now 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 the dct4x4_params).
      • DCT (6)ReadDctParams() only.
        ...
Read more

v0.0.8

07 May 18:03

Choose a tag to compare

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

25 Apr 06:42
527f6fb

Choose a tag to compare

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

17 Apr 19:28

Choose a tag to compare

chore: Release package oxideav-jpegxl version 0.0.3