Closes four coverage gaps. Existing 8-/16-bit RGB/RGBA VarDCT and all lossless output is byte-for-byte unchanged; full XCTest suite green (717 tests, 0 failures). Verified against reference libjxl 0.11.2 (djxl) and libjpeg-turbo 3.1.4 (djpeg).
Added
- Grayscale lossy VarDCT — 1-channel grayscale + 2-channel grayscale+alpha, 8/16-bit, encoded as a 3-channel XYB frame (X ≈ 0 → R = G = B) with a grayscale
ColorEncoding— the representation libjxl uses; the decoder collapses RGB→gray on output. Multi-group and animation covered;djxldecodes it as grayscale. - Oversized VarDCT — encoder size cap raised 8192 → 16384 px/side (a resource-safety limit, not correctness). Verified wide (8704×256), tall (256×8448), and square (8256×8256).
- Signed 16-bit pixels (
PixelType.int16) — the DICOM CT case. The encoder level-shifts to unsigned offset-binary up front so the VarDCT/Modular core is untouched;JXLDecoder.decode(_:signedOutput:)reconstructs the signed frame (byte-exact through lossless Modular). - Broader JPEG decode —
JPEGDecoder.decodenow handles progressive (SOF2), extended-sequential (SOF1), and 12-bit precision (→.uint16), plus SOF3 lossless (newJPEGLosslessDecoder: 7 predictors, 2–16-bit, gray/RGB, interleaved/non-interleaved, restart). Byte-exact vsdjpegfor lossless; ≤ 6 LSB for the DCT family.
Fixed
- Multi-frame VarDCT reported
wasLossless=true, mislabelling every animation as a lossless encode.
Scope note
12-bit and SOF3-lossless JPEGs cannot be losslessly recompressed to JXL — the jbrd recompression format is 8-bit-DCT-only, and libjxl's own cjxl rejects them. The JPEG work here is pixel decode (JPEG → ImageFrame, re-encodable as a normal JXL), not recompression.
🤖 Generated with Claude Code