All formats

gen2brain/heic

LanguageGo
LicenseMIT (the embedded WebAssembly decoder is built from the Rust heic crate: AGPL-3.0 or commercial)
Repositorygithub.com/gen2brain/heic
Documentationpkg.go.dev/github.com/gen2brain/heic
Depends onwazero (pure Go WebAssembly runtime); optionally a system libheif
Read / writeyes / no

gen2brain/heic decodes HEIC (HEVC in HEIF) without cgo. At startup it tries to load a system libheif shared library through purego; if there is none, it falls back to an embedded decoder compiled to WebAssembly and run with wazero. Programs build and cross-compile like pure Go code.

It follows the standard image package conventions and registers the heic format, so image.Decode reads .heic files after a blank import. It can’t encode.

License note: the package code is MIT, but the embedded WebAssembly decoder is compiled from the heic Rust crate, which is licensed AGPL-3.0 or under a commercial license. If you distribute a program or run a network service with the WebAssembly fallback, check whether the AGPL applies to you. Building with the nodynamic tag forces the WebAssembly path; a system libheif (LGPL) avoids it.

Installation#

Shell
go get github.com/gen2brain/heic

Build tags: nodynamic uses only the embedded WebAssembly, wasm2go transpiles the WebAssembly to Go instead of using wazero.

Basic usage: convert HEIC to JPEG#

Go
package main

import (
	"image/jpeg"
	"log"
	"os"

	"github.com/gen2brain/heic"
)

func main() {
	in, err := os.Open("IMG_0001.heic")
	if err != nil {
		log.Fatal(err)
	}
	defer in.Close()

	img, err := heic.Decode(in)
	if err != nil {
		log.Fatal(err)
	}

	out, err := os.Create("IMG_0001.jpg")
	if err != nil {
		log.Fatal(err)
	}
	defer out.Close()
	if err := jpeg.Encode(out, img, &jpeg.Options{Quality: 90}); err != nil {
		log.Fatal(err)
	}
}

How to read HEIC with image.Decode#

Go
import (
	"image"
	_ "github.com/gen2brain/heic"
)

img, format, err := image.Decode(f) // format == "heic"

How to get width and height without decoding#

Go
cfg, err := heic.DecodeConfig(f)
if err == nil {
	fmt.Println(cfg.Width, cfg.Height)
}

How to read Exif metadata#

Go
exif, err := heic.DecodeExif(f)
if errors.Is(err, heic.ErrNoExif) {
	fmt.Println("no Exif")
} else if err == nil {
	fmt.Println(exif.Make, exif.Model, exif.DateTimeOriginal)
	fmt.Println(exif.GPSLatitude, exif.GPSLongitude)
}

How to read an image sequence#

Go
seq, err := heic.DecodeAll(f)
if err != nil {
	log.Fatal(err)
}
for i, frame := range seq.Image {
	fmt.Println(i, frame.Bounds(), seq.Delay[i], "s")
}

Tips#