【问题标题】:Faster way to copy array values into canvas pixel data将数组值复制到画布像素数据中的更快方法
【发布时间】:2018-06-09 08:24:06
【问题描述】:

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

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

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

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

而且效果很好!

【问题讨论】:

  • 切片后再次调用context.putImageData(imgData, 0, 0);...
  • @JonasW。不幸的是,我试过了,但无论如何它都不会更新画布。

标签: javascript arrays html canvas


【解决方案1】:
 context.putImageData(new ImageData(workerData, 100, 100), 0, 0);

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

【讨论】:

  • 哦,我明白你的意思了。我试过了,但是我得到了这个错误:TypeError: Not enough arguments to ImageData
  • @YAHsaves 哦对了,你需要传递一个宽度和一个高度
  • 这似乎仍然不起作用,无论我尝试什么尺寸,它都说无效或负长度。也许我只是设置错误,但是经过更多搜索后我确实找到了答案。您可以像这样使用集合:imageData.data.set(myData);
【解决方案2】:

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

因此,通过从这样的 TypedArray 直接在您的工作人员中工作,您可能会获得更快的结果。除了可能使工作部分更快一点之外,它还允许您将底层 ArrayBuffer 从工作线程 transfer 到主线程,而不必复制数据,永远不会。

一旦主线程接收到 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);
};

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

另外请注意,当您可以直接从 HTMLImageElement 或 ImageBitmap 直接使用 drawImage 时,不清楚为什么要通过 ImageData,如果您真的想从工人那里工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-04
    • 1970-01-01
    • 2015-08-09
    • 2022-01-26
    • 1970-01-01
    • 2016-06-11
    • 2018-03-24
    • 2021-10-02
    相关资源
    最近更新 更多