-
Notifications
You must be signed in to change notification settings - Fork 0
What it does
The Portable Network Graphics (PNG) Specification does not support animated images, but uses a clever file format that is easy to extend.
The APNG Specification, created by the Mozilla Corporation, is such an extension that allows to put an animation into a png file (similary to animated GIF) and also remains compatible with programs which do not support APNG (those show the first image only).
The PNG group rejected APNG as an official extension, nevertheless most browser an image viewer support it.
The following description is very brief, please refer to the specifications for more details.
The most simple PNG is built up by four chunks
| PNG signature |
IHDR |
IDAT |
IEND |
while the most simple APNG looks like this:
| PNG signature | IHDR | acTL | fcTL | IDAT | IEND |
Let´s try again with three images:
PNG1:
| PNG signature |
IHDR |
IDAT |
IEND |
PNG2:
| PNG signature |
IHDR |
IDAT |
IEND |
PNG3:
| PNG signature |
IHDR |
IDAT |
IEND |
APNG:
| PNG signature |
IHDR |
acTL |
fcTL |
IDAT |
fcTL |
fdAT |
fctl |
fdAT |
IEND |
The IDAT (Image Data) chunk of the APNG is the 'original' IDAT chunk of the first image (PNG1).
The first fdAT chunk of the APNG (pink) contains the image date of the second PNG, plus a sequence number.
The second fdAT chunk of the APNG (yellow) contains the image date of the third PNG, plus a sequence number.
And so on if there are more images.
A program which does not support APNG simply ignores all unknown chunks, what remains is a standard one-image PNG.
This version just takes the chunks of existing PNG files to build the APNG.
If there is no PNG but an 'in-memory-image' (like a BufferedImage), it creates a PNG (in memory) using Java API (ImageIO). Too bad this API is quite poor, not in image quality but in filesize, i´ll come back to that in a second.
Now this version can create the chunks/the PNG itself and therefore make use of the 'filtering' feature.
The PNG imagedata is encoded scanline by scanline.
Each scanline starts with a filter type (a byte) followed by all 'pixel bytes' of that scanline.
A filter does not compress but only change the 'pixel bytes' in a way so that the following compressor performs better. Again, please refer to the Portable Network Graphics (PNG) Specification for more details.
There are 5 filter types. For some reason, Java´s ImageIO API ignores them, always chooses 'type 0', the one which does nothing.
After filtering is done, the imagedata gets compressed using the Deflate algorithm.
I tried a bunch of strategies to find the 'best' filter type for each scanline, this is the result so far:
- for each scanline, simply try each of the 5 filter types
- compress the second last + the last+ the current scanline (using java.util.zip.Deflater)
- take the filter type which gained the smallest size
While playing around with the filter types, i used a folder full of various PNG files, always converted each file (file --> Java image --> new PNG file), and compared the filesize. A lot grew bigger. Of course i was looking for a bug in my code for some time. Then, i simply took those files, did nothing but to recompress (inflate and deflate) the image data, and yes, got the same bad result, even though java.util.zip.Deflater was set to 'best compression'. This class seems to be a quite poor implementation. Or maybe there are some secret switches i haven´t found yet :-)
One APNG feature to further reduce the filesize is to reduce the image size if possible.
Let´s look at those two test images:

To create an animation, the second image can be cut:


Now to the results in bytes. Please note, these simple images could be lossless converted to a palette images. Let´s ignore that for the moment.
| CREATED WITH | SIZE IN BYTES | COMMENT |
|---|---|---|
| PNG with ImageIO, APNG with APNG-builder | 2500 | Does not convert to palette image. Does not make use of PNG filter. Does not cut the second image. Uses a better Deflate algorithm. |
| APNG-builder, image 2 full sized | 3040 | |
| APNG-builder, image 2 cut | 2473 |
The second image was shrinked, but still there are a lot of pixel that do not change. These can be set to transparent. The idea is: While a series of different pixel is hard to compress, setting them to the same 'transparent pixel' should give the compressor a better chance to perform better.
...to be continued