EdgeFirst Codec
Zero-allocation image decoding framework for pre-allocated tensor buffers in real-time vision pipelines.
Overview
edgefirst-codec decodes JPEG and PNG images directly into pre-allocated
Tensor<T> or TensorDyn buffers, supporting strided memory layouts
(GPU pitch-aligned DMA-BUF, PBO). This eliminates per-frame allocations
in the hot loop — the primary design goal.
JPEG decoding uses a custom from-scratch baseline decoder with reusable
state, achieving zero heap allocations after the first decode at each
resolution. SIMD-optimized kernels (NEON on AArch64, SSE4.1/SSSE3/SSE2 on
x86-64) are selected automatically at init via dynamic dispatch. Vectorised
type conversion (NEON/SSE2) accelerates f32, u16, and i16 output paths.
PNG decoding uses zune-png.
Quick Start
use ;
use ;
// Allocate once at init (prefer ImageProcessor::create_image() for DMA/PBO)
let mut tensor = image.unwrap;
let mut decoder = new;
// Decode in the hot loop — zero allocations after warmup for JPEG
let jpeg_bytes = read.unwrap;
let info = tensor.load_image.unwrap;
println!;
Recommended Pattern
For maximum performance, use tensors allocated by
ImageProcessor::create_image():
use ;
use ;
let mut processor = new?;
let mut src = processor.create_image?;
let mut dst = processor.create_image?;
let mut decoder = new;
loop
Benefits of ImageProcessor::create_image() tensors:
- DMA-BUF backing for zero-copy GPU import
- PBO backing when OpenGL is the active transfer path
- GPU pitch alignment (64-byte for Mali compatibility)
Free-standing tensors work but cannot use PBO and may lack GPU-aligned pitch.
Supported Formats
| Format | Input | Output Formats |
|---|---|---|
| JPEG | &[u8] |
RGB, RGBA, Grey, BGRA, NV12 |
| PNG | &[u8] |
RGB, RGBA, Grey, BGRA |
Decoder Limitations
The codec decodes a strict subset of the JPEG / PNG specs. Inputs that fall
outside the supported subset surface a typed CodecError::Unsupported(...)
variant so callers can pattern-match programmatically (no string parsing
required).
JPEG
| JPEG feature | Status |
|---|---|
| Baseline DCT (SOF0) | Supported |
| 8-bit sample precision | Supported |
| 1 component (greyscale) or 3 components (YCbCr) | Supported |
| Chroma subsampling 4:4:4 / 4:2:2 / 4:2:0 / 4:4:0 | Supported |
| EXIF orientation (apply_exif) | Supported |
| Progressive DCT (SOF2) | Unsupported — Unsupported(ProgressiveJpeg) |
| Extended sequential DCT (SOF1) | Unsupported |
| Lossless predictive (SOF3) | Unsupported — Unsupported(LosslessJpeg) |
| Hierarchical (SOF5/6/7) | Unsupported — Unsupported(HierarchicalJpeg) |
| Arithmetic coding (SOF9/10/11/13/14/15) | Unsupported — Unsupported(ArithmeticCodedJpeg) |
| Sample precision other than 8-bit | Unsupported — Unsupported(JpegPrecision { bits }) |
| CMYK / YCCK / >3 components | Unsupported — Unsupported(JpegComponentCount { components }) |
| Chroma sampling that exceeds luma | Unsupported — Unsupported(JpegChromaSubsampling) |
| Thumbnails (JFIF / APP markers) | Ignored |
| EXIF rotation with planar NV12 output | Not applied (apply_exif silently ignored for NV12) |
PNG
PNG decoding goes through zune-png; the codec applies its own
post-processing (format conversion, EXIF rotation, stride-aware writes).
| PNG feature | Status |
|---|---|
| 8-bit colorspace: Luma / LumaA / RGB / RGBA | Supported |
16-bit colorspace: RGB / RGBA / Luma → u16 / i16 / f32 tensors |
Supported |
eXIf chunk orientation, u8 output |
Supported |
| Palette (indexed-color) PNG | Per zune-png (expanded to RGB/RGBA by the decoder) |
eXIf chunk orientation, 16-bit output paths |
Not applied (apply_exif ignored on u16/i16/f32) |
| APNG (animated) | Not exercised (decoder set to png_set_decode_animated(false)) |
| Interlaced (Adam7) | Per zune-png |
Data Types
| Type | Support | Notes |
|---|---|---|
u8 |
✓ | Direct copy (identity) |
u16 |
✓ | Scaled * 257 from 8-bit; native from 16-bit PNG |
i8 |
✓ | XOR 0x80 sign-bit flip |
i16 |
✓ | XOR 0x8000 sign-bit flip |
f32 |
✓ | Normalized to [0.0, 1.0] |
API Reference
ImageDecoder
Reusable decoder with internal scratch buffers. Create once, reuse across frames — scratch buffers amortize after the first decode.
ImageLoad Extension Trait
load_image(&mut self, decoder, data, opts)— decode from&[u8]load_image_read(&mut self, decoder, reader, opts)— decode fromReadload_image_file(&mut self, decoder, path, opts)— decode from file path
DecodeOptions
format: Output pixel format (None= native from file)apply_exif: Apply EXIF orientation (default true)
ImageInfo
Returned by all decode methods with actual decoded dimensions:
width,height: Decoded image sizeformat: Output pixel formatrow_stride: Row stride in bytes
License
Apache-2.0