GitHub - lenchan139/stb_avif

GitHub

6 min read Original article ↗

A small, single-header C89 AVIF decoder with a built-in scalar AV1 decoder.

This project is inspired by the design philosophy of the stb libraries: keep the decoder self-contained, portable, and easy to integrate into applications that need AVIF image decoding without a large codec framework.

The project is currently work in progress. The built-in AV1 decoder is being developed incrementally against real-world AVIF samples and is not yet a drop-in replacement for mature AV1 decoders.

Features

AVIF / ISOBMFF

The AVIF layer currently handles the parts of the HEIF/ISOBMFF container needed by the test corpus, including:

  • ftyp
  • meta
  • iloc
  • iinf
  • iprp
  • ipco
  • ipma
  • ispe
  • pixi
  • colr
  • av1C
  • AV1 image items and auxiliary alpha items
  • Multiple AV1 tile groups
  • Multi-tile AVIF images
  • Auxiliary alpha images

The decoder ultimately returns ordinary RGB/RGBA pixels through the public API.

AV1

The built-in scalar decoder is split into small headers covering individual parts of AV1:

  • OBU parsing
  • Sequence headers
  • Frame headers
  • Tile parsing and decoding
  • Partition decoding
  • Intra prediction
  • Transform decoding and inverse transforms
  • Quantization/dequantization
  • Coefficient decoding
  • MSAC entropy decoding
  • CDF tables
  • Deblocking
  • CDEF
  • Loop restoration
  • Palette modes
  • Segmentation support
  • 8/10/12-bit sample handling
  • 4:2:0, 4:2:2 and 4:4:4 pixel formats

The decoder uses a 64-bit unsigned integer type where required:

#if defined(_MSC_VER)
    typedef unsigned __int64 stbv_u64;
#else
    typedef unsigned long long stbv_u64;
#endif

This is intentional: some AV1 arithmetic cannot be implemented safely using only 32-bit integers.

Public API

The main interface is in stb_avif.h.

unsigned char *stb_avif_load_from_memory(
    const unsigned char *data,
    int len,
    int *x,
    int *y,
    int *channels,
    int req_channels);

Free the returned image with:

void stb_avif_free(void *ptr);

The last error can be obtained with:

const char *stb_avif_failure_reason(void);

Basic usage

Put stb_avif.h in your project and define STB_AVIF_IMPLEMENTATION in exactly one C source file:

#define STB_AVIF_IMPLEMENTATION
#include "stb_avif.h"

Then:

int x, y, channels;
unsigned char *pixels;

pixels = stb_avif_load_from_memory(
    data, data_size,
    &x, &y, &channels,
    4);

if (!pixels) {
    printf("AVIF decode failed: %s\n",
           stb_avif_failure_reason());
    return 1;
}

/* Use pixels here. */

stb_avif_free(pixels);

req_channels may be:

  • 0 --- use the image's natural channel count
  • 3 --- RGB
  • 4 --- RGBA

Optional dav1d backend

For comparison and for applications that require a mature AV1 decoder, the project can use dav1d instead of the built-in scalar AV1 decoder.

Define:

#define STB_AVIF_USE_DAV1D
#define STB_AVIF_IMPLEMENTATION
#include "stb_avif.h"

and link with dav1d:

The dav1d backend is particularly useful when validating the built-in decoder: the same AVIF file can be decoded through both implementations and the resulting pixels compared.

Test program

test_avif2pnm.c is a simple test driver which:

  1. Reads an AVIF file.
  2. Decodes it through stb_avif.
  3. Writes a PPM/PGM image.
  4. Writes a sidecar PGM when an alpha plane is available.

Build it with a C89 compiler:

cc -std=c89 -o test_avif2pnm test_avif2pnm.c -lm

Run it on individual files:

./test_avif2pnm image.avif

or use the built-in test list:

On Windows, the same source can be built with a C compiler supporting the required C89 features.

Test corpus

The development tree currently contains a collection of AVIF files covering different AV1 configurations, including:

  • 8-bit 4:2:0
  • 10-bit 4:2:0
  • 8-bit 4:4:4
  • 10-bit 4:4:4
  • 10-bit 4:2:2
  • 12-bit samples
  • monochrome images
  • palette-coded images
  • multi-tile images
  • images with auxiliary alpha
  • images using CDEF and loop restoration

