为什么 HTML Canvas getImageData() 不返回与刚刚设置的完全相同的值?
当使用 putImageData 将像素写入 HTML Canvas 上下文时,我发现再次获取像素值时并不完全相同。我已经发布了一个显示问题的示例测试页面。归根结底,问题是:
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()
(根据 这篇不太可能的文章)没有什么区别。
When writing pixels to an HTML Canvas context using putImageData
I find that the pixel values are not exactly the same when I fetch them again. I have put up a sample test page showing the problem. Boiled down, the problem is:
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] );
On Chrome v8, the red value comes back as 63
; on Firefox v3.6, Safari v5, and IE9 the red value comes back as 67
(all on Windows). On OS X, Chrome v7, Safari v5, and Firefox v3.6 also come back as 67
. None of them come back as the 64
value originally set!
Using setTimeout
to delay between setting and re-fetching makes no difference. Changing the background of the page makes no difference. Using save()
and restore()
on the context (per this unlikely article) makes no difference.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
ImageData 在 HTML5 中被定义为未预乘,但大多数画布实现都使用预乘后备缓冲区来加速合成等。这意味着当写入数据然后从后备缓冲区读取数据时,它可能会发生变化。
我假设 Chrome v8 从 webkit.org 中获取了 [un]premultiplying 代码的一个有错误的版本(它之前已经被破坏过,尽管我不记得最近发生过任何事情,并且这并不能解释 Windows 唯一的差异)
[编辑:在 Windows 上每晚检查一个 webkit 可能值得吗?由于 imagedata 实现没有任何特定于平台的内容,因此它在所有 webkit 浏览器之间共享,并且可能在基于 MSVC 的构建中被简单地破坏]
ImageData is defined in HTML5 as being unpremultiplied, but most canvas implementations use a premultiplied backing buffer to speed up compositing, etc. This means that when data is written and then read from the backing buffer it can change.
I would assume that Chrome v8 picked up a buggy version of the [un]premultiplying code from webkit.org (It has been broken before, although i don't recall any recent occurances, and that doesn't explain the windows only variance)
[edit: it could be worth checking a webkit nightly on windows? as the imagedata implementation doesn't have anything platform specific it's shared between all webkit browsers and could simply be broken in MSVC based builds]
HTML5 规范鼓励浏览器供应商使用称为
Premultiplied Alpha
的东西。本质上,这意味着像素存储在 32 位整数中,其中每个通道包含一个 8 位颜色值。出于性能原因,浏览器使用预乘 Alpha。它的意思是它根据 alpha 值预乘颜色值。这是一个例子。您的颜色的 RGB 值为
128
、64
、67
。现在,为了获得更高的性能,颜色值将预先乘以 alpha 值。因此,如果 Alpha 值为16
,则所有颜色值都将乘以16/256
(=0.0625
)。在这种情况下,RGB 的结果值变为8
、4
、4.1875
(四舍五入为4
,因为像素颜色值不是浮点数)。当您完全按照此处所做的操作时,问题就会出现;使用特定的 alpha 值设置颜色数据,然后拉回实际颜色值。当您调用
时,之前的蓝色
。4.1875
四舍五入为4
将变为64
而不是67
>getImageData()这就是为什么您会看到这一切,并且它永远不会改变,除非浏览器引擎中的底层实现更改为使用不受此影响的颜色系统。
HTML5 specification encourages browser vendors to use something that is called
Premultiplied Alpha
. In essence this means that pixels are stored in 32-bit integers where each channel contains a 8-bit color value. For performance reasons, the Premultiplied Alpha is used by browsers. What it means is that it premultiplies color values based on the alpha value.Here's an example. You have a color such that the values for RGB are
128
,64
,67
. Now, for the sake of higher performance, the color values will be premultiplied by the alpha value. So, in case the alpha value is16
, all the color values will get multiplied by16/256
(=0.0625
). In this case, the resulting values for RGB become8
,4
,4.1875
(rounded to4
because pixel color values are not floats).The problem shows up when you do exactly what you are doing here; setting color data with a specific alpha value and then pulling back the actual color values. The previous Blue color of
4.1875
that got rounded to4
will become64
instead of67
when you callgetImageData()
.That is why you are seeing all this and it will never change unless the underlying implementation in a browser engine changes to use a color system that does not suffer from this.
对我来说看起来像是一个舍入问题...
记住 255 是最大值,而不是 256 ;)
编辑:当然,这不能解释为什么 alpha 通道的行为...
Looks like a rounding issue to me...
Remember that 255 is the maximum value, not 256 ;)
EDIT: Of course, this wouldn't explain why the alpha channel is behaving...