gen2brain/heic
| Language | Go |
|---|---|
| License | MIT (the embedded WebAssembly decoder is built from the Rust heic crate: AGPL-3.0 or commercial) |
| Repository | github.com/gen2brain/heic |
| Documentation | pkg.go.dev/github.com/gen2brain/heic |
| Depends on | wazero (pure Go WebAssembly runtime); optionally a system libheif |
| Read / write | yes / 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#
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#
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#
import (
"image"
_ "github.com/gen2brain/heic"
)
img, format, err := image.Decode(f) // format == "heic"
How to get width and height without decoding#
cfg, err := heic.DecodeConfig(f)
if err == nil {
fmt.Println(cfg.Width, cfg.Height)
}
How to read Exif metadata#
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#
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#
- Call
heic.Dynamic()to check whether the system libheif was loaded (it returns the load error, or nil). heic.Decodereturns the first frame of a sequence.