heic 0.1.2

Pure Rust HEIC/HEIF image decoder with SIMD acceleration
Documentation

heic CI crates.io lib.rs docs.rs codecov license

Pure Rust HEIC/HEIF image decoder. No C/C++ dependencies, no unsafe code.

  • #![forbid(unsafe_code)] — zero unsafe blocks in the entire codebase
  • no_std + alloc compatible (compiles for wasm32-unknown-unknown)
  • Multi-codec: HEVC (built-in), AV1 via rav1d-safe (av1 feature), uncompressed HEIF via zenflate (unci feature)
  • AVX2/SSE4.1/NEON SIMD acceleration with automatic scalar fallback
  • Cooperative cancellation via enough — all decode paths check Stop tokens
  • Configurable resource limits (dimensions, pixel count, memory) enforced before allocation
  • Optional tile-parallel decoding via rayon

Status

Decodes most HEIC files from iPhones and cameras. 118/162 HEIF test files decode successfully (with av1 + unci features). 49/49 ITU-T HEVC conformance vectors pass. Best quality: 77.3 dB PSNR (BT.709), 73% pixel-exact on example.heic.

What works

  • HEIF container parsing (ISOBMFF boxes, grid images, overlays, identity-derived items)
  • HEIF image sequences (msf1/moov — first I-frame extraction from movie structure)
  • Multi-codec dispatch: HEVC, AV1 (av1 feature), uncompressed HEIF (unci feature)
  • Full HEVC I-frame decoding (VPS/SPS/PPS, CABAC, intra prediction, transforms)
  • AV1 still image decoding via rav1d-safe (av1 feature)
  • Uncompressed HEIF with deflate/zlib decompression via zenflate (unci feature)
  • Deblocking filter and SAO (Sample Adaptive Offset)
  • YCbCr→RGB with BT.601/BT.709/BT.2020 matrices (full + limited range)
  • CICP color info from HEVC VUI and HEIF colr nclx (container overrides codec)
  • 10-bit HEVC with transparent 8-bit downconvert or 16-bit output preservation
  • Alpha plane decoding from auxiliary images (HEVC and AV1)
  • HDR gain map extraction (Apple urn:com:apple:photo:2020:aux:hdrgainmap)
  • EXIF, XMP, and ICC profile extraction (zero-copy where possible)
  • Thumbnail decode, image rotation/mirror transforms (ipma ordering)
  • HEVC scaling lists (custom dequantization matrices)
  • AVX2 SIMD: color conversion, IDCT 8/16/32, residual add, dequantize
  • SSE4.1 SIMD: IDST 4x4
  • NEON SIMD: color conversion, IDCT 8/16/32, IDST 4x4
  • Tile-parallel grid decoding via rayon (parallel feature)

Known limitations

  • HEVC I-slices only (sufficient for HEIC still images; no inter prediction for video)
  • JPEG and H.264/AVC codecs in HEIF: detected but not decoded
  • Brotli-compressed uncompressed HEIF: not yet supported (deflate/zlib only)
  • PQ/HLG transfer functions: parsed and exposed via ImageInfo, but no EOTF applied — callers handle tone-mapping

Features

Feature Default Description
std yes Standard library support. Disable for no_std + alloc.
parallel no Parallel tile decoding via rayon. Implies std.
av1 no AV1 codec support via rav1d-safe. Implies std.
unci no Uncompressed HEIF (ISO 23001-17) with deflate/zlib decompression via zenflate.

Usage

use heic::{DecoderConfig, PixelLayout};

let data = std::fs::read("image.heic")?;
let output = DecoderConfig::new().decode(&data, PixelLayout::Rgba8)?;
println!("{}x{} image, {} bytes", output.width, output.height, output.data.len());

Limits and cancellation

use heic::{DecoderConfig, PixelLayout, Limits};

let mut limits = Limits::default();
limits.max_width = Some(8192);
limits.max_height = Some(8192);
limits.max_pixels = Some(64_000_000);
limits.max_memory_bytes = Some(512 * 1024 * 1024);

let output = DecoderConfig::new()
    .decode_request(&data)
    .with_output_layout(PixelLayout::Rgba8)
    .with_limits(&limits)
    .decode()?;

Probe without decoding

use heic::ImageInfo;

let info = ImageInfo::from_bytes(&data)?;
println!("{}x{}, bit_depth={}, alpha={}, exif={}",
    info.width, info.height, info.bit_depth, info.has_alpha, info.has_exif);
// CICP color info also available:
// info.color_primaries, info.transfer_characteristics, info.matrix_coefficients, info.video_full_range

Zero-copy into pre-allocated buffer

let info = ImageInfo::from_bytes(&data)?;
let mut buf = vec![0u8; info.output_buffer_size(PixelLayout::Rgba8).unwrap()];
let (w, h) = DecoderConfig::new()
    .decode_request(&data)
    .with_output_layout(PixelLayout::Rgba8)
    .decode_into(&mut buf)?;

For grid images (most iPhone photos), decode_into streams tile color conversion directly into the output buffer, avoiding the intermediate full-frame YCbCr allocation.

Metadata extraction

use std::borrow::Cow;

let decoder = DecoderConfig::new();
// Zero-copy for single-extent items, owned for multi-extent
let exif: Option<Cow<'_, [u8]>> = decoder.extract_exif(&data)?;  // raw TIFF bytes
let xmp: Option<Cow<'_, [u8]>> = decoder.extract_xmp(&data)?;    // raw XML bytes
let icc: Option<Vec<u8>> = decoder.extract_icc(&data)?;           // ICC profile bytes
let thumb = decoder.decode_thumbnail(&data, PixelLayout::Rgb8)?;  // smaller preview

HDR gain map

