Skip to content

Decoder audit — SourceColor / ImageInfo wiring gaps across zen codecs #11

@lilith

Description

@lilith

Cross-codec audit of how decoders populate SourceColor and ImageInfo.
Result: PNG is best-in-class; most others have missing fields, hardcoded
defaults, or authority-mismatch bugs. This issue tracks the full set;
individual bugs can be split into per-codec issues as they're tackled.

High-impact bugs

1. HEIC: CICP authority not set when nclx box is the only color metadata

Location: heic/src/codec.rs:1286..1346 (approximate, build_image_info_full)

HEIF spec (ISO 23008-12) says nclx is the primary color authority. Code
sets cicp from nclx but never calls
.with_color_authority(ColorAuthority::Cicp). Under the new
SourceColor::to_color_context() drop-dupe logic, this means HEIC-decoded
pixels with only nclx get their CICP field dropped — ColorContext ends
up with no color metadata, and pipeline stages downstream assume sRGB.

Fix: add .with_color_authority(ColorAuthority::Cicp) when the only
color metadata came from nclx.

2. WebP / GIF: bit_depth hardcoded to 8

Locations:

  • zenwebp/src/.../codec.rs (~2376 area)
  • zengif/src/.../codec.rs (similar)

Actual source bits are available from the format (WebP VP8L can carry
8-bit only today, but the pattern is wrong for future containers; GIF is
always 8-bit indexed but bit_depth should be 8 because that's the
source, not because we hardcoded it). Real impact is on downstream
precision-aware negotiation — callers using source.bit_depth to pick
output f32 vs u8 see 8 everywhere.

Fix: parse from bitstream or set None when unknown. GIF's 8 is
correct-by-accident; WebP needs the source bits.

3. HEIC: gain map detected but metadata not parsed

Location: heic/src/codec.rs:1297, 1333-1336

Code sets GainMapPresence::Unknown even when the file declares
has_gain_map=true. AVIF does this correctly via
convert_gain_map_presence() at zenavif/src/.../codec.rs:2281-2305,
parsing dimensions and metadata into GainMapPresence::Available.

