Skip to content

Commit 35be9bf

Browse files
matmenclaude
andcommitted
Encode AVIF: an AV1 intra encoder mirroring the decoder
Image.encodeAVIF encodes AV1 still pictures in an AVIF container (alpha as a monochrome auxiliary image). The encoder's syntax and contexts mirror the dav1d based decoder and its reconstruction uses the decoder's predictors and inverse transforms, so dav1d decodes the files to exactly the encoder's reconstruction. Partitions, intra modes, chroma from luma and transform types are chosen by rate and distortion, coefficients quantized by rate and distortion; the loop filter level and up to 8 CDEF strengths by the filtered image. Lossless (quality 100) uses the Walsh-Hadamard transform and RGB with the identity matrix. The symbol encoder and the forward WHT are ported from libaom. On photos the files are within about 1-4% of avifenc's (libaom, speed 6) at the same PSNR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lfv41PqHEGbU7SLg5onpG1
1 parent eaf5221 commit 35be9bf

11 files changed

Lines changed: 2016 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44

55
### Features
66

7+
- `Image.encodeAVIF({ quality, alphaQuality, subsampling, effort })` encodes AVIFs: an AV1 encoder of its own (still
8+
pictures, intra coding) whose syntax mirrors the dav1d based decoder, so dav1d decodes its files to exactly the
9+
encoder's reconstruction. It chooses partitions (64x64 to 8x8), the 13 intra modes, chroma from luma and the transform
10+
types by rate and distortion, quantizes the coefficients by rate and distortion, and picks the loop filter level and
11+
up to 8 CDEF strengths (per 64x64 block) by the filtered image. On photos its files are within a few percent of
12+
libaom's (avifenc's default speed) at the same PSNR, but encoding takes about 15 times as long. `quality` (0-100,
13+
default 60) maps to the quantizer like avifenc's -q; 100 is lossless (RGB, identity matrix). Transparency is a
14+
monochrome alpha image, lossless by default (`alphaQuality`). `subsampling`: '4:4:4' (default) or '4:2:0'. `effort`
15+
(0-2, default 1): how many modes and partitions are tried.
716
- `Image.encodeJXL()` encodes lossless JPEG XLs: ported from libjxl's fast lossless encoder, the codestream is byte for
817
byte what `cjxl -e 1` writes on CPUs with AVX2 or NEON (libjxl's output depends on the CPU: it works in chunks of 16
918
pixels there, of 8 without SIMD, of 32 with AVX-512). Images of at most 511 colors besides black get a palette,

