选中此jsfiddle demo

唯一的区别是单击之前在左侧画布中绘制了笔触矩形,单击时在右侧画布中绘制了矩形。

function getPointColor(ctx, x, y) {
    return ctx.getImageData(x, y, 1, 1).data;
}


function getTime(ctx, action) {
    console.time(action);
    for (let i = 0; i < 1000; i++) {
        getPointColor(ctx, 1, 1);
    }
    console.timeEnd(action);
}
ctx1.strokeRect(0, 0, 20, 20)

canvas1.onclick = function () {
    getTime(ctx1, 'click canvas1');
}

canvas2.onclick = function () {
    ctx2.strokeRect(0, 0, 20, 20)
    getTime(ctx2, 'click canvas2');
}


并且控制台显示出canvas1的耗时是canvas2的100倍;

javascript - 为什么在不同情况下执行canvas函数getImageData时会有很大的耗时-LMLPHP

最佳答案

这是a Chrome bug,大约是v76开始的。
这似乎与“硬件加速”相关联,禁用此选项将使两个操作花费相同的最短时间。

请注意,尽管当前的Canary版本(v78)现在具有不同的行为(可能是由于this commit所致),其中两种操作都将花费最大的时间...

我已经打开了链接的错误报告,因此除了您可以在此处提出修复建议之外,没有更多的事情要做。



如果我不得不猜测会发生什么,我会说他们现在大部分时间将画布缓冲区保存在GPU内存中,但是要获取ImageData,他们需要将其传输回CPU。因此,像您一样多次调用此方法将需要他们多次执行此传输。

因此,无论您是否解决该错误,都应该使用一个简单的修补程序,以仅执行一次getPointColor的方式重写您的getImageData

不必多次调用getImageData(x, y, 1, 1)来获取不同的点,您应该只获取一个代表所有画布的ImageData,然后从这个大的ImageData中选择像素的值:



const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

// gets the slot at x y coords from a flatten pixel matrix represented as 32bits
function getPixelColor32(data, x, y, width) {
  return data[y * width + x];
}
// set the value of the slots at x, y (32bits version)
function setPixelColor32(val, data, x, y, width) {
  data[y * width + x] = val;
}
// the same functions
// in case you wish to keep the orignal Uint8ClampedArray from ImageData
// returns a new Uint8ClampedArray, not that good for memory
function getPixelColor8(data, x, y, width) {
  const index = (y * width + x) * 4;
  return data.slice(index, index + 4);
}
// here vals is an Array like object of length 4 [r, g, b, a]
function setPixelColor8(vals, data, x, y, width) {
  const index = (y * width + x) * 4;
  for(let i = 0; i < 4; i ++) {
    data[index + i] = vals[i];
  }
}

// so we can draw a circle
function distanceFromCenter(x, y, cx, cy) {
  return Math.hypot(cx - x, cy - y);
}

let mouse_down = false;
canvas.onmousedown = (evt) => {
  mouse_down = true;
  draw(evt);
};
canvas.onmousemove = (evt) => {
  if(mouse_down) {
    draw(evt);
  }
};
canvas.onmouseup = (evt) => {
  mouse_down = false;
}
function draw(evt) {
  const { width, height } = canvas;
  const rad = height / 10;
  const cx = evt.offsetX;
  const cy = evt.offsetY;

  // we call getImageData only once
  const imgData = ctx.getImageData(0, 0, width, height);
  // using an Uint32Array view for easier pixel manipulation
  // we could obviously also use the Uint8Clamped version,
  // though it would use more memory
  const data = new Uint32Array(imgData.data.buffer);

  for( let x = 0; x < width; x ++) {
    for( let y = 0; y < height; y ++) {
      // make a circle
      if(distanceFromCenter(x, y, cx, cy) < rad + 1) {
        // get the pixel value (here as 32bit)
        const px = getPixelColor32(data, x, y, width);
        // do something with it (negative)
        setPixelColor32(~px | 0xFF000000, data, x, y, width);
      }
    }
  }
  ctx.putImageData(imgData, 0, 0);
};

// just to draw something at beginning
const px_size = 10;
const noise = new ImageData(canvas.width / px_size, canvas.height / px_size);
new Uint32Array(noise.data.buffer)
  .forEach((v, i, a) => {
    a[i] = Math.random() * 0xFFFFFF + 0xFF000000;
  });
ctx.putImageData(noise, 0 ,0);
ctx.scale(px_size, px_size);
ctx.imageSmoothingEnabled = false;
ctx.drawImage(canvas, 0, 0);
ctx.setTransform(1,0,0,1,0,0);
ctx.imageSmoothingEnabled = true;

<pre>click &amp; drag to do pixel manip</pre>
<canvas id="canvas"></canvas>

关于javascript - 为什么在不同情况下执行canvas函数getImageData时会有很大的耗时,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57834004/

10-10 08:57