This page is somewhat incomplete and quite possibly incorrect – use with caution.

APNG tests

For all these tests, wait at least a second after all the images have downloaded, before checking that the rendered output is correct.

Test output conventions: A solid green 128×64 rectangle means success. Any red means failure. Anything else means you need to read the instructions. "Transparent" is indicated by a light yellow background.

Sections of the relevant specifications are sometimes quoted when they clarify the expected behaviour.

Please don't link directly to the images in this page, since they may get renamed and will break any such links.

You can download the script and the source data for this page.

Valid images

Basic cases

Trivial static image.

This should be solid green. IHDR => [W, H], IDAT => [create_surface(W, H, 'green')], IEND => [],

Trivial animated image - one frame; using default image.

This should be solid green. IHDR => [W, H], acTL => [1, 0], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], IDAT => [create_surface(W, H, 'green')], IEND => [],

Trivial animated image - one frame; ignoring default image.

This should be solid green. IHDR => [W, H], acTL => [1, 0], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'green')], IEND => [],

IDAT, fdAT splitting

There may be multiple IDAT chunks; if so, they shall appear consecutively with no other intervening chunks. The compressed datastream is then the concatenation of the contents of the data fields of all the IDAT chunks.

The compressed datastream is then the concatenation of the contents of the data fields of all the `fdAT` chunks within a frame.

Basic split IDAT.

This should be solid green. IHDR => [W, H], IDAT_split => [0,32, create_surface(W, H, 'green')], IDAT_split => [32,-1, create_surface(W, H, 'green')], IEND => [],

Split IDAT with zero-length chunk.

This should be solid green. IHDR => [W, H], IDAT_split => [0,32, create_surface(W, H, 'green')], IDAT_split => [32,32, create_surface(W, H, 'green')], IDAT_split => [32,-1, create_surface(W, H, 'green')], IEND => [],

Basic split fdAT.

This should be solid green. IHDR => [W, H], acTL => [1, 0], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT_split => [1, 0,32, create_surface(W, H, 'green')], fdAT_split => [2, 32,-1, create_surface(W, H, 'green')], IEND => [],

Split fdAT with zero-length chunk.

This should be solid green. IHDR => [W, H], acTL => [1, 0], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT_split => [1, 0,32, create_surface(W, H, 'green')], fdAT_split => [2, 32,32, create_surface(W, H, 'green')], fdAT_split => [3, 32,-1, create_surface(W, H, 'green')], IEND => [],

Dispose ops

APNG_DISPOSE_OP_NONE - basic.

This should be solid green. IHDR => [W, H], acTL => [3, 1], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], IDAT => [create_surface(W, H, 'red')], fcTL => [1, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [2, create_surface(W, H, 'green')], fcTL => [3, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [4, create_surface(W, H, 'transparent')], IEND => [],

APNG_DISPOSE_OP_BACKGROUND - basic.

This should be transparent. IHDR => [W, H], acTL => [3, 1], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], IDAT => [create_surface(W, H, 'red')], fcTL => [1, W, H, 0, 0, 10, 100, DISPOSE_BACKGROUND, BLEND_OVER], fdAT => [2, create_surface(W, H, 'red')], fcTL => [3, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [4, create_surface(W, H, 'transparent')], IEND => [],

APNG_DISPOSE_OP_BACKGROUND - final frame.

This should be solid green. IHDR => [W, H], acTL => [2, 1], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], IDAT => [create_surface(W, H, 'red')], fcTL => [1, W, H, 0, 0, 10, 100, DISPOSE_BACKGROUND, BLEND_OVER], fdAT => [2, create_surface(W, H, 'green')], IEND => [],

APNG_DISPOSE_OP_PREVIOUS - basic.

This should be solid green. IHDR => [W, H], acTL => [3, 1], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'green')], fcTL => [2, W, H, 0, 0, 10, 100, DISPOSE_PREVIOUS, BLEND_OVER], fdAT => [3, create_surface(W, H, 'red')], fcTL => [4, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [5, create_surface(W, H, 'transparent')], IEND => [],

APNG_DISPOSE_OP_PREVIOUS - final frame.

This should be solid green. IHDR => [W, H], acTL => [2, 1], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], IDAT => [create_surface(W, H, 'red')], fcTL => [1, W, H, 0, 0, 10, 100, DISPOSE_PREVIOUS, BLEND_OVER], fdAT => [2, create_surface(W, H, 'green')], IEND => [],

APNG_DISPOSE_OP_PREVIOUS - first frame.

