Releases: Raster-Lab/JXLSwift
Release list
v1.3.0 — 16-bit lossy VarDCT for RGB/RGBA
16-bit lossy VarDCT. The lossy VarDCT codec now accepts 16-bit RGB/RGBA input (previously 8-bit only), matching the bit-depth range the lossless Modular codec has supported since v1.0. A lossy encode request on a 16-bit frame now routes to VarDCT instead of silently falling back to lossless Modular. Encoded bytes for existing 8-bit lossy VarDCT and all lossless paths are unchanged — byte-for-byte regression-checked against the pre-change build.
Added
- 16-bit lossy VarDCT encode + decode, RGB and RGBA.
VarDCTEncoder.forwardaccepts.uint16frames; the sRGB↔linear conversion generalises to any bit depth via amaxValueparameter, with the 8-bit code path left untouched (no per-pixel branch added to the default hot path).VarDCTBitstreamWriterthreads the real bit depth intoImageMetadata/ExtraChannelInfoinstead of hardcoding 8;JXLDecoder's VarDCT reconstruction reads the declared bit depth and emits auint16ImageFramewhen appropriate. Grayscale VarDCT remains unsupported at any bit depth (falls back to lossless Modular, unchanged from before).
Fixed
- 16-bit RGBA alpha-channel corruption in VarDCT encode —
buildFrameSectionsread the alpha channel with a byte stride only correct for 8-bit samples; a 16-bit RGBA frame would read the wrong bytes entirely. Alpha now round-trips bit-exact (verified againstdjxl). VarDCTBitstreamWriter.encodeAnimationnow rejects frames that disagree on bit depth, extending the pre-existing xsize/ysize/hasAlpha agreement guard, instead of silently misdeclaring the sharedImageMetadata.
Verification
djxl 0.11.2oracle: our 16-bit RGB/RGBA/multi-group VarDCT bitstreams decode via djxl within 3–16 (out of 65535) max-diff of our own decoder on the same bytes, with the alpha channel bit-exact; djxl accepts every generated bitstream (exit 0).- Byte-for-byte regression check against a pre-change build (isolated git worktree): 8-bit lossy VarDCT, 8/16-bit lossless Modular, and multi-frame animation encode all produce identical output — zero drift on the existing default path.
swift build -c releaseclean. The full XCTest suite needs an Xcode host (this dev env has only CommandLineTools); the djxl byte-exact spec gate was exercised directly via CLI round-trips, and the new tests' logic was additionally verified by running the identical API calls through a throwaway executable target linked against the built library (then removed).
🤖 Generated with Claude Code
v1.2.0 — reverse JPEG recompression API + YCoCg-R lossless RCT
JPEG-XL transfer-syntax completion + lossless ratio. The reverse half of JPEG recompression is now public API, the lossless Modular path gains the YCoCg-R reversible colour transform (a large ratio win on RGB/RGBA), and the async facade reaches parity for the recompression pair. Encoded bytes change for RGB/RGBA lossless (RCT — see Changed); grayscale, the JPEG bridge, and every decode path are unchanged.
Added
- Public reverse JPEG recompression —
JXLDecoder.decodeLosslessJPEG(_:) throws -> Data, the symmetric mirror ofencodeLosslessJPEG(_:). Reconstructs the byte-identical original JPEG from a JPEG-bridge JXL (baseline + common progressive; 4:4:4 / 4:2:2 / 4:2:0 / grayscale; APP / ICC metadata). The reverse pipeline's adapter/bridge types staypackage; only the one-call wrapper is public.jxl transcode --mode reverseis now a thin client over it. asyncoverloads forencodeLosslessJPEG/decodeLosslessJPEG, completing the J2KSwift async-facade parity for the recompression syntax.
Changed
- Lossless RGB/RGBA now uses the YCoCg-R reversible colour transform (§C.7.7), single- and multi-group. R, G, B are decorrelated to Y, Co, Cg before prediction and inverted by the decoder from the global-header transform list. Encoded bytes change for RGB/RGBA lossless (smaller); grayscale and all decode output are unchanged. Measured 21–66 % smaller on correlated RGB/RGBA versus the previous independent-channel coding.
- RCT is cost-gated — applied only when its estimated residual cost beats raw R, G, B, so it can never inflate a frame (already-decorrelated / independent-channel content keeps the identity path). Round-trips stay pixel-exact either way.
Fixed
- The modular gradient predictor's redundant
[0, sampleHi]clamp is dropped in the residual paths — a no-op for in-range channels (existing byte-exact output preserved) but it diverged from the decoder's ClampedGradient for the RCT-transformed (signed / over-range) chroma channels.
Verification
- RCT lossless output decodes byte-exact through reference
djxl 0.11.2across single/multi-group, 8/16-bit RGB/RGBA, correlated + random content. - JPEG recompression:
cjxl --lossless_jpeg=1→decodeLosslessJPEG→ byte-identical original, and forward bridge →djxl→ byte-identical, across 4:4:4 / 4:2:2 / 4:2:0 / grayscale. swift build -c releaseclean. (Full XCTest suite needs an Xcode host; the djxl byte-exact spec gate was exercised via the CLI.)
🤖 Generated with Claude Code
v1.1.0 — Performance-programme release (7.1× default-effort encode)
Performance-programme release. Public API unchanged; encoded bytes change at effort ≥ 7 (retuned ladder), everything else bit-identical to v1.0.1.
Highlights
- Default-effort lossless encode 7.1× faster (538 → 76 ms, 512² 16-bit reference); effort 1 is 2.1× faster.
- Effort 8/9 now beat the old best ratio while encoding faster — greedy MA-tree moved to effort ≥ 8 with 16/32-leaf budgets (−0.4 % on 16-bit grayscale medical content, −8.7 % on 16-bit RGB vs the old effort 7/9), all djxl byte-exact.
jxl batchis parallel: a 64-slice CT stack encodes in ~1.7 s end-to-end (~20× vs v1.0.1 sequential).- Multi-group decode 4.5× faster (per-group parallel decode); single-image decode −10–20 %.
- JPEG transcode: reverse transcode −36 %, JPEG decode −18 % (64-bit bit I/O + 8-bit Huffman fast path), byte-identical reconstruction.
- Encoder cost gating is now analytic (exact sizes, winner-only emission); the MA-tree learner is radix-sorted; large-plate (> 4 M px) frames now get the learned tree at effort ≥ 8.
Validation
- 694/694 tests (round-trip, conformance vectors, djxl byte-exact).
- CID22: 49/49 lossless byte-exact through our decoder and djxl at the default effort.
- Medical DICOM (823 stratified from 4 441 CT/MR/XA/CR/DX/PX files, 16-bit): 814 pass / 0 fail at effort 7 and effort 9, every pass byte-exact through both decoders.
Full methodology and the phase-by-phase record: Documentation/OPTIMISATION-PLAN-2026-06.md + CHANGELOG.md.
🤖 Generated with Claude Code
v1.0.0 — Production-ready lossless JPEG XL codec (medical-grade)
JXLSwift v1.0.0 — production-ready, medical-grade lossless JPEG XL codec
A pure-Swift implementation of JPEG XL (ISO/IEC 18181) — Swift-first (Swift 6.2, strict concurrency), zero runtime dependencies (swift-argument-parser is CLI-only). v1.0 commits to the public API surface as stable.
What "1.0" means
Lossless production-grade. 8/16-bit grayscale / gray+alpha / RGB / RGBA; arbitrary dimensions ≤ 16384 (multi-group + multi-DC-group); cost-gated predictors + multi-property MA-trees + learned activity-split thresholds + effort knob 1..9. Every emitted byte traces to a spec section.
Medical-grade validated. 2,867 / 0 byte-exact on a 30,329-file radiology DICOM corpus (CT, MR, CR, DX, MG, PX, US, XA — including JPEG / JPEG-LS encapsulated, multi-frame XA cine, colour-YBR ultrasound, MONOCHROME1, and radiographs up to 4784×3521). Every PASS reconstructed byte-identical by the libjxl reference decoder djxl. Reproducible via scripts/medical-dicom-validate.sh.
Non-DICOM validated. 49 / 0 byte-exact on the Cloudinary Image Dataset '22 (natural photographic). scripts/cid22-validate.sh.
Conformance gate. Official jxl-conformance vectors wired into the test harness; lossless subset green on the committed fixtures (lz77_flower, alpha_triangles) by default, plus the full corpus via JXL_CONFORMANCE_DIR. djxl is the pixel oracle (the project's correctness invariant).
Robustness hardening. A parameterised lossless sweep (400 cases × dims × depths × channels × content × the full effort ladder 1…9) plus a decoder fuzz pass over truncated and byte-mutated codestreams — 0 traps across 1,812 malformed inputs.
JPEG ⇄ JXL transcode (lossless): forward (≤ 2048 px / side) + reverse (byte-identical reconstruction of the source JPEG, including baseline / progressive / ICC).
VarDCT lossy decoder ships as a preview (djxl-matching, Phase R filters included).
Public API stable + Sendable. No force-unwraps in the public surface. Family-aligned with J2KSwift presets (.lossless, .highQuality, .balanced, .fast, .maxCompression) and CLI flag conventions (-i / -o).
Performance. Honest baseline + risk-assessed roadmap in Documentation/PERFORMANCE-ANALYSIS.md. The JXLPerfC SwiftPM C target is scaffolded as the prescribed boundary for the upcoming C BitWriter / rANS hot path (CLAUDE.md constraint 1, amended 2026-05 to permit C/C++ for measured hot paths).
Documented deliberate 1.0 limitations (post-1.0 roadmap)
- Forward JPEG → JXL transcode capped at 2048 px / side. The lossless Modular path handles all sizes ≤ 16384, and reverse transcode is unaffected.
- Reconciling
fillModularPropertiesslots 6/7/8/11/13/14 with libjxl to widen the greedy MA-tree property set (a ratio lever, not a correctness gap). - Full lossy VarDCT encode (pixels → lossy JXL) — headline of v2.0.
- libjxl-class encode speed — see the C/C++ entropy hot-path roadmap in
PERFORMANCE-ANALYSIS.md.
Test gate
697 tests / 0 failures (swift test -c release, ~80 s) plus the medical DICOM (2 867) + Cloudinary CID22 (49) corpora — all djxl-byte-exact.
Family
JXLSwift is part of a Swift compression-library family alongside J2KSwift (JPEG 2000). Public API + CLI surface mirror, so callers can switch codecs without re-learning the syntax (see Documentation/FAMILY-API-PARITY.md).
🤖 Generated with Claude Code
v0.14.0 — Measured perf + family-parity preset alignment
v0.14.0 — Measured perf + family-parity preset alignment
A maintenance release on top of v0.13.0 (medical-grade lossless DICOM validation). All output is byte-identical to v0.13.0 on lossless paths; the perf wins below cost zero correctness.
Measured perf (byte-identical, full 695-test djxl-exact suite green)
BitWriterUInt64-accumulator — the most fundamental output primitive rewritten to a 64-bit accumulator that flushes whole bytes (was per-bit-chunkappend+ bounds-checked subscript). Emits the identical bit stream;bitCountandpartialpreserved exactly, so cost-gating is unaffected. ~5 % faster on the effort-1 fast path.- Shared WP per-pixel pass — the activity-split (effort ≥ 4) and greedy (effort ≥ 7) lossless candidates each re-ran the same full-image weighted-predictor pass; now computed once in
buildSingleSectionand handed to both via optionalprecomputed:params (other callers untouched). ~2.8 % faster at effort 7 (controlled A/B, 512² 16-bit: 763 → 741 ms). - Both wins are modest — and that's the documented headline finding of
Documentation/PERFORMANCE-ANALYSIS.md: the per-candidate rANS entropy encoding is the irreducible floor under byte-identity. A larger speed-up requires either fewer candidates (would change the chosen output / ratio) or moving the entropy hot path to C/C++ with the scalar Swift staying as the reference — the prescribed next step (CLAUDE.md constraint 1, amended 2026-05).
Family parity with J2KSwift
- Preset-quality alignment.
JXLConfiguration.balanced0.9 → 0.85;.fast0.75 → 0.70; added.maxCompression = 0.50mirroringJ2KConfiguration.maxCompression. High-quality (0.95) and lossless presets were already aligned. Closes the last accidental preset-quality divergence flagged inFAMILY-API-PARITY.md.
Validation
- Cloudinary CID22 (non-DICOM natural photos) — 49 / 49 byte-exact through our decoder and
djxl, aggregate lossless ratio 40.6 %. Reproducible viascripts/cid22-validate.sh. - Medical DICOM re-validation — 978 / 0 byte-exact (sample re-run confirming the perf changes preserve v0.13.0's 2 867 / 0 baseline).
Public-API audit
All public types are Sendable; no force-unwraps in the public surface; doc comments in place. The API is stable in shape — pending an explicit v1.0 freeze.
🤖 Generated with Claude Code
v0.13.0 — Road to lossless v1.0 + medical-grade DICOM validation
v0.13.0 — Road to lossless v1.0 + medical-grade DICOM validation
A pure-Swift JPEG XL (ISO/IEC 18181) codec, lossless-first. This release builds the foundation for a lossless v1.0: a real conformance gate, robustness hardening, the convert CLI verb, a performance baseline — and medical-grade validation against a real radiology DICOM corpus. One genuine decode-correctness bug was found and fixed along the way.
Medical-grade validation (real DICOM corpus)
The lossless path was validated end-to-end against a 30,329-file radiology DICOM corpus (CT, MR, CR, DX, MG, PX, US, XA). JXLSwift stays DICOM-unaware by design — an external extractor (pydicom) pulls the raw pixel buffers and feeds them to the codec, and reconstruction is checked byte-exact through both the pure-Swift decoder and the libjxl reference decoder djxl. A stratified sample covering every distinct pixel configuration (modality × transfer-syntax × 8/16-bit × grayscale/colour × single/multi-frame, including JPEG/JPEG-LS-sourced and MONOCHROME1):
- 2,867 images pass, 0 fail — every pass byte-exact via
djxl. (62 non-image structured-report files have no pixel data and are skipped.) - Coverage: MR, CT, multi-frame XA cine, colour YBR-JPEG ultrasound, CR/PX incl. JPEG-LS & JPEG-lossless sources, MONOCHROME1, and radiographs up to 4784×3521.
- A default-effort (effort 7, greedy MA-tree) subset over the common configs also passes 31/31,
djxl-byte-exact.
Reproducible and PHI-safe via scripts/medical-dicom-validate.sh.
What's new
- Lossless conformance gate — the official
jxl-conformancevectors are wired into the test suite (env-gated for the full corpus + a committed lossless subset that runs green by default), withdjxlas the pixel oracle.scripts/fetch-conformance.shpopulates the full corpus. - Decode-correctness fix — decoded samples are now clamped to the declared bit depth
[0, 2^bps−1]; a 9-bit RGBA image previously reconstructed out-of-gamut values where the reference decoder clamps. No-op for valid in-range streams and for 8/16-bit. - Robustness hardening — a parameterised lossless sweep (400 cases across dimensions × bit-depth × channels × content) + the full effort ladder, and a decoder fuzz pass: 0 traps across 1,812 malformed inputs (every one is cleanly rejected or decoded).
convertsubcommand — PNM ↔ JXL and JPEG → PNM/JXL, format inferred by extension (family parity with J2KSwift'sconvert).- Performance baseline —
jxl-tool benchmark --mode lossless --effort N+scripts/benchmark-lossless.shmake the speed↔ratio ladder measurable; hot-path analysis recorded for the upcoming SIMD work. - Lossless completeness — confirmed the >8-cluster context-map and rANS full (complex) histogram paths are implemented and
djxl-byte-exact (the prior deferral notes were stale); added pinning tests.
Known limitations (deliberate, documented for post-1.0)
- Forward JPEG→JXL transcode is limited to ≤ 2048 px per side (multi-DC-group transcode is planned). The native lossless Modular path handles all sizes ≤ 16384, and reverse transcode is unaffected.
- The greedy MA-tree property set is restricted to the
djxl-verified subset (a ratio lever, not a correctness gap).
Notes
- Lossless-first. Lossy encode (pixels → lossy VarDCT) remains deferred; the VarDCT lossy decoder ships as a preview.
- Zero runtime dependencies beyond
swift-argument-parser(CLI only).
🤖 Generated with Claude Code
v0.12.0 — lossless JPEG XL codec
A comprehensive, djxl-validated lossless JPEG XL implementation (ISO/IEC 18181) — pure Swift 6.2, strict concurrency, zero runtime dependencies beyond swift-argument-parser (CLI only).
Lossless encode (native Modular)
- 8- and 16-bit grayscale, grayscale+alpha, RGB, RGBA
- Arbitrary dimensions ≤ 16384 (single-group, multi-group, and multi-DC-group)
- Multi-property MA-trees + learned activity thresholds
- Encode-effort knob (
EncodingEffort1–9) to trade compression for speed - Every path byte-exact through
djxl
Transcoding (lossless JPEG ⇄ JXL)
- Forward JPEG → JXL (coefficient-faithful, ≤ 2048 px/side),
djxl-valid - Reverse JXL → JPEG byte-identical: baseline + progressive, all chroma, grayscale, ICC profiles
Decode
- Lossless Modular + VarDCT lossy decode (Phase R restoration: Gaborish + EPF),
djxl-matching
Known limitations
- Lossy encode (pixels → lossy JXL) is not implemented — deferred to a later phase. This is a lossless-first release.
- Forward JPEG transcode is capped at 2048 px/side.
- Decoder gaps: DCT128/256 transforms; Splines / Noise / Patches synthesis.
- Dimensions > 16384 and bit depths > 16 are not supported.
688 tests, 0 failures. See CHANGELOG.md for per-milestone detail.
🤖 Generated with Claude Code