将数组值复制到画布像素数据的更快方法

时间:2017-12-28 19:38:13

标签: javascript arrays html5 canvas

我有一个网络工作者用javascript解码jpeg图像。

然后它将该图像的像素数据以相同的格式传递给主线程画布图像数据接受。

目标是使用标准将解码的像素数组绘制到canvas元素上:

context.putImageData(imgData, 0, 0);

这看起来很简单,但是我能弄清楚如何做到这一点的唯一方法是创建一个循环,从工作者接收数据的长度,并将像素值1一次复制到imgData.data数组中像这样:

var workerData = //Data received from the worker;

for(i=0;i<workerData.length;i++){
    imgData.data[i] = workerData[i];
}

我试图像这样复制数组:

imgData.data = workerData.slice();

但是,当将它绘制到画布时,它不会更改imageData的值。有没有更快或更简洁的方法来做到这一点?

编辑:我从this post发现你可以像这样使用set:

var canvas = document.createElement('canvas');
var imageData = canvas.getContext('2d').createImageData(width, height);
imageData.data.set(myData);

它完美无缺!

2 个答案:

答案 0 :(得分:0)

 context.putImageData(new ImageData(workerData, 100, 100), 0, 0);

重新创建一个ImageData对象不应该是一个问题,因为它只是一个小包装器。

答案 1 :(得分:0)

ImageData期望的是一个TypedArray的Uint8ClampedArray,但听起来你的工作人员发送的是一个数组。

因此,通过直接在这样的TypedArray中工作,您可能会获得更快的结果。除了可能使工作者部分更快一些之外,它还允许您从工作线程到主线程transfer基础ArrayBuffer,而不必复制数据,从不。 / p>

一旦在主线程上收到了ArrayBuffer,你只需要从它创建一个UInt8ClampedArray视图,并从这个TypedArray创建一个ImageData包装器。

此时,所有主线程将完成的是在缓冲区(最小内存浪费)上创建一个视图,并在此视图上创建一个ImageData包装器(再一次,创建一个简单的对象)。它不会创建甚至读取您的工作人员生成的数据。这将由putImageData完成。

worker.js

data = new UInt32Array(width * height); //can even be an other type of TypedArray
... // fill 'data'
self.postMessage({
  data: data,
  width: width,
  height: height
},
[data.buffer] // tranfer the ArrayBuffer
);

main.js

worker.onmessage = e => {
  const msg = e.data;
  const view = new Uint8ClampedArray(msg.data.buffer);
  const imageData = new ImageData(view, msg.width, msg.height);
  ctx.putImageData(imageData, 0,0);
};

&#13;
&#13;
const ctx = c.getContext('2d');
const worker = new Worker(generateWorkerURL());
worker.onmessage = e => {
  const msg = e.data;
  const view = new Uint8ClampedArray(msg.data.buffer);
  const imageData = new ImageData(view, msg.width, msg.height);
  ctx.putImageData(imageData, 0,0);
};
worker.postMessage('go');



// stacksnippet only
function generateWorkerURL() {
  const workerScript = document.querySelector('script[type="worker-script"]');
  const blob = new Blob([workerScript.textContent], {type: 'application/javascript'});
  return URL.createObjectURL(blob);
}
&#13;
<script type="worker-script">
  onmessage = e => {
    const canvasWidth = 500,
      canvasHeight = 500,
      // can be any TypedArray
      data = new Uint32Array(canvasWidth * canvasHeight);

    // fill 'data'
    // taken from https://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays/
    for (let y = 0; y < canvasHeight; ++y) {
        for (let x = 0; x < canvasWidth; ++x) {
            let value = x * y & 0xff;
            data[y * canvasWidth + x] =
                (255   << 24) |    // alpha
                (value << 16) |    // blue
                (value <<  8) |    // green
                value;             // red
        }
    }
    self.postMessage({
      data: data,
      width: canvasWidth,
      height: canvasHeight
    },
    [data.buffer] // tranfer the ArrayBuffer
    );
    console.log('transfered', !data.length);
}
</script>
<canvas id=c width=500 height=500></canvas>
&#13;
&#13;
&#13;

还要注意,当你可以直接从HTMLImageElement使用drawImage时,或者如果你真的想要使用ImageBitmap那么,你不清楚你想要通过ImageData的原因从工人那里工作。