let gainmap = DecoderConfig::new().decode_gain_map(&data)?;
// gainmap.data: Vec<f32> (normalized 0.0–1.0), gainmap.width, gainmap.height
// Apply Apple HDR reconstruction:
//   sdr_linear = sRGB_EOTF(sdr_pixel)
//   gain_linear = sRGB_EOTF(gainmap_pixel)
//   scale = 1.0 + (headroom - 1.0) * gain_linear
//   hdr_linear = sdr_linear * scale

Performance

Benchmarked on AMD Ryzen 9 7950X, WSL2, Rust 1.93, release profile (thin LTO, codegen-units=1).

Image Time
1280x854 (single tile) 54 ms
3024x4032 (48-tile, sequential) 451 ms
3024x4032 (48-tile, parallel) 180 ms
Probe (metadata only) 1.3 µs
EXIF extraction 4.4 µs

SIMD-accelerated on x86-64 (AVX2 for color conversion, IDCT 8/16/32, residual add, dequantize; SSE4.1 for IDST 4x4) and AArch64 (NEON for color conversion, IDCT 8/16/32, IDST 4x4). Scalar fallback when SIMD is unavailable.

Dependencies

4 runtime crates (default features), none with C/FFI:

heic
├── archmage          — SIMD dispatch via CPU feature tokens
│   └── safe_unaligned_simd  — safe wrappers over std::arch intrinsics
├── enough            — cooperative cancellation (0 unsafe)
├── safe_unaligned_simd
└── whereat           — error location tracking (deny(unsafe_code))

With parallel: adds rayon + crossbeam (6 more crates, all pure Rust). With av1: adds rav1d-safe (pure Rust AV1 decoder with archmage SIMD). With unci: adds zenflate (pure Rust DEFLATE/zlib).

Memory

Use DecoderConfig::estimate_memory() to check memory requirements before decoding. decode_into() uses a streaming path for grid images that reduces peak memory by ~60% compared to decode().

Security

Designed for untrusted input. All decode paths enforce resource limits before allocation, not after.

  • #![forbid(unsafe_code)] — the entire crate, including SIMD dispatch via archmage
  • Pre-decode dimension checksLimits (max_width, max_height, max_pixels, max_memory_bytes) checked against HEIF ispe box dimensions before any codec allocates frame buffers
  • AV1 frame_size_limitlimits.max_pixels fed directly into rav1d-safe's Settings::frame_size_limit, rejecting oversized frames during OBU parsing before pixel buffer allocation
  • Decompression bomb protection — unci decompressor capped at min(512 MiB, limits.max_memory_bytes) with expected size validated against declared dimensions
  • Cooperative cancellationenough::Stop tokens checked in all decode loops (per-CTU for HEVC, per-row for unci, per-tile for grids, inside rav1d, inside zenflate decompression)
  • Checked arithmetic — all dimension, offset, and size calculations use checked_mul/checked_add with explicit error returns
  • Fallible allocationtry_reserve / try_vec! throughout; OOM returns an error, not a panic
  • Container parser hardening — resource limits on item count (64K), property count (64K), extents per item (1K), references (64K), sample table entries (1M), string lengths (4K), NAL unit size (16 MiB), ICC profile size (4 MiB)
  • Fuzz targets — 5 libfuzzer targets covering decode, decode-with-limits, probe, AV1 decode, and unci decode

Use DecoderConfig::estimate_memory() to check memory requirements before decoding. Pass Limits to reject files that exceed your resource budget. Pass an enough::Stop token for cooperative cancellation of long-running decodes.

Image tech I maintain

State of the art codecs* zenjpeg · zenpng · zenwebp · zengif · zenavif (rav1d-safe · zenrav1e · zenavif-parse · zenavif-serialize) · zenjxl (jxl-encoder · zenjxl-decoder) · zentiff · zenbitmaps · heic · zenraw · zenpdf · ultrahdr · mozjpeg-rs · webpx
Compression zenflate · zenzop
Processing zenresize · zenfilters · zenquant · zenblend
Metrics zensim · fast-ssim2 · butteraugli · resamplescope-rs · codec-eval · codec-corpus
Pixel types & color zenpixels · zenpixels-convert · linear-srgb · garb
Pipeline zenpipe · zencodec · zencodecs · zenlayout · zennode
ImageResizer ImageResizer (C#) — 24M+ NuGet downloads across all packages
Imageflow Image optimization engine (Rust) — .NET · node · go — 9M+ NuGet downloads across all packages
Imageflow Server The fast, safe image server (Rust+C#) — 552K+ NuGet downloads, deployed by Fortune 500s and major brands

* as of 2026

General Rust awesomeness

archmage · magetypes · enough · whereat · zenbench · cargo-copter

And other projects · GitHub @imazen · GitHub @lilith · lib.rs/~lilith · NuGet (over 30 million downloads / 87 packages)

License

Dual-licensed: AGPL-3.0 or commercial.

I've maintained and developed open-source image server software — and the 40+ library ecosystem it depends on — full-time since 2011. Fifteen years of continual maintenance, backwards compatibility, support, and the (very rare) security patch. That kind of stability requires sustainable funding, and dual-licensing is how we make it work without venture capital or rug-pulls. Support sustainable and secure software; swap patch tuesday for patch leap-year.

Our open-source products

Your options:

  • Startup license — $1 if your company has under $1M revenue and fewer than 5 employees. Get a key →
  • Commercial subscription — Governed by the Imazen Site-wide Subscription License v1.1 or later. Apache 2.0-like terms, no source-sharing requirement. Sliding scale by company size. Pricing & 60-day free trial →
  • AGPL v3 — Free and open. Share your source if you distribute.

See LICENSE-COMMERCIAL for details.

AI-Generated Code Notice

Developed with Claude (Anthropic). Not all code manually reviewed. Review critical paths before production use.