This document is the practical API reference for integrating heic_decoder in apps and automation.
- Decode HEIF/HEIC/AVIF to RGBA (
u8oru16storage). - Decode from bytes,
Read,BufRead, and file paths. - Optional
imagecrate integration through hook registration. - Keep default decode output aligned with primary item transforms (
clap/irot/imir) and expose explicit EXIF orientation helpers.
default: no optional features.image-integration: enablesheic_decoder::image_integrationmodule and hook APIs.
Cargo.toml:
[dependencies]
heic_decoder = { path = "../heic-decoder" }With image integration:
[dependencies]
heic_decoder = { path = "../heic-decoder", features = ["image-integration"] }
image = { version = "0.25", default-features = false, features = ["png"] }DecodeGuardrailsmax_input_bytes: Option<u64>max_pixels: Option<u64>max_temp_spool_bytes: Option<u64>temp_spool_directory: Option<PathBuf>
DecodedRgbaImagewidth,heightsource_bit_depthpixels: DecodedRgbaPixels(U8(Vec<u8>)orU16(Vec<u16>))icc_profile: Option<Vec<u8>>
ExifOrientationHintexif_orientation: Option<u8>primary_item_has_orientation_transform: boolshould_apply_exif_orientation()orientation_to_apply()
DecodeError,DecodeErrorCategory,DecodeGuardrailError
Return type for RGBA decode functions:
Result<DecodedRgbaImage, DecodeError>decode_bytes_to_rgba_with_guardrails(input, guardrails)decode_read_to_rgba_with_guardrails(reader, guardrails)decode_bufread_to_rgba_with_guardrails(bufread, guardrails)decode_path_to_rgba_with_guardrails(path, guardrails)
Non-guardrailed convenience variants also exist (decode_*_to_rgba) and use DecodeGuardrails::default().
decode_bytes_to_png_with_guardrails(input, output_path, guardrails)decode_read_to_png_with_guardrails(reader, output_path, guardrails)decode_bufread_to_png_with_guardrails(bufread, output_path, guardrails)decode_path_to_png_with_guardrails(input_path, output_path, guardrails)
Legacy aliases:
decode_file_to_rgba->decode_path_to_rgbadecode_file_to_png->decode_path_to_png
These are useful for tooling and specialized pipelines, but most applications should use the core entry points above.
decode_primary_avif_to_image(input)->Result<DecodedAvifImage, DecodeAvifError>assemble_primary_heic_hevc_stream(input)->Result<Vec<u8>, DecodeHeicError>decode_primary_heic_to_image(input)->Result<DecodedHeicImage, DecodeHeicError>decode_primary_heic_to_metadata(input)->Result<DecodedHeicImageMetadata, DecodeHeicError>decode_primary_uncompressed_to_image(input)->Result<DecodedUncompressedImage, DecodeUncompressedError>
Also note:
isobmffandsourcemodules are public and available for advanced use.- These low-level APIs expose more container/codec details and have a larger surface for malformed-input handling.
Use this decision order:
- If you already have
&[u8], usedecode_bytes_to_rgba_with_guardrails. - If you have a file path, use
decode_path_to_rgba_with_guardrails. - If you have
Read/BufRead(non-seek stream), usedecode_read_to_rgba_with_guardrailsordecode_bufread_to_rgba_with_guardrails.
Important behavior:
Read/BufReadinputs are spooled once to a temporary file to enable random-access decode.Pathand seekable decode paths avoid full-file in-memory buffering.
Use explicit guardrails in production.
Example preset:
use heic_decoder::DecodeGuardrails;
let guardrails = DecodeGuardrails {
max_input_bytes: Some(128 * 1024 * 1024),
max_pixels: Some(64_000_000),
max_temp_spool_bytes: Some(128 * 1024 * 1024),
temp_spool_directory: None,
};Notes:
max_input_bytesapplies to all entry points.max_temp_spool_bytesprotects non-seekRead/BufReadingestion.max_pixelsblocks oversized decoded geometry before RGBA materialization.
Top-level errors are DecodeError. Use category() for stable handling:
use heic_decoder::{DecodeError, DecodeErrorCategory};
fn classify(err: &DecodeError) -> DecodeErrorCategory {
err.category()
}Stable categories:
IoParseMalformedInputUnsupportedFeatureResourceLimitDecoderBackendOutputEncoding
Typical policy mapping:
ResourceLimit: return413/ reject input.MalformedInputorParse: return422.UnsupportedFeature: return415.IoorDecoderBackend: retry/fallback/log depending on context.
Inspect storage and branch explicitly:
use heic_decoder::DecodedRgbaPixels;
match &decoded.pixels {
DecodedRgbaPixels::U8(p) => {
// p: &[u8], RGBA8 interleaved
}
DecodedRgbaPixels::U16(p) => {
// p: &[u16], RGBA16 interleaved
}
}Helpers on DecodedRgbaImage / DecodedRgbaPixels:
storage_bit_depth()as_rgba8(),as_rgba16()into_rgba8(),into_rgba16()apply_exif_orientation(exif_orientation)
Default decode behavior:
- Decode applies HEIF primary item transforms from
clap/irot/imir. - Decode does not implicitly apply EXIF orientation.
- This keeps output stable for libheif parity and avoids hidden double-rotation behavior.
Orientation APIs:
exif_orientation_hint(input_bytes)->ExifOrientationHintexif_orientation_hint_from_path(input_path)->Result<ExifOrientationHint, DecodeError>primary_exif_orientation(input_bytes)->Option<u8>primary_exif_orientation_from_path(input_path)->Result<Option<u8>, DecodeError>primary_item_has_orientation_transform(input_bytes)->boolpath_extension_is_heif(path)->boolpath_extension_is_heif_family(path)->boolDecodedRgbaImage::apply_exif_orientation(exif_orientation)->Result<DecodedRgbaImage, DecodeError>
Recommended pattern:
use heic_decoder::{decode_path_to_rgba, exif_orientation_hint};
use std::path::Path;
fn decode_with_explicit_orientation(path: &Path) -> Result<(), heic_decoder::DecodeError> {
let input = std::fs::read(path).map_err(heic_decoder::DecodeError::Io)?;
let hint = exif_orientation_hint(&input);
let decoded = decode_path_to_rgba(path)?;
let decoded = if let Some(orientation) = hint.orientation_to_apply() {
decoded.apply_exif_orientation(orientation)?
} else {
decoded
};
eprintln!("{}x{}", decoded.width, decoded.height);
Ok(())
}Path-only variant (no full input read, no image decode):
use heic_decoder::{exif_orientation_hint_from_path, path_extension_is_heif};
use std::path::Path;
fn orientation_to_apply(path: &Path) -> Result<Option<u8>, heic_decoder::DecodeError> {
if !path_extension_is_heif(path) {
return Ok(None);
}
Ok(exif_orientation_hint_from_path(path)?.orientation_to_apply())
}Requires image-integration feature.
use heic_decoder::DecodeGuardrails;
use heic_decoder::image_integration::register_image_decoder_hooks_with_guardrails;
let registration = register_image_decoder_hooks_with_guardrails(DecodeGuardrails {
max_input_bytes: Some(128 * 1024 * 1024),
max_pixels: Some(64_000_000),
max_temp_spool_bytes: Some(128 * 1024 * 1024),
temp_spool_directory: None,
});
assert!(registration.any_decoder_hook_registered());use image::ImageReader;
use heic_decoder::exif_orientation_hint;
use heic_decoder::image_integration::apply_exif_orientation_dynamic;
let bytes = std::fs::read("input.heic").map_err(image::ImageError::IoError)?;
let hint = exif_orientation_hint(&bytes);
let img = ImageReader::new(std::io::Cursor::new(&bytes))
.with_guessed_format()?
.decode()?;
let img = if let Some(orientation) = hint.orientation_to_apply() {
apply_exif_orientation_dynamic(img, orientation)
} else {
img
};HeifImageDecoder constructors:
from_bytes[_with_guardrails]from_read[_with_guardrails]from_bufread[_with_guardrails]from_seekable[_with_guardrails]from_path[_with_guardrails]
Under image-integration, DecodedRgbaImage provides conversion helpers:
into_image_buffer_with_metadata()into_image_buffer()into_dynamic_image_with_metadata()into_dynamic_image()
Metadata-preserving wrappers:
ImageBufferWithMetadataDynamicImageWithMetadata
Possible conversion failures:
ImageConversionError::SampleCountOverflowImageConversionError::SampleCountMismatch
Additional helper under image-integration:
apply_exif_orientation_dynamic(image, exif_orientation)
For reliable agent behavior:
- Always use
*_with_guardrailsAPIs for untrusted input. - Treat
Read/BufReadas temp-spooled random-access decode, not progressive decode. - Branch on
decoded.storage_bit_depth()and avoid implicit down-conversion. - Match
DecodeError::category()for stable retry/reject decisions. - Register image hooks exactly once during process startup.
- Keep guardrail values centralized in one config module for consistency.
use heic_decoder::{decode_path_to_rgba_with_guardrails, DecodeGuardrails};
use std::path::Path;
fn decode(path: &Path) -> Result<(), heic_decoder::DecodeError> {
let guardrails = DecodeGuardrails {
max_input_bytes: Some(128 * 1024 * 1024),
max_pixels: Some(64_000_000),
max_temp_spool_bytes: Some(128 * 1024 * 1024),
temp_spool_directory: None,
};
let decoded = decode_path_to_rgba_with_guardrails(path, guardrails)?;
eprintln!(
"decoded {}x{}, storage={}bit, source={}bit",
decoded.width,
decoded.height,
decoded.storage_bit_depth(),
decoded.source_bit_depth
);
Ok(())
}