Transparent PNG images in PHP: imagesavealpha() versus imagecolortransparent()
Posted by Jim DeLaHunt on 30 Nov 2010 at 11:43 pm | Tagged as: robobait, web technology
Are you using PHP (or libGD) to generate PNG images? Are you having problems getting your text anti-aliased, and also having your “transparent” colour recognised as transparent? Well, I had that problem too. libGD, the component which PHP uses to handle image operations, gives you a choice: you can have anti-aliased text, or a designated colour as transparent… but not both. Here’s why, and what you can do about it.
I was recently improving Ducky Sherwood’s maps framework for a client. Her framework is PHP code which generates 256×256-pixel PNG images with coloured shapes representing geographical data, which the Google Maps framework overlays on top of a map. The shapes don’t cover all of the map, so the images must be partly transparent. The task was to add text labels to the shapes. The text was to be anti-aliased, so it would look better.
Our first approach was to draw the transparent parts of the image in a designated “transparent” colour, not used for any other purpose, and call imagecolortransparent() to tell PHP to treat the colour specially. Then PHP’s imagepng() would generate a PNG image with the pixels in that colour transparent. For other reasons, our images always had to start started completely covered in opaque shapes. Using this “transparent” colour for some of the shapes was our only path to transparency. We used imagefttext() to draw the text.
The problem with this approach was that the text came out very fat. imagefttext() draws text with anti-aliasing: where the shape of a glyph overlaps a pixel partially, imagefttext() (actually, the FreeType system, which GD calls when PHP calls GD) colours that pixel with the desired text colour, but with a partial transparency (alpha) value. But imagepng() rounds all such alpha values to fully opaque. The result is text which looks worse than if imagefttext() did no anti-aliasing at all.
Enter the PHP function imagesavealpha(). This tells GD to preserve alpha values of pixels when writing a PNG image. Calling imagesavealpha() on the image with a flag TRUE does indeed preserve the alpha values of the anti-aliased text pixels. This is an improvement. Unfortunately, it also overrides the designation of a “transparent” colour by imagecolortransparent(). The result is an image with nicely anti-aliased text, but an opaque colour drawn where the transparent shapes should be.
I resolved this conflict by setting the imagesavealpha() flag only in those cases where I wanted text and didn’t care about having transparent shapes in the image. Fortunately, my application never requires me to have both text labels and a combination of transparent and opaque shapes.
If your application does require this combination, the only option I can see is for you to start with a true-colour image, set the imagesavealpha() flag to TRUE, and then carefully draw only the shapes you want in the opaque or transparent colour values you want. You don’t have the option to use imagecolortransparent() to make parts of the image transparent.
Note that a precondition for all this is that your images be true-colour, not palette-based.
Note also that we had set the imagealphablending() flag to FALSE, because we didn’t want our images to be forced opaque. Leaving the imagealphablending() flag at the default, TRUE, may help you clear this obstacle in your application.