Skip to content

v3.4.0: AVIF, JPEG XL, lossy WebP

Choose a tag to compare

@matmen matmen released this 25 Oct 06:39
Immutable release. Only release title and notes can be modified.

This release reads AVIF and JPEG XL, encodes lossy WebP and adds BMP, ICO/CUR, QOI, Netpbm and Radiance HDR. Every codec is plain TypeScript: no WebAssembly and no native code. There are no breaking API changes.

New

AVIF

Image.decode reads AVIFs through an AV1 decoder ported from dav1d. It decodes exactly like dav1d, and the conversion to RGB is libavif's, so the pixels are the ones libavif gives.

  • 4:2:0, 4:2:2, 4:4:4 and monochrome, 8, 10 and 12 bits
  • Every intra coding tool and post filter: palettes, intra block copy, filter intra, chroma from luma, lossless, tiles, deblocking, CDEF, superres, loop restoration and film grain
  • The container: grids, alpha (also premultiplied), crop, rotation and mirroring
  • HDR (PQ and HLG, BT.2020 and Display P3) is converted to sRGB, see toneMapping

Image sequences give their first frame, also from Animation.decode. Frames that reference other frames (inter frames) aren't decoded.

JPEG XL

Image.decode reads JPEG XLs and Animation.decode their animations, with durations and loop count. The decoder is ported from jxl-oxide and libjxl and supports every coding tool:

  • Modular and VarDCT, lossless and lossy, including recompressed JPEGs
  • Progressive passes and LF frames, patches, splines, noise, Gaborish and the edge-preserving filter, upsampling
  • Layers: cropped and blended frames and reference frames
  • Alpha (also premultiplied), spot colors, CMYK and the orientation

Every file of the JPEG XL conformance suite decodes within 1 of libjxl's djxl. XYB images are rendered to sRGB and HDR ones are tone mapped. ICC profiles aren't applied, so CMYK images become their colors times black, like CMYK JPEGs.

decoding (Node, one thread) lossy (VarDCT) lossless (-e 1) lossless (-e 7)
1024×1024 0.46 s 0.25 s 1.2 s
4000×3000 4.8 s 2.6 s 11.5 s

Lossy WebP

encodeWebP({ quality }) encodes lossy WebPs. The encoder is ported from libwebp and writes the same VP8 bitstream, byte for byte, as cwebp with the same quality. Transparent images get a losslessly compressed alpha plane.

await image.encodeWebP({ quality: 80 });              // like cwebp -q 80
await image.encodeWebP({ quality: 80, effort: 0 });   // like cwebp -q 80 -m 0: several times faster
await animation.encodeWebP({ quality: 80 });          // lossy frames

effort (0-6, default 4) is cwebp's -m. Without a quality, WebPs stay lossless.

Small formats

  • BMP: 1 to 64 bits, channel masks of any size, alpha, RLE4 and RLE8, embedded JPEGs and PNGs, and OS/2 headers. The output matches BMP Suite's reference images.
  • ICO and CUR: the largest image, as a bitmap with its mask or as a PNG.
  • QOI
  • Netpbm: PBM, PGM, PPM (plain and raw, up to 16 bits), PAM and PFM.
  • Radiance HDR: RGBE and XYZE, every orientation.

Image.encodeBMP, Image.encodeQOI and Image.encodePNM write them. The QOI encoder's output is byte for byte the reference encoder's. encodePNM picks PBM, PGM, PPM or PAM by what the image needs.

ImageType recognizes all new formats: 'avif', 'jxl', 'bmp', 'ico', 'cur', 'qoi', 'pnm' and 'hdr'.

HDR and toneMapping

Radiance HDRs, PFMs and HDR AVIFs and JPEG XLs can be brighter than white. By default ('reinhard'), the brightest parts are compressed like Reinhard's tone mapping operator does and the brightest pixel becomes white. Images no brighter than white stay as they are. {toneMapping: 'clamp'} clips instead.

Fixes

  • TrueType hinting matches FreeType in more fonts. SPVTL, SFVTL and SDPVTL pointed their vectors the wrong way, S45ROUND now rounds to FreeType's 45° grid, and ISECT and ALIGNPTS compute like FreeType's integer arithmetic.
  • rand alternates: the randomly picked alternates are now the ones HarfBuzz picks.
  • SVG presentation attributes: spaces around their values (fill=" url(#gradient) ") are ignored, as in CSS.

The new codecs grow the package from 1.1 MB to 1.8 MB unpacked (472 kB packed).