我是黑,你是白,我们俩加起来就是白加黑?讲讲Alpha混色模式合成算法

2013年北京的西三旗夏天夜晚

本文首次发表于BEFE,修正后收录于个人专栏《神奇的颜色》

一直比较好奇,如果把两张图片叠加在一起会怎么样,最终生成的图形是怎么得来的。我们知道,显示器使用的是色光的 RGB 模型,一种加色混合模型,三原色是红绿蓝,三种色光等量混合形成白光。那么当两张图片叠加在一起的时候,像素间的叠加原理是怎样的?图像与背景结合的过程又是怎么回事,今天带着大家聊聊--Alpha 合成算法。

文章概览

本文基于 Thomas Porter 和 Tom Duff 发表于1984年原始论文,介绍 Alpha 通道的合成及混合算法,帮助我们更好的理解图像与背景色结合的过程。

你将了解:

  • RGBA 模型和 Alpha 通道
  • Compositing Operators
  • Blending
  • 公式推导过程和示例

Alpha通道

我们在写 CSS 的时候,经常使用 rgba,例如,给 div 一个背景色,使用

div {
  background-color: rgba(255, 0, 0, 1);
}
复制代码

RGBA 模型是由 RGB 色彩空间(RGB 模型的加色色彩空间)和 Alpha 通道组成。Alpha 是图像的不透明度参数,数值可用百分比、整数或者0-1的实体数表示。我们知道,当 Alpha 值为0的时候,表示完全透明,无法被看见;当数值为100%或者是1,表示像素完全不透明。当通道值介于0%~100%之间,像素就可以像透过玻璃一样,形成半透明的效果,这也使像素的混合成为可能。

RGBA 也可以写作 ARGB,例如,100%不透明的红色red用16进制表示为0xFF0000或者0xFF0000FF,50%透明的红色,可以表示为0xFF000080,ARGB 格式即为:0x80FF0000。

Compositing Operators

废话不多说,先直接看Demo,看看才知道它是个啥。

在 Canvas 中,通过设置globalCompositeOperation在绘制形状时应用合成或混合模式操作符。

globalCompositeOperation 的初始值是 source-over
可选范围为 <composite-mode> | <blend-mode>
<composite-mode> = 
    clear | copy | source-over | destination-over | source-in | destination-in |
    source-out | destination-out | source-atop | destination-atop | xor | lighter

<blend-mode> = normal | multiply | screen | overlay | darken | lighten |
    color-dodge |color-burn | hard-light | soft-light | difference | exclusion |
    hue | saturation | color | luminosity
复制代码

下图是利用 compositing operators 的12种操作符绘制的红蓝组合示例。 image.png

从图中很容易明白:

  • source为前景,destination为背景
  • clear全部表示清除,任何区域都不可用
  • copy 仅显示 source 区域
  • over 为一个在另一个之上
  • in 取二者的交集
  • out 取二者不相交的区域
  • atop 一个在另一个的顶部
  • xor 取或
  • lighter 加色法

解释原理

示例中为12种 Porter-Duff 操作符,这些操作符控制四个子像素区域的混合结果。

合成算法基于像素模型,sourcedestination构成像素的最终颜色。像素被划分为4个子像素区域(二进制组合方式:00/01/10/11),每个区域表示源和目标的组合情况。

  • both(source & destination),两者共同影响最终像素颜色
  • source,最终颜色仅仅与 source 有关系
  • destination,最终颜色仅仅与 destination 有关系
  • none,两者都不影响最终的像素颜色

image.png

这样,每个区域对最终像素颜色影响,就由该区域像素所处形状的覆盖率和使用的 operator 操作符来决定。用 alpha 来表示覆盖率,alpha 等于1就是完全覆盖,等于0就是没有覆盖,算法给出的每块区域的面积方程为:

destination 和 backdrop 是同样的概念,此模型中用 αs 表示 source 的覆盖率,αb 表示backdrop 的覆盖率

both = αs * αb
source = αs * (1 - αb)
destination = (1 - αs) * αb
none = (1 - αs) * (1 - αb)
复制代码

在颜色模型的例子中,已知source 和 destination 的覆盖率均为0.5(模型中假设),即 αs 和 αb 都等于0.5,代入可得:

both = 0.5 * 0.5 = 0.25
source = 0.5 * (1 - 0.5) = 0.25
destination = (1 - 0.5) * 0.5 = 0.25
none = (1 - 0.5) * (1 - 0.5) = 0.25
复制代码

所以,每种情况的覆盖率为0.25。

算法中,像素最终覆盖率的方程式为:

αo = αs * Fa + αb * Fb
复制代码

对应的覆盖区颜色值为:

co = αs * Fa * Cs + αb * Fb * Cb
复制代码

我们需要的运算结果Co应该为:

Co = co / αo
复制代码

解释下几个术语的含义:

  • + - * / 为数学运算符,即一元加、一元减、乘法操作符和除法操作符
  • 所有值的取值范围为[0, 1],颜色值需要除以255换算,如0xFF / 255 = 1
  • co是重叠区域,输出的颜色,是预乘 Alpha后的值
  • αo表示合成后的颜色 alpha
  • αssource 的 alpha
  • αbdestination 的 alpha
  • Co重叠区域的颜色值
  • Cssource 的像素颜色值
  • Cbbackdrop(同 destination)的像素颜色值
  • Fa由 operator 决定 Fa 值
  • Fb由 operator 决定 Fb 值

我们用operator = source-over来具体验证一下公式。

image.png

