zencodec

Implementation audit / review PR

What is still missing?

Existing signatures and tested mechanisms do not establish the complete 0.3 target. This page separates missing shared contracts, missing native adapters and behavior that still needs qualification.

Read alongside the executing API guide. This is review-branch scope, not a claim that these changes are released or all on main.

Render the canvas by default.

Current companion and remaining target: The public companion renders known composited packed images with canvas orientation and required color through Renderer::new(storage).render_first(&mut images). Raw layers use explicit collection selection; the renderer refuses unsupported composition. Default rendering through every codec entry point is not implemented yet. Planar video, HDR display mapping, resizing and complete workspace admission remain separate gates.

Decode required state: keep reference / off-canvas data → Compose visible contribution: respect blend and disposal → Apply requested rendering: canvas, orientation, color → Receive rendered canvas: raw layers require explicit accessIMPLEMENTORDecode required statekeep reference / off-canvas dataADAPTERCompose visible contributionrespect blend and disposalADAPTERApply requested renderingcanvas, orientation, colorCONSUMERReceive rendered canvasraw layers require explicit access
implementorDecode required statekeep reference / off-canvas data
adapterCompose visible contributionrespect blend and disposal
adapterApply requested renderingcanvas, orientation, color
consumerReceive rendered canvasraw layers require explicit access
Decode required state → Compose visible contribution → Apply requested rendering → Receive rendered canvas

The current OpenRequest::new(view, discovery_attempts) requires a view. ImageRequest::new() asks for the whole selected image; in a raw view that may exceed the presentation canvas. Placement::visible() returns matching sample and canvas rectangles but does not change pixels. Native tests include a 1024×576 JXL layer with only a 188×202 visible intersection.

Clipping must happen at the appropriate composition stage, without destroying data required by later references. A smaller layer still renders onto the complete canvas; a fully off-canvas layer is valid, not EOF. Orientation can change the final width and height. Video crop and display resize are separate operations. An output crop must not evade admission of larger coded dependencies.

The naming concern is concrete: ImageGeometry::canvas_width describes the selected image’s local full width, while Placement::canvas is its presentation canvas. Also, with_pixels means packed access; it does not establish display readiness. Track default rendering and its adversarial tests in issue 170.

Signatures from this revision · bodies omitted

Inherent methodszencodec::image::Placement
impl Placement {
    pub fn visible(
        self,
        geometry: ImageGeometry,
    ) -> Result<Option<PlacementOverlap>, PlacementError>;
}
Read defaults, imports and full contract ↗
Type definitionzencodec::image::ImageInterpretation
pub struct ImageInterpretation {
    /// Returned stored extent within the selected canvas.
    pub geometry: ImageGeometry,
    /// Selected composited or raw-layer view, when established by the adapter.
    /// The checked collection binds this before publication. `None` makes no
    /// claim; packed storage, placement and dimensions cannot establish it.
    /// Composited samples can still require orientation and color conversion.
    pub composition: Option<crate::collection::Composition>,
    /// Placement on a separate presentation canvas, before orientation.
    /// None means this adapter establishes no separate placement contract.
    /// This does not describe blending or permit discarding off-canvas samples.
    pub placement: Option<Placement>,
    /// None means that this adapter does not establish an orientation contract.
    pub orientation: Option<Orientation>,
    /// Retains native component roles, code depth, siting, crop, color and epoch.
    pub signal: Option<Arc<FrameInterpretation>>,
    /// Original component interpretation after explicit conversion to packed
    /// color. This never describes current packed samples or retains pixels.
    /// Keep it when cloning interpretation for a later crop/color transform;
    /// current geometry, descriptor and context describe the transformed output.
    pub source_frame: Option<Arc<FrameInterpretation>>,
    /// Presentation timing, when established; never inferred from item order.
    pub timing: Option<FrameTiming>,
    /// Native temporal presentation facts, if supplied by a frame adapter.
    pub presentation: Option<Presentation>,
    /// Unmodified source facts, not instructions for interpreting transformed
    /// output and not permission to copy opaque metadata into a derivative.
    pub source_details: crate::Extensions,
}
Read defaults, imports and full contract ↗

