Imagick: Maintain (fake) transparency when saving as JPEG

I haven’t blogged in a while (have been busy you know), so I’ve decided to share this small piece of knowledge I’ve obtained by experimenting. I wrote a small test app (it’s for a feature of the next version of Zend Server – maybe I’ll share it one day when the API is stable), which does some image manipulation with the ImageMagick extension.

For those of you who don’t know ImageMagick allows one to preform pretty cool stuff on images – except for the usual drawing, conversion, rotation, rescaling etc., it also exposes some API to easily preform neat effects, like drop shadow, round corners and my newest favorite (apparently only available in the very latest builds of the extension) – the Polaroid effect.

In his blog Mikko Koppanen, the author of the ImageMagick PHP extension, shows how to create drop shadows (as well as other neat things – you should check out his blog!), but in his examples Mikko will always save as PNG, which is something I dare to say most web users will not do, and prefer saving as JPEG.

Problem with many of those effects, is that they leave parts of the image as transparent. When saving the picture as JPEG (as I do, since saving as PNG produces too big files), these transparent areas appear as black.

So after some experimenting, I’ve found out that the way to work around this is to composite another opaque layer as your background layer, filled with your background color of choice (white in my case). You will of course loose the ability to place the picture on other background colors and still have a nice “transparency” look – but as long as you stick to the background color you’ve set, it will look great.

Here is a code sample producing the same thumbnail + drop shadow as in Mikko’s example, but saving it with white matte color as JPEG:


$bgColor = '#ffffff'; // End result will have a white background

/* This was taken from Mikko's example */
$im = new Imagick( 'strawberry.png' );
$im->thumbnailImage( 200, null );
$im->roundCorners( 5, 5 );

$shadow = $im->clone();
$shadow->setImageBackgroundColor( new ImagickPixel( 'black' ) );
$shadow->shadowImage( 80, 3, 5, 5 );
$shadow->compositeImage( $im, Imagick::COMPOSITE_OVER, 0, 0 );

/* My addition: clone the entire image again to create the background layer */
$bg = $shadow->clone();

/* I'm using colorFloodFiilImage with high tolerance to paint it all white - maybe there are 'cleaner' ways to do it though */
$bg->colorFloodFillImage($bgColor, 100, '#777777', 0, 0);
$bg->compositeImage($shadow, Imagick::COMPOSITE_OVER, 0, 0);

/* Display the image */
header( "Content-Type: image/jpeg" );
echo $bg;

While there’s another step in the way, and the image will only look good on white backgrounds, you can now save it as a JPEG file with good compression and acceptable file size.