Quirks of PNG compression

I've got a weird hobby. I like compressing images the best they can be and PNGs are the best for this. I've come to use a method I call the 'Staffordshire Cocktail' which involves OptiPNG, PNGOUT, zopflipng, AdvanceCOMP (specifically advdef), and the GIMP.

I typically use the GIMP to check for embedded colour profiles which must be dealt with carefully (I didn't do this at first and have many mangled images saved on my PC as a result) and remove 'transparency fringing' where image creators scaled an image without first removing an alpha channel resulting in a transparent fringe around the edges. I tried multiple different methods for removing this until I found going into Layer, Transparency, Threshold Alpha and setting it to 0 (I've since saved this as an 'Opaque' preset).

Step two is to use OptiPNG to remove extraneous chunks, concatenate IDAT chunks, perform basic compression, and give me some data about the image.

Step three is to send the image through zopflipng with the predefined filter settings as both standard and high compression. This is to make a copy of the file with OptiPNG's mixed filter setting (which is distinct from zopflipng's minimum sum filter and occasionally superior) and to check zopflipng's normal compression versus high (which, again, is occasionally superior).

Step four is to send the image through PNGOUT with mixed filtering and the force switch enabled. This is to add PNGOUT's mixed filter which is similar to zopflipng's entropy filter but often superior.

Step five is to go back to zopflipng with high compression (and normal if indicated to be useful) and all possible filters. It used to be possible to prune the amount of filters because zopflipng gave not just the size of the compressed image but each of the filters it tried as well. Unfortunately someone requested a quiet mode and rather than give it to them the zopflipng developers killed all output with no option to turn it back on. There is an undocumented verbose switch but it's more of a debug switch that gives literally thousands of line of output useless to anyone but a zopflipng developer. I wish someone would add that back.

I've modified these a bit since enacting them. On files where OptiPNG does not use its mixed filter I skip step three. This may miss out on certain corner cases where normal compression is better than high compression but those are so rare even I don't care enough. Also on files with OptiPNG uses no filtering I move straight to step four. If PNGOUT is also better with no filtering I keep fiddling with PNGOUT's Huffman block settings until optimal then send the files through advdef with Zopfli compression and 15 iterations. This is a lot less work.

Oddities

Weirdness first comes in with transparent images. You may think a transparent PNG is not complicated. Add alpha channel, mark all pixels with alpha transparency value, job done. Right? Well I think the complexity comes from fully transparent pixels. It seems to me that there are two ways to make a pixel entirely transparent in a PNG. Either set the alpha value to 0 while keeping the previous colour values or blank out all values except alpha. The former being noticeably less efficient than the latter.

So just do the latter and you're good, right? Well this is where I can tell you what works but not why it works. I have two methods for doing this. The first is the resave the file with the GIMP with all optional PNG saving settings switched off (probably overkill but it's just easier) and the second is to use zopflipng's --lossy_transparent setting. What's odd is that the former is usually better. But sometimes the latter is better and other times doing the former then the latter beats doing either alone. I don't understand why.

Then there's indexing. Even easier, right? Examine which colours are in the image, put them in a palette, mark each pixel with a corresponding value. Job done. Right? Surely? Nope. Amazingly there are at least four distinct methods of indexing a PNG (one each for OptiPNG, PNGOUT, and zopflipng, plus at least one more on images I've found online). How does that work? Hell if I know.

zopflipng's method is usually the best followed by PNGOUT's. OptiPNG's method is usually the worst by a large margin but occasionally things get really weird. On certain images (most notably sprite work from the 2D Ace Attorney games and greyscale pages of the webcomic El Goonish Shive for some bloody reason) OptiPNG's method is the best but only if combined with mixed filtering. The best method for this used to be compressing with OptiPNG then recompressing with zopflipng using the brute force filter. Unfortunately the zopflipng developers have changed zopflipng so rather than keeping indexing methods on already indexed images it forced its method on any images it touches. So now the best method is to compress with OptiPNG, use PNGOUT to add its mixed filter (PNGOUT only reindexes if asked), then recompress with advdef. This is another change I wish zopflipng's developers would revert.

Another is RGB+transparency. You may think this makes no sense. Isn't an image either RGB or RGB+alpha? Nope. A PNG can have a tRNS chunk but no alpha channel. You may think this means any image with transparency but not alpha transparency can be RGB+transparency but that also seems to not be the case. I have some images where I know for sure there is transparency but not alpha transparency but they will only compress as RGB+alpha. I think the reason is that this type of transparency can only be applied on a scanline basis not a pixel basis.

Then there's 4, 2, and 1-bit greyscale. These are rarely used lower bit greyscale modes that appear to be fixed to specific greyscale values. This makes the first two next to useless but the latter has some use as a strictly black-and-white mode. You may think it's actually very useful as 1-bit greyscale would seem objectively superior to a black-and-white image saved as 1-bit indexed due to the lack of a palette and the overheard therein. You should know the answer by now though. 1-bit indexed is still superior in the majority of cases. Don't know why.

This is just what I know and my guesses as to why things work the way they do.