Wednesday, 20 February 2013

Fun with Canvas and Web Workers

I was asked to do a short presentation/introduction about HTML5 to some colleagues at work. Apart from mentioning some of the most interesting features that have been encompassed under the HTML5 umbrella, modernizr.js, polyfills and so on, I also gave some notes about ES5. I didn't feel much comfortable with just reading over a mash-up of paragraphs taken from Dive into HTML5 and HTML5 rocks, and as I didn't have any significant code samples of my own apart from this basic experiment with the Canvas from 2 years ago, I decided to prepare a couple of basic pieces (which was pretty fun by the way).

I ended up pairing up together the Canvas and Web Workers in the same sample. Long in short, I have an image on the left and want to paint its gray scale version on the right. We'll leverage the canvas element twice for this, along with the ImageData object. The ImageData object is powerful one, as it gives us raw access to the image pixels. First, we create a temporal canvas in memory (we won't append it to the DOM) that we'll use to obtain the ImageData object corresponding to an existing Image (<img>).

getPixels: function(img) {
		function createCanvas(w, h){
			var c = document.createElement('canvas');
			c.width = w;
			c.height = h;
			return c;
		};
		
		var c = createCanvas(img.width, img.height);
		var ctx = c.getContext('2d');
		ctx.drawImage(img, 0, 0);
		return ctx.getImageData(0,0,c.width,c.height);
	}

As I've said, the ImageData object gives us access to the pixels through its data property (it's a UInt8ClampedArray), so we'll manipulate its RGB components to obtain the Gray Scale version, and then we'll paint the modified ImageData into a Canvas (this time a normal Canvas appended to the DOM).

Probably you'll be wondering how can I justify bringing Web Workers into this scene? Well a Gray Scale filter is pretty fast, but if we repeat it 5000 times we get to that ugly moment when after a long while with the browser unresponsive, we get presented with a "do you want to close this script" window:

The single thread of the JavaScript engine is more than busy with the number crunching necessary for this repeated calculation, and so it can't keep the UI responsive. This warrants launching the filter into a separate thread via Web Workers.

You can check it here

Even when I seriously doubt it can be of much help to anyone, I've also uploaded here the notes that I used for the presentation.

The idea for obtaining an ImageData object from an Image, and the code for the Gray Scale filter were taken from this cute article

By the way, the band in the picture are the almighty Downfall of Gaia, Gods of Blackened Crust

No comments:

Post a Comment