
Lossy RGBA images are something that always has been missing on the web (If you ignore Mozilla's brief support of JNG, that is). Nowadays, browsers typically support JPG, PNG, GIF, ICO, and BMP. There is obviously some feature overlap, but there isn't any good choice for true color images which also happen to use alpha.
Generally your choices boil down to JPG and PNG. If you ignore animated GIFs (which are pretty useless for Canvas usage), PNG can easily replace GIF, ICO, and of course BMP.
Today, I'll demonstrate how you can create RGBA images which use JPG for the RGB channels. Since this can decrease the file size up to 75%, it's a very easy and intriguing way to speed up the loading time of your game or application. (If you are currently using heavy PNG32 images, that is.)
PNG's interesting modes are:
JPG's interesting modes are rather straightforward in comparison. There is:
All 3 methods below utilize my handy renderToCanvas utility function. For convenience the code is replicated here:
var renderToCanvas = function (width, height, renderFunction) {
var buffer = document.createElement('canvas');
buffer.width = width;
buffer.height = height;
renderFunction(buffer.getContext('2d'));
return buffer;
};This function takes one RGB image (e.g. a JPEG) and another one which contains an inverse alpha mask (PNG8). It combines both using the Porter-Duff xor rule. The result is an RGBA image.

var toRgbaFromInverseAlphaMask = function (rgbImage, inverseAlphaMask) {
var width = rgbImage.width, height = rgbImage.height;
return renderToCanvas(width, height, function (ctx) {
ctx.drawImage(rgbImage, 0, 0);
ctx.globalCompositeOperation = 'xor';
ctx.drawImage(inverseAlphaMask, 0, 0);
});
};Thanks to this function you can now use that one supported lossy image format and still get your alpha channel. This can help you to reduce the download size drastically. The downside is that there is another file to download, but this problem can be also solved by using a single blob file for all resources (I'll cover this topic in the future).
Demo: toRgbaFromInverseAlphaMask.html
Another similar function I've written uses a gray-scale JPEG for the alpha channel. This can help reducing the file size further if the alpha channel is somewhat "gradienty" in nature. That is, it doesn't look like a vector clipping path. Rendered flames or explosions are a good example for cases where this makes a lot of sense.

var toRgbaFromAlphaChannel = function (rgbImage, alphaChannelImage) {
var width = rgbImage.width, height = rgbImage.height;
return renderToCanvas(width, height, function (ctx) {
var alpha = renderToCanvas(width, height, function (ctx) {
var id, data, i;
ctx.drawImage(alphaChannelImage, 0, 0);
id = ctx.getImageData(0, 0, width, height);
data = id.data;
for (i = data.length - 1; i > 0; i -= 4) {
data[i] = 255 - data[i - 3];
}
ctx.clearRect(0, 0, width, height);
ctx.putImageData(id, 0, 0);
});
ctx.drawImage(rgbImage, 0, 0);
ctx.globalCompositeOperation = 'xor';
ctx.drawImage(alpha, 0, 0);
});
};This one even uses renderToCanvas twice. Once for creating an inverse alpha mask from the gray-scale JPEG and the other one for combining those two things.
I could have read the pixel data of both images to create an RGBA image right away, but since reading and iterating over all those pixels is a tad slow, I decided to do only half of the work in JavaScript and let the native code take care of the other half. toRgbaFromInverseAlphaMask could have been reused here, but since you'll probably only use one of those functions I decided against that. Well, that function is very short either way. There isn't much to gain there.
Note: This one won't work locally (i.e. from file://) since it uses getImageData, which requires read access. For security reasons reading pixel data is restricted. You can only read pixels from images which came from the same origin (domain) as your script.
If you run it locally, Firefox will show an error message like the following one:
Error: uncaught exception: [Exception... "Security error" code: "1000" nsresult: "0x805303e8 (NS_ERROR_DOM_SECURITY_ERR)" location: "file:///X:/example.js Line: XX"]
Chrome's error message is basically the same:
Uncaught Error: SECURITY_ERR: DOM Exception 18
Opera allows it for some reason. Well, strictly speaking the origin is the same; the script and the image came from the local file system. So, technically Opera behaves correctly, I guess.
Either way, I highly recommend to run a local server for testing. You'll need one at some point. Be it for read access to pixel data, auto generating blob files, or for passing data or messages around.
Demo: toRgbaFromAlphaChannel.html
This one takes one RGB image and a clipping path which uses SVG's path data attribute format. It doesn't cover the whole specs, just enough to make it work with the kind of path data Inkscape generates.

var toRgbaFromSvgClippingPathData = function (rgbImage, svgClippingPathData) {
var width = rgbImage.width, height = rgbImage.height;
return renderToCanvas(width, height, function (ctx) {
var p, i, len;
p = svgClippingPathData.split(/,| /);
ctx.beginPath();
for (i = 0, len = p.length; i < len; i++) {
switch (p[i]) {
case 'M':
ctx.moveTo(p[i + 1], p[i + 2]);
i += 2;
break;
case 'L':
ctx.lineTo(p[i + 1], p[i + 2]);
i += 2;
break;
case 'C':
ctx.bezierCurveTo(p[i + 1], p[i + 2], p[i + 3], p[i + 4], p[i + 5], p[i + 6]);
i += 6;
break;
case 'z':
case 'Z':
ctx.closePath();
break;
default:
if (window.console) {
window.console.log(p[i]);
}
break;
}
}
ctx.clip();
ctx.drawImage(rgbImage, 0, 0);
});
};Demo: toRgbaFromSvgClippingPathData.html
Download: rgba-examples.zip (122kb – zero-clause BSD)
Comments
JPEG2000
Safari supports JPEG2k, which is lossy and has alpha.
re: JPEG2000
Yea, it does support alpha. Same goes for JPEG XR (aka Windows Media Photo or HD Photo). But it will take yet another decade until all patents (and submarine patents) have expired.
I'd rather see a comeback of JNG. (I'm actually writing a short article about that right now.)
APNG
For animation with full color and alphachannels, you might want to take a look at APNG. Supported in Firefox and Opera. I suspect webkit supports it as well. However, it'll probably take until IE 12.0 until it's implemented there.
re: APNG
Yes, I know about APNG.
It's not supported by Webkit.
It's also not lossy (i.e. it's really huge) and for Canvas it's also completely useless.
JNG would be great though.
Post new comment