Reducing File Size for Images With Alpha Channels

We’ve been building a lot of spritesheets and other images for use in HTML5 projects. To preserve alpha channels, you need to use PNG32, which can get pretty large for some types of content because it’s a lossless compression format.

I wanted to find a way to reduce these file sizes, so that games and other experience would load more quickly. Ultimately I settled on a solution that separates an image into two parts: a JPG with all of the RGB information, and a PNG32 with the alpha channel. In my testing, the end results vary wildly – some content is larger when split, but some content can see a reduction in size of up to 70%. This is can have a pretty significant effect when applied to a 2MB sprite sheet image!

To enable this, I added a utility method in EaselJS that takes these two images and merges them back into a single canvas, which can be used for sprite sheets, drawn to other canvases, added to the DOM, or used as an image source via toDataURL(). The great thing is, it runs REALLY fast (a few milliseconds for most large images), because it leverages composite operations.

var combinedCanvas = SpriteSheetUtils.mergeAlpha(rgbImage, alphaImage);

For initial testing, I prepared my images using Photoshop, but this was slow, time consuming, and cumbersome. To make the process easier, I wrote a simple AIR application codenamed “Omega” (as in, alpha and omega – such boundless wit!). It will prepare multiple images at a time, and display a report showing the original and total resulting sizes, so you can decide which assets to use it on.

It’s currently very barebones, and requires you to do everything via the command line. In some ways, this is kind of nice – you can create batch files or commands to quickly run it on specific files or folders of images – but I plan to add a UI soon. I’m also planning to open source it the not too distant future, but I thought I should share it in the interim so others can use it.

You can grab “Omega” here. It comes with a text file that describes all of the available arguments and shows some examples. It works great with Zoë or your spritesheet creation tool of choice.

One important note: if you’re using Omega on OSX, you’ll need to quit the application before running it a second time. For some reason AIR apps on OSX do not receive parameters with invocation events if they are already open.

Grant Skinner

The "g" in gskinner. Also the "skinner".

@gskinner

9 Comments

  1. Rather than jpeg compressing an image with black background, I’d suggest to repeatedly blur the image whilst copying the original over the top each time, prior to encoding. in psuedocode:

    copy = image.clone();
    for( i = 0 to 10){
    image = image.blur();
    image = image.overlay(copy)
    }
    jpg = image.colorchannel.toJpeg()
    png = copy.alphachannel.toPng()

    When the image is decompressed all the blur will disappear because it is under fully transparent alpha.

    This will result in a color channel that is far more suitable for jpg encoding as it has no sharp edges around the image boundary. You’ll find you can use lower jpg quality (thus smaller filesize) without introducing black halo artifacts around the decompressed image.

    Also, the alpha channel can be compressed as png8, which will be smaller too.

    Regards, Jake

  2. Øyvind Nordhagen August 30, 2012 at 4:42pm

    Why not just use ImageAlpha?

  3. I was messing with this today… you can also get further savings (30-40% of the alpha png) by using pngcrush with the -c 4 option.

  4. If the alpha png only contains the two colors transparent and black, then you can reduce it quite a lot using `pngquant 2 image.png` making it a png8 with a 2-colro pallette. Then run pngcrush or optipng on it to further improve the compression.

    If there are more colors, but still less than 256, you can detect the number of colors with a histogram before you pngquant it down to that number of colors. We use that technique in https://github.com/One-com/assetgraph-builder/blob/master/lib/PngQuantWithHistogram.js

  5. btw jpeg rate could probably be improved by some kind of “glow” around the edge, letting colors “fade” into the darkness instead of sharp edge.

  6. The mergeAlpha function in the spriteSheetUtil doesnt work well when used in Chrome. I believe this is because of the destination-in Global composite operation setting.
    It seems like destination-in is not supported well in Chrome Browser (Tested in Chrome 25 ).
    http://www.rekim.com/2011/02/11/html5-canvas-globalcompositeoperation-browser-handling/

    Is there any other approach to merge alpha and RGB images?

  7. Thanks for creating this tool. But i see risk in using this mergeAlpha approach. I’ve just found http://pngmini.com/ ( Mac ) and http://tinypng.org/ which are doing great job.

    They create 8bit PNG with smooth alpha.

  8. The omega app is not working . Can you give another link for download grant ?

Comments are closed.