Fidelity follow-ups: content-dependent lossless (LosslessSupport + HDR/bit-depth), post-encode achieved fidelity, GIF honesty
@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
- 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.
Assessment
This issue has not been assessed yet.