WebDev Tips - Alpha mask PNG technique

Reducing the file size of PNG images by extracting the alpha mask.


When you have to build a layer based client side product configurator like Land Rover or Scrambler, the first problem that you face is certainly related to the load of the assets that the user will have to download.

We are talking about hundreds of image layers to multiply by n views.
Images network list

Assuming that we already have the PNG layers generated from a 3D software, the problem is now how to reduce their weight without losing quality?

Well, the technique that we adopted was able to reduce up to 50% the total weight of the images.

Let’s start for example with the following picture: it is an already compressed PNG image that weights about 400KB.

Example png image ~ 400KB

You can compress the source image with the tool that you like. 
Personally I like to use TinyPng which also offers a good API service, but there are many other equally efficient.

The purpose of the technique we used is to separate the alpha mask from the color level.

To do this you can use a CLI image editing software like ImageMagick, which is free to use and has a large support community.

$ convert bike.png -alpha extract -colors 8 alpha.png

This line of code:

-alpha extract -colors 8

extracts the alpha channel from the original PNG and reduces the number of colors of the image to 8 before writing the result to another file called “alpha.png”. With 8 color you can easily keep image edges nice and smooth.

alpha.png - no transparency

As you can see now the image alpha.png has no transparency now.

To get i back you can write :

$ convert alpha.png -transparent white alpha.png
alpha.png — with transparency

Now you can remove the alpha channel from the original image by turning it into a JPG to reduce its weight.

$ convert -flatten bike.png bike.jpg
bike.jpg ~ 200KB

Merge color JPG and alpha PNG

At this point we have to rebuild our image with the JPG and alpha.

This is really simple using an HTML5 canvas

ctx.drawImage('bike.jpg',0,0,500,298);
ctx.globalCompositeOperation = 'xor';
ctx.drawImage('alpha.png',0,0,500,298);

First we draw the color JPG image to canvas, then we change the context globalCompositeOperation property to XOR and then we draw the “alpha.png” image.

With globalCompositeOperation = ‘xor’ the source image is combined by using an exclusive OR with the destination image.

Live example

Live example canvas

What if I say you can actually use two JPG to achieve the same result?

That is possible thanks to a technology known as WebGL. Here is an example using PIXI.JS Framework.

WebGL Example

Anyway, if you use a 8 colors PNG, there aren’t big advantages using a JPG also for the alpha mask, because the file size is almost the same.


So, at the end of the process, let’s see how much we have gained in terms of traffic data:

Original PNG file bike.png : 400KB

Converted to JPG bike.jpg : 200KB

Alpha channel PNG alpha.png : 25KB

Total converted image size bike.jpg + alpha.png : ~ 225KB

Total save : ~ 44%

This can be translated in money saving if you are using, for example, a CDN to serve your assets.

Thank you for reading.

Bye

Show your support

Clapping shows how much you appreciated Luca’s story.