已知source-over对应的:
Fa = 1; Fb = 1 – αs
则:
co = αs * Cs + αb * Cb * (1 – αs)
αo = αs + αb * (1 – αs)
示例中,αs为1,结果简化为:
co = Cs
αo = 1
最终生成的重叠区域颜色为Cs本身的颜色,即图中的蓝色,与图像事实相符。
其他操作图,同理可得。
复制代码

source-over 等 operator 的具体设定值,比如 Fa = 1,是算法中给出的。通过字面含义也可以理解,over 即在最前,故 source 的覆盖率是1,相对应的背景是 1 - αs。

再来看一下 plus 操作符,operator = lighter的情况。

image.png

效果图:

image.png

已知lighter对应的:
Fa = 1; Fb = 1
则:
co = αs * Cs + αb * Cb;
αo = αs + αb
示例中,αs和αb均为1,公式简化为:
co = Cs + Cb
ao = 1
即红色(1, 0, 0, 1)和蓝色(0, 0, 1, 1)加色后
最终得出magenta(1, 0, 1, 1),也即洋红色或者品红色#FF00FF。
复制代码

平直Alpha

即 straight alpha,简单来说,图象中的RGB仅表示像素的颜色,与是否透明无关。

例如:像素值rgba(0.2, 0.6, 0.4, 0.5)表示像素有20%的红色亮度,60%的绿色亮度,40%的蓝色亮度,同时不透明度是50%,对应的纯色亮度为(1, 1, 1, 0.5)。

预乘Alpha

即 premultiplied alpha,是说图象中的RGB也表示像素的颜色,但已经和透明度做了乘法。(好处是混合计算逻辑中简单、准确)

例如,上述的rgba(0.2, 0.6, 0.4, 0.5)换算成预乘Alpha,是RGB的每一个值都乘以0.5为(0.1, 0.3, 0.2, 0.5),此时对应的纯色为(0.5, 0.5, 0.5, 0.5)

Blending

混合 Blending 是合成的一种应用,是计算前景和背景重叠后的混合颜色,是将前景色和背景色结合从而获得混合后的新颜色。

前景色的透明度不限,如果前景色完全透明,混合后的颜色就是背景色,如果前景色完全不透明,混合后的颜色就是前景色。介于(0, 1)中间的透明度,混合后的颜色,需要通过前景色和背景色的加权公式来计算,混合计算必须使用平直Alpha颜色值。

从概念上来理解 blending:

  1. 首先将 source 和 destination 混合,得到修正后的 source 颜色(Cs)
  2. 然后再用修正后的颜色与 destination 合成。

实际在混合中,是不分步骤的,一次性操作。

  1. 先看下混合公式被定义为:
Cm = B(Cb, Cs)
复制代码

解释下术语含义:

  • 所有值的取值范围[0, 1]
  • Cm 不同的混合函数计算得出的混合后的颜色值
  • B 不同混合模式的混合函数
  • Cb 同 Compositing 中的概念,为 backdrop 的颜色值
  • Cs source 的颜色值
  1. 修正后的颜色,公式定义为:
Cr = (1 - αb) * Cs + αb * B(Cb, Cs)
复制代码

解释术语含义:

  • Cr 修正后的颜色
  • αb backdrop 的 alpha 值

理解定义的话,可以通过类比operator = destination-over时的场景来理解

接下来,我们用operator = source-over来推导一下最终的计算公式。

  1. 已知source-over的 operator 内容。
Fa = 1; Fb = 1 – αs
αo = αs + αb * (1 – αs)
co = αs * Cs + αb * Cb * (1 – αs)
复制代码
  1. 推导一下
修正后的source颜色,Cr,也就是co中的Cs。代入

co = cs + cb * (1 - αs) // 预乘
αo * Co = αs * Cs + (1 - αs) * αb * Cb
αo * Co = αs * ((1 - αb) * Cs + αb * B(Cb, Cs)) + (1 - αs) * αb * Cb
        = αs * (1 - αb) * Cs + αs * αb * B(Cb, Cs) + (1 - αs) * αb * Cb
        = αs * Cs - αb * αs * Cs + αs * αb * Cm + αb * Cb - αs * αb * Cb
        = αs * Cs + αb * (Cb - αs * Cs + αs * Cm - αs * Cb)
        = αs * Cs + αb * (Cb - αs * (Cb + Cs - Cm)
最终:
Co = (αs * Cs + αb * (Cb - αs * (Cb + Cs - Cm))) / αo
复制代码

使用简化后的推导公式,我们就可以根据不同的场景需求,代入不同的 Cm 函数处理。

下图为根据 blending 的12种函数绘制的红蓝组合示例: image.png

在 blending 场景中的红蓝组合,右上角新增的小正方形,是利用上述推导公式得出的颜色绘制的,和图中红蓝交汇处的颜色,完全一样。

示例展示前12种混合函数效果,后四种(hue | saturation | color | luminosity)过于复杂,暂且不介绍。在 CSS 中使用mix-blend-mode属性来指定混合模式

结语

深入研读两位大佬的算法后,更加觉得写代码真是小搬运工,算法才是王道。 关于《神奇的颜色》专栏,还在努力建设中,期待能够把颜色的宁静与鲜活用独特的方式呈现出来。

参考资料

  1. wiki百科
  2. 神奇的颜色:你真的了解颜色吗
  3. Compositing and Blending
  4. PORTERDUFF算法
  5. github
  6. 示例中源码

Guess you like

Origin juejin.im/post/7054380726300475405