This should be transparent. IHDR => [W, H], acTL => [2, 1], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_PREVIOUS, BLEND_OVER], IDAT => [create_surface(W, H, 'red')], fcTL => [1, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [2, create_surface(W, H, 'transparent')], IEND => [],

Dispose ops and regions

APNG_DISPOSE_OP_NONE in region.

This should be solid green. IHDR => [W, H], acTL => [3, 1], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], IDAT => [create_surface(W, H, 'doublerect', 0,1,0,1, 1,0,0,1)], fcTL => [1, W/2, H/2, W/4, H/4, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [2, create_surface(W/2, H/2, 'green')], fcTL => [3, 1, 1, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [4, create_surface(1, 1, 'transparent')], IEND => [],

APNG_DISPOSE_OP_BACKGROUND before region.

This should be transparent. IHDR => [W, H], acTL => [2, 1], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_BACKGROUND, BLEND_OVER], IDAT => [create_surface(W, H, 'red')], fcTL => [1, 1, 1, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [2, create_surface(1, 1, 'transparent')], IEND => [],

APNG_DISPOSE_OP_BACKGROUND in region.

This should be a solid blue rectangle containing a smaller transparent rectangle. IHDR => [W, H], acTL => [3, 1], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], IDAT => [create_surface(W, H, 'doublerect', 0,0,1,1, 1,0,0,1)], fcTL => [1, W/2, H/2, W/4, H/4, 10, 100, DISPOSE_BACKGROUND, BLEND_OVER], fdAT => [2, create_surface(W/2, H/2, 'red')], fcTL => [3, 1, 1, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [4, create_surface(1, 1, 'transparent')], IEND => [],

APNG_DISPOSE_OP_PREVIOUS in region.

This should be solid green. IHDR => [W, H], acTL => [3, 1], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'green')], fcTL => [2, W/2, H/2, W/4, H/4, 10, 100, DISPOSE_PREVIOUS, BLEND_OVER], fdAT => [3, create_surface(W/2, H/2, 'red')], fcTL => [4, 1, 1, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [5, create_surface(1, 1, 'transparent')], IEND => [],

Blend ops

APNG_BLEND_OP_SOURCE on solid colour.

This should be solid green. IHDR => [W, H], acTL => [2, 1], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], fcTL => [2, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_SOURCE], fdAT => [3, create_surface(W, H, 'green')], IEND => [],

APNG_BLEND_OP_SOURCE on transparent colour.

This should be transparent. IHDR => [W, H], acTL => [2, 1], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], fcTL => [2, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_SOURCE], fdAT => [3, create_surface(W, H, 'transparent')], IEND => [],

APNG_BLEND_OP_SOURCE on nearly-transparent colour.

This should be very nearly transparent. IHDR => [W, H], acTL => [2, 1], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], fcTL => [2, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_SOURCE], fdAT => [3, create_surface(W, H, 'solid', 0, 1, 0, 0.01)], IEND => [],

APNG_BLEND_OP_OVER on solid and transparent colours.

This should be solid green. IHDR => [W, H], acTL => [2, 1], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'green')], fcTL => [2, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'transparent')], IEND => [],

APNG_BLEND_OP_OVER repeatedly with nearly-transparent colours.

This should be solid green. IHDR => [W, H], acTL => [128, 1], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'solid', 0, 1, 0, 1)], (map { (fcTL => [2+$_*2, W/2, H, 0, 0, 1, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3+$_*2, create_surface(W/2, H, 'solid', 0, 1, 0, 0.01)]) } 0..126), IEND => [],

Blending and gamma

APNG_BLEND_OP_OVER

This should be solid slightly-dark green. IHDR => [W, H], gAMA => [1], acTL => [16, 1], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'solid', 0, 1, 0, 1)], (map { (fcTL => [2+$_*2, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3+$_*2, create_surface(W, H, 'solid', 0, 0.9, 0, 0.5)]) } 0..14), IEND => [],

APNG_BLEND_OP_OVER

This should be solid nearly-black. IHDR => [W, H], gAMA => [1/2200], acTL => [16, 1], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], (map { (fcTL => [2+$_*2, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3+$_*2, create_surface(W, H, 'solid', 0.9, 0, 0, 0.5)]) } 0..14), IEND => [],

Chunk ordering

fcTL before acTL.

This should be solid green. IHDR => [W, H], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], acTL => [2, 1], IDAT => [create_surface(W, H, 'red')], fcTL => [1, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [2, create_surface(W, H, 'green')], IEND => [],

Delays

Basic delays.

This should flash blue for half a second, then yellow for one second, then repeat. IHDR => [W, H], acTL => [4, 0], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 50, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'blue')], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'yellow')], fcTL => [4, W, H, 0, 0, 10000, 20000, DISPOSE_NONE, BLEND_OVER], fdAT => [5, create_surface(W, H, 'blue')], fcTL => [6, W, H, 0, 0, 1, 1, DISPOSE_NONE, BLEND_OVER], fdAT => [7, create_surface(W, H, 'yellow')], IEND => [],

