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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions