我需要在彼此之上绘制同一图像的 2 个变体,其 alpha 值加起来为 1。
例如:
-imageA
alpha 为0.4
,而imageB
alpha 为0.6
.
- 所需的结果是完全不透明的图像(每个像素的 alpha 为 1.0
),它显示为 imageB
的 60% 混合,以及 40% 的 imageA
混合物.
但是,我相信 html5 Canvas 默认使用混合模式,而不是在 alpha 混合图像上添加,因此使用低于 1 的 alpha 绘制的两个内容相加不会等于 1。
我尝试了所有不同的合成模式以及 Adobe 混合模式,但没有一个达到预期的结果。
一个简单的测试用例是绘制一个洋红色矩形两次,alpha 均为 0.5
。我的愿望是生成的像素为 rgb(255, 0, 255)
。然而,结果是稍微透明的。
有人知道实现这个结果的方法吗?
最佳答案
• 如果您查看有关默认混合模式(“source-over”)的 Canvas 'context2D 规范:
http://dev.w3.org/fxtf/compositing-1/#simplealphacompositing
您将看到使用的公式是(为了清楚起见,我重命名了变量):
colorOut = prevColor x prevAlpha + newColor x newAlpha x (1 - prevAlpha)
对于结果 alpha :
αlphaOut = prevAlpha + newAlpha x (1 - prevAlpha)
请注意,
a) 出乎意料的是,该公式不是线性的(由于:newAlpha x (1 - prevAlpha) 因子)。
b) 一个点具有较小的 r、g、b 值和较小的 alpha。所以在重新使用它的时候,
将为最终图像贡献 square(0.6)==0.36 。 (...完全违反直觉...)
• 那么您的 60%/40% 平局会发生什么?
1) 图像 A 是用 alpha = 60% 绘制的。 上面的公式给出:
colorOut = color_A * 0.6 ;
alphaOut = 0.6 ;
2)图像B是用alpha = 40%绘制的
colorOut = color_A * 0.6 * 0.6 + color_B x 0.4 x (0.4);
alphaOut = 0.6 + 0.4 * 0.4;
所以最终公式是==>
colorOut = 0.36 * color_A + 0.16 * color_B ;
alphaOut = 0.76 ;
您会发现,这根本不是您期望的 60/40 混合比例。
• 如何修复?
1) 您可以使用 getImageData/putImageData 手动完成。请注意跨源问题:如果您的图像不是来自您的域,则它们的 imageData 将为空。就性能而言,与完成这项工作的 Canvas (= GPU)相比,速度会慢 50 以上。根据图像大小/计算能力/浏览器,“实时”(=60fps) 可能无法实现。但你拥有完全的控制权。
2)但如果你现在看看“更轻”的复合模式,似乎我们有我们的人:
http://dev.w3.org/fxtf/compositing-1/#porterduffcompositingoperators_plus
现在的公式是:
colorOut = prevColor x prevAlpha + newColor x newAlpha
对于结果 alpha :
αlphaOut = prevAlpha + newAlpha
您会发现这是一个简单的“加号”运算符,非常适合您的要求。
所以解决方案是:
- 保存ctx。
- 设置更轻的复合模式。
- 以 60% alpha 绘制第一张图像。
- 以 40% alpha 绘制第二张图像。
- 恢复ctx。
最后的话:如果你想检查最终的 alpha 是否正确(==255),请使用这种函数:
function logAlpha(ctx,x,y) {
var imDt = ctx.getImageData(x,y,1,1).data;
if (!(imDt[0] || imDt[1] || imDt[2])) console.log('null point. CORS issue ?');
console.log(' point at (' +x+ ',' +y+ ') has an alpha of ' + imDt[3] ) ;
}
关于javascript - 使用 html5 canvas 混合两个图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26200800/