我需要扫描画布图像中的每个像素并调整颜色等。为了获得最佳性能,我是否应该一次性获取所有数据并通过数组处理它?或者我应该在处理每个像素时调用它。
所以基本上...
data = context.getImageData(x, y, height, width);
VS
data = context.getImageData(x, y, 1, 1); //in a loop height*width times.
我需要扫描画布图像中的每个像素并调整颜色等。为了获得最佳性能,我是否应该一次性获取所有数据并通过数组处理它?或者我应该在处理每个像素时调用它。
所以基本上...
data = context.getImageData(x, y, height, width);
VS
data = context.getImageData(x, y, 1, 1); //in a loop height*width times.
通过一次抓取图像,您将获得更高的性能,因为:a) 对数组的(连续)访问比函数调用快得多。b) 尤其是当此函数是具有一些开销的 DOM 对象的方法时。c)并且可能存在可能延迟响应的缓冲区刷新问题(如果画布在视线内......或不取决于双缓冲实现)。
所以去一次性抓住。
我建议您研究 Javascript Typed Arrays 以充分利用 imageData 结果。
如果我可以引用自己的话,看看你如何在我的这个旧帖子中快速处理像素(看 2):
(我在下面引用了相关部分:)
您可以使用以下命令在 ImageData 上获得 UInt32Array 视图:
var myGetImageData = myTempCanvas.getImageData(0,0,sizeX, sizeY);
var sourceBuffer32 = new Uint32Array(myGetImageData.data.buffer);
然后 sourceBuffer32[i] 包含打包成一个无符号 32 位 int 的红色、绿色、蓝色和透明度。将其与 0 进行比较以了解像素是否为非黑色( != (0,0,0,0) )
或者您可以使用 Uint8Array 视图更精确:
var myGetImageData = myTempCanvas.getImageData(0,0,sizeX, sizeY);
var sourceBuffer8 = new Uint8Array(myGetImageData.data.buffer);
如果您只处理灰色阴影,则 R=G=B,因此请注意
sourceBuffer8[4*i]>Threshold
您可以使用 UInt32Array 视图一次性将第 i 个像素设置为黑色:
sourceBuffer32[i]=0xff000000;
设置为任何颜色/alpha:
sourceBuffer32[i]= (A<<24) | (B<<16) | (G<<8) | R ;
或只是任何颜色:
sourceBuffer32[i]= 0xff000000 | (B<<16) | (G<<8) | R ;
(确保 R 是四舍五入的)。
听@Ken 的评论,是的,当您开始一次使用 32 位时,字节序可能会成为一个问题。大多数计算机都使用little-endian,因此RGBA在一次处理32位时变为ABGR。
由于它是绝大多数系统,如果处理 32 位整数假设是这种情况,并且您可以 - 为了兼容性 - 在大端系统上写入 32 位结果之前反转您的计算。让我分享这两个功能:
function isLittleEndian() {
// from TooTallNate / endianness.js. https://gist.github.com/TooTallNate/4750953
var b = new ArrayBuffer(4);
var a = new Uint32Array(b);
var c = new Uint8Array(b);
a[0] = 0xdeadbeef;
if (c[0] == 0xef) { isLittleEndian = function() {return true }; return true; }
if (c[0] == 0xde) { isLittleEndian = function() {return false }; return false; }
throw new Error('unknown endianness');
}
function reverseUint32 (uint32) {
var s32 = new Uint32Array(4);
var s8 = new Uint8Array(s32.buffer);
var t32 = new Uint32Array(4);
var t8 = new Uint8Array(t32.buffer);
reverseUint32 = function (x) {
s32[0] = x;
t8[0] = s8[3];
t8[1] = s8[2];
t8[2] = s8[1];
t8[3] = s8[0];
return t32[0];
}
return reverseUint32(uint32);
};
除了 GameAlchemist 所说的,如果你想同时获取或设置一个像素的所有颜色,但你不想检查字节序,你可以使用DataView
:
var data = context.getImageData(0, 0, canvas.width, canvas.height);
var view = new DataView(data.data.buffer);
// Read or set pixel (x,y) as #RRGGBBAA (big endian)
view.getUint32(4 * (x + y*canvas.width));
view.setUint32(4 * (x + y*canvas.width), 0xRRGGBBAA);
// Read or set pixel (x,y) as #AABBGGRR (little endian)
view.getUint32(4 * (x + y*canvas.width), true);
view.setUint32(4 * (x + y*canvas.width), 0xAABBGGRR, true);
// Save changes
ctx.putImageData(data, 0, 0);
这取决于你到底在做什么,但我建议一次抓住它,然后循环通过它。
一次抓取它比逐个像素地抓取它要快,因为通过数组搜索比通过画布搜索要快得多,每个像素一次。
如果您真的需要速度,请查看网络工作者。您可以将每个设置为抓取画布的特定部分,并且由于它们可以同时运行,它们将更好地利用您的 CPU。
getImageData()
根据我使用该功能的经验,如果您一次或单独抓住它,并不足以让您注意到差异。