【问题标题】:clearRect() on a single pixel in html5 canvas doesn't actually set alpha to 0html5 画布中单个像素上的 clearRect() 实际上并未将 alpha 设置为 0
【发布时间】:2015-10-22 11:55:46
【问题描述】:

我在 html5 画布中手动绘制单个像素。我只使用纯黑色和纯白色。只是稍微简化一下:

var canvas = document.querySelector(".main-canvas");
var ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;

var isNonNegativeInteger = function(val) {
  return Number.isFinite(val) && val % 1 === 0 && val >= 0;
};

var paintPixel = function(x, y) {
    if (!isNonNegativeInteger(x) ||
        !isNonNegativeInteger(y) ) {
        debugger;       
    }
    ctx.fillRect( x, y, 1, 1 );
};

var clearPixel = function( x, y ) {
    if (!isNonNegativeInteger(x) ||
        !isNonNegativeInteger(y) ) {
        debugger;       
    }
    ctx.clearRect( x, y, 1, 1 );
};

我只传递整数作为 x 和 y 坐标。是的,我非常确定,我已经进行了测试。

我不是在操纵 RGB。所有像素保持黑色。我只是在操纵阿尔法。我发现的是,经过大量的绘画和清理,经过大量的来回,有一个微弱的视觉“重影”,其中由fillRect() 设置为完全不透明的黑色像素实际上并不是调用clearRect() 时将其alpha 重置为零。

我知道这一点是因为当我在受影响的像素邻域中调用 ctx.getImageData() 时,我看到像素数据 alpha 值中散布着低但非零的 alpha 值(3、5 或 12 等)。这是……疯狂的制造。有趣的是,我还看到黑色像素的一些非 255 alpha 值,这也是有问题的,但在视觉上不像“脏”白色那样令人反感。

这些是大像素网格,每帧有数千个绘制调用。所以我不愿意使用getImageData()putImageData() 手动设置alpha,因为它们很慢。问题在于clearRect() 并没有按照它上面所说的那样做。

非常感谢任何建议。

【问题讨论】:

  • 如果您仍然保持所有像素为黑色,则可以使用 fillRect 代替。您是否在任何地方更改转换矩阵?你测试过多个浏览器吗?
  • 如何用fillRect“擦除”?将 ctx.fillStyle 设置为零 alpha 仅意味着填充完全没有效果。没有变换矩阵。在 FF 和 Chrome 中看到类似的结果。
  • Here 有更多相关信息。
  • 不,我绝对不建议这样做。阅读下面的答案,以及问题上的 cmets。
  • 有两件事值得细细琢磨。 1. 低 alpha 值 (drawImage,则允许浏览器进行可能影响 alpha 的 gamma 校正。如果没有代码,很难进一步分析,但我强烈怀疑当您以 1 个像素的宽度和高度重复 fillRect 时,您正在与浏览器的抗锯齿算法发生冲突。

标签: canvas html5-canvas


【解决方案1】:

这真的很奇怪,我能想到的唯一原因是你的 x 和 y 坐标不是int。 在浮动位置绘制确实会在clearRect 上创建一个抗锯齿工件:

var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
ctx.textBaseline = "middle"
var paintPixel = function(x, y) {
  ctx.fillRect(x, y, 1, 1);
};
var clearPixel = function(x, y) {
  ctx.clearRect(x, y, 1, 1);
};
var i = 0;
for (i = 0; i < 25; i += .1) {
  paintPixel(i, 10);
  clearPixel(i, 10);
}
ctx.fillText('float', 30, 10)

for (i = 0; i < 25; i++) {
  paintPixel(i, 30);
  clearPixel(i, 30);
}
ctx.fillText('int', 30, 30)
// an other solution could have been to use globalCompositeOperation
var gCOClear = function(x, y) {
  ctx.globalCompositeOperation = 'destination-out';
  ctx.fillRect(x, y, 1, 1);
  ctx.globalCompositeOperation = 'source-over';
}
for (i = 0; i < 25; i += .1) {
  paintPixel(i, 50);
  gCOClear(i, 50);
}
ctx.fillText('gCO float', 30, 50)
for (i = 0; i < 25; i++) {
  paintPixel(i, 70);
  gCOClear(i, 70);
}
ctx.fillText('gCO int', 30, 70)

所以解决方案是:要么在绘制之前将坐标四舍五入,要么你的清晰比你绘制的稍大。

但正如您指定的那样,您绝对确定“只将整数作为 x 和 y 坐标传递”,这可能不是您的问题...

另外,请注意,遗憾的是,ctx.imageSmoothingEnabled 对 fillRect()clearRect() 等绘图方法没有影响

【讨论】:

  • 谢谢,但当我说我确定参数是整数时,我是认真的。 :) 我已经编辑了这个问题,希望能澄清一下。如果我通过用不透明的白色绘画将其更改为“擦除”,或者使用ctx.globalCompositeOperation = 'destination-out' 都可以修复 FF 中的重影,但我仍然在 Blink 中得到它。
  • @Ben Oups 错过了这一行:-/ 我会给出答案,因为它可能对未来的读者有一些兴趣。你能添加一个活生生的例子吗?奇怪的是 gCO 有效,而不是 clearRect,更奇怪的是它只在 FF 上有效。
猜你喜欢
  • 1970-01-01
  • 2013-02-04
  • 2011-06-06
  • 1970-01-01
  • 2012-02-23
  • 2013-07-31
  • 2013-01-22
  • 2013-01-08
  • 2015-04-29
相关资源
最近更新 更多