imazen / imazen/zencodec

Fidelity follow-ups: content-dependent lossless (LosslessSupport + HDR/bit-depth), post-encode achieved fidelity, GIF honesty

Open
#104 1 comment 0 reactions 1 assignee View on GitHub

@lilith is already working on this.

Since Jun 23, 2026.

Dominant language
Rust
Stars
2
Forks
0
PR merge metrics
No merged PRs in 30d

Description

## Context

The `Fidelity` API (#12) shipped on `main`:
`Fidelity { Lossless, Lossy(LossyTarget) }`, `EncoderConfig::try_with_fidelity →
FidelityMatch { Exact, RaisedTo, LoweredTo, Translated, Unsupported }`,
`resolved_target_fidelity`. Working the GIF case through surfaced that **"lossless"
is content-dependent**, which the current `bool` lossless capability and the
*config-time* `FidelityMatch` can't fully express. This issue collects the
follow-ups and the one design question that gates them.

## 1. `LosslessSupport` tri-state on `EncodeCapabilities`

A `bool` flattens three real states:

| state | codecs | meaning |
|---|---|---|
| **`Always`** | PNG, WebP-lossless, JXL-modular | lossless for any *representable* input |
| **`ContentDependent`** | GIF | lossless only for ≤256-colour input |
| **`Never`** | JPEG | lossy-only |

Lets a selector avoid picking GIF for *guaranteed* lossless while still using it
for paletted/screen content.

## 2. OPEN QUESTION (settle first) — HDR / bit depth makes even "Always" conditional

A format is lossless only for input **within its representable precision**: a
16-bit format (PNG) truncates 32-bit-float / HDR-linear input; a format lacking
≥64 bpp can't store high-bit-depth HDR losslessly. So PNG isn't *unconditionally*
"Always" lossless either.

**Proposed resolution (to confirm):** `LosslessSupport` describes lossless ability
for **representable** input (within `supported_descriptors`); a bit-depth / gamut
mismatch (e.g. 32f → a 16-bit codec) is a **pre-encode conversion** the caller
controls via format negotiation, *not* the codec's lossless flag. Then PNG =
`Always` (for what it accepts), GIF = `ContentDependent` (colour count, *within*
8-bit RGB), JPEG = `Never`.

**Needs settling:** is "lossless for representable input" the right boundary, or
should there be a per-`(codec, input-descriptor)` lossless query that accounts for
depth/gamut directly? How does HDR (PQ/HLG, wide gamut, ≥16-bit) interact — is a
codec "lossless" if it preserves samples but the descriptor pins a transfer/gamut
it can't signal? This is the gate for §1 and §3.

## 3. Post-encode achieved fidelity (to the extent we have it)

Config-time `FidelityMatch` can't know content-dependent outcomes — GIF on a
200-colour screenshot really *is* exact; on a photo it's palette-capped. Add
**post-encode reporting of achieved fidelity** on the encode result: what was
actually delivered (exact vs quantized vs truncated), *to the extent the codec
knows it*. This is the content-aware ground truth the static verdict can't give,
and the honest place for "GIF-on-a-screenshot = Lossless".

## 4. GIF honesty (links #26)

- GIF must **override `try_with_fidelity`** to return `Unsupported` for
`Lossless` — the *default* classifier would wrongly say `Exact` because GIF's
`is_lossless()` reports the flag, not the guarantee.
- Fix the `with_lossless`-on-colour lie: `is_lossless()` / capabilities must not
claim guaranteed lossless for >256-colour input.
- Exact GIF verdicts once fixed: `Lossless → Unsupported`,
`CodecSpecificQuality → Exact`, metric targets → `Translated`.

## Scope

All additive — a capability enum, a result-side achieved-fidelity field, and
per-codec method overrides. No breaking changes to the shipped `Fidelity` /
`FidelityMatch`. Sequence: settle §2 → land §1 + §3 → §4 per-codec.

Refs: #12 (Fidelity API), #26 (per-codec Fidelity impls). Fidelity commit chain on
`main`: `1da7c3b` … `44fe22e`.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.