Some particularly useful regression samples are:

kimono.avif
steam_2253100.avif
app-icon.avif
G-0trmKXsAA1sQZ.avif
avif-yuv444p.avif
avif-yuv444p10le.avif
fox.profile1.10bpc.yuv444.avif

These are intentionally kept as part of the development/test corpus because they exercise different AV1 features.

Current development status

The built-in decoder is not yet fully conformant with AV1.

Recent development has concentrated on:

  • multi-tile decoding
  • tile-local entropy/context state
  • AVIF ipma association handling
  • segment ID handling
  • CDEF
  • loop restoration
  • 10-bit 4:4:4 decoding
  • palette decoding
  • chroma prediction and edge handling
  • right-edge reconstruction
  • comparison against dav1d/FFmpeg output

Several difficult samples now decode substantially correctly, but there are still samples where the scalar decoder produces incorrect colors or becomes desynchronized.

In particular, avif-yuv444p10le.avif is currently useful for debugging multi-tile/high-bit-depth decoding: tile 0 can be visually reasonable while other tiles still show incorrect colors.

Do not assume that successful decoding of one AVIF sample means that all AV1 coding tools are implemented correctly.

Design goals

The main goals of the project are:

  • Single-header public API
  • C89-compatible implementation
  • No mandatory external codec dependency
  • Small, understandable source files
  • Scalar C implementation
  • Reuse of well-understood AV1/dav1d algorithms where practical
  • Ability to compare the internal decoder against dav1d
  • Support for real-world AVIF images rather than only minimal conformance samples

The AV1 implementation is deliberately split into multiple internal headers so individual codec components can be developed and tested without turning stb_avif.h into an unmaintainable monolithic implementation.

Source layout

dist/stb_avif.h
    All functions

Source layout (for development)

stb_avif.h
    AVIF/ISOBMFF parsing and public API

stb_av1_avifbox.h
    AV1/AVIF container-related helpers

stb_av1_obu.h
    OBU parsing

stb_av1_seqhdr.h
    AV1 sequence header

stb_av1_framehdr.h
    AV1 frame header

stb_av1_tile.h
    Tile geometry and tile bitstream handling

stb_av1_tile_decode.h
    Tile decoding

stb_av1_partition.h
stb_av1_partition_decode.h
    Block partitioning

stb_av1_leaf.h
    Leaf/block syntax and reconstruction state

stb_av1_intra.h
stb_av1_ipred.h
    Intra prediction

stb_av1_tx.h
stb_av1_txstate.h
    Transform syntax/state

stb_av1_itx.h
stb_av1_itx1d.h
    Inverse transforms

stb_av1_coef.h
    Coefficient decoding

stb_av1_quant.h
    Quantization/dequantization

stb_av1_msac.h
    MSAC entropy decoder

stb_av1_cdf.h
    AV1 CDF tables

stb_av1_cdef.h
    CDEF

stb_av1_deblock.h
    Deblocking filter

stb_av1_lr.h
    Loop restoration

test_avif2pnm.c
    Development/test program

build_single_header.py
    Python script for creating distributable single file stb_avif.h in dist/ 

References

The implementation is developed with reference to:

The project repository is:

https://github.com/roytam1/stb_avif

License

The project is intended to be released into the public domain, following the style of the stb libraries.

Where the public-domain dedication is not recognized, you are granted a perpetual, irrevocable license to use, copy, modify, and distribute this software for any purpose.

See:

https://creativecommons.org/publicdomain/zero/1.0/

Contributing / debugging

When adding or fixing AV1 functionality, it is useful to compare the scalar decoder with dav1d using the same AVIF input.

When reporting a decoding problem, please include:

  • the AVIF sample
  • image dimensions
  • bit depth
  • chroma format
  • whether the image uses multiple tiles
  • whether an auxiliary alpha item is present
  • the generated output
  • whether STB_AVIF_USE_DAV1D produces the expected output

Small, feature-specific regression samples are especially valuable because AV1 syntax errors often desynchronize the entropy decoder several symbols before the visible corruption appears.