Documentation
¶
Overview ¶
Package goavif provides AVIF image decoding via CGO bindings to libavif.
All CGO and unsafe code is isolated in this file.
Package goavif provides AVIF image decoding via CGO bindings to libavif.
This package implements the standard Go image decoding interface, making it a drop-in replacement for image/jpeg, image/png, and similar packages. The API design mirrors image/gif for consistency with animated image handling.
Basic Usage ¶
For single images or the first frame of an animation:
img, err := goavif.Decode(reader)
if err != nil {
log.Fatal(err)
}
// img is *image.NRGBA (8-bit) or *image.NRGBA64 (10/12-bit)
For metadata without decoding pixels (faster):
cfg, err := goavif.DecodeConfig(reader)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Size: %dx%d, Depth: %d-bit\n", cfg.Width, cfg.Height, cfg.BitDepth)
For animated AVIF files:
avif, err := goavif.DecodeAll(reader)
if err != nil {
log.Fatal(err)
}
for i, frame := range avif.Image {
fmt.Printf("Frame %d: delay %dms\n", i, avif.Delay[i])
}
Auto-Detection ¶
Import the package with a blank identifier to enable automatic AVIF detection:
import _ "github.com/julez-dev/goavif"
// Now image.Decode will auto-detect AVIF files
img, format, err := image.Decode(reader)
if format == "avif" {
// Handle AVIF-specific features
}
Image Types ¶
The package returns different Go image types based on bit depth:
- 8-bit AVIF: *image.NRGBA (non-premultiplied alpha)
- 10/12-bit AVIF: *image.NRGBA64 (scaled to 16-bit range)
For HDR images (10/12-bit), values are scaled:
- 10-bit: value * 65535 / 1023
- 12-bit: value * 65535 / 4095
Metadata ¶
The Config struct provides rich metadata access:
- BitDepth: Color depth per channel (8, 10, or 12)
- HasAlpha: Whether alpha channel is present
- ImageCount: Number of frames (1 for still images)
- ICC: Raw ICC color profile bytes (nil if absent)
- XMP: Raw XMP metadata bytes (nil if absent)
- EXIF: Parsed EXIF data including camera info and GPS
- ColorPrimaries, TransferCharacteristics, MatrixCoefficients: CICP values
Error Handling ¶
Errors are wrapped with context and support errors.Is() for type checking:
img, err := goavif.Decode(reader)
if errors.Is(err, goavif.ErrInvalidData) {
// Not a valid AVIF file
}
if errors.Is(err, goavif.ErrTruncatedData) {
// Incomplete file
}
For detailed error information:
var decErr *goavif.DecodeError
if errors.As(err, &decErr) {
fmt.Printf("Operation: %s, Code: %d, Message: %s\n",
decErr.Op, decErr.Code, decErr.Message)
}
Build Requirements ¶
This package requires libavif to be installed on the system:
# Arch Linux pacman -S libavif # Ubuntu/Debian apt install libavif-dev # macOS brew install libavif # Verify installation pkg-config --modversion libavif
The package uses pkg-config to locate libavif, so ensure pkg-config is available and the libavif.pc file is in the PKG_CONFIG_PATH.
Thread Safety ¶
Each decode operation creates an independent decoder instance, so concurrent calls to Decode, DecodeAll, and DecodeConfig are safe. The underlying libavif library handles multi-threaded decoding automatically based on runtime.NumCPU().
Limitations ¶
This package is decode-only; encoding is not supported. The entire input is read into memory before decoding (no streaming). Progressive/layered decoding is not supported.
Example (AutoDetection) ¶
package main
import (
"fmt"
"image"
"log"
"os"
)
func main() {
// With the blank import, image.Decode auto-detects AVIF
// import _ "github.com/julez-dev/goavif"
f, err := os.Open("testdata/simple.avif")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// Standard library image.Decode works with AVIF
img, format, err := image.Decode(f)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Format: %s\n", format)
fmt.Printf("Bounds: %v\n", img.Bounds())
}
Output: Format: avif Bounds: (0,0)-(4,4)
Example (Decode) ¶
package main
import (
"fmt"
"image"
"log"
"os"
"github.com/julez-dev/goavif"
)
func main() {
// Open an AVIF file
f, err := os.Open("testdata/simple.avif")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// Decode the image
img, err := goavif.Decode(f)
if err != nil {
log.Fatal(err)
}
// The image is ready to use
bounds := img.Bounds()
fmt.Printf("Decoded %dx%d image\n", bounds.Dx(), bounds.Dy())
// Check the concrete type for bit depth
switch img.(type) {
case *image.NRGBA:
fmt.Println("8-bit image")
case *image.NRGBA64:
fmt.Println("HDR image (10/12-bit)")
}
}
Output: Decoded 4x4 image 8-bit image
Example (DecodeAll) ¶
package main
import (
"fmt"
"log"
"os"
"github.com/julez-dev/goavif"
)
func main() {
// Open an animated AVIF file
f, err := os.Open("testdata/animated.avif")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// Decode all frames
avif, err := goavif.DecodeAll(f)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total frames: %d\n", len(avif.Image))
fmt.Printf("Loop count: %d (0=infinite)\n", avif.LoopCount)
// Process each frame
for i := range avif.Image {
fmt.Printf("Frame %d: delay %dms\n", i, avif.Delay[i])
}
}
Output: Total frames: 3 Loop count: 0 (0=infinite) Frame 0: delay 100ms Frame 1: delay 100ms Frame 2: delay 100ms
Example (DecodeConfig) ¶
package main
import (
"fmt"
"log"
"os"
"github.com/julez-dev/goavif"
)
func main() {
// Open an AVIF file
f, err := os.Open("testdata/animated.avif")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// Get metadata without decoding pixels (faster)
cfg, err := goavif.DecodeConfig(f)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Size: %dx%d\n", cfg.Width, cfg.Height)
fmt.Printf("Bit depth: %d\n", cfg.BitDepth)
fmt.Printf("Has alpha: %v\n", cfg.HasAlpha)
fmt.Printf("Frame count: %d\n", cfg.ImageCount)
}
Output: Size: 4x4 Bit depth: 8 Has alpha: false Frame count: 3
Example (ErrorHandling) ¶
package main
import (
"bytes"
"errors"
"fmt"
"github.com/julez-dev/goavif"
)
func main() {
// Try to decode invalid data
invalidData := []byte("not a valid AVIF file")
_, err := goavif.Decode(bytes.NewReader(invalidData))
// Check for specific error types using errors.Is
if errors.Is(err, goavif.ErrInvalidData) {
fmt.Println("Error: invalid AVIF data")
}
// Get detailed error info using errors.As
var decErr *goavif.DecodeError
if errors.As(err, &decErr) {
fmt.Printf("Operation: %s\n", decErr.Op)
fmt.Printf("Code: %d\n", decErr.Code)
}
}
Output: Error: invalid AVIF data Operation: parse Code: 9
Example (Metadata) ¶
package main
import (
"fmt"
"log"
"os"
"github.com/julez-dev/goavif"
)
func main() {
f, err := os.Open("testdata/with_exif.avif")
if err != nil {
log.Fatal(err)
}
defer f.Close()
cfg, err := goavif.DecodeConfig(f)
if err != nil {
log.Fatal(err)
}
// Check for EXIF data
if cfg.EXIF != nil {
fmt.Printf("Camera: %s %s\n", cfg.EXIF.Make, cfg.EXIF.Model)
fmt.Printf("Orientation: %d\n", cfg.EXIF.Orientation)
if cfg.EXIF.ISOSpeed > 0 {
fmt.Printf("ISO: %d\n", cfg.EXIF.ISOSpeed)
}
}
// Check for ICC profile
if cfg.ICC != nil {
fmt.Printf("ICC profile: %d bytes\n", len(cfg.ICC))
} else {
fmt.Println("No ICC profile")
}
}
Output: Camera: TestCam Model X100 Orientation: 6 ISO: 400 No ICC profile
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrInvalidData = errors.New("avif: invalid data") ErrTruncatedData = errors.New("avif: truncated data") ErrNoContent = errors.New("avif: no content") ErrNoCodec = errors.New("avif: no codec available") ErrUnsupported = errors.New("avif: unsupported format") ErrOutOfMemory = errors.New("avif: out of memory") ErrIO = errors.New("avif: I/O error") ErrInternalError = errors.New("avif: internal error") )
Sentinel errors for common decode failures.
Functions ¶
func Decode ¶
Decode reads an AVIF image from r and returns it as an image.Image.
For 8-bit images, the returned image is *image.NRGBA. For 10/12-bit images, the returned image is *image.NRGBA64 with values scaled to the 16-bit range.
For animated AVIFs, only the first frame is decoded. Use DecodeAll to decode all frames.
Types ¶
type AVIF ¶
type AVIF struct {
// Image contains all frames of the animation.
// For still images, this contains a single frame.
Image []image.Image
// Delay contains the delay for each frame in milliseconds.
// Delay[i] is the delay after displaying Image[i].
// For still images, this may be empty or contain a single zero value.
Delay []int
// LoopCount controls the number of times the animation loops.
// 0: loop forever
// -1: play once (no loop)
// n: loop n+1 times (play n+1 times total)
LoopCount int
// Config contains image metadata from the first frame.
Config Config
}
AVIF represents an animated AVIF image. It mirrors the structure of image/gif.GIF for familiarity.
type Config ¶
type Config struct {
image.Config
// BitDepth is the bit depth per channel (8, 10, or 12).
BitDepth int
// HasAlpha indicates whether the image has an alpha channel.
HasAlpha bool
// ImageCount is the number of images/frames in the AVIF.
// For still images this is 1; for animations it's the frame count.
ImageCount int
// Duration is the total duration of an animated AVIF.
// For still images this is 0.
Duration time.Duration
// ICC contains the raw ICC color profile bytes.
// Nil if no ICC profile is embedded.
ICC []byte
// XMP contains the raw XMP metadata bytes.
// Nil if no XMP metadata is embedded.
XMP []byte
// ColorPrimaries indicates the color primaries (e.g., BT.709, BT.2020).
// Values match CICP (Coding-Independent Code Points).
ColorPrimaries uint16
// TransferCharacteristics indicates the transfer function (e.g., sRGB, PQ, HLG).
// Values match CICP.
TransferCharacteristics uint16
// MatrixCoefficients indicates the matrix coefficients for YUV conversion.
// Values match CICP.
MatrixCoefficients uint16
// EXIF contains parsed EXIF metadata.
// Nil if no EXIF data is embedded or parsing failed.
EXIF *EXIF
}
Config contains AVIF image metadata. It embeds image.Config for basic dimensions and color model.
type DecodeError ¶
type DecodeError struct {
Code ResultCode // libavif result code
Message string // human-readable message from libavif
Op string // operation that failed (e.g., "parse", "decode")
}
DecodeError wraps libavif errors with context.
func (*DecodeError) Error ¶
func (e *DecodeError) Error() string
func (*DecodeError) Is ¶
func (e *DecodeError) Is(target error) bool
Is implements errors.Is support for sentinel error matching.
func (*DecodeError) Unwrap ¶
func (e *DecodeError) Unwrap() error
Unwrap returns the underlying sentinel error if applicable.
type EXIF ¶
type EXIF struct {
// Make is the camera manufacturer (EXIF tag 0x010F).
Make string
// Model is the camera model (EXIF tag 0x0110).
Model string
// Orientation indicates how the image should be rotated/flipped
// for correct display (EXIF tag 0x0112).
// Values: 1=normal, 2=flip-h, 3=rotate-180, 4=flip-v,
// 5=transpose, 6=rotate-90-cw, 7=transverse, 8=rotate-270-cw.
Orientation int
// DateTime is when the image was created (EXIF tag 0x0132 or 0x9003).
// Zero value if not present or unparseable.
DateTime time.Time
// ExposureTime is the exposure duration in seconds (EXIF tag 0x829A).
// Typically a fraction like 1/125. Nil if not present.
ExposureTime *big.Rat
// FNumber is the f-stop value (EXIF tag 0x829D).
// For f/2.8, this would be 2.8. Nil if not present.
FNumber *big.Rat
// FocalLength is the lens focal length in millimeters (EXIF tag 0x920A).
// Nil if not present.
FocalLength *big.Rat
// ISOSpeed is the ISO sensitivity (EXIF tag 0x8827).
// 0 if not present.
ISOSpeed int
// GPSLatitude is the latitude in decimal degrees (positive=N, negative=S).
// Derived from EXIF GPS tags 0x0001-0x0002.
// 0 if not present (use GPSValid to check).
GPSLatitude float64
// GPSLongitude is the longitude in decimal degrees (positive=E, negative=W).
// Derived from EXIF GPS tags 0x0003-0x0004.
// 0 if not present (use GPSValid to check).
GPSLongitude float64
// GPSValid indicates whether GPS coordinates are present.
// Check this before using GPSLatitude/GPSLongitude since 0,0 is valid.
GPSValid bool
}
EXIF contains parsed EXIF metadata from the image. Fields are nil/zero when not present in the source data.
type ResultCode ¶
type ResultCode int
ResultCode represents libavif result codes.
const ( ResultOK ResultCode = 0 ResultUnknownError ResultCode = 1 ResultInvalidFtyp ResultCode = 2 ResultNoContent ResultCode = 3 ResultNoYUVFormatSelected ResultCode = 4 ResultReformatFailed ResultCode = 5 ResultUnsupportedDepth ResultCode = 6 ResultEncodeColorFailed ResultCode = 7 ResultEncodeAlphaFailed ResultCode = 8 ResultBMFFParseFailed ResultCode = 9 ResultMissingImageItem ResultCode = 10 ResultDecodeColorFailed ResultCode = 11 ResultDecodeAlphaFailed ResultCode = 12 ResultColorAlphaSizeMismatch ResultCode = 13 ResultISPESizeMismatch ResultCode = 14 ResultNoCodecAvailable ResultCode = 15 ResultNoImagesRemaining ResultCode = 16 ResultInvalidExifPayload ResultCode = 17 ResultInvalidImageGrid ResultCode = 18 ResultInvalidCodecSpecificOption ResultCode = 19 ResultTruncatedData ResultCode = 20 ResultIONotSet ResultCode = 21 ResultIOError ResultCode = 22 ResultWaitingOnIO ResultCode = 23 ResultInvalidArgument ResultCode = 24 ResultNotImplemented ResultCode = 25 ResultOutOfMemory ResultCode = 26 ResultCannotChangeSetting ResultCode = 27 ResultIncompatibleImage ResultCode = 28 ResultInternalError ResultCode = 29 ResultEncodeGainMapFailed ResultCode = 30 ResultDecodeGainMapFailed ResultCode = 31 ResultInvalidToneMappedImage ResultCode = 32 ResultEncodeSampleTransformFailed ResultCode = 33 ResultDecodeSampleTransformFailed ResultCode = 34 )
libavif result codes.