All formats

libheif

LanguageC API (with a header-only C++ wrapper), implementation in C++
LicenseLGPL-3.0 (library), MIT (sample programs)
Repositorygithub.com/strukturag/libheif
DocumentationREADME, wiki and the comments in the libheif/heif_*.h headers
Depends onlibde265 (HEVC decoding), x265 or kvazaar (HEVC encoding); optional libaom/dav1d/rav1e/SVT-AV1 (AVIF), libjpeg, OpenJPEG
Read / writeyes / yes, HEIC, AVIF and other HEIF codecs; grids, alpha, depth maps, thumbnails, Exif/XMP, image sequences

libheif parses and writes the HEIF container: items, properties, grids, thumbnails, auxiliary images, metadata and sequences. It hands the coded data to codec plugins: libde265 decodes HEVC, x265 or kvazaar encode it, and AV1, VVC, JPEG and JPEG 2000 codecs are supported too. Most open-source software that opens HEIC uses it: GIMP, Krita, ImageMagick, libvips, darktable, digiKam, and the bindings for Python, Go, Rust and .NET.

libheif itself is LGPL-3.0. Codec plugins have their own licenses: libde265 is LGPL-3.0, x265 is GPL-2.0, kvazaar is BSD-3-Clause. HEVC patents are a separate matter from these copyright licenses; see HEIC patents.

It ships command-line tools: heif-dec (decode to JPEG/PNG/TIFF), heif-enc (encode) and heif-info (print the item structure of a file).

Installation#

Shell
# Debian / Ubuntu (libheif-plugin-* packages add codecs, e.g. libheif-plugin-x265 for encoding)
sudo apt install libheif-dev libheif-plugin-libde265 libheif-plugin-x265 libheif-examples
# Fedora
sudo dnf install libheif-devel libheif-tools
# macOS
brew install libheif
# Windows (vcpkg)
vcpkg install libheif

With CMake:

cmake
find_package(libheif REQUIRED)
target_link_libraries(myapp PRIVATE heif)

Basic usage: decode a HEIC file to RGB#

This program decodes the primary image and writes it as a binary PPM file:

C
#include <libheif/heif.h>
#include <stdio.h>

int main(void) {
    heif_init(NULL);
    struct heif_context *ctx = heif_context_alloc();
    struct heif_error err = heif_context_read_from_file(ctx, "in.heic", NULL);
    if (err.code != heif_error_Ok) {
        fprintf(stderr, "read error: %s\n", err.message);
        return 1;
    }

    struct heif_image_handle *handle;
    heif_context_get_primary_image_handle(ctx, &handle);
    printf("%d x %d, alpha: %d\n", heif_image_handle_get_width(handle),
           heif_image_handle_get_height(handle), heif_image_handle_has_alpha_channel(handle));

    struct heif_image *img;
    err = heif_decode_image(handle, &img, heif_colorspace_RGB, heif_chroma_interleaved_RGB, NULL);
    if (err.code != heif_error_Ok) {
        fprintf(stderr, "decode error: %s\n", err.message);  // e.g. no HEVC decoder plugin
        return 1;
    }

    // grid tiles, cropping and rotation (irot/imir) are already applied
    int w = heif_image_get_width(img, heif_channel_interleaved);
    int h = heif_image_get_height(img, heif_channel_interleaved);
    size_t stride;
    const uint8_t *data = heif_image_get_plane_readonly2(img, heif_channel_interleaved, &stride);

    FILE *f = fopen("out.ppm", "wb");
    fprintf(f, "P6\n%d %d\n255\n", w, h);
    for (int y = 0; y < h; y++)
        fwrite(data + y * stride, 1, (size_t)w * 3, f);  // rows may be padded: use stride
    fclose(f);

    heif_image_release(img);
    heif_image_handle_release(handle);
    heif_context_free(ctx);
    heif_deinit();
    return 0;
}

heif_image_get_plane_readonly2 (size_t stride) is in libheif 1.20 and later. With older versions use heif_image_get_plane_readonly, which returns an int stride.

