我正在制作一个基于Venn图的游戏,并实现了一个洪水填充算法来填充如下所示的每个区域。

因为我不想被洪水淹没的圆圈轮廓,我有一个检查,以确保如果边缘被点击,那么它是不被填补。这是通过检查颜色不是红色来完成的。
function circleEdgeClicked(x, y) {
var colorLayer = context.getImageData(0, 0, canvasWidth, canvasHeight);
var pixelPos = (x * canvasWidth + y) * 4;
startR = colorLayer.data[pixelPos];
startG = colorLayer.data[pixelPos + 1];
startB = colorLayer.data[pixelPos + 2];
if (startR === 255 && startG === 0 && startB === 0) {
console.log("Colour is red");
}
return (startR === 255 && startG === 0 && startB === 0);
}所需的行为是点击将洪水淹没到黄色,而另一个点击将恢复洪水填充白色。toHowever,有时在圆圈内单击时,无论是黄色还是白色,区域都不会填充到相反的颜色。调试原因显示,问题是因为getImageData错误地说单击的像素是红色的。
下面是JSFiddle中的代码,只要点击圆圈就可以复制bug,几次尝试之后,它就不会被填充,日志会说颜色是红色的,尽管它不是红色。
在getImageData文档中,有一个注释Due to the lossy nature of converting to and from premultiplied alpha color values, pixels that have just been set using putImageData() might be returned to an equivalent getImageData() as different values.
这和它有什么关系吗?有解决办法吗?
发布于 2016-12-15 22:01:59
将此行更改为:
var pixelPos = (x * canvasWidth + y) * 4;至
var pixelPos = (y * canvasWidth + x) * 4; // or use <<2 instead of * 4因此,y (代表线条/步幅)而不是x被乘以宽度。
提前缓存位图或读取单个像素也会更快:
var colorLayer = context.getImageData(x, y, 1, 1);在这种情况下,您可能需要对x/y和宽度/高度进行边界检查。您还可以将其读取为32位值,并直接在没有alpha的情况下比较0xff0000ff (ABGR),或者将其移到删除alpha上:
function circleEdgeClicked(x, y) {
var color = new Uint32Array(context.getImageData(x, y, 1, 1).data.buffer)[0];
return (color<<8) === 0xff00; // is red? ignoring alpha
}一个额外的提升将是在初始化红色轮廓时获取位图。由于我们将防止填充这些数据,所以您可以在这种状态下缓存位图,并且可以在每次需要检查像素时不传输新的位图而进行检查(并且没有太多的内存开销,因为Uint32视图将共享原始位图的缓冲区):
// by here the red outlines are drawn, so we grab it at this stage
var pixels =
new Uint32Array(context.getImageData(0, 0, canvasWidth, canvasHeight).data.buffer);
// then reuse the buffer when a pixel needs to be checked
function circleEdgeClicked(x, y) {
var color = pixels[y * canvasWidth + x]; // all values are 32-bit
return (color<<8) === 0xff00; // is red? ignoring alpha
}关于有损性质:有这个问题,在某些情况下是由舍入错误引起的。在此基础上,如果源图是从图像中提取的,则位图值可以在加载时被伽马或ICC配置文件更改。
解决办法是实现一个公差,例如,对于红色,接受值在250到255之间,而不是确切的255,蓝色和绿色的范围为0-5等等(+/- 5在这里是任意的,只是实验来找到一个最优的值)。您还会发现,如果底图有非不透明的背景,就需要考虑alpha。
发布于 2016-12-15 21:51:49
我不太确定,但由于像素数据是一维数组,为了将x,y转换为像素数据中数组的索引,您必须使用
(x + canvasWidth * y) * 4因为y位置将表示要跳过多少行水平像素才能到达像素,这将是canvasWidth * y,然后+x表示水平轴。
以上只是为了修正你的错误,但我的建议和问题是,为什么你不直接使用getImageData呢?getImageData()采用x,y,宽度和高度参数,所以你可以通过它的位置提取一个像素。
function circleEdgeClicked(x, y) {
// will return an array of 4 integers (rgba)
var colorLayer = context.getImageData(x, y, 1, 1);
startR = colorLayer.data[0];
startG = colorLayer.data[1];
startB = colorLayer.data[2];
if (startR === 255 && startG === 0 && startB === 0) {
console.log("Colour is red");
}
return (startR === 255 && startG === 0 && startB === 0);
}https://stackoverflow.com/questions/41173784
复制相似问题