‎README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ deno add jsr:@matmen/imagescript # Deno (npm:imagescript works too)
6767
, [JPEGs](https://imagescript.matmen.dev/classes/Image.html#encodejpeg)
6868
, lossless and lossy [WebPs](https://imagescript.matmen.dev/classes/Image.html#encodewebp) (lossy ones byte for
6969
byte like libwebp's cwebp)
70+
, [AVIFs](https://imagescript.matmen.dev/classes/Image.html#encodeavif) (lossy and lossless, with alpha)
7071
, lossless [JPEG XLs](https://imagescript.matmen.dev/classes/Image.html#encodejxl) (byte for byte like libjxl's
7172
`cjxl -e 1`)
7273
, [BMPs](https://imagescript.matmen.dev/classes/Image.html#encodebmp)
@@ -129,7 +130,7 @@ the text shaping is ported from [HarfBuzz](https://github.com/harfbuzz/harfbuzz)
129130
[dav1d](https://code.videolan.org/videolan/dav1d), AVIFs are converted to RGB like
130131
[libavif](https://github.com/AOMediaCodec/libavif) does, the JPEG XL decoder is ported from
131132
[jxl-oxide](https://github.com/tirr-c/jxl-oxide) and [libjxl](https://github.com/libjxl/libjxl), the JPEG XL encoder
132-
from libjxl's fast lossless encoder.
133+
from libjxl's fast lossless encoder, the AV1 encoder mirrors the decoder and uses libaom's symbol encoder.
133134
See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for their licenses.
134135

135136
---

‎THIRD_PARTY_NOTICES.md‎

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ following projects, under their own licenses. Each derived file says so in its h
1313
| [RFC 9649](https://www.rfc-editor.org/rfc/rfc9649) (WebP Image Format) | `src/codecs/vp8l.ts` (lossless bitstream, short distance code table), `src/codecs/webp.ts` (container) | BSD-3-Clause (Revised BSD) |
1414
| [RFC 6386](https://www.rfc-editor.org/rfc/rfc6386) (VP8 Data Format and Decoding Guide, with the libvpx reference decoder) | `src/codecs/vp8-tables.ts` (probability and quantizer tables), `src/codecs/vp8.ts` (decoding procedures) | BSD-3-Clause |
1515
| [libwebp](https://chromium.googlesource.com/webm/libwebp) | `src/codecs/vp8.ts` (YUV to RGB conversion, "fancy" chroma upsampling), `src/codecs/webp.ts` (animation frame blending), `src/codecs/vp8-enc.ts`, `src/codecs/vp8-enc-dsp.ts` (the lossy encoder: RGB to YUV conversion, analysis, segments, rate-distortion optimized mode decisions, quantization, token probabilities, bitstream, alpha filtering), `src/codecs/vp8-enc-tables.ts` (its tables, generated from libwebp by `scripts/vp8-enc-tables.py`) | BSD-3-Clause |
16-
| [dav1d](https://code.videolan.org/videolan/dav1d) by VideoLAN, the dav1d authors and Two Orioles | `src/codecs/av1/*` (the AV1 decoder: bitstream parsing, symbol decoding, prediction, inverse transforms, loop filters, film grain; the tables in `src/codecs/av1/data.ts`, generated from dav1d by `scripts/av1-tables.c`) | BSD-2-Clause |
16+
| [dav1d](https://code.videolan.org/videolan/dav1d) by VideoLAN, the dav1d authors and Two Orioles | `src/codecs/av1/*` (the AV1 decoder, and the encoder's syntax, contexts and reconstruction mirrored from it: bitstream parsing, symbol decoding, prediction, inverse transforms, loop filters, film grain; the tables in `src/codecs/av1/data.ts`, generated from dav1d by `scripts/av1-tables.c`) | BSD-2-Clause |
17+
| [libaom](https://aomedia.googlesource.com/aom/) by the Alliance for Open Media | `src/codecs/av1/msac-enc.ts` (the symbol encoder), `src/codecs/av1/fdct.ts` (the forward Walsh-Hadamard transform), `src/codecs/av1/encode.ts` (the loop filter level from the quantizer), `src/codecs/avif-enc.ts` (the quantizer to qindex table) | BSD-2-Clause |
1718
| [libavif](https://github.com/AOMediaCodec/libavif) by Joe Drago | `src/codecs/avif.ts` (YUV to RGB conversion with bilinear chroma upsampling, alpha range expansion and unpremultiplication) | BSD-2-Clause |
1819
| [jxl-oxide](https://github.com/tirr-c/jxl-oxide) by Wonwoo Choi | `src/codecs/jxl/*` (the JPEG XL decoder: bit reader, header and frame header fields, entropy decoding, VarDCT, dequantization matrices, transforms, restoration filters, upsampling, the parsing of patches, splines and noise) | MIT (or Apache-2.0) |
1920
| [libjxl](https://github.com/libjxl/libjxl) by the JPEG XL Project Authors | `src/codecs/jxl/modular.ts` (Modular decoding: MA trees, predictors, the self-correcting predictor, transforms), `src/codecs/jxl/frame.ts` (the sections of frames, the order of the rendering stages, reference frames), `src/codecs/jxl/blend.ts`, `src/codecs/jxl/color.ts` (XYB to RGB), `src/codecs/jxl/features.ts` (rendering patches, splines and noise), `src/codecs/jxl/fast-lossless.ts` (the lossless encoder: palette detection, YCoCg, the gradient predictor, prefix codes, runs as LZ77, the headers) | BSD-3-Clause |
@@ -188,6 +189,41 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
188189
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
189190
```
190191

192+
## libaom
193+
194+
The AV1 encoder's symbol encoder and forward Walsh-Hadamard transform are ported from libaom, the AV1 reference
195+
implementation, as are its guess of the loop filter level and the quantizer table of the quality. AV1 is covered by the
196+
[Alliance for Open Media Patent License 1.0](https://aomedia.org/license/patent-license/).
197+
198+
```
199+
Copyright (c) 2016, Alliance for Open Media. All rights reserved.
200+
201+
Redistribution and use in source and binary forms, with or without
202+
modification, are permitted provided that the following conditions
203+
are met:
204+
205+
1. Redistributions of source code must retain the above copyright
206+
notice, this list of conditions and the following disclaimer.
207+
208+
2. Redistributions in binary form must reproduce the above copyright
209+
notice, this list of conditions and the following disclaimer in
210+
the documentation and/or other materials provided with the
211+
distribution.
212+
213+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
214+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
215+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
216+
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
217+
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
218+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
219+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
220+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
221+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
222+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
223+
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
224+
POSSIBILITY OF SUCH DAMAGE.
225+
```
226+
191227
## libavif
192228

193229
The conversion of decoded AVIFs to RGB reproduces libavif's reference conversion (in floating point, with bilinear

0 commit comments

Comments
 (0)