【问题标题】:Why does HTML Canvas getImageData() not return the exact same values that were just set?为什么 HTML Canvas getImageData() 不返回刚刚设置的完全相同的值?
【发布时间】:2011-05-17 14:22:44
【问题描述】:

使用putImageData 将像素写入 HTML Canvas 上下文时,我发现再次获取像素值时它们并不完全相同。我已经提出了一个sample test page 来显示这个问题。归根结底,问题是:

var id = someContext.getImageData(0,0,1,1);
id.data[0]=id.data[3]=64; // 25% red, 25% alpha
id.data[1]=id.data[2]=0;  // No blue or green
someContext.putImageData(id,0,0);
var newData = someContext.getImageData(0,0,1,1);
console.log( newData.data[0] );

在 Chrome v8 上,红色值返回为 63;在 Firefox v3.6、Safari v5 和 IE9 上,红色值返回为 67(全部在 Windows 上)。在 OS X 上,Chrome v7、Safari v5 和 Firefox v3.6 也返回为 67。它们都不会像最初设置的 64 值一样返回!

使用setTimeout 在设置和重新获取之间进行延迟没有区别。更改页面的背景没有任何区别。在上下文中使用save()restore()(根据this unlikely article)没有区别。

【问题讨论】:

  • 我已经建立了另一个显示effects of alpha on a set/returned value的测试页面。 alpha 越低,结果的差异越大。设置 R:5 G:0 B:0 A:1 返回为 R:255 G:0 B:0 A:1
  • 你有没有解决过这个问题?
  • @Billy 不;甚至 HTML5 规范也有关于 set/get 可能导致不同值的事实的注释(请参阅下面我评论中的链接)。

标签: javascript html canvas


【解决方案1】:

对我来说似乎是一个四舍五入的问题...

64/255 = 0.2509... (rounded down to give 0.25)
0.25 * 255 = 63.75 (rounded down to give 63)
== OR ==
64/255 = 0.2509... (rounded up to give 0.26)
0.26 * 255 = 66.3  (rounded up to give 67)

记住 255 是最大值,而不是 256 ;)

编辑:当然,这不能解释为什么 alpha 通道的行为...

【讨论】:

  • 有趣的数据点。我想如果在round()ceil() 之前的底层代码中有一个*100,那可能是有意义的。我会说这不太可能,但你已经设法半直接地达到了这两个神奇的数字。 :)
  • 然而,有一个奇怪的提示:如果您将 alpha 设置为 255,红色会根据需要返回为 64。也许涉及到预乘管道?
【解决方案2】:

ImageData 在 HTML5 中被定义为未预乘,但大多数画布实现都使用预乘后备缓冲区来加速合成等。这意味着当数据被写入然后从后备缓冲区读取时,它可能会发生变化。

我假设 Chrome v8 从 webkit.org 获取了 [un]premultiplying 代码的错误版本(它之前已被破坏,尽管我不记得最近发生过任何事件,但这并不能解释 windows只有方差)

[编辑:是否值得每晚在 Windows 上检查 webkit?由于 imagedata 实现没有任何特定于平台的东西,它在所有 webkit 浏览器之间共享,并且可以在基于 MSVC 的构建中简单地被破坏]

【讨论】:

  • 明确地说,所有浏览器都是“错误的”:设置相同后,它们都没有返回64。 Chrome v8b 恰好返回与其他浏览器不同的值(尽管更接近原始值)。我已经编辑了问题以突出显示这一点。
  • 感谢您对预乘 alpha 规格的评论。规范explicitly note this problem 表示:“由于与预乘 alpha 颜色值之间的转换具有损耗特性,刚刚使用 putImageData() 设置的像素可能会以不同的值返回到等效的 getImageData()。 "
  • 我记得 Opera 没有遇到这个问题,但也许他们已经改变了他们的实现。
【解决方案3】:

HTML5 规范鼓励浏览器供应商使用称为Premultiplied Alpha 的东西。本质上,这意味着像素存储在 32 位整数中,其中每个通道包含一个 8 位颜色值。出于性能原因,浏览器使用预乘 Alpha。这意味着它根据 alpha 值预乘颜色值。

这是一个例子。您有一种颜色,使得 RGB 的值为1286467。现在,为了更高的性能,颜色值将与 alpha 值进行预乘。因此,如果 alpha 值为16,所有颜色值将乘以16/256(=0.0625)。在这种情况下,RGB 的结果值变为844.1875(四舍五入为4,因为像素颜色值不是浮点数)。

当您完全按照您在此处执行的操作时,问题就会出现;使用特定的 alpha 值设置颜色数据,然后拉回实际的颜色值。当您调用getImageData() 时,4.1875 之前的蓝色四舍五入到4 将变为64 而不是67

这就是为什么您会看到这一切,除非浏览器引擎中的底层实现更改为使用不受此影响的颜色系统,否则它永远不会改变。

【讨论】:

  • 您能举出 HTML 5 规范鼓励这样做的地方吗?我看到了要求所有图像数据获取/设置使用 un 预乘 alpha 的规范,并且我看到了注释(我在上面引用的)他们说 if 实现可能会发生什么碰巧使用了预乘。
  • @Phrogz 那是什么规格?
  • @NoBugs 我在对 olliej 的回答的评论中链接到的那个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-28
  • 2014-12-19
  • 1970-01-01
  • 2020-12-04
  • 2022-12-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多