Skip to content

v3.2.0: smaller animations, options objects, faster codecs

Choose a tag to compare

@matmen matmen released this 16 Oct 08:01
Immutable release. Only release title and notes can be modified.

This release makes animations a lot smaller, makes options easier to pass and speeds up the most common codecs. There are no breaking changes.

New

Smaller animations (#59)

encodeGIF(), encodeAPNG() and encodeWebP() now store each frame as only the area that changed since the frame before, and merge identical frames. Within that area, unchanged pixels become transparent where that compresses better.

Re-encoding 762 animations took this share of the previous size in total:

GIF APNG WebP
51% 63% 54%
  • The less moves in an animation, the smaller it gets. Animations where everything moves stay about the same size.
  • Large animations encode faster, small ones up to about twice as slow.
  • The files show the same animation in browsers, Pillow and libwebp. They don't decode to the same frames anymore, though. {optimize: false} stores the frames as they are.

Options objects (#58)

Every method with optional arguments now also takes them as an object, so you can leave options out without counting positions:

image.resize(100, 100, {mode: Image.RESIZE_BICUBIC});
image.composite(logo, {x: 10, y: 10, opacity: 0.5});
Image.renderText(font, 32, 'Hi', {color: 0xff0000ff, strokeWidth: 2});
animation.encodeGIF({quality: 80});

The positional arguments keep working until 4.0.

Metadata in every format (#56)

encodeJPEG(), encodeWebP(), encodeTGA() and the animation encoders take the same metadata as encode(), as the metadata option:

image.encodeJPEG({quality: 80, metadata: {author: 'me'}});

Each format stores the fields it has a place for: Exif and a comment for JPEG, Exif for WebP, comments for GIF, the extension area for TGA, text chunks for PNG and APNG.

All files now name ImageScript as the software and include the creation time, as PNGs already did. {software: ''} leaves the software out.

trim() (#67)

Removes borders of one color, for example the empty space around rendered text or a solid background around a logo.

  • Without a color, the top left pixel's color is used.
  • tolerance also trims near colors, such as JPEG artifacts.
  • Frames keep their position in the animation.

Animation.frames (#60)

The frames of an animation as an array. Changing the array (push(), splice(), …) changes the animation, and you can replace it as a whole.

Performance (#62–#66)

Every change gives bit-identical results, checked against 3.1 on about 1600 images and the resvg test suite.

Faster by about
GIF decoding 25%
SVG rendering 15–20%
PNG decoding 25%
PNG encoding 10%
Lossless WebP encoding 15%
Resizing 10%

The package is also about 10 kB smaller, because the type declarations of internal modules are no longer included.

Benchmarks (#61)

bench/ compares ImageScript with Jimp, image-js, Photon, sharp, @napi-rs/canvas and ImageScript 1.x. It covers:

  • decoding and encoding
  • image operations
  • text and SVG rendering
  • animations
  • file sizes and install sizes

The results are refreshed automatically after every release.

Deprecated

  • Animations as arrays: animation[0], animation.length, animation.map() and the other array methods on an animation still work, but in 4.0 Animation won't extend Array anymore. Use animation.frames.
  • PNGMetadata is now called Metadata. The old name stays an alias until 4.0.

Fixes

  • TGA encoding: run length encoded packets end with the row, as TGA 2.0 requires. Some readers rejected the files before, Pillow among them (#57).
  • PNG text metadata: text that isn't Latin-1 (such as Ⓒ or emoji) is written as UTF-8 into iTXt chunks. Readers showed it garbled before (#56).
  • creationTime: an invalid value throws a RangeError instead of writing "Invalid Date" (#56).

Upgrading

No code changes are needed. If you compare the frames of re-encoded animations with the originals, pass {optimize: false} to the encoder, because optimized files decode to different (smaller) frames.