admin管理员组文章数量:1313168
Providing you have an array of colors and you want to fill a canvas with its content, the fastest way I'm aware of is:
var my_array = /* already have it */;
var img_data = ctx.createImageData(canvas.width, canvas.height);
for (var i=0; i<canvas.width*canvas.height; ++i)
img_data[i] = my_array[i];
ctx.putImageData(img_data,0,0);
This seems too slow as I'm copying the entire array twice! One to make the img_data
and another to put it on the canvas. Isn't there a way to simply plug the original my_array
into the element?
Providing you have an array of colors and you want to fill a canvas with its content, the fastest way I'm aware of is:
var my_array = /* already have it */;
var img_data = ctx.createImageData(canvas.width, canvas.height);
for (var i=0; i<canvas.width*canvas.height; ++i)
img_data[i] = my_array[i];
ctx.putImageData(img_data,0,0);
This seems too slow as I'm copying the entire array twice! One to make the img_data
and another to put it on the canvas. Isn't there a way to simply plug the original my_array
into the element?
-
Why are you calculating
canvas.width*canvas.height
at every iteration of the loop? – some Commented Dec 28, 2013 at 5:14 -
That is just an example, I'm actually just using
img_data.data.set
. I'm also angry that JS engines aren't able to optimize that so consider it a protest. – MaiaVictor Commented Dec 28, 2013 at 5:27 -
@MaiaVictor The reason it might not be optimised is that you might change the value of
.width
or.height
properties inside the loop. And it most likely is optimised when you are not. – pishpish Commented Jan 16, 2018 at 13:57
2 Answers
Reset to default 5You should directly make use of a typed array instead of a javascript array for your main putations, 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 ) {
// ponent 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./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 ing 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);
}
本文标签: javascriptFast way to fill a canvas with an array of rgba colorsStack Overflow
版权声明:本文标题:javascript - Fast way to fill a canvas with an array of [rgba] colors - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741926662a2405329.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论