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 onnothing for HEIC; dav1d for AVIF
Read / writeyes / no (decoder only)

heicdec decodes HEIC and HEIF still images, and AVIF when linked with dav1d. Unlike libheif, which needs the separate libde265 decoder for the HEVC data, it contains its own HEVC decoder: a C port of the Rust imazen/heic. Output is checked against libheif. SumatraPDF uses it to open HEIC and AVIF files.

It handles what phones produce: grid images made of tiles, alpha, rotation and mirroring, crop, overlays, thumbnails, EXIF, XMP and ICC profiles, 10 and 12-bit images (output is 8-bit), image sequences, and the Apple HDR gain map (as a separate image; no tone mapping). It doesn’t encode, and doesn’t decode multi-layer HEVC or JPEG-in-HEIF.

The AGPL-3.0 license requires you to publish the source of a program that uses it, including network services. HEVC is also covered by patents; the license grants no patent rights.

Installation#

Copy dist/heic.h and dist/heic.c from the repository into your project and compile heic.c with your code. That’s enough for HEIC. To decode AVIF too, define HEIC_HAVE_DAV1D and link dav1d.

Shell
git clone https://github.com/kjk/heicdec
cp heicdec/dist/heic.h heicdec/dist/heic.c myproject/
# clang and gcc on x86-64 need -msse4.1 for the SIMD code; MSVC needs no flag
clang -O2 -msse4.1 main.c heic.c -o myapp

Basic usage: decode a HEIC photo#

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("IMG_0001.HEIC", &len);
    if (!data) return 1;

    heic_init();  /* once per process */
    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;

    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_RGB);
    if (img) {
        /* img->data: top-down rows of img->stride bytes, R,G,B */
        heic_image_destroy(ctx, img);
    }

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

The decoded image is already rotated, mirrored and cropped as the file says, and grid tiles are joined into one image.

How to get HEIC dimensions without decoding#

heic_doc_open + heic_doc_info read only the container boxes:

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 EXIF metadata#

heic_doc_exif returns the EXIF block as a TIFF structure (the 4-byte HEIF offset prefix is already removed), ready for an EXIF parser such as libexif:

C
uint8_t *exif;
size_t exif_len;
if (heic_doc_exif(doc, &exif, &exif_len)) {
    printf("EXIF: %zu bytes, byte order %c%c\n", exif_len, exif[0], exif[1]);
    heic_free(ctx, exif);  /* free with heic_free, not free() */
}

heic_doc_xmp and heic_doc_icc return the XMP packet and the ICC profile the same way.

How to convert HEIC to JPEG#

heicdec only decodes. Pair it with an encoder such as stb_image_write or libjpeg-turbo:

C
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

heic_image *img = heic_doc_decode(doc, HEIC_FORMAT_RGB);
if (img) {
    stbi_write_jpg("photo.jpg", (int)img->width, (int)img->height, 3, img->data, 90);
    heic_image_destroy(ctx, img);
}

stbi_write_jpg expects tightly packed rows; if img->stride isn’t width * 3, copy the rows first. stb_image_write doesn’t copy EXIF, so the JPEG loses the camera metadata.

How to limit memory on untrusted files#

C
heic_limits lim = {0};
lim.max_width = 8192;
lim.max_height = 8192;
lim.max_memory_bytes = 256u << 20;  /* 256 MB */
heic_ctx_set_limits(ctx, &lim);     /* before heic_doc_open */

Tips#