Fix: mirror AVIF's parser. Prerequisite for HEIC-sourced HDR in the
HdrProvenance design (imazen/zenpixels#16).

4. is_progressive hardcoded false for several codecs

  • WebP: no detection
  • GIF: actually uses the probe interlace flag (codec.rs:1042/1010) — OK
  • TIFF: no detection of baseline/progressive TIFF
  • RAW: N/A (not progressive)
  • JXL: progressive-capable, not flagged

Impact: DecodePolicy::allow_progressive gate can't reject progressive
content from these codecs.

5. HDR metadata missing from UltraHDR JPEG

Location: zenjpeg/zenjpeg/src/codec.rs

JPEG can carry CLL/MDCV and gain maps via APP11 JUMBF (UltraHDR) or APP2
(ISO 21496-1 secondary image). Currently no content_light_level,
mastering_display, or gain_map population for JPEG.

Blocks: UltraHDR round-trip encoding (imazen/zenpixels#16).

Minor gaps

  • TIFF: CICP not populated (TIFF spec has no CICP; OK). bit_depth
    correct. Missing is_progressive baseline-vs-progressive detection.
  • RAW/DNG: ICC from DCP/EXIF extracted but not consistently fed to
    descriptor helper. channel_count assumed RGB.
  • PDF: fully rasterized; no source color metadata meaningful. OK as
    assumed sRGB but could populate from embedded PDF color spaces when
    they match CICP-expressible values.
  • Radiance HDR: hardcodes Cicp::SRGB but Radiance is scene-linear
    BT.709, not sRGB transfer. Authority not explicitly set.

Non-integration

  • fax (CCITT): standalone, no zencodec integration. N/A.

Tables

Table 1: SourceColor field population

codec cicp icc authority bit_depth ch_count cll mdcv
JPEG ✅ APP2 ✅ Icc default ✅ SOS
PNG ✅ cICP ✅ iCCP ✅ Cicp when set ✅ cLLi ✅ mDCv
WebP ✅ ICCP ⚠️ implicit Icc ❌ hardcoded 8
GIF ⚠️ implicit Icc ❌ hardcoded 8
TIFF ✅ tag 34675 ⚠️ implicit Icc
JXL ✅ Cicp when set ⚠️ unclear ⚠️ unclear
AVIF ✅ nclx ✅ colr ✅ MIAF order ✅ AV1 SPS ✅ cLLi ✅ mDCv
HEIC ✅ nclx ✅ colr not set for nclx-only ✅ cLLi ✅ mDCv
BMP/PNM/Farbfeld ⚠️ implicit Icc ❌ mostly hardcoded
Radiance HDR ⚠️ SRGB (wrong) ⚠️ implicit Icc ✅ 32 3
RAW/DNG ⚠️ from DCP/EXIF ⚠️ implicit Icc ⚠️ assumed RGB
PDF ⚠️ implicit Icc ❌ hardcoded

Legend: ✅ correct · ⚠️ partial/suspect · ❌ not populated · ➖ N/A

Table 2: ImageInfo extras

codec is_progressive sequence gain_map orientation resolution effective_bits can_overshoot
JPEG ✅ SOF2 ✅ Single ❌ UltraHDR not detected ✅ EXIF ✅ JFIF ⚠️ channel bits not source ✅ f32 debiased
PNG ✅ Adam7 ✅ Animation ✅ EXIF ✅ pHYs
WebP ✅ Animation ✅ EXIF
GIF ✅ interlace ✅ Animation
TIFF ✅ Multi ✅ tag 274 ✅ tag 282/283 ✅ tag 258
JXL ⚠️ capable, not flagged ✅ Animation ⚠️ partial ✅ frame header
AVIF ✅ Single ✅ Unknown/Available/Absent ✅ SPS ⚠️
HEIC ✅ Multi ⚠️ Unknown only ✅ Identity
BMP/PNM/Farbfeld ✅ Single ⚠️
Radiance HDR ✅ Single ✅ 32 ⚠️
RAW/DNG ✅ Single ✅ EXIF
PDF ✅ Multi ✅ 8

Table 3: Descriptor derivation path

codec descriptor fn used corrected_to passed notes
JPEG descriptor_for_decoded_pixels (deprecated) corrected_cicp 5 call sites in codec.rs:1644..1950. Migrate to _v2.
PNG via zencodec helpers (unclear) ⚠️ caller
WebP via implicit base descriptor Minimal; relies on format inference
GIF none explicit Picks RGBA8_SRGB/RGB8_SRGB directly
TIFF not visible in zencodec_impl Decode-path selection in tiff crate
JXL native_descriptor (jxl_info_to_image_info) ⚠️ unclear
AVIF convert_image_info builds SourceColor Caller handles correction
HEIC build_image_info_full
BMP/PNM/Farbfeld hardcoded per format N/A
Radiance HDR hardcoded RGBF32_LINEAR N/A CICP set but unused
RAW/DNG hardcoded RGB16_SRGB or RGBF32_LINEAR N/A ICC extracted but not used
PDF hardcoded RGBA8_SRGB N/A

Table 4: Format-spec authority compliance

codec spec says code sets matches?
JPEG ICC (JFIF/APP2) Icc default
PNG cICP > iCCP Cicp when cicp.is_some()
WebP ICC (ICCP) Icc default ✅ implicit
GIF sRGB assumed Icc default ✅ implicit
TIFF ICC (tag 34675); no CICP Icc default
JXL CICP > ICC (ISO 21496-1) Cicp when CICP present
AVIF ICC (colr-Restricted) > CICP (nclx) per MIAF Icc when ICC; else Cicp
HEIC nclx primary (ISO 23008-12) Icc default (bug) fix needed
BMP/PNM sRGB assumed Icc default ✅ implicit
Radiance HDR scene-linear BT.709 Cicp::SRGB (wrong) ⚠️ semantic mismatch
RAW/DNG ICC from DCP/EXIF Icc default
PDF sRGB or embedded ICC Icc default ✅ implicit

Suggested fix order

  1. HEIC authority bug (small, specific, blocks correct HEIF decode)
  2. WebP bit_depth (small, affects precision decisions)
  3. HEIC gain-map parse (unblocks HEIC HDR path for Unified HdrProvenance on ColorContext — luma-gain-map-first HDR design for 0.3.0 zenpixels#16)
  4. UltraHDR JPEG HDR metadata + gain map (bigger; needs APP11 JUMBF parser)
  5. is_progressive detection for WebP/TIFF/JXL
  6. Shared test helper SourceColor::assert_spec_authority(format) to
    catch these at decoder test time

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions