Xen*_*hyl 7 graphics html5 canvas blend pixel
编辑:我不一定需要解决这个问题 - 而是我想了解它为什么会发生.我不明白为什么我应该得到以下奇怪的结果......
虽然这个问题是针对我在HTML5画布应用程序中遇到的问题,但我认为问题不那么具体.
我有一个HTML5画布应用程序,允许您在屏幕上标记图像.这些图像是32位PNG,所以我正在使用透明度.如果我在同一位置多次(大约100)标记高度透明的图像,我最终会得到一个绝对可怕的结果:

我用作图章的图像的颜色和我正在加盖RGB(167, 22, 22)的背景是RGB(255, 255, 255).这是源图像,如果有人感兴趣:

如您所知,图像的alpha级别极低.可能左右2/255 to 5/255左右.我期望发生的是,如果你反复将图像标记重复应用到画布上,你将获得颜色像素RGBA(167, 22, 22, 255).不幸的是,我得到了一个混合的颜色袋,包括一些非常奇怪的灰色区域,其值为RGB(155, 155, 155).
我只是加载Excel并插入了源代码alpha混合的等式(维基百科参考),我似乎在RGB(167, 22, 22)经过足够的迭代后收敛.我可能遗漏了一些关于alpha混合操作的基本知识以及HTML5 canvas如何实现源代码合成...任何人都可以帮助理顺我吗?
谢谢!
注意:这个问题与我的问题类似,但我不太明白为什么我得到了我在这里发布的结果.
画布数学内部的精确度和舍入规则大多是未定义的,因此很难确切地说出这里发生了什么.我们真正知道的是像素是无符号字节,alpha是预乘的.
但是,我们可以通过getImageData在绘制图章时检查像素来获取一些信息,如下所示:
var px = 75;
var py = 100;
var stamp = new Image;
stamp.onload = function() {
for (var i = 0; i < 100; ++i) {
imageData = context.getImageData(px, py, 1, 1);
console.log(Array.prototype.slice.call(imageData.data, 0, 4));
context.drawImage(stamp, 0, 0);
}
};
stamp.src = 'stamp.png';
Run Code Online (Sandbox Code Playgroud)
在样品px = 75,py = 100是在权利灰色斑点的中间.在白色画布上绘制一次印章后,日志显示:
[254, 254, 254, 255]
Run Code Online (Sandbox Code Playgroud)
在px = 120,py = 150样品处于红色区域的中间.在绘制一次印章后,日志显示:
[254, 253, 253, 255]
Run Code Online (Sandbox Code Playgroud)
因此,看起来画布被灰色像素修改为(-1,-1,-1),红色像素修改为(-1,-2,-2).
使用RMagick在图章图像中采样这些相同的像素给出:
[167, 22, 22, 1] // x = 75, y = 100
[167, 22, 22, 2] // x = 120, y = 150
Run Code Online (Sandbox Code Playgroud)
通过数学运算,使用标准的alpha混合方程,您可以测试每个颜色值:
function blend(dst, src) {
var a = src[3] / 255.0
return [
(1.0 - a) * dst[0] + a * src[0],
(1.0 - a) * dst[1] + a * src[1],
(1.0 - a) * dst[2] + a * src[2]
];
}
console.log(blend([255, 255, 255], [167, 22, 22, 1]));
// output: [254.6549..., 254.0862..., 254.0862...]
console.log(blend([255, 255, 255], [167, 22, 22, 2]));
// output: [254.3098..., 253.1725..., 253.1725...]
Run Code Online (Sandbox Code Playgroud)
从这里,我们可以猜测画布混合代码实际上是对结果进行分层,而不是对它们进行舍入.这将使你的结果[254, 254, 254]和[254, 253, 253],就像我们从画布上看到的.它们可能根本不会进行任何舍入,并且在被转换为无符号字节时会隐式地进行舍入.
这就是为什么其他帖子建议将图像数据存储为浮点数组,自己进行数学运算,然后用结果更新画布.通过这种方式可以获得更高的精度,并且可以控制诸如舍入之类的事
编辑:事实上,这个blend()函数并不完全正确,即使结果被抛光,因为画布像素值120, 150稳定在[127, 0, 0],并且此函数稳定在[167, 22, 22].同样地,当我绘制的图片只是一旦进入一个透明画布,getImageData在像素为120, 150是[127, 0, 0, 2].什么?!
事实证明,这是由预乘引起的,它似乎应用于加载的Image元素.有关示例,请参阅此jsFiddle.
预乘像素存储为:
// r, g, b are 0 to 255
// a is 0 to 1
// dst is all 0 to 255
dst.r = Math.floor(r * a);
dst.g = Math.floor(g * a);
dst.b = Math.floor(b * a);
dst.a = a * 255;
Run Code Online (Sandbox Code Playgroud)
他们后来打开包装:
inv = 1.0 / (a / 255);
r = Math.floor(dst.r * inv);
g = Math.floor(dst.g * inv);
b = Math.floor(dst.b * inv);
Run Code Online (Sandbox Code Playgroud)
运行此包/解压缩[167, 22, 22, 2]显示:
a = 2 / 255; // 0.00784
inv = 1.0 / (2 / 255); // 127.5
r = Math.floor(Math.floor(167 * a) * inv); // 127
g = Math.floor(Math.floor(22 * a) * inv); // 0
b = Math.floor(Math.floor(22 * a) * inv); // 0
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1781 次 |
| 最近记录: |