首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用getImageData从画布中获取像素的颜色,给出错误的颜色

使用getImageData从画布中获取像素的颜色,给出错误的颜色
EN

Stack Overflow用户
提问于 2016-12-15 21:41:27
回答 2查看 752关注 0票数 2

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

因为我不想被洪水淹没的圆圈轮廓,我有一个检查,以确保如果边缘被点击,那么它是不被填补。这是通过检查颜色不是红色来完成的。

代码语言:javascript
复制
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.

这和它有什么关系吗?有解决办法吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-12-15 22:01:59

将此行更改为:

代码语言:javascript
复制
var pixelPos = (x * canvasWidth + y) * 4;

代码语言:javascript
复制
var pixelPos = (y * canvasWidth + x) * 4; // or use <<2 instead of * 4

因此,y (代表线条/步幅)而不是x被乘以宽度。

提前缓存位图或读取单个像素也会更快:

代码语言:javascript
复制
var colorLayer = context.getImageData(x, y, 1, 1);

在这种情况下,您可能需要对x/y和宽度/高度进行边界检查。您还可以将其读取为32位值,并直接在没有alpha的情况下比较0xff0000ff (ABGR),或者将其移到删除alpha上:

代码语言:javascript
复制
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视图将共享原始位图的缓冲区):

代码语言:javascript
复制
// 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。

票数 2
EN

Stack Overflow用户

发布于 2016-12-15 21:51:49

我不太确定,但由于像素数据是一维数组,为了将x,y转换为像素数据中数组的索引,您必须使用

代码语言:javascript
复制
(x + canvasWidth * y) * 4

因为y位置将表示要跳过多少行水平像素才能到达像素,这将是canvasWidth * y,然后+x表示水平轴。

以上只是为了修正你的错误,但我的建议和问题是,为什么你不直接使用getImageData呢?getImageData()采用x,y,宽度和高度参数,所以你可以通过它的位置提取一个像素。

代码语言:javascript
复制
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);
 }
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41173784

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档