There has been a long-standing issue in WordPress with the resizing of PNG8 images. More accurately, if you upload an efficient indexed PNG image, WordPress will (typically) create thumbnails multiple sizes larger than the original that you upload. When I say “long-standing”, there are bug reports going back over nine years ago. Recently though, a fellow EWWW IO user inspired me to dig into the issue a little deeper. Better than that, Tom even gave us some helpful links to get us going in the right direction. Way to go Tom!

What is PNG8?

Hold on though, let’s not dive headfirst down that hole just yet! PNG8 isn’t really an official term, and some folks will just call them 8-bit PNGs. In either case, we refer to an “indexed” PNG image with a palette that has a “bit depth” of 8. In other words, the image has a palette of 256 colors, and each pixel in an image uses eight 1’s and 0’s to indicate a particular color from the palette. Those 256 colors can be anything we want, even transparent, but we can only choose 256 colors at most. PNG images with a palette can also have a bit depth of 4, 2, or 1, which correspond to 16, 4, and 2 colors respectively (21, 22, and 24). Thus the issues mentioned above can affect any PNG images that use a palette, but I’ll focus mostly on PNG8 images from here on out.

Why do PNG8 images get larger when you make them smaller?

Woah, now that’s a mind bender. There are two things at play here, the “canvas” size, and the file size. For example, let’s say we have a 1920px wide by 1920px tall image:

This image has a very large “canvas”, and yet the file size is a mere 19kb. Just to show the benefits of using a palette, the original “un-paletted” image was 57kb. When we upload it to WordPress, it is going to create several other sizes of the image, medium, large, thumbnail, and so on. That’s a whole different mess that can be blown up by themes and plugins registering dozens more sizes, but for now let’s just look at the default “large” size. This image is 1024 x 1024, and the canvas is about 3.5x smaller than the original. Yet the file size of the large version on a default WP install is 55kb, nearly as large as the “un-paletted” original! What happened?

Essentially, because of smoothing, aliasing, and other artifacts of down-scaling an image, we end up with more colors than the original. If we end up with more than 256 colors, which is very often the case, then we can’t use a palette anymore, and the file size can easily triple or quadruple.

What can we do then?

Detection

First off, we need a way to check if WP is handling an indexed image. From there, we need to determine the bit depth so we know how many colors a resized image ought to have. Throughout all of this, my hope was that we could find a fix using PHP-native code. Specifically, something using the Imagick image-manipulation extension that any competent web host has available.

Oddly, ImageMagick (IM) doesn’t like to just do the “normal” thing. While it has no trouble reading the bit depth from the 26th byte, it decides to be a little too clever when detecting the color type, rather than just reading it from the 27th byte according to the PNG specification. Fortunately, our friend Tom had sent over a link to a helpful post that showed exactly how to find the bit depth and color type via PHP. That wasn’t so hard, was it? Was it, ImageMagick?

This won’t be the last time we see erratic behavior around ImageMagick’s color type handling, but at least we can reliably detect indexed PNG images and their bit depth. So far, so good…

Quantization – The Elephant in the Room

Seriously, we need to get rid of that elephant! When we resize an indexed PNG, the number of colors skyrockets, and now we’ve got to put this elephant back in a rabbit-sized hole. We’ve talked about the file size already, but the image above goes from 77 colors to 12,778. It gets worse though! One of my test images went from 253 colors to a whopping 85,395 after scaling down 68%. That’s no elephant, that’s a whale!

So how do we go from 85k colors back down to 253? Since we aren’t in Wonderland like Alice, we don’t have any shrinking potions, and we’ll have to use something called “quantization“. We won’t get too much into the weeds on that, but it involves complicated algorithms, of which there are several. ImageMagick has a handy quantizeImage() function that will do the trick… most of the time. At least quantizeImage() is the better of two methods that IM provides for reducing the number of colors, but it isn’t perfect.

Baby Elephants

Sometimes, quantization isn’t as smooth as we’d like, which isn’t really IM’s fault. Ultimately, quantization is a “lossy” operation, but we’re already resizing, which is lossy, and so we’re not expecting miracles. Or are we? In my testing, I ran into two images that IM struggled with more than the others. Which was kind of fortuitous, otherwise, one of you lovely folks would have had to find the problems later on. I know I’d sure prefer to find such things during development than when I’m trying to build a real live site!

Edges and Transparency

The first of these issues involves some “fuzzing” around the boundaries between color and transparency. See, indexed PNG doesn’t support transparency in the same way as the RGBA color type, and uses a color that is not present in the image to represent transparency. Often, this is done with full black (#000000) or full white (#ffffff). So what happens if we’re quantizing along an edge between a random color and transparency, masquerading as black? We sometimes get black pixels that don’t belong there.

magnified snapshot of image with black fuzzing
Magnified 2x so you can see the “fuzz”

Turns out I’m not the first one to notice this issue, and guess what? The best way to fix this is using a tool we already have in EWWW IO. Pngquant (as the name might suggest) is an advanced PNG quantizer, and can slim down our “color budget” with no fuzzing. It uses a more advanced algorithm, and the author (Kornel) is kind of a wizard at just about anything image-related. The only downside is that it does result in a small increase in file size sometimes, and since this issue isn’t very common, it isn’t the default behavior in EWWW IO. If you run into this issue though, be sure to let us know, and add this to your wp-config.php:

define( 'EWWWIO_PNGQUANT_REDUCE', true );

Grayscale vs. Palette

The second issue came up when testing the image that Tom sent us:

Gray background added so you can see the image

There were no visual issues with this image, but no matter what I tried, ImageMagick kept saving it with the Grayscale+Alpha color type (4) instead of Indexed (3). I mean, you can kind of see why, but Grayscale is not as efficient as Indexed mode. And with a whopping 9 colors, we can cut the file size in half (or better) if we can get IM to save it in Indexed mode. After a bunch of fiddling around with IM, I finally realized that IM had converted the image to a GRAY color space (think RGB, sRGB, or CMYK).

Sadly, changing the color space back to RGB or sRGB had zero effect. But remember when I said ImageMagick had two different ways to quantize an image? No? Well I did, and while the other method isn’t normally as good, on this sort of image it works pretty well. They’ve implemented a bit of a “hack” in IM, that lets you set the output format to ‘png8’ to do a “quick and dirty” quantization:

$image->setOption( 'png:format', 'png8' );

So when EWWW IO detects that IM has converted the color space to GRAY, it sets the output format to ‘png8’, and we get a lovely indexed PNG.

Summing Up

Didn’t understand half of that? I totally understand! I’ve been immersed in this stuff for 3-4 weeks, and I’m still uncovering new nuggets. EWWW IO now ensures that WordPress doesn’t produce overly large thumbnails of indexed PNGs. I’m going to be checking through some of our PNG images at ewww.io to see if they are indexed and make sure all the sizes are also indexed correctly.

If you want to check out your PNG images, a good way to spot evidence of these PNG shenanigans is to put the Media Library into List mode, and then check the optimization details. There you’ll see the file size of the original and all the various sizes. If you see any that are larger than the original, you’ve likely got a PNG8. Quick fix is to run a thumbnail regeneration with EWWW IO active for any suspected PNG8 images, and see what happens. Otherwise, enjoy your smaller images, and happy optimizing!