Canvas getImageData()获得最佳性能.要一次提取所有数据还是一个?

lou*_*ong 6 javascript performance canvas

我需要扫描画布图像中的每个像素并做一些颜色等等.为了获得最佳性能,我应该一次性获取所有数据并通过阵列处理它吗?或者我应该在处理每个像素时调用它们.

基本上......

data = context.getImageData(x, y, height, width);
Run Code Online (Sandbox Code Playgroud)

VS

data = context.getImageData(x, y, 1, 1); //in a loop height*width times.
Run Code Online (Sandbox Code Playgroud)

Gam*_*ist 16

通过一次抓取图像,您将获得更高的性能,因为: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);
Run Code Online (Sandbox Code Playgroud)

然后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);
Run Code Online (Sandbox Code Playgroud)

如果你只处理灰色阴影,那么R = G = B,所以请注意

sourceBuffer8[4*i]>Threshold
Run Code Online (Sandbox Code Playgroud)

并且您可以使用UInt32Array视图一次将第i个像素设置为黑色:

sourceBuffer32[i]=0xff000000;
Run Code Online (Sandbox Code Playgroud)

设置为任何颜色/ alpha:

sourceBuffer32[i]= (A<<24) | (B<<16) | (G<<8) | R ;
Run Code Online (Sandbox Code Playgroud)

或者只是任何颜色:

sourceBuffer32[i]= 0xff000000 | (B<<16) | (G<<8) | R ;
Run Code Online (Sandbox Code Playgroud)

(确保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);
};
Run Code Online (Sandbox Code Playgroud)


Ori*_*iol 5

除了 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);
Run Code Online (Sandbox Code Playgroud)