Rounding of division.

This should flash blue for half a second, then yellow for one second, then repeat. IHDR => [W, H], acTL => [2, 0], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 1, 2, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'blue')], fcTL => [2, W, H, 0, 0, 1, 1, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'yellow')], IEND => [],

16-bit numerator/denominator.

This should flash blue for half a second, then yellow for one second, then repeat. IHDR => [W, H], acTL => [2, 0], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 32767, 65534, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'blue')], fcTL => [2, W, H, 0, 0, 65535, 65535, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'yellow')], IEND => [],

Zero denominator.

If the denominator is 0, it is to be treated as if it were 100

This should flash blue for half a second, then yellow for one second, then repeat. IHDR => [W, H], acTL => [2, 0], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 50, 0, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'blue')], fcTL => [2, W, H, 0, 0, 1000, 1000, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'yellow')], IEND => [],

Zero numerator.

If the the value of the numerator is 0 the decoder should render the next frame as quickly as possible, though viewers may impose a reasonable lower bound.

This should flash cyan for a short period of time (perhaps zero), then magenta for the same short period of time, then blue for half a second, then yellow for one second, then repeat. IHDR => [W, H], acTL => [4, 0], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 0, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'cyan')], fcTL => [2, W, H, 0, 0, 0, 0, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'magenta')], fcTL => [4, W, H, 0, 0, 500, 1000, DISPOSE_NONE, BLEND_OVER], fdAT => [5, create_surface(W, H, 'blue')], fcTL => [6, W, H, 0, 0, 1000, 1000, DISPOSE_NONE, BLEND_OVER], fdAT => [7, create_surface(W, H, 'yellow')], IEND => [],

num_plays

num_plays = 0

This should flash yellow for one second, then blue for one second, then repeat forever. IHDR => [W, H], acTL => [2, 0], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'yellow')], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'blue')], IEND => [],

num_plays = 1

When first loaded, this should flash yellow for one second, then stay blue forever. IHDR => [W, H], acTL => [2, 1], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'yellow')], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'blue')], IEND => [],

num_plays = 2

When first loaded, this should flash yellow for one second, then blue for one second, then yellow for one second, then blue forever. IHDR => [W, H], acTL => [2, 2], IDAT => [create_surface(W, H, 'red')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'yellow')], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'blue')], IEND => [],

Other depths and colour types

16-bit colour.

This should be dark blue. IHDR => [W, H, 16, 6], acTL => [2, 1], IDAT => [create_raw_surface(W, H, 64, "\xFF\x00\x00\x00\x00\x00\xFF\xFF" x (W*H))], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_raw_surface(W, H, 64, "\x00\x00\x00\x00\x00\x00\xFF\xFF" x (W*H))], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_raw_surface(W, H, 64, "\x00\x00\x00\x00\xFF\xFF\x80\x00" x (W*H))], IEND => [],

8-bit greyscale.

This should be a solid grey rectangle containing a solid white rectangle. IHDR => [W, H, 8, 0], acTL => [2, 1], IDAT => [create_raw_surface(W, H, 8, "\x00\xFF" x (W*H/2))], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_raw_surface(W, H, 8, "\x80" x (W*H))], fcTL => [2, W/2, H/2, W/4, H/4, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_raw_surface(W/2, H/2, 8, "\xFF" x (W*H/4))], IEND => [],

8-bit greyscale and alpha, with blending.

This should be solid grey. IHDR => [W, H, 8, 4], acTL => [2, 1], IDAT => [create_raw_surface(W, H, 16, "\x00\xFF\xFF\xFF" x (W*H/2))], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_raw_surface(W, H, 16, "\x00\xFF" x (W*H))], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_raw_surface(W, H, 16, "\xFF\x80" x (W*H))], IEND => [],

2-color palette.

This should be solid green. IHDR => [W, H, 1, 3], PLTE => [255,0,0, 0,255,0], acTL => [2, 1], IDAT => [create_raw_surface(W, H, 1, "\x00" x (W*H/8))], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_raw_surface(W, H, 1, "\x00" x (W*H/8))], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_raw_surface(W, H, 1, "\xFF" x (W*H/8))], IEND => [],

2-bit palette and alpha.

This should be solid green. IHDR => [W, H, 2, 3], PLTE => [255,0,0, 255,0,0, 0,255,0], tRNS => [255, 0, 255], acTL => [2, 1], IDAT => [create_raw_surface(W, H, 2, "\x00" x (W*H/4))], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_raw_surface(W, H, 2, "\xAA" x (W*H/4))], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_raw_surface(W, H, 2, "\x55" x (W*H/4))], IEND => [],

1-bit palette and alpha, with blending.

