gfx/gif.h
GIF, every frame of it, into premultiplied RGBA Bitmaps.
#include <sonore/gfx/gif.h>- 3types
- 1other declaration
On this page
Design notes
Why a plugin needs it
Not for its 256 colours. For the one thing the other formats here do not do: move. A spinner while a sample library loads, a character that blinks, the animated logo a brand's designer already made -- every one of them arrives as a GIF, because GIF is what every tool exports when asked for "an animation" and every browser plays.
What a frame is
A GIF frame is not a picture. It is a rectangle of palette indices painted onto a canvas that keeps whatever the previous frames left, some indices transparent so the canvas shows through, and an instruction for what to do to the rectangle once the frame's delay is over: leave it, clear it, or put back what was there before. A decoder that hands those rectangles to a plugin hands it the hard part.
So this composites. Each GifFrame holds the WHOLE canvas as it looks while that frame is shown -- what a player blits, once per delay -- and also the rectangle, the disposal and the delay as the file gave them, for a player that wants them. Disposal follows the browsers rather than the letter of the 1989 specification: "restore to background" clears to transparent, because that is what every browser does and so what every GIF is authored against, and the background colour index is ignored for the same reason.
What is read
- GIF87a and GIF89a; global and local colour tables of 2 to 256 entries, and none at all (indices are then greys, as Pillow reads them);
- LZW at every minimum code size, codes growing to twelve bits, a clear code anywhere, and a full table that keeps going without one (the "deferred clear" encoders use to compress better);
- interlaced frames, in the four passes;
- the graphic control extension: delay, transparency, disposal;
- NETSCAPE2.0's loop count.
The delay is reported as the file gives it. Browsers draw any delay under 20 ms as 100 ms -- a GIF saying 0 means "as fast as you like", and old ones relied on that being slow -- and GifFrame::displayMs() does the same for a player that wants to look the way a browser does.
Every count in the file is attacker-controlled
LZW can describe four thousand pixels in a dozen bits, so a small file can honestly be a very large canvas, and an animation multiplies that by its frame count. Both are capped -- the canvas as PNG caps its image, and the sum over every frame -- and an animation that reaches the second cap stops there, keeps the frames it has, and says so in warning.
Types 3
One frame of a GIF, and the canvas as it looks while it shows.
A decoded GIF, or a reason it is not one.
Decodes a GIF -- every frame, composited onto the canvas -- from bytes in memory. See the file comment for what a frame holds and what is read.
Stateless: decode() is a static function of its input, safe from any thread but not on the audio thread (it allocates every canvas). A file that is not a GIF comes back with error set, and one that goes wrong partway keeps the frames before the damage and says why in warning. The caps in Options are there because the file is untrusted.
Enums 1
- enum
enum class GifDisposal : uint8_tWhat happens to a frame's rectangle once its delay is over.gfx::GifDisposal · gif.h:66