How to encode RGB pixels to HEIC#

C
#include <libheif/heif.h>
#include <string.h>

int save_heic(const char *path, const uint8_t *rgb, int w, int h, int quality) {
    struct heif_image *img;
    heif_image_create(w, h, heif_colorspace_RGB, heif_chroma_interleaved_RGB, &img);
    heif_image_add_plane(img, heif_channel_interleaved, w, h, 8);
    size_t stride;
    uint8_t *dst = heif_image_get_plane2(img, heif_channel_interleaved, &stride);
    for (int y = 0; y < h; y++)
        memcpy(dst + y * stride, rgb + (size_t)y * w * 3, (size_t)w * 3);

    struct heif_context *ctx = heif_context_alloc();
    struct heif_encoder *encoder;
    struct heif_error err = heif_context_get_encoder_for_format(ctx, heif_compression_HEVC, &encoder);
    if (err.code != heif_error_Ok) return 1;  // no HEVC encoder plugin (x265/kvazaar) installed
    heif_encoder_set_lossy_quality(encoder, quality);  // 0-100; 50 is about JPEG quality 90
    err = heif_context_encode_image(ctx, img, encoder, NULL, NULL);
    heif_encoder_release(encoder);
    if (err.code == heif_error_Ok)
        err = heif_context_write_to_file(ctx, path);

    heif_image_release(img);
    heif_context_free(ctx);
    return err.code == heif_error_Ok ? 0 : 1;
}

libheif converts RGB to YCbCr 4:2:0 for HEVC. Use heif_compression_AV1 instead to write AVIF with the same code.

How to check if a file is HEIC#

C
// data: the first bytes of the file (12 or more)
enum heif_filetype_result r = heif_check_filetype(data, len);
if (r == heif_filetype_yes_supported) {
    // a HEIF file libheif can read
}

How to read Exif metadata#

C
heif_item_id exif_id;
int n = heif_image_handle_get_list_of_metadata_block_IDs(handle, "Exif", &exif_id, 1);
if (n == 1) {
    size_t size = heif_image_handle_get_metadata_size(handle, exif_id);
    uint8_t *exif = malloc(size);
    heif_image_handle_get_metadata(handle, exif_id, exif);
    // the first 4 bytes are the offset to the TIFF header ("MM" or "II")
    free(exif);
}

XMP is stored as a mime item: list IDs with type "mime" and check heif_image_handle_get_metadata_content_type for application/rdf+xml.

How to get the thumbnail and the depth map#

C
heif_item_id thumb_id;
if (heif_image_handle_get_list_of_thumbnail_IDs(handle, &thumb_id, 1) == 1) {
    struct heif_image_handle *thumb;
    heif_image_handle_get_thumbnail(handle, thumb_id, &thumb);
    // decode thumb with heif_decode_image like the main image
    heif_image_handle_release(thumb);
}

if (heif_image_handle_has_depth_image(handle)) {
    heif_item_id depth_id;
    heif_image_handle_get_list_of_depth_image_IDs(handle, &depth_id, 1);
    struct heif_image_handle *depth;
    heif_image_handle_get_depth_image_handle(handle, depth_id, &depth);
    // decode with heif_colorspace_monochrome, heif_chroma_monochrome
    heif_image_handle_release(depth);
}

How to list all images in a file#

C
int count = heif_context_get_number_of_top_level_images(ctx);
heif_item_id ids[64];
count = heif_context_get_list_of_top_level_image_IDs(ctx, ids, count < 64 ? count : 64);
for (int i = 0; i < count; i++) {
    struct heif_image_handle *h;
    heif_context_get_image_handle(ctx, ids[i], &h);
    printf("item %u: %d x %d%s\n", ids[i], heif_image_handle_get_width(h),
           heif_image_handle_get_height(h), heif_image_handle_is_primary_image(h) ? " (primary)" : "");
    heif_image_handle_release(h);
}

Grid tiles and thumbnails are not top-level images, so a normal phone photo has one.

Tips#