Decoder audit — SourceColor / ImageInfo wiring gaps across zen codecs
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 2
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
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 imazen/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
- imazen/zenpixels#16 — HDR provenance design depends on decoders
populating CLL/MDCV/gain-map metadata correctly.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the suggested fix order, beginning at heic/src/codec.rs:1286..1346 and compare HEIC gain-map handling with zenavif/src/.../codec.rs:2281-2305. Then inspect the listed WebP, GIF, TIFF, JXL, and JPEG locations and related issue imazen/zenpixels#16. Done means the affected decoders populate the specified SourceColor and ImageInfo fields, with decoder tests or the proposed shared authority helper covering the changes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- computer-graphics
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100