views:

66

answers:

1

I am working with a relatively large Canvas where various (complex) stuff is drawn to. I then want to save the Canvas' state, so I can quickly reset it to the state it now is at a later point. I use getImageData for this and store the data in a variable. I then draw some more stuff to the canvas and will later reset the Canvas to where it was when I saved it's state, using putImageData.

However, it turns out, that putImageData is very slow. Infact, it is slower than simply redrawing the entire Canvas from scratch, which involves several drawImage covering most of the surface, and over 40.000 lineTo operations followed up by strokes and fills.

Redrawing the approx 2000 x 5000 pixel canvas from scratch takes ~ 170ms, using putImageData though takes whopping 240ms. Why is putImageData so slow compared to redrawing the canvas, although redrawing the canvas involves filling nearly the entire canvas with drawImage and then again filling roughly 50% of the canvas with polygons using lineTo, stroke and fill. So basicly every single pixel ist touched at least once when redrawing.

Because drawImage seems to be so much faster then putImageData (after all, the drawImage part of redrawing the canvas takes less than 30 ms). I decided to try to save the state of the canvas not using getImageData, but instead using canvas.toDataURL and then creating an Image from the data URL which I would stick into drawImage to draw it to the canvas. Turns out this whole procedure is much faster and only takes roughly 35ms to complete.

So why is putImageData so much slower then the alternatives (using getDataURL or simply redrawing)? How could I speed things up further? Is there and if, what is in general the best way to store the state of a canvas?

(All the numbers are measured using Firebug from within Firefox)

+2  A: 

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.

andrewmu
since my numbers are for the most part from native code execution, I doubt that Firebug will have a significant effect on them. Nevertheless, we are not talking about fractions of milliseconds, but actually but actually a quarter of a second for basicly a single function call (putImageData). Bad performance due to the JS array might be. I'll check that by testing how fast JS can handle (copy, manipulate, etc) such an array outside of putImageData.
Daniel Baulig
Continuation: Deocding, uncompressing,etc is not happening at the point where the canvas state is restored, but when it is saved. So this happens only once and I actually didn't measure it, because how long it takes to save the state is not of much concern. The critical part is restoring the canvas state. At that point I have the Image object long created. So if the Image object contains it's data in a native buffer, this indeed could be the cause of the problem (or better the lack of it for the drawImage approach).
Daniel Baulig
I know that `putImageData` performance can be ok (80-100fps with a 480x320 buffer) - but you are dealing with very large images!
andrewmu
Ah, +1 for mentioning WebGL ByteArray. I've been looking for information about Javascript binary arrays and your comment helped me to find it. Here is the current discussion: http://www.listware.net/201009/w3c-public-webapps/21504-arraybuffer-and-bytearray-questions.html. And here is the proposed standard: https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/doc/spec/TypedArray-spec.html
kanaka