This should be solid dark blue. IHDR => [W, H, 1, 3], PLTE => [0,0,0, 0,0,255], tRNS => [255, 128], acTL => [2, 1], IDAT => [create_raw_surface(W, H, 1, "\x00" x (W*H/8))], fcTL => [0, W, H, 0, 0, 10, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_raw_surface(W, H, 1, "\x00" x (W*H/8))], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_raw_surface(W, H, 1, "\xFF" x (W*H/8))], IEND => [],

Invalid images

It is strongly recommended that when any error is encountered decoders should discard all subsequent frames, stop the animation, and revert to displaying the default image. A decoder which detects an error before the animation has started should display the default image. An error message may be displayed to the user if appropriate.

(If some decoders accept broken images, it seems quite possible that people will create and distribute broken images, and then the error-handling would have to be reverse-engineered by other implementations; hence all these tests to ensure errors get detected properly.)

For the following images, the default image (solid green) or an error should be displayed.

Incorrect chunks

Missing acTL. IHDR => [W, H], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], IEND => [],

Repeated acTL. IHDR => [W, H], acTL => [1, 0], acTL => [1, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], IEND => [],

acTL after IDAT. IHDR => [W, H], IDAT => [create_surface(W, H, 'green')], acTL => [1, 0], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], IEND => [],

Missing fcTL.

Disabled for now, since it crashes Opera 9.5 alpha 1589 (bug 287173).

Repeated fcTL. IHDR => [W, H], acTL => [1, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], IEND => [],

Missing fdAT. IHDR => [W, H], acTL => [2, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fcTL => [1, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [2, create_surface(W, H, 'red')], IEND => [],

num_frames

num_frames = 0; no default image.

0 is not a valid value.

IHDR => [W, H], acTL => [0, 0], IEND => [],

num_frames = 0; ignoring default image.

0 is not a valid value.

IHDR => [W, H], acTL => [0, 0], IDAT => [create_surface(W, H, 'green')], IEND => [],

num_frames too low.

This must equal the number of `fcTL` chunks. ... If this value does not equal the actual number of frames it should be treated as an error.

IHDR => [W, H], acTL => [1, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'red')], IEND => [],

num_frames too high by 1.

This must equal the number of `fcTL` chunks. ... If this value does not equal the actual number of frames it should be treated as an error.

IHDR => [W, H], acTL => [3, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'red')], IEND => [],

num_frames too high by 2.

This must equal the number of `fcTL` chunks. ... If this value does not equal the actual number of frames it should be treated as an error.

IHDR => [W, H], acTL => [4, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'red')], IEND => [],

num_frames outside valid range.

an "unsigned int" shall be a 32-bit unsigned integer in network byte order limited to the range 0 to (2^31)-1

IHDR => [W, H], acTL => [0x80000001, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], IEND => [],

Sequence numbers

Not starting from 0. IHDR => [W, H], acTL => [2, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [1, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [2, create_surface(W, H, 'red')], fcTL => [3, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [4, create_surface(W, H, 'red')], IEND => [],

Gap in sequence. IHDR => [W, H], acTL => [2, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [4, create_surface(W, H, 'red')], IEND => [],

Duplicated sequence number. IHDR => [W, H], acTL => [2, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [2, create_surface(W, H, 'red')], IEND => [],

Duplicated chunk. IHDR => [W, H], acTL => [2, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [3, create_surface(W, H, 'red')], fdAT => [3, create_surface(W, H, 'red')], IEND => [],

Reordered fdAT chunks. IHDR => [W, H], acTL => [2, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT_split => [4, 32,-1, create_surface(W, H, 'red')], fdAT_split => [3, 0,32, create_surface(W, H, 'red')], IEND => [],

Reordered sequence numbers. IHDR => [W, H], acTL => [2, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], fcTL => [2, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT_split => [4, 0,32, create_surface(W, H, 'red')], fdAT_split => [3, 32,-1, create_surface(W, H, 'red')], IEND => [],

Separated fdAT and fcTL sequences. IHDR => [W, H], acTL => [2, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [0, create_surface(W, H, 'red')], fcTL => [1, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H, 'red')], IEND => [],

Invalid image-data sizes

Default image's fcTL size not matching IHDR. IHDR => [W, H], acTL => [2, 0], fcTL => [0, W, H/2, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], IDAT => [create_surface(W, H/2, 'red')], fcTL => [1, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [2, create_surface(W, H, 'red')], IEND => [],

fdAT too small. IHDR => [W, H], acTL => [1, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H/2, 'red')], IEND => [],

fdAT too large. IHDR => [W, H], acTL => [1, 0], IDAT => [create_surface(W, H, 'green')], fcTL => [0, W, H, 0, 0, 100, 100, DISPOSE_NONE, BLEND_OVER], fdAT => [1, create_surface(W, H*2, 'red')], IEND => [],

References