Heifer
Pure-Rust HEIF/HEIC image decoder (encoder planned). No C dependencies, #![forbid(unsafe_code)].
Try it in your browser — HEIC photos decoded by heifer compiled to WebAssembly (≈ 250 KB); your file never leaves your device.
Status: early development. Complete HEIC files decode to RGB(A), with grids, overlays, alpha and transforms. The HEVC decoder is validated against the official conformance bitstreams, and real iPhone, Samsung and Xiaomi photos decode correctly (see below). HDR gain maps and ICC profiles are not applied yet.
Conformance
The first picture of 66 official HEVC conformance bitstreams (ITU-T H.265.1: Main, Main 10 and Range Extensions, 8 to 12 bits, 4:0:0 / 4:2:0 / 4:2:2 / 4:4:4, tiles, WPP, PCM, scaling lists, slices...):
| Check | Result |
|---|---|
| Identical to ffmpeg, sample for sample | 62 / 62 comparable streams |
| Verified against the reference decoder's picture hash (MD5 / checksum SEI) | 41 streams, including 3 that ffmpeg cannot decode |
| Not supported | 1 (extended precision / CABAC bypass alignment, 16-bit video tools) |
Real-world files
43 real and synthetic HEIC/HEIF files from pillow-heif's test
suite (scripts/fetch-real.sh, not committed):
| File | Content | Result |
|---|---|---|
| iPhone photo | 4032×3024, 48-tile grid, HDR gain map, depth map | ≈ 61 dB vs ffmpeg, ~100 ms |
| iPhone photo | rotated 270° | ≈ 60 dB, correct orientation |
| iPhone 15 Pro spatial photo | stereo pair | ≈ 62 dB |
| Samsung 200 MP | 12240×16320, 768 tiles | decoded in ~2 s (ffmpeg fails) |
| Xiaomi, Samsung, 8000×6000 | grids, gain maps | 58–62 dB |
| 8/10/12-bit gray, RGB, RGBA | odd sizes, alpha | ✓ |
| corrupted / truncated | clean errors, no panic |
Remaining differences with ffmpeg are in heifer's favour (it honours ispe/grid crops and the primary
item). Phone "HDR" photos are 8-bit images plus a gain map: heifer decodes the base image; applying the
gain map (ISO 21496-1) is not implemented.
Fuzzing
Five cargo-fuzz targets, with HEIF and HEVC dictionaries, seeded with the sample files, the conformance bitstreams and libheif's fuzzing corpus:
| Target | Checks |
|---|---|
decode |
full HEIC decoding never panics, hangs or exhausts memory |
container |
container parsing and item data access |
hevc |
HEVC decoding, including picture hash verification |
parallel |
parallel and sequential decoding give identical results |
cabac |
the CABAC encoder and decoder round-trip any bin sequence |
Fuzzing found a memory exhaustion bug (overlapping iloc extents), now fixed and covered by a test,
and led to configurable size limits (max_pixels). It runs every night on GitHub Actions
(.github/workflows/fuzz.yml) and can run locally for hours:
WebAssembly demo (rustup target add wasm32-unknown-unknown, cargo install wasm-bindgen-cli --version 0.2.129):
&&
Why
HEIC is the default photo format on iPhones. In Rust, reading it usually means binding to libheif (C/C++),
and there is no pure-Rust encoder at all. heifer aims to be a safe, portable, well-tested alternative.
Usage
[]
= "0.1"
let bytes = read?;
let image = decode?; // primary image, alpha, crop/rotation/mirror applied
let rgba: = image.to_rgba8; // or to_rgb8(); image.data holds 16-bit samples
println!;
// Grid tiles are decoded in parallel on all cores (no dependency: std threads).
// Limit or disable it with options:
let image = decode_with_options?;
Metadata, without decoding pixels:
let meta = read_metadata?;
if let Some = meta.exif_fields
let icc: = meta.icc_profile; // also meta.exif (raw TIFF), meta.xmp
With the image crate (heifer = { version = "0.1", features = ["image"] }):
register_image_decoder_hooks;
let img = open?; // .heic/.heif/.hif, and content detection
Convert a file from the command line:
Lower-level APIs
use HeifFile;
let bytes = read?;
let file = parse?;
let primary = file.primary_item?; // e.g. a `grid` of `hvc1` tiles
let size = file.image_size?; // Some((4032, 3024))
let tiles = file.referenced_items;
let hevc = file.hevc_bitstream?; // Annex B stream, decodable by ffmpeg
Decoding one HEVC image item to pixels:
use ;
let frame = decode_picture?;
let = ; // u16 samples
Crates
| Crate | Role | State |
|---|---|---|
heifer |
Public API: decode(), grids, overlays, alpha, transforms, YCbCr → RGB |
✅ |
heifer-isobmff |
HEIF container: boxes, items, properties, references, grids | ✅ reading |
heifer-hevc-dec |
HEVC intra decoder, picture hash verification | ✅ conformance-tested (8–12 bit, all chroma formats) |
heifer-hevc-enc |
HEVC intra encoder | 🚧 CABAC encoder only |
Roadmap
- Phase 1 — Decoder
- Parse container:
ftyp,meta,iinf,iloc,iref,iprp/ipco/ipma,idat, grids, rotation/mirror, alpha - Extract HEVC bitstreams (Annex B) for each image item
- Grid assembly (tiles decoded in parallel), overlays (
iovl), alpha planes,clap/irot/imir - YCbCr → RGB (BT.601/709/2020, full/limited range;
colrnclx, else HEVC VUI) - EXIF, XMP and ICC profile access (+ minimal EXIF reader)
- Applying ICC profiles,
ilocconstruction method 2, bilinear chroma upsampling - HEVC intra decoding
- Bit reader, Exp-Golomb codes, NAL units, emulation prevention
- VPS, SPS (VUI, HRD, scaling lists, range extension), PPS (tiles, deblocking)
- Slice segment header (I slices, entry points, dependent slices)
- CABAC entropy decoding (+ encoder, verified by round-trip)
- Coding tree syntax: SAO, quadtree, intra modes, transform tree, residuals, QP, PCM, tiles, WPP
- Intra prediction, scaling, inverse DCT/DST — bit-exact vs ffmpeg (
-skip_loop_filter all) - Deblocking filter, SAO — bit-exact vs ffmpeg
- 10/12-bit, 4:0:0, 4:2:2, 4:4:4, unequal luma/chroma bit depths
- Range extension tools: cross-component prediction, implicit RDPCM, transform-skip rotation/contexts, persistent Rice adaptation
- Decoded picture hash SEI (MD5, CRC, checksum) verification
- Validated on official HEVC conformance bitstreams
- Extended precision processing, CABAC bypass alignment (16-bit profiles)
- Real-world HEIC corpus (iPhone, Samsung, Xiaomi photos) compared with ffmpeg
- HDR gain maps (Apple / ISO 21496-1), ICC profiles
- Faster single-image decoding (allocation-free reconstruction, parallel filters, WPP/tiles)
- Fuzzing (5 targets, dictionaries, nightly CI) and size limits against malicious files
-
imagecrate integration (featureimage), WebAssembly browser demo
- Parse container:
- Phase 2 — Encoder
- Write HEIF container
- Minimal HEVC intra encoder (fixed block size, DC prediction, fixed QP) producing valid files
- All 35 intra modes, block-size decisions, rate-distortion optimisation
- 10-bit, alpha, HDR
Development
# Inspect a file
Conformance bitstreams (not committed, ~23 MB download from itu.int):
Decoding is checked against ffmpeg -i tile.h265 -f rawvideo -pix_fmt yuv420p ref.yuv
(and with -skip_loop_filter all / --no-filters to check reconstruction alone).
RGB output is compared with ffmpeg -i file.heic -pix_fmt rgb24 ref.png (≈ 53 dB PSNR; the remaining
difference comes from chroma upsampling and rounding).
Header parsing is checked against ffmpeg -i tile.h265 -c copy -bsf:v trace_headers -f null -:
the params example prints fields with the same names as the specification and ffmpeg.
Patents
HEVC is covered by patents. This project is a clean-room implementation for research and interoperability; users are responsible for complying with applicable patent licensing in their jurisdiction.
License
Licensed under either of Apache License 2.0 or MIT, at your option.