Why is putImageData so slow?
This is presumably because modern browsers use hardware acceleration for <canvas>
elements, and getImageData()
/putImageData()
require transferring image data from the graphics card to the host or vice versa. That is notoriously slow.
Using two canvases will be faster because all the data stays on the graphics card.
Just a small update on what the best way is to do this. I actually wrote my Bachelor Thesis on High Performance ECMAScript and HTML5 Canvas (pdf, German; password: stackoverflow), so I gathered some expertise on this topic by now. The clearly best solution is to use multiple canvas elements. Drawing from one canvas onto another canvas is just as fast as drawing an arbitrary image to a canvas. Thus "storing" the state of a canvas is just as fast as restoring it later again when using two canvas elements.
This jsPerf testcase shows the various approaches and their benefits and drawbacks very clearly.
Just for completeness, here how you really should do it:
// setup
var buffer = document.createElement('canvas');
buffer.width = canvas.width;
buffer.height = canvas.height;
// save
buffer.getContext('2d').drawImage(canvas, 0, 0);
// restore
canvas.getContext('2d').drawImage(buffer, 0, 0);
This solution is, depending on browser, up to 5000x faster than the one getting the upvotes.
In Firefox 3.6.8 I was able to workaround the slowness of putImageData by using toDataUrl/drawImage instead. For me it's working fast enough that I can call it within handling a mousemove event:
To save:
savedImage = new Image()
savedImage.src = canvas.toDataURL("image/png")
The to restore:
ctx = canvas.getContext('2d')
ctx.drawImage(savedImage,0,0)
Firstly you say you are measuring with Firebug. I actually find that Firebug slows down JS execution considerably, so you may not be getting good numbers for performance.
As for putImageData
, I suspect it's because the functions takes a large JS array containing many Number
objects, all of which have to be checked for range (0..255) and copied into a native canvas buffer.
Maybe once the WebGL ByteArray types are available, this sort of thing can be made quicker.
It does seem odd that base64 decoding and uncompressing the data (with the PNG data URL) is quicker, but that only calls one JS function with one JS string, so it is using mostly native code and types.