libheif
| Language | C (usable from C++) |
|---|---|
| License | LGPL-3.0 (library), MIT (sample programs) |
| Repository | github.com/strukturag/libheif |
| Documentation | README, comments in the API headers |
| Depends on | AV1 codecs for AVIF: dav1d or libaom (decode), libaom, SVT-AV1 or rav1e (encode); libde265 / x265 for HEIC |
| Read / write | yes / yes, 8/10/12-bit, alpha, grids, Exif/XMP; image sequences in recent versions |
libheif reads and writes HEIF files with any supported codec: AVIF (AV1), HEIC (HEVC), JPEG and JPEG 2000 in HEIF, and uncompressed HEIF. It’s used by ImageMagick, GIMP, Krita, libvips and darktable. Pick it over libavif when you need HEIC too; for AVIF-only code libavif has more AVIF-specific options.
A file is a heif_context holding image items. Get a heif_image_handle for an item (usually the primary image), decode it into a heif_image with the colorspace you want, and read planes from it. Functions return a heif_error struct; code == heif_error_Ok means success.
The license is LGPL-3.0: dynamic linking is the simple way to use it in closed-source programs.
Installation#
# Debian / Ubuntu
sudo apt install libheif-dev libheif-plugin-dav1d libheif-plugin-aomenc
# Fedora
sudo dnf install libheif-devel
# macOS
brew install libheif
# Windows (vcpkg)
vcpkg install libheif
With CMake:
find_package(libheif REQUIRED)
target_link_libraries(myapp PRIVATE heif)
Debian and Ubuntu package codecs as separate plugins; without a dav1d or aomdec plugin libheif can’t decode AVIF. heif-enc --list-encoders shows what’s installed.
Basic usage: decode AVIF to RGBA#
#include <libheif/heif.h>
#include <stdio.h>
int main(void) {
heif_init(NULL);
heif_context *ctx = heif_context_alloc();
heif_image_handle *handle = NULL;
heif_image *img = NULL;
int rc = 1;
heif_error err = heif_context_read_from_file(ctx, "in.avif", NULL);
if (err.code != heif_error_Ok) { fprintf(stderr, "%s\n", err.message); goto done; }
err = heif_context_get_primary_image_handle(ctx, &handle);
if (err.code != heif_error_Ok) { fprintf(stderr, "%s\n", err.message); goto done; }
err = heif_decode_image(handle, &img, heif_colorspace_RGB, heif_chroma_interleaved_RGBA, NULL);
if (err.code != heif_error_Ok) { fprintf(stderr, "%s\n", err.message); goto done; }
int width = heif_image_get_primary_width(img);
int height = heif_image_get_primary_height(img);
size_t stride;
const uint8_t *data = heif_image_get_plane_readonly2(img, heif_channel_interleaved, &stride);
// data: height rows of stride bytes, 4 bytes (RGBA) per pixel
printf("%d x %d, first pixel %u %u %u %u\n", width, height, data[0], data[1], data[2], data[3]);
rc = 0;
done:
if (img) heif_image_release(img);
if (handle) heif_image_handle_release(handle);
heif_context_free(ctx);
heif_deinit();
return rc;
}
heif_image_get_plane_readonly2 (with a size_t stride) was added in libheif 1.20; older versions have heif_image_get_plane_readonly with an int stride.
How to encode an RGBA buffer to AVIF#
Ask for an encoder for heif_compression_AV1. libheif picks the highest-priority AV1 encoder that is installed (libaom, SVT-AV1 or rav1e):
#include <libheif/heif.h>
#include <string.h>
int save_avif(const char *path, const uint8_t *pixels, int width, int height) {
heif_context *ctx = heif_context_alloc();
heif_image *img = NULL;
heif_encoder *encoder = NULL;
int ok = 0;
if (heif_image_create(width, height, heif_colorspace_RGB, heif_chroma_interleaved_RGBA, &img).code)
goto done;
if (heif_image_add_plane(img, heif_channel_interleaved, width, height, 8).code)
goto done;
size_t stride;
uint8_t *dst = heif_image_get_plane2(img, heif_channel_interleaved, &stride);
for (int y = 0; y < height; y++)
memcpy(dst + y * stride, pixels + (size_t)y * width * 4, (size_t)width * 4);
if (heif_context_get_encoder_for_format(ctx, heif_compression_AV1, &encoder).code)
goto done; // no AV1 encoder installed
heif_encoder_set_lossy_quality(encoder, 60); // 0..100
if (heif_context_encode_image(ctx, img, encoder, NULL, NULL).code == heif_error_Ok &&
heif_context_write_to_file(ctx, path).code == heif_error_Ok)
ok = 1;
done:
if (encoder) heif_encoder_release(encoder);
if (img) heif_image_release(img);
heif_context_free(ctx);
return ok;
}
The brands in the written file (avif, mif1, miaf) follow from the AV1 compression.
How to get the size and check for alpha without decoding#
heif_context *ctx = heif_context_alloc();
heif_image_handle *handle;
if (heif_context_read_from_file(ctx, "in.avif", NULL).code == heif_error_Ok &&
heif_context_get_primary_image_handle(ctx, &handle).code == heif_error_Ok) {
printf("%d x %d, %d-bit, alpha: %s\n",
heif_image_handle_get_width(handle), heif_image_handle_get_height(handle),
heif_image_handle_get_luma_bits_per_pixel(handle),
heif_image_handle_has_alpha_channel(handle) ? "yes" : "no");
heif_image_handle_release(handle);
}
heif_context_free(ctx);
How to list all images in a file#
A HEIF file can hold several top-level images (for example a burst or alternatives):
int n = heif_context_get_number_of_top_level_images(ctx);
heif_item_id ids[64];
n = heif_context_get_list_of_top_level_image_IDs(ctx, ids, n < 64 ? n : 64);
for (int i = 0; i < n; i++) {
heif_image_handle *h;
if (heif_context_get_image_handle(ctx, ids[i], &h).code == heif_error_Ok) {
printf("image %u: %d x %d\n", ids[i], heif_image_handle_get_width(h),
heif_image_handle_get_height(h));
heif_image_handle_release(h);
}
}
How to encode lossless AVIF#
heif_encoder_set_lossless(encoder, 1);
Lossless AVIF also needs the RGB data coded without YUV conversion (matrix coefficients 0, 4:4:4). The heif-enc -L tool does this for you; in code, set a heif_color_profile_nclx with matrix_coefficients = heif_matrix_coefficients_RGB_GBR on the image before encoding.
Tips#
- The command line tools
heif-enc,heif-decandheif-infocome with libheif:heif-enc -A -q 60 in.png -o out.avifencodes AVIF (-Aselects AV1). - Call
heif_init(NULL)once at startup to load codec plugins, andheif_deinit()at exit. - Limit resources for untrusted input with
heif_context_set_security_limits.