Upstream candidates from zenpipe/zencodecs: ~1400 lines of codec-agnostic policy + 4 zero-blocker duplicates
- Dominant language
- Rust
- Stars
- 2
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Read-only survey of `zenpipe/zencodecs` (~16k lines) against zencodec. Every item verified for `std` / concrete-codec deps.
> Scope note: the in-flight `docs/pixel-descriptor-negotiation.md` §9/§13.1 sends the **fidelity classifier** to `zenpixels`, **not** zencodec. That is a separate move and does not touch zencodecs — do not conflate.
## Tier A — already duplicated (zero blockers)
- **A1 memory-limit gate.** `zencodecs/src/estimate.rs:166` `check_estimate_against_limits` re-implements `ResourceLimits::check_memory` (`src/limits.rs:543`) — and *downgrades* the typed `LimitExceeded::Memory{actual,max}` into a formatted string. `zencodecs::Limits` **is** `zencodec::ResourceLimits` (`zencodecs/src/limits.rs:17`). The novel part worth upstreaming is the `unwrap_or_else(frame_buffer_bytes)` "unmodeled codec → frame buffer is the certain floor" policy, as `ResourceLimits::check_estimate(&ResourceEstimate, &ImageCharacteristics)` — the missing third sibling of `check_image_info` (`:608`) / `check_output_info` (`:630`).
**Precedent:** `zencodecs/src/limits.rs:1-10` documents this exact collapse *already happening once* for `Limits`.
- **A2 `peak_job_bytes`** (`estimate.rs:142`) — `max(decode_peak, encode_peak) + frame_buffer`, depends only on zencodec types. Move the composition rule; **not** `frame_buffer_bytes` (`:22`, `pixels*4` hardcodes an RGBA8 pipeline intermediate — zencodec already has the descriptor-honest `ImageCharacteristics::input_bytes()` at `src/estimate.rs:255`).
- **A6 `SourceColorExt::is_srgb`** (`zencodecs/src/color.rs:26-54`) — an extension trait impld **on zencodecs own `SourceColor`**, existing only because the method isnt upstream. Reads only `SourceColor` fields + `zenpixels::icc::is_common_srgb`; zencodec already depends on zenpixels with `features=["icc"]` and already has `src/icc.rs` as a shim. Should be an inherent `SourceColor::is_srgb()`. Directly relevant to `resolve_color_emit`s "never emit a redundant SynthesizeFrom(sRGB)".
- **A5 transcode.** `CodecSet::transcode` (`src/set.rs:545`) has **zero usages in zenpipe**; `zencodecs::transcode` (`transcode.rs:209`) is the live one, and its `TranscodeOutput` (`:156`) is a strict-subset re-clone of `zencodec::EncodeOutput` (`src/output.rs:21`). Worth upstreaming from it: `SupplementPolicy`/`SupplementSet` (`transcode.rs:97,117` — GAIN_MAP/DEPTH_MAP/THUMBNAIL retention, pure bitflags, belongs beside `MetadataPolicy` + `zencodec::gainmap`) and `matte: Option<[u8;3]>` (`:67`, alpha-flatten color — `CodecSet::transcode` has no answer for RGBA→JPEG). Conversely `TranscodeOptions` has **no color field**, while `CodecSet::transcode` takes `ColorEmitPolicy`.
## Tier B — pure policy, verified zero `std::` and zero codec deps
- **B1 the format-selection engine** (~1400 lines): `select.rs`, `decision.rs`, `intent.rs`, `policy.rs`, `format_set.rs`, `quality.rs`, `trace.rs`. `alloc`-only today. `intent.rs:8-9` says outright *"these types are codec-agnostic"*; `select.rs:112-115` says the ML picker *"lives outside this crate so the publishable core takes no model or analysis dependency"* — **the injection seam is already designed for this split** (the `FormatPicker` trait moves, `picker.rs` stays).
**Fills a real gap:** `CodecSet::encodable_formats()` (`set.rs:308`) hands consumers a list with **no way to choose among them**; every consumer reinvents selection.
**Blocker is editorial, not technical:** `build_preference_order` (`select.rs:478-519`) encodes taste with a shelf life ("JXL→AVIF→JPEG below 3MP"). Recommend shipping as an explicitly *opinionated default* (`zencodec::select`, or feature-gated) with the `FormatPicker` seam preserved.
- **B2 `ImageFacts::from_image_info`** (`select.rs:31,48`) → `ImageInfo::facts()`. Contains **duplication-with-drift**: `is_lossless_source` (`:51`) hardcodes `Png|Gif|Bmp|Pnm|Farbfeld`, duplicating knowledge zencodec already owns via `ImageFormat::supports_lossless()` (`src/format/mod.rs:278`) — and it is **already rotting**: zencodec has `Tiff` (`:195`) and `Qoi` (`:199`) variants the list omits. `is_hdr` (`:61`) open-codes `matches!(transfer, Pq|Hlg)` — wants a `Cicp::is_hdr()` predicate.
- **B4 `AllowedFormats` vs `CodecSet`** — identical method names/semantics (`registry.rs:249,271,280,285` vs `set.rs:279,284,301,308`), different authority (registered-at-runtime vs compiled-in-and-allowed). **Split**: the allowlist half is pure policy and can move; the `#[cfg(feature)]`-gated `COMPILED_DECODE` consts (`registry.rs:21,43,55`) cannot.
- **B5 two `CodecError`s** — `zencodecs::CodecError` (`error.rs:16`, flat thiserror) vs zencodecs taxonomy (`error.rs:543` + `ErrorCategory`/`CategorizedError`). Overlapping `UnsupportedFormat`/`DisabledFormat`/`NoSuitableEncoder` vs `CodecSetError::{NoDecoder,NoEncoder}` (`set.rs:144`). Deserves its own pass.
## Explicitly should NOT move
`codecs/*` + `config.rs` + `dispatch.rs` + `dyn_dispatch.rs` + `macros.rs::dispatch_format` (the `#[cfg]`-gated static match dispatch is **why zencodecs exists** — a deliberate zero-vtable alternative to `CodecSet`s runtime `Box`); `cms.rs` (moxcms/std — the negotiation docs Tier 2, and `color-emit-model.md` states zencodec is *"no CMS, no codec deps"*); `picker.rs` (zenpicker/zenpredict); `zennode_defs.rs`; `riapi_parse.rs`; `estimate_{encode,decode}`; `CodecId` (enumerates concrete *implementations* — inherently downstream; it also partially blocks `CodecPolicy`, whose killbit half is built on it).
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.