为什么putImageData这么慢?

我正在用各种(复杂)的东西绘制到一个相对较大的canvas。 然后我想保存Canvas的状态,这样我就可以快速地将它重置到现在的状态。 我为此使用getImageData并将数据存储在variables中。 然后,我将更多的东西画到canvas上,然后使用putImageData将Canvas重置到它保存状态的位置。

但是,事实certificate,putImageData是非常缓慢的。 事实上,它比从头开始重绘整个Canvas要慢很多,这涉及几个drawImage覆盖大部分曲面,以及超过40.000个lineTo操作,随后是中风和填充。

重新绘制约2000 x 5000像素的canvas从头开始需要约170毫秒,使用putImageData虽然需要高达240ms。 为什么putImageData与重绘canvas相比如此之慢,尽pipe重画canvas包括用drawImage填充几乎整个canvas,然后用lineTo,stroke和fill再次填充大约50%的canvas。 所以基本上每个像素在重绘时至less触摸一次。

因为drawImage似乎比putImageData快得多(毕竟,重绘canvas的drawImage部分less于30毫秒)。 我决定尝试保存canvas的状态,而不是使用getImageData,而是使用canvas.toDataURL,然后从数据URL中创build一个图像,然后将其放入drawImage中以将其绘制到canvas上。 原来这整个过程更快,只需要大约35毫秒来完成。

那么,为什么putImageData比替代方法(使用getDataURL或简单地重绘)慢得多? 我怎么能进一步加快速度? 是否有,如果一般来说最好的方式来存储canvas的状态?

(所有的数字都使用Firefox内的Firebug进行测量)

只是一个小的更新什么是最好的办法是做到这一点。 其实我写了关于高性能ECMAScript和HTML5 Canvas的学士论文(pdf,德文),所以我现在就收集了一些关于这个主题的专业知识。 最好的解决scheme是使用多个canvas元素。 从一个canvas绘制到另一个canvas上,就像在canvas上绘制任意图像一样快。 因此,“存储”canvas的状态与使用两个canvas元素时再次恢复一样快。

这个jsPerftesting用例非常清楚地显示了各种方法及其优点和缺点。

只是为了完整性,在这里你应该怎么做:

// 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); 

这个解决scheme取决于浏览器,比获得upvotes的速度快5000倍。

在Firefox 3.6.8中,我可以通过使用toDataUrl / drawImage来解决putImageData的缓慢问题。 对我来说,它的工作速度足够快,我可以在处理mousemove事件中调用它:

保存:

 savedImage = new Image() savedImage.src = canvas.toDataURL("image/png") 

恢复:

 ctx = canvas.getContext('2d') ctx.drawImage(savedImage,0,0) 

首先你说你用Firebug测量。 实际上我发现Firebug大大减慢了JS的执行速度,所以你可能不会获得很好的性能。

至于putImageData ,我怀疑这是因为函数需要一个包含许多Number对象的大型JS数组,所有这些对象都必须检查范围(0..255)并复制到本机canvas缓冲区中。

也许一旦WebGL ByteArraytypes可用,这种事情可以做得更快。

这看起来很奇怪,base64解码和解压数据(使用PNG数据的URL)更快,但只使用一个JSstring调用一个JS函数,所以它主要使用本地代码和types。