Javascript 为什么HTML Canvas getImageData()不返回刚才设置的完全相同的值?

Javascript 为什么HTML Canvas getImageData()不返回刚才设置的完全相同的值?,javascript,html,canvas,Javascript,Html,Canvas,使用putImageData将像素写入HTML画布上下文时,再次获取像素值时,我发现像素值不完全相同。我已经贴了一张照片来说明这个问题。简而言之,问题是: 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);

使用
putImageData
将像素写入HTML画布上下文时,再次获取像素值时,我发现像素值不完全相同。我已经贴了一张照片来说明这个问题。简而言之,问题是:

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上)。在OSX上,ChromeV7、SafariV5和FirefoxV3.6也以
67
的形式出现。没有一个返回原来设置的
64


使用
setTimeout
延迟设置和重新获取之间的时间没有区别。更改页面的背景没有任何区别。在上下文(per)上使用
save()
restore()
没有任何区别。

对我来说似乎是一个取整问题

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通道的行为……HTML5将ImageData定义为未乘法,但大多数画布实现使用预乘法备份缓冲区来加速合成等。这意味着,当数据从备份缓冲区写入然后读取时,它可以更改

我假设Chrome v8从webkit.org上获得了一个错误版本的[un]预乘代码(它以前被破坏过,虽然我不记得最近发生过什么,但这并不能解释windows独有的差异)


[编辑:在windows上每晚检查一个webkit可能是值得的?因为imagedata实现没有任何特定于平台的内容,它在所有webkit浏览器之间共享,并且可以在基于MSVC的构建中被破坏]

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

这里有一个例子。您的颜色使RGB的值为
128
64
67
。现在,为了提高性能,颜色值将被alpha值预乘。因此,如果alpha值为
16
,则所有颜色值将乘以
16/256
(=
0.0625
)。在这种情况下,RGB的结果值变为
8
4
4.1875
(四舍五入为
4
,因为像素颜色值不是浮点数)

当你做你在这里所做的事情时,问题就出现了;使用特定的alpha值设置颜色数据,然后拉回实际的颜色值。调用
getImageData()
时,先前四舍五入到
4
的蓝色
4.1875
将变为
64
,而不是
67


这就是为什么您会看到所有这些,并且它永远不会改变,除非浏览器引擎中的底层实现改变为使用一种不会受到影响的颜色系统。

有趣的数据点。我想如果在
round()
ceil()
之前的底层代码中有一个
*100
,这可能是有意义的。我会说这不太可能,但你已经成功地半直接达到了这两个神奇的数字然而,有一个奇怪的提示:如果您将alpha设置为
255
,红色会根据需要显示为
64
。可能涉及到一个预多重管道?要清楚的是,所有浏览器都是“有缺陷的”:设置相同后没有一个返回
64
。ChromeV8B恰好返回与其他浏览器不同的值(尽管更接近原始值)。我编辑了这个问题来强调这一点。感谢您对规范wrt预乘alpha的评论。规范中说:“由于预乘alpha颜色值之间的转换具有损耗性,刚刚使用
putImageData()
设置的像素可能会作为不同的值返回到等效的
getImageData()
。”我记得Opera没有遇到这个问题,但也许他们已经改变了他们的实现。我已经在另一个测试页面上显示了。阿尔法越低,结果的差异越大。设置
R:5g:0b:0a:1
返回的结果是
R:255g:0b:0a:1
。你能绕过这个问题吗?@Billy No;即使是HTML5规范也有一个注释(见下面我评论中的链接),说明set/get可能会产生不同的值。你能举出HTML5规范鼓励这一点的地方吗?我看到了要求所有imagedata获取/设置使用非预乘alpha的规范,我看到了注释(我在上面引用过),其中说明了如果实现恰好使用预乘,可能会发生什么。@Phrogz那是什么规范?@NoBugs是我在对olliej答案的评论中链接到的规范。