[whatwg] Canvas pixel manipulation and performance

Jason Oster parasyte at kodewerx.org
Sun Nov 29 12:33:17 PST 2009


The patch changed something like this:

for (y in canvasHeight) {
  for (x in canvasWidth) {
    putPixel();
  }
}

To something like this:

for (y in roomHeight) {
  for (x in roomWidth) {
    putPixel();
  }
}
for (rooms_y in canvasHeight) {
  for (rooms_x in canvasWidth) {
    putRoom();
  }
}

This pseudo-code is a bit unintelligible, please bear with me.  Basically, there is a fixed number of "rooms" (256px x 256px images), only about 15 in the smaller stages.  The full map might have a total area for 10 x 3 rooms; half of them are duplicated.  The patch caches the rooms drawn, and then "blits" from the cache into the canvas.

this.fgmap used to be one giant canvas.  Now it is an array of smaller canvases (the size of a single room).  The .render() method draws pixels to the associated ImageData in each case (using the four R,G,B,A elements per pixel, as we are discussing).  In this case, cutting down on the number of pixels that this.fgmap.render() needs to poke into ImageData made the overall drawing approximately 2x faster.

It might be important to note that this.fgmap.render() method also does some tile decoding (to convert the SNES tile format into a usable bitmap), and caches the results.

Does that make more sense?  I know it is difficult to follow unfamiliar code, but I would like to clear up any questions you might have.

Jay

On Nov 29, 2009, at 1:03 PM, Boris Zbarsky wrote:

> On 11/29/09 1:20 PM, Jason Oster wrote:
>>>> Changeset 2b56c4771d5c reduced the number of pixel array elements
>>>> accessed by caching the 256px x 256px "rooms" within the stage map, and
>>>> passing the cached rooms to putImageData().
>>> 
>>> As opposed to doing what before the change?
>> The previous code used a "non-cached" approach.  Where every pixel in the canvas was explicitly drawn into the ImageData array.  Keep in mind, the largest of these was 4864px × 3072px.  If anything, the change took time away from JavaScript and placed it in native code: putImageData().
> 
> I'm not sure I follow.  Looking at the diff, it looks like you used to do a single putImageData call, passing it this.fgmap.render(), right?
> 
> Now you do a bunch of putImageData calls, passing this.fgmap[rooms[i++]].img, where right before that you called this.fgmap[i].render() for a bunch if "i".
> 
> I really don't see how this would have made things faster, unless render() is just not being called on all rooms now.
> 
> -Boris




More information about the whatwg mailing list