能否直接将Uint8ClampedArray绘制到画布上?

9
在JavaScript中,您可以直接使用Uint8ClampedArray操纵图像的像素。在将该图像渲染到画布上之前,您需要执行以下操作:
  1. 创建一个ImageData对象。

  2. 使用ImageData.data.setUint8ClampedArray渲染到ImageData对象中。

  3. 使用context.putImageDataImageData对象绘制到画布上。

因此,从具有图像的Uint8ClampedArray到实际在屏幕上看到它,需要进行不少于3次O(N)的复制操作。对于大型图像,这可能非常有害。一种解决方案是缓存ImageData对象,并将指针imageData.data替换为您的Uint8ClampedArray - 但是imageData.data是只读的!有没有更有效的方法来做到这一点?

1
注意:虽然我的答案似乎是正确的(也可能是唯一的)方法来完成你所要求的,但它可能无法在IE10或更低版本上运行(但我没有副本进行测试)。请参见我的答案中的评论。 - apsillers
1个回答

9
根据 MDN关于ImageData的页面ImageData(array, width, height) 构造函数接受第一个参数,即

包含图像基础像素表示的 Uint8ClampedArray

快速测试验证了该参数被引用使用(即未复制),作为 ImageDatadata 属性。此测试记录了 true

var arr = new Uint8ClampedArray([0,0,0,0]);
var idata = new ImageData(arr,1,1);
console.log(idata.data === arr);

因此,您可以通过在第一步中构建具有引用Uint8ClampedArray对象的data属性的ImageData对象来消除第二步。

3
点赞。顺便说一句,虽然这在大多数浏览器中都有效,但是 IE 浏览器的奇怪版本(IE9 和我认为 IE10)并不使用 Uint8ClampedArrays - markE
此外,imagedata构造器会抛出“应为函数”错误。 - Hans
@Hans,根据MDN和当前的WHATWG规范,这是一个合法的构造函数。您是否使用了该答案中的精确代码示例?用的是哪个浏览器? - apsillers
还有什么进一步的改进可以做吗?我正在大量使用Uint8ClampedArray上的直接像素值操作,如果我可以将该数组直接交给画布进行渲染,那将是非常棒的。或者直接编辑ImageData也可以。 - Frank
@Frank 您可以创建一个 ImageData,并操纵其 data 属性(一个 Uint8ClampedArray),然后每次更改像素时调用 putImageData。为了获得更好的性能,请使用一个边界框来调用 putImageData ,只重新绘制子集像素(即仅向画布提供在 [(x,y),(x+w,y+h)] 中的 ImageData 像素)。 (请参见 https://jsfiddle.net/3hcny297/1)。如果您还有更多问题,请发布一个新的问题帖子以获得更好的答案(如果您这样做,请给我发送链接!)。 - apsillers
谢谢!我发现我只需要创建ImageData一次,由于它的数据属性是指向Uint8ClampedArray的指针,因此我只需要在内存中保留一个Uint8ClampedArray,可以随时修改,这还不错。主要缺点是当我的r、g、b、a值传递到画布进行渲染时,它们显然会被计算为1个预先计算的alpha值。为了解决这个问题,我使用getContext("2d", { alpha:false });获取我的画布上下文,但是我相当确定当我使用putImageData时,r、g、b、a值仍然会被修改。 - Frank

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