All formats

heicdec

LanguageC (usable from C++)
LicenseAGPL-3.0 or commercial (the terms of imazen/heic, which it's ported from)
Repositorygithub.com/kjk/heicdec
Documentationsrc/heic.h
Depends ondav1d (AV1 decoder)
Read / writeyes / no (decoder only)

heicdec decodes HEIF-based still images: HEIC with its own HEVC decoder, and AVIF by passing the AV1 data to dav1d. It parses the container itself: grid images, alpha, rotation and mirroring, crop, EXIF, XMP and ICC profiles, and AVIF image sequences. 10 and 12-bit images are decoded to 8 bits. SumatraPDF uses it to open AVIF and HEIC files.

Pick it when you need one small decoder for both AVIF and HEIC from phones. For AVIF only, libavif is the reference library, BSD-licensed and also encodes. heicdec’s AGPL-3.0 license requires you to publish the source of a program that uses it, including network services.

Installation#

Copy dist/heic.h and dist/heic.c from the repository into your project. AV1 decoding is compiled in only when HEIC_HAVE_DAV1D is defined, and then you link dav1d, which the amalgamation doesn’t include. dav1d builds with Meson and is packaged on most systems:

Shell
git clone https://github.com/kjk/heicdec
cp heicdec/dist/heic.h heicdec/dist/heic.c myproject/
# dav1d
sudo apt install libdav1d-dev   # Debian / Ubuntu
brew install dav1d              # macOS
vcpkg install dav1d             # Windows
# clang and gcc on x86-64 also need -msse4.1; MSVC needs no flag
clang -O2 -msse4.1 -DHEIC_HAVE_DAV1D main.c heic.c -ldav1d -o myapp

Basic usage: decode an AVIF image#

C
#include "heic.h"
#include <stdio.h>
#include <stdlib.h>

static uint8_t *read_file(const char *path, size_t *len) {
    FILE *f = fopen(path, "rb");
    if (!f) return NULL;
    fseek(f, 0, SEEK_END);
    *len = (size_t)ftell(f);
    fseek(f, 0, SEEK_SET);
    uint8_t *d = malloc(*len);
    if (d && fread(d, 1, *len, f) != *len) { free(d); d = NULL; }
    fclose(f);
    return d;
}

int main(void) {
    size_t len;
    uint8_t *data = read_file("image.avif", &len);
    if (!data) return 1;

    heic_init();
    heic_ctx *ctx = heic_ctx_new(NULL, NULL, NULL, NULL);
    heic_doc *doc = heic_doc_open(ctx, data, len);  /* data must outlive doc */
    if (!doc) return 1;

    if (heic_doc_kind(doc) == HEIC_KIND_AVIF) puts("AVIF still image");

    heic_image_info info;
    heic_doc_info(doc, &info);
    printf("%u x %u, %d-bit, alpha: %s\n", info.width, info.height,
           info.bit_depth, info.has_alpha ? "yes" : "no");

    heic_image *img = heic_doc_decode(doc, HEIC_FORMAT_RGBA);  /* A=255 without alpha */
    if (img) {
        /* img->data: top-down rows of img->stride bytes */
        heic_image_destroy(ctx, img);
    }

    heic_doc_close(doc);
    heic_ctx_free(ctx);
    free(data);
    return 0;
}

How to get AVIF dimensions without decoding#

C
heic_doc *doc = heic_doc_open(ctx, data, len);
heic_image_info info;
if (doc && heic_doc_info(doc, &info) == 0)
    printf("%u x %u\n", info.width, info.height);

How to read HDR color information#

AVIF images carry CICP codes for color primaries, transfer function and matrix. heicdec reports them but doesn’t tone-map; a PQ or HLG image (transfer 16 or 18) needs tone mapping before display on an SDR screen:

C
heic_image_info info;
heic_doc_info(doc, &info);
printf("primaries %u, transfer %u, matrix %u, full range %d\n",
       info.color_primaries, info.transfer_characteristics,
       info.matrix_coefficients, info.full_range);
if (info.transfer_characteristics == 16 || info.transfer_characteristics == 18)
    puts("HDR image: tone-map before display");

How to decode an animated AVIF#

C
heic_sequence_info si;
if (heic_doc_sequence_info(doc, &si) == 0) {
    heic_sequence_decoder *dec = heic_sequence_decoder_new(doc, HEIC_FORMAT_RGBA);
    for (uint32_t i = 0; i < si.frame_count; i++) {
        heic_sequence_frame_info fi;
        heic_doc_sequence_frame_info(doc, i, &fi);
        heic_image *frame = heic_sequence_decoder_decode_frame(dec, i);
        if (!frame) break;
        printf("frame %u: %.0f ms\n", i, 1000.0 * fi.duration / si.timescale);
        heic_image_destroy(ctx, frame);
    }
    heic_sequence_decoder_destroy(dec);
}

The sequence decoder keeps codec state between frames, so decoding frames in order doesn’t restart from a key frame each time.

Tips#