Canvas geometry now executes in the shared renderer.

The shared zencodec_render::geometry::project implementation accepts a known composited output, applies orientation and selects a display-coordinate region. Omitting the region requests the whole oriented canvas. It refuses raw layers, unknown interpretation and missing source samples before returning an output. Pixels retain their sample bits; the geometry stage does not convert color.

Native TIFF cases cover all eight orientations, Classic TIFF/BigTIFF, partial source regions, RGBA8 and floating-point payloads. Native JXL cases include an independent libjxl orientation comparison and oversized raw-layer refusals. Cancellation, admission failure and unwind controls check that unpublished payload charges are released. Read the qualification receipt.

Remaining geometry scope

AuxiliaryGrid now carries the supported alignment fact explicitly: ColorSamples means emitted sample centers match color. Unknown remains the default and refuses projection before output admission, even for equal dimensions. The packaged JXL adapter supplies this fact; odd-origin crops preserve exact native samples for seven auxiliary roles at source shifts 1/2/3. General grid origins, phases and resampling are still unrepresented. Read the registration qualification. The geometry helper performs no color conversion and does not establish native planar conversion, resizing or a whole-operation memory bound; the public Renderer composes it with required packed-color processing. Source and destination storage coexist during projection.

Signatures from this revision · bodies omitted

Functionzencodec_render::geometry::project
pub fn project(
    image: &ImageOutput,
    region: Option<Rect>,
    storage: &StorageBudget,
    pulse: Option<&dyn zencodec::Pulse>,
) -> Result<ImageOutput, Cause>;

Selected methods · 1 more in the full contract.

Read defaults, imports and full contract ↗
Type definitionzencodec::image::AuxiliaryGrid
pub enum AuxiliaryGrid {
    /// Registration has not been established. Equal dimensions do not change it.
    #[default]
    Unknown,
    /// Each auxiliary sample has the same center as the color sample at the
    /// same row and column, throughout the selected stored region. Dimensions
    /// must match color. This describes emitted samples, including any native
    /// upsampling; it does not authorize changing their numerical values.
    ColorSamples,
}
Read defaults, imports and full contract ↗
Inherent methodszencodec::image::ValueSlice
impl<'a> ValueSlice<'a> {
    pub fn new(
        bytes: &'a [u8],
        width: u32,
        height: u32,
        stride: usize,
        channel: ChannelType,
    ) -> Result<Self, At<OutputError>>;

    pub fn with_grid(mut self, grid: AuxiliaryGrid) -> Self;

    pub fn grid(&self) -> AuxiliaryGrid;

    pub fn as_strided_bytes(&self) -> &'a [u8];
}
Read defaults, imports and full contract ↗

Requirements from the TIFF parity work

The experiment pins image-tiff main at 10a3b6b2. The spatial gate now passes 136 tests with strict lint, including native old-JPEG consumption controls. Those controls qualify two pinned JPEG layouts and a generated predictor case; they do not establish support for every TIFF variant. Read the native evidence.

The shared walker now follows external JPEG tables (519–521) under scan and extent budgets. Rules::jpeg_table defaults to Unknown, independently of strip and JPEG-stream fate. In a real external-table file, all three table families are read and changing them changes decoded samples. Predictor 65535 is ignored for uncompressed samples but returns UnknownPredictor with LZW.

Read does not mean necessary or private-data-free. A zero-length JPEG stream declaration made the native decoder read an appended opaque tail without changing any pixels. The private adapter can describe that range with Count::ToEnd; it keeps its fate unknown. Whole-stream resource admission and complete zentiff consumer rules remain open. Issue 171 tracks their integration.

Signatures from this revision · bodies omitted

