Fast way to fill a canvas with an array of [rgba] colors

廉价感情. 提交于 2019-12-04 19:07:51

You should directly make use of a typed array instead of a javascript array for your main computations, so you won't have to convert later :

var myArray = new Uint8Array(pixelCount);

or

var myArray = new Uint8ClampedArray(pixelCount);

The access is just the same as a standard js array :

for (var pxIndex = 0; pxIndex<myArray.length; pxIndex+=4 ) {
    // component for the (pxIndex >>2)th pixel :
    var r = myArray[pxIndex  ];
    var g = myArray[pxIndex+1];
    var b = myArray[pxIndex+2];
    var a = myArray[pxIndex+3];
}

This way you just have to copy this array to update the screen :

ctx.putImageData(my_array,0,0);

Notice that you can retrieve the buffer of this array, and have another view on this array.
This way you can also have, say, a 32 bit view to perform copy operations 4 bytes at a time.

var sourceBuffer32  = new UInt32Array(myArray.buffer);  

If you are using this 32 view, remember that the endianness of each system might be different, which leads to load either ABGR ( PC / mac ) or RGBA in the array. This changes nothing for a copy, but might be annoying in some cases (:-)).

Don't forget also you can copy an array buffer with the ArrayBuffer slice function :

var myArrayCopy = new  new Uint8ClampedArray(myArray.buffer.slice(0));

You can know the endianness with this small function :

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');
}

you can reverse a 32 bit ( ABCD -> DCBA ) with the following :

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

Use 32-bits typed arrays instead. This will allow you to copy the whole "pixel" (4 bytes) for each iteration reducing number of iterations to 25%:

var buffer32 = new Uint32Array(img_data.data.buffer),
    len = buffer32.length;

while(len--)
    buffer32[len] = my_array[len];

ctx.putImageData(img_data,0,0);

This means you also need to provide your my_array as a 32-bits array as well. Hope this helps.

Update to address "endianess":

If you have data is coming from a system (or for other reasons) using a different endianess (LSB versus MSB) you can either reverse the byte-order if they are different or use a DataView instead where you can specify to use for example little-endian by an optional flag using its set/get methods.

var my_buffer = new ArrayBuffer(size);
var dataView = new DataView(my_buffer);

var uint32lsb = dataView.getUint32(pos, true); // true = little-endian

toggle the flag depending on what's in your source buffer.

To test your destination system for LSB/MSB (LSB = Least Significant Bit first or little-endian, MSB = Most Significant Bit first or big-endian) you can do:

function isLSB() {
    var b = new Uint8Array([255, 0]);
    return ((new Uint16Array(b, b.buffer))[0] === 255);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!