移动设备上Canvas的GetImageData() / PutImageData()性能很糟糕

8

我正在制作一个HTML5游戏,在地图开始加载我的精灵时,我使用GetImageData() /循环所有图像/ PutImageData()进行一些处理。

这在我的电脑上运行得非常好,然而,在我的手机上运行极其缓慢。

PC: 5-6 ms
iPhone 4: 300-600 ms
Android HTC Desire S: 2500-3000 ms

我进行了一些非常基本的基准测试,得出GetImageData和PutImageData都运行得非常快,而循环内容却耗时较长。我当然预计在手机上会有速度放缓,但1000倍听起来有点过分了,我的HTC加载需要大约4分钟,所以这是行不通的。此外,游戏中其他所有内容的速度都非常合理(主要是因为屏幕非常小,但仍然出乎意料地适用于手机上的JS)。
在处理中,我基本上是将精灵“变暗”到一定程度。我只是循环遍历所有像素,并将它们乘以一个<1的值。就是这样。
由于这太慢了...有没有更好的方法,可以利用Canvas功能(合成、不透明度等),而无需一个一个地循环遍历所有像素?
注意:此图层具有一些100%透明像素和一些100%不透明像素。两者都需要保持100%不透明或100%透明。
我想到的一些行不通的事情:
1)在新画布中绘制精灵,具有较低的不透明度。这行不通,因为我需要精灵保持不透明,只是变暗了。
2)绘制精灵,并在其上绘制半透明黑色矩形。这会使它们变暗,但也会使我的透明像素不再透明...
有什么想法吗?
这是我的代码,以防您在其中看到了一些可怕的愚蠢之处:
function DarkenCanvas(baseImage, ratio) {
    var tmpCanvas = document.createElement("canvas");
    tmpCanvas.width = baseImage.width;
    tmpCanvas.height = baseImage.height;
    var ctx = tmpCanvas.getContext("2d");
    ctx.drawImage(baseImage, 0, 0);

    var pixelData = ctx.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height);
    var length = pixelData.data.length;
    for (var i = 0; i < length; i+= 4) {
        pixelData.data[i] = pixelData.data[i] * ratio;
        pixelData.data[i + 1] = pixelData.data[i + 1] * ratio;
        pixelData.data[i + 2] = pixelData.data[i + 2] * ratio;
    }

    ctx.putImageData(pixelData, 0, 0);
    return tmpCanvas
}

编辑: 这是我想在图像上做的事情示例:
原始图片: http://www.crystalgears.com/isoengine/sprites-ground.png
变暗后的图片: http://www.crystalgears.com/isoengine/sprites-ground_darkened.png

谢谢!
丹尼尔


请向我们展示你试图实现的效果的前后照片。 - Simon Sarris
刚刚在问题底部添加了链接。谢谢! - Daniel Magliola
3
你只是想让一些精灵变暗吗?请不要使用每个像素图像数据来实现。只需在顶部绘制半透明的黑色(使用不同的 globalCompositeOperation 以保留 alpha 值)。 - Phrogz
谢谢!这正是我要找的答案!我这样做是因为在互联网时代之前,我就是这样做的,所以这是我所知道的全部。我应该使用哪种组合?另外,你能把这个发表成答案吗?这样我就可以尽可能地给你点赞了。谢谢! - Daniel Magliola
2个回答

12

Phrogz的想法是正确的。你只需要使用“源于顶部”(source-atop)的globalCompositeOperation在整个画布上涂上半透明的黑色(或比例透明的黑色)。

像这样:http://jsfiddle.net/F4cNg/

实际上,有一个很好的问题与此类似,但作者已经删除了,这真是太可惜了。即使使用复杂的形状,也可以对绘制的内容进行深色遮罩。它们可能对你没有帮助,但出于完整性考虑,以及为那些正在搜索“加深画布”的人提供帮助(其中一些区域保持亮度),可以使用“异或”(xor)globalCompositeOperation来实现,如下所示:

http://jsfiddle.net/k6Xwy/1/


非常感谢,这正是我所需要的。我考虑将绘图的最终结果变暗。XOR技巧很酷,比我原本计划的要简单得多,但即使没有它,我也可以用“孔洞”来绘制矩形。此时,我不再关心我的透明像素。然而,我不采用这种方法,因为我正在使用ISO透视,而我的方法虽然慢得多,但让我在一些我关心的事情上更加准确。谢谢你的提示,我肯定会在某个时候用得到。谢谢! - Daniel Magliola

11

你看过这个性能提示吗?: http://ajaxian.com/archives/canvas-image-data-optimization-tip

他们讨论了通过减少对DOM的调用来提高性能。

因此,根据这个提示,你可以尝试:

var pixel = ctx.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height);
    pixelData=pixel.data; // detach the pixel array from DOM
    var length = pixelData.length;
    for (var i = 0; i < length; i+= 4) {
        pixelData[i] = pixelData[i] * ratio;
        pixelData[i + 1] = pixelData[i + 1] * ratio;
        pixelData[i + 2] = pixelData[i + 2] * ratio;
    }

你的.data调用可能会拖慢你的速度。


绝对正确。虽然差别不大,但可以节省一些毫秒。 - Amit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接