Trait methodszenmeta::ifd::walk::Rules (implementor inventory evidence)
pub trait Rules {
    fn jpeg_table(&self, _w: &Walk<'_>, _ifd: &Ifd, _tag: u16) -> Fate;

    fn extents(&self, w: &Walk<'_>, ifd: &Ifd) -> Vec<ExtentRule>;
}

Selected methods · 15 more in the full contract.

Required methods: follows, follows_next, ifd, entry, image_data, jpeg_stream. Other methods have defaults; read them before relying on their behavior.

Read defaults, imports and full contract ↗
Type definitionzenmeta::ifd::walk::Count
pub enum Count {
    /// Byte counts in this tag, one per offset.
    Tag(u16),
    /// One extent from the first offset to the end of the TIFF.
    ToEnd,
    /// One extent of this many bytes from the first offset.
    Bytes(u64),
    /// A 64-byte old-style JPEG quantization table at each nonzero offset.
    /// Zero means no separate table; repeated offsets may share a table.
    JpegQuantization,
    /// An old-style JPEG Huffman table at each nonzero offset: 16 code-count
    /// bytes followed by their sum in symbol bytes. Reading the counts uses
    /// the walk's scan budget. Zero means no separate table.
    JpegHuffman,
}
Read defaults, imports and full contract ↗

Remaining contracts by API

Names below exist today. The final column describes missing behavior, not proposed methods disguised as an implemented API.

Concern / ownerExisting APIMissing or unqualified
Video output transitionsPrepared adapters, destination admission and native sessions.Question::FrameRoute · FrameRouteOffer · resolve_frame_route
What is implemented

Prepared native encoders share the fallible resolver. Independent segments preserve exact AV1 samples; fixed output refuses changed dimensions after header publication. Cancellation, partial publication and unwind are terminal.

The coupled native converter/encoder witness now covers changing BT.709/sRGB YCbCr to fixed RGB8 output. Explicit one-input/one-output retiming now preserves requested native packet PTS/durations with unchanged samples. No HDR, resizing, frame-rate conversion or in-band muxer reconfiguration; a retained exact MP4 muxer now preserves retimed native packets under short writes. The private prepared destination is now retained before policy, conversion and initial encoder input; the public std output driver now owns the muxer/writer; StagedEncode now owns finite fixed-configuration frame encoding and supplies actual destination, staging and encoder-fidelity evidence to policy. General segmentation and live sequential routes remain open. Committed-output facts do not reserve destination support. Finite compressed staging does not establish live sequential-output or complete workspace bounds. Evidence / tracking ↗
Display renderingShared request/result contract; codec composition; consumer adapter.CodecSet::open_collection · OpenRequest · ImageRequest · Placement::visible
What is implemented

Explicit native composited/raw selection retained before policy, contradiction checks and checked placement intersection. The public geometry::project helper validates canvas orientation and display-coordinate regions and requires explicit ColorSamples registration for auxiliaries; native TIFF/JXL qualification is recorded separately.

The public zencodec-render companion now combines qualified canvas projection, orientation, required color and aligned auxiliaries. Planar video, HDR display mapping, resizing, proportional region decode and complete workspace bounds remain unqualified. Evidence / tracking ↗
Planar video conversionzenpixels-convert and video adapters; shared facts/decisions in zencodec.ImageConversionOffer · resolve_image_conversion · PreparedStorage · ImageOutput::from_frame_pixels
What is implemented

Native AV1 borrowed-plane conversion with policy before destination initialization. Scoped reservations, declared siting assumptions, clipping disclosure, cancellation, original error causes and converted ImageOutput ownership are exercised across the native crop matrix.

The shared CodePlane and YcbcrToRgbF32 now replace the private float kernel, with typed pre-write refusals and identical native output. OutputPlan processes that result. Other matrices/siting, integer/mono/GBR output, sample-type consolidation, numerical-model guarantees, broader converter/encoder transitions and native workspace admission remain open. Native preparation still scans all active source words even for small crops. Evidence / tracking ↗
Opaque and nested privacyShared evidence and offers; format inspectors and rewrite adapters.PolicyResolver::resolve · Question::Transcode · MetadataTransfer
What is implemented

One fallible callback, scoped offers and reported metadata/color treatment. Opaque-profile retention defaults to unknown; strict exclusion requires explicit absence evidence from the executing route.

No whole-container PII guarantee over unknown packets, unparsed XML, reconstruction data and nested items. Missing inspection evidence cannot authorize pass-through under a strict exclusion requirement. Need explicit inspection scope and prepared preserve/drop/refuse mechanisms. Evidence / tracking ↗
Native interpretation suspensionCodec state machines and their controlled adapters.DecodeRequest::start_controlled · Question::Boundary
What is implemented

Checked suspension/resume and native whole-source JPEG/PNG routes, including relevant late metadata refusals.

Native byte-resuming reconstruction is not qualified by those whole-source implementations. Other codec/configuration combinations and late interpretation changes need exact suspension points before affected effects. Evidence / tracking ↗
Whole-operation memoryNative allocation owners plus adapter accounting.StorageBudget · OpenRequest::with_output_storage · CollectionOpenContext · ResourceLimits
What is implemented

Managed payload admission, retained owner charges, native AV1 picture-plane admission and scoped driver input limits.

No universal bound including native codec/CMS workspaces, metadata, profile generation, parser state, conversion scratch, source caches and encoded staging. max_output_bytes is not an allocation bound. Evidence / tracking ↗
Safe slow-writer behaviorEncoder configurations, storage-aware drivers and transcode offers.Encoder::begin · Encoder::pending_output · OutputTiming · WorkQuota
What is implemented

CPU fairness is separate from writer pressure; qualified finite routes finish encoding before awaiting output.

Explicit finite disk staging now has disk/index caps, admitted packet replay and policy-visible storage facts. Spool writes can still block with raw encoder state retained. The shared OutputSchedule question now selects retained live/finish-first routes with explicit source facts and unknown retention by default. Native JPEG executes both; StagedEncode supplies its finish-first guarantee automatically. No configuration-specific live sequential offer yet proves total retained memory, and the synchronous row_encode facade now implements source acquisition and failure reporting. Raster disk staging now supports bounded chunk replay after encoder release. Shared packet/raster storage internals and native retention qualification remain open. The native JPEG matrix now verifies exact bytes across partial drains and short writes for fixed/optimized, sequential/progressive and tiny-image cases. It does not qualify memory: full-raster PixelSlice callers retain all input. A live offer must bind actual pausable source ownership as well as native state. Byte availability does not prove that blocking the writer is safe. Evidence / tracking ↗
Seek-back and repeated inputOptional output/source mechanisms in adapters; no mandatory general I/O hierarchy.Encoder · DynReadAt · ReadAtReader
What is implemented

Positional input and standard Read/Seek parser bridge; sync/async drivers. Media MuxFinish validates all patches before final effects; a native three-container writer trial preserves prefixed disk files.

The public std output driver now preflights and applies relative seek-back patches. OutputFinish/Patch now live in the core and actual destination wrappers in zencodec-io, with media aliases preserving existing consumers. Raster Encoder negotiation and driver patch execution now have typed/dyn and private native AV1/IVF validation. Production raster-codec adoption remains open; the private witness buffers its input. I/O patch failures can leave partial effects; it is not a transaction. No complete bounded multi-pass raster-source route. Those must distinguish seekable disk output from network publication and declare staging before work. Evidence / tracking ↗
Packaged spatial adaptersCodec packages and explicit realizable selection offers.CollectionFactory::open_with · Collection::children · Collection::describe · Collection::decode
What is implemented

Shared collection and CodecSet registration; packaged JXL review adapter; real private TIFF/SubIFD/BigTIFF, tiled/strip and conditional HTTP controls. The current TIFF parity fork passes the spatial gate plus native JPEG-table/EOF/predictor controls; shared table discovery defaults to unknown consumption.

TIFF is not yet a packaged factory. No universal efficient pyramid/region route, common blend/reference graph or full required-configuration coverage. A valid region request does not establish proportional decode work or memory. Evidence / tracking ↗
Conversion effects and ownershipzenpixels-convert effects contract and output adapters.OutputPlan::new_with_descriptor · ImageOutput::replace_packed_color · PackedImageOwner::retain_auxiliaries
What is implemented

Required packed descriptors, sample-exact planning without CMS, native float RGB/RGBA CMS cases, same-coordinate color replacement and separately retained auxiliary allocations.

No geometry-changing replacement preserving all auxiliary coordinates; component codes refuse packed replacement. General ICC/LUT and float CMYK conversion remain unqualified. retain_channels needs explicit clip_out_of_gamut(false) for the qualified unclipped route. Evidence / tracking ↗
Required metadata and settingsNative conformance loop, then migration/deprecation.DecodeRequest::decode · EncodeRequest::prepare · EncodeRequest::encode_with
What is implemented

Required-versus-preferred requests, prepared native choices, typed refusal and qualified JPEG/PNG metadata/density cases.

No codec-wide proof for every configuration, fidelity and metadata combination. Wider density reconciliation and whole-container retention remain open. Legacy best-effort policy setters are deprecated with checked replacements; compatibility implementations still retain their previous behavior. Evidence / tracking ↗
0.3 surface cleanupCross-crate migration after native consumers qualify.CodecSet::with_send_encoder · EncodeRequest::prepare_send · frame::SampleEncoding
What is implemented

Additive Send registrations/preparation and shared image/video ownership.

Send is not the ordinary registry default. Duplicate sample-encoding types and parallel legacy decode/configuration paths remain. No coordinated removal or renamed 0.3 surface has landed. Evidence / tracking ↗
Freestanding no_stdDependency graph and qualified math implementation.Default zencodec dependency graph
What is implemented

WASM build coverage and alloc-based core interfaces.

The freestanding default graph still has the tracked std/math dependency gap. Passing wasm32 does not establish a bare-metal build. Evidence / tracking ↗

Compile time

The measurements below identify their own source revisions. The latest core measurement includes coupled frame conversion and the lossless preference correction. It excludes the TIFF/JXL adapters, codecs and zenpixels-convert, whose complete application build costs remain unqualified.

Exploratory cold-build observations, 2026-10-27. A minimal consumer re-exports zencodec; this does not build codecs, CMS, tests or the site. Each target directory is new, dependencies are prefetched and the OS file cache is warm. Rust 1.99.1, Ryzen 9 7900X, two Cargo jobs, incremental off.

Median of five buildsPublished 0.1.26Main a20e4dd3API review 85c33cc8
Debug, default1.96 s2.29 s2.66 s
Debug, std1.99 s2.28 s2.69 s
Release, default2.35 s2.90 s3.64 s

Not a confirmed performance comparison. Zenbench completed 60 interleaved builds with A/A controls and verified uniform CPU-class placement. CPU-load observations exceeded 5% in all 60; 17 had heavy-process observations. No samples were discarded or acceptance gates changed. These medians do not predict a complete application build or incremental rebuild.

Cargo’s zencodec unit median changed from 0.69 to 1.08 s in debug and from 1.25 to 1.98 s in release between main and the review revision. The common dependencies resolve identically; main adds how-far relative to published, and review adds spin. Identifying which APIs account for compiler work still needs profiling.

Resource wrapper: rc=0, 180 s, peak RSS 0.30 GiB, minimum available 11360 MiB, peak load 1.98. This is the whole compile experiment, not codec memory. Method, evidence and reproduction.

Earlier observation and its source revisions.

Core plus I/O and media companions

The newer experiment includes zencodec-io and zencodec-media at review ec5d5089. It still excludes codecs, rendering/CMS and application monomorphization. Main media has no std feature; the review std case includes its new output/staging drivers. This measures the expanded surface rather than identical supported operations.

Median of five buildsMain a20e4dd3Review ec5d5089
Debug, no requested std2.44 s2.86 s
Debug, available std2.44 s2.95 s
Release, available std3.19 s4.20 s

Exploratory only: 45 interleaved builds with A/A controls; all 45 had CPU-load observations above 5%, and six had heavy-process observations. No observations were discarded. Resource wrapper: rc=0, 157 s, peak RSS 0.30 GiB, minimum available 12552 MiB, peak load 1.56. These are compilation observations, not runtime or codec memory bounds. Complete method, unit timings and retained evidence.