【问题标题】:Blending two images with html5 canvas将两个图像与 html5 画布混合
【发布时间】:2014-11-29 19:14:48
【问题描述】:

我需要将同一图像的 2 个变体叠加在一起,它们的 alpha 值加起来为 1。

例如:
- imageA 的 alpha 为 0.4,而 imageB 的 alpha 为 0.6
- 所需的结果是完全不透明的图像(每个像素的 alpha 为 1.0),显示为 imageB 的 60% 混合和 imageA 的 40% 混合。

但是,我相信 html5 画布默认使用混合模式而不是添加 alpha 混合图像,因此使用低于 1 的 alpha 绘制的两件东西加起来不会等于 1。
我已经尝试了所有不同的合成模式以及 Adob​​e 混合模式,但都没有达到预期的效果。
一个简单的测试用例是绘制一个品红色矩形两次,其 alpha 均为0.5。我希望得到的像素是rgb(255, 0, 255)。但是,结果有点透明。

有谁知道达到这个结果的方法吗?

【问题讨论】:

    标签: javascript html canvas html5-canvas alphablending


    【解决方案1】:

    • 如果您查看有关默认混合模式 ('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) 使用 alpha = 60% 绘制图像 A。 上面的公式给出:

     colorOut = color_A * 0.6 ;
     alphaOut = 0.6 ;
    

    2) 使用 alpha = 40% 绘制图像 B

     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 将为空。对于性能,与执行工作的画布(= GPU)相比,押注 50+ 的减速因素。根据图像大小/计算能力/浏览器,可能无法实现“实时”(=60fps)。但是你有完全的控制权。

    2) 但是如果你现在看一下'ligther' 复合模式,似乎我们有我们的家伙:

    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] ) ;
     }
    

    【讨论】:

    • 'lighter' 为我工作。非常感谢!非常翔实的答案。
    猜你喜欢
    • 2021-09-04
    • 2011-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    • 2015-02-03
    • 1970-01-01
    相关资源
    最近更新 更多