libheif-sys
Low-level, version-gated Rust FFI bindings to the libheif C library for reading and writing HEIF and AVIF images.
Repository Health
Technical Analysis
libheif-sys is a Rust “sys” crate exposing raw, unsafe FFI bindings to libheif, the C/C++ reference library for the HEIF and AVIF image container formats. Rather than wrapping the C API in idiomatic Rust, it ships pre-generated bindings for six libheif API versions (1.17 through 1.23) selected via Cargo feature flags, letting consumers pin to whatever version their target platform ships.
The crate’s real complexity lives in its build script: it discovers a system-installed libheif via pkg-config on Linux, via vcpkg on Windows, or optionally compiles a vendored copy of libheif from source with CMake and links it statically through the embedded-libheif feature. A use-bindgen feature can also regenerate the bindings at build time against a locally installed libheif’s headers instead of relying on the checked-in binding files.
Most Rust applications that need to decode a HEIC photo or manipulate HEIF/AVIF files use this crate as a build-time dependency for full control over the C API, or indirectly through the higher-level libheif-rs wrapper maintained by the same author.
What You Get
- Pre-generated FFI bindings for six libheif API versions (v1.17 through v1.23), selected via Cargo feature flags
- Automatic libheif discovery via pkg-config on Linux (through the
system-depscrate) or vcpkg on Windows, with no manual header/library path configuration - An optional CMake-based embedded build of libheif from vendored sources (a git submodule), for static linking without a system dependency
- Optional bindgen-based regeneration of bindings against a custom-installed libheif via the
use-bindgenfeature, for setups where installed headers diverge from what’s shipped
Common Use Cases
- Decoding HEIC images captured on iPhones in a Rust backend, CLI, or image pipeline
- Building an HEIF/AVIF image converter or thumbnail generator in Rust
- Adding cross-platform (Linux/Windows) native HEIF support to a desktop application
- Statically embedding libheif via the
embedded-libheiffeature to avoid a system dependency in containerized or hermetic builds
Under The Hood
Architecture
The crate’s build.rs orchestrates platform-specific library discovery: on non-Windows targets it calls find_libheif(), which uses the system-deps crate to locate an installed libheif-dev via pkg-config; on Windows MSVC targets it installs libheif through vcpkg instead. When the embedded-libheif feature is enabled, build.rs copies the vendored libheif source tree (a git submodule under vendor/libheif) into OUT_DIR, patches its CMakeLists.txt to skip building example tooling, and compiles it statically via the cmake crate. src/lib.rs then conditionally includes either bindgen-generated bindings from OUT_DIR (when use-bindgen is enabled) or falls back to the checked-in src/bindings/{v1_17..v1_23}.rs modules, selected through a cfg_if chain in src/bindings/mod.rs keyed to whichever version feature is active. This is intentionally a thin, single-layer sys crate: nearly all structural complexity lives in the build script’s platform branching rather than in runtime Rust abstractions, and changing the core surface means regenerating all six per-version binding files.
Tech Stack
Written in Rust (edition 2021) with minimal runtime dependencies — libc and cfg-if. Build-time dependencies include system-deps for pkg-config discovery, and optional bindgen plus prettyplease for on-demand binding codegen, and optional cmake for the embedded libheif build; Windows builds additionally pull in vcpkg and walkdir. Feature flags gate both the target libheif API version (v1_17 through v1_23, or latest) and build behavior (use-bindgen, embedded-libheif). CI runs on GitHub Actions across Ubuntu 24.04 (installing libheif via apt) and Windows (via cargo-vcpkg), exercising both cargo check and cargo test with the latest feature set.
Code Quality
Test coverage is a single integration test that allocates a heif context, checks the linked libheif version string, and frees the context through raw unsafe FFI calls — thin, but appropriate given that almost all logic lives in generated bindings and the external C library rather than Rust code that would benefit from unit testing. There’s no clippy step visible in CI, only cargo check/cargo test on two platforms with build-cache-on-failure; a rustfmt.toml is present for formatting consistency. Naming mirrors the C API directly (the crate explicitly allows non-Rust-idiomatic casing lints), and error handling follows the C convention of returned heif_error structs rather than Rust Result types, since translating that into idiomatic error handling is left to the higher-level libheif-rs wrapper.
API Design
As a sys crate, the public surface is intentionally raw and unsafe — there is no ergonomic Rust API here by design; that trade-off is what enables libheif-rs (and other consumers) to build their own abstractions on top. Where this crate does invest in developer experience is at build time: version-pinning feature flags, three independent library-acquisition paths (pkg-config, vcpkg, or a self-contained embedded build), and an opt-in bindgen escape hatch give integrators real flexibility in how they satisfy the libheif dependency across platforms, at the cost of needing to understand unsafe FFI calling conventions to use the crate itself.