alpha混合算法资料整理

转载:http://www.xuebuyuan.com/2073463.html

1、 背景理论

1.1 对于alpha混合的理解

所谓 Alpha-Blending,其实就是按照Alpha混合向量的值来混合源像素和目标像素。

简单地说这是一种让物件产生透明感的技术。屏幕上显示的物件,每个像素中有红R、绿G、蓝B三组数值。若环境中允许像素能拥有一组α值,我们就称它拥有一个α通道α值的内容,是记载像素的透明度。这样一来使得每一个物件都可以拥有不同的透明程度。

颜色在本质上是光的产物,如果把透明度理解为玻璃的透光性,则一切就变得非常简单。例如一个 alpha = 0.2 的颜色,就可以将其想像为透光率为 80% 的彩色玻璃。我们透过这块玻璃看去,由于 80% 的光都透过了,因此留下来的颜色只剩 20%,即所谓 0.2  alpha。现在我们来做一个混合:将 alpha  0.2  0.6 的颜色进行叠加。这时,我们有了两块玻璃,一块透光率为 80%,另一块为 40%。一道光束穿过,经过 80% 透光率的玻璃时,光线强度剩下 80%,再经过 40% 透光率的玻璃时,光线进一步被削弱,只剩下 80%
* 40% = 32%
。这意味着有 32% 的透明性,即 alpha
= 0.68
。总结上面的算法,我们可以得出:

1.2  Alpha混合与Porter-Duff混合

对于alpha混合,最简单的说法是Src*alpha
+ Dst*(1-alpha)
,首先,一个大前提,以下porter/duff公式是针对预乘(premultiplied)后的结果的。什么是预乘?假设一个像素点,用RGBA 四个分量来表示,记做(R,G,B,A),那预乘后的像素就是(R*A,G*A,B*A,
A)
,这里A的取值范围是[0,1]。所以,预乘就是每个颜色分量都与该像素的alpha分量预先相乘。可以发现,对于一个没有透明度,或者说透明度为的像素来说,预乘不预乘结果都是一样的。

为什么要用预乘方式来表示像素?主要是这样会使公式更简单。而且实际上,在实际运算过程中,使用预乘像素进行运算在某些情况下效率会更高。但预乘会 对运算精度有一定影响。关于预乘的种种内容,这里不讨论,有兴趣可以自行研究。

开始讨论porter/duff混合公式之前,先定义几个符号

C - 表示像素的颜色,即(RGBA)RGB部分,Ccolor的缩写

A - 表示像素的透明度,Aalpha

s  - 表示两个混合像素的源像素,ssource

d - 表示两个混合像素的目标像素,ddestination

r - 表示两个像素混合后的结果,rresult

F - 表示作用于CA上的因子,Ffactor

有了这个符号之后,可以开始用它们来描述porter/duff混合公式了,如下,

Cr = Cs*Fs + Cd*Fd

Ar = As*Fs + Ad*Fd

对于12种不同的混合方式,仅仅是FsFd的取值不同,比如最为常见的SRC_OVER混合方式,

Fs = 1, Fd = (1-As)

所以

Cr = Cs + Cd*(1-As)

Ar = As + Ad*(1-As)

我们之前提到的Src*alpha + Dst*(1-alpha)其实就是SRC_OVER的一种特殊情况,即目标像素的alpha1的情况。你可能决定这两个公式长得还有点区别,别忘了 porter/duff公式是以预乘方式来讨论的,展开成非预乘之后就是

Cr = Cs*As + Cd*Ad*(1-As)

如果Ad = 1,那就和之前的公式一样了。对于通常的GUI应用,因为最终的背景一定是alpha1的,所以使用这个公式就足够了。但是,对于OSDOnScreen-Display)与video 加的情况,由于OSD的最终背景很可能是半透明的,如果再使用这个简化过的公式,可能就会造成结果不正确了。这种时候 就必须按照porter/duff公式严格计算。

下面是12porter/duff混合的具体定义:

1. CLEAR

Fs = Fd = 0

2. SRC

Fs = 1 Fd = 0

3. DST

Fs = 0 Fd = 1

4. SRC OVER

Fs = 1 Fd = (1-As)

5. DST OVER

Fs = (1-Ad) Fd = 1

6. SRC IN

Fs = Ad Fd = 0

7. DST IN

Fs = 0 Fd = As

8. SRC OUT

Fs = (1-Ad) Fd = 0

9. DST OUT

Fs = 0 Fd = (1-As)

10.SRC ATOP

Fs = Ad Fd = (1-As)

11.DST ATOP

Fs = (1-Ad) Fd = As

12.XOR

Fs = (1-Ad) Fd = (1-As)

 

1.3 中间件原先alpha混合的关键代码

if(eDstFormat == EM_UDIOSG_PIXEL_FORMAT_ARGB_8888)

    {

          UINT tmpDst, tmpSrc, *dstAddr, *srcAddr;

          UCHAR dR, dG, dB, dA, sR, sG, sB, sA, alpha;

       

          for (ii = 0; ii < h; ++ii)

          {

                 dstAddr = (UINT*)pucDst;

                 srcAddr = (UINT*)pucSrc;

                 for (jj = 0; jj < w; ++jj)

                 {

                        tmpSrc = *srcAddr;

                        alpha = (UCHAR)(tmpSrc>>24);

                        if(alpha != 0)

                        {

                               if(blending_factor != 0xFF)

                               {

                                      alpha =  (alpha * blending_factor) >>8;

                                         //全局Alpha参与后,如果源的Alpha位为0,则不拷贝

                                         if(alpha == 0)

                                         {

                                                continue;

                                         }

                               }

 

                                  sB = (UCHAR)tmpSrc;

                               sG = (UCHAR)(tmpSrc>>8);

                               sR = (UCHAR)(tmpSrc>>16);

                                  sA = alpha;

                                 

                               tmpDst = *dstAddr;

                               dB = (UCHAR)tmpDst;

                               dG = (UCHAR)(tmpDst>>8);

                               dR = (UCHAR)(tmpDst>>16);

                               dA = (UCHAR)(tmpDst>>24);

 

                                     /** 多层Alpha混合公式 */

                                         dA = sA + dA - ((sA * dA)/0xff);

                                         dR = dR + (sR - dR) * sA /dA;

                                         dG = dG + (sG - dG) * sA /dA;

                                         dB = dB + (sB - dB) * sA /dA;

 

                               *dstAddr = ((UINT)dA<<24)|((UINT)dR<<16)|((USHORT)dG<<8)|dB;

                        }

                        ++dstAddr;

                        ++srcAddr;

                 }

                 pucDst += dstPitch;

                 pucSrc += srcPitch;

          }

}

 

1.4 OpenGLalpha混合计算方法

OpenGL 会把源颜色和目标颜色各自取出,并乘以一个系数(源颜色乘以的系数称为源因子,目标颜色乘以的系数称为目标因子),然后相加,这样就得到了新的颜 色。(也可以不是相加,新版本的OpenGL可以设置运算方式,包括加、减、取两者中较大的、取两者中较小的、逻辑运算等,但我们这里为了简单起见,不讨 论这个了)
下面用数学公式来表达一下这个运算方式。假设源颜色的四个分量(指红色,绿色,蓝色,alpha值)是(Rs,
Gs, Bs, As)
,目标颜色的四个分量是(Rd, Gd, Bd, Ad),又设源因子为(Sr,
Sg, Sb, Sa)
,目标因子为(Dr, Dg, Db, Da)。则混合产生的新颜色可以表示为:

(Rs*Sr+Rd*Dr, Gs*Sg+Gd*Dg, Bs*Sb+Bd*Db, As*Sa+Ad*Da)
当然了,如果颜色的某一分量超过了1.0,则它会被自动截取为1.0,不需要考虑越界的问题。

源因子和目标因子是可以通过glBlendFunc函数来进行设置的。glBlendFunc有两个参数,前者表示源因子,后者表示目标因子。这两个参数可以是多种值,下面介绍比较常用的几种。

GL_ZERO
     表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。

GL_ONE
      表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。

GL_SRC_ALPHA
:表示使用源颜色的alpha值来作为因子。

GL_DST_ALPHA
:表示使用目标颜色的alpha值来作为因子。

GL_ONE_MINUS_SRC_ALPHA
:表示用1.0减去源颜色的alpha值来作为因子。

GL_ONE_MINUS_DST_ALPHA
:表示用1.0减去目标颜色的alpha值来作为因子。
 此以外,还有GL_SRC_COLOR(把源颜色的四个分量分别作为因子的四个分量)、GL_ONE_MINUS_SRC_COLOR GL_DST_COLORGL_ONE_MINUS_DST_COLOR等,前两个在OpenGL旧版本中只能用于设置目标因子,后两个在OpenGL 旧版本中只能用于设置源因子。新版本的OpenGL则没有这个限制,并且支持新的GL_CONST_COLOR(设定一种常数颜色,将其四个分量分别作为 因子的四个分量)、GL_ONE_MINUS_CONST_COLORGL_CONST_ALPHA GL_ONE_MINUS_CONST_ALPHA。另外还有GL_SRC_ALPHA_SATURATE。新版本的OpenGL还允许颜色的alpha 值和RGB值采用不同的混合因子。

举例来说:
如果设置了glBlendFunc(GL_ONE, GL_ZERO);,则表示完全使用源颜色,完全不使用目标颜色,因此画面效果和不使用混合的时候一致(当然效率可能会低一点点)。如果没有设置源因子和目标因子,则默认情况就是这样的设置。
如果设置了glBlendFunc(GL_ZERO, GL_ONE);,则表示完全不使用源颜色,因此无论你想画什么,最后都不会被画上去了。(但这并不是说这样设置就没有用,有些时候可能有特殊用途)
 果设置了glBlendFunc(GL_SRC_ALPHA,
GL_ONE_MINUS_SRC_ALPHA);
,则表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值,这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,而目标颜色所占比例则减 小。这种情况下,我们可以简单的将源颜色的alpha值理解为不透明度。这也是混合时最常用的方式。
如果设置了glBlendFunc(GL_ONE, GL_ONE);,则表示完全使用源颜色和目标颜色,最终的颜色实际上就是两种颜色的简单相加。例如红色(1,
0, 0)
和绿色(0, 1, 0)相加得到(1,
1, 0)
,结果为黄色。

 

 

 

 


1.5 Qt关于预乘alpha混合算法的代码:

1)RGB分量进行预乘操作

unsigned premultipliedARGBFromColor(const Color& color)

{

    unsigned pixelColor;

 

    if (unsigned alpha = color.alpha()) {

        pixelColor = alpha << 24 |

             ((color.red() * alpha  + 254) / 255) << 16 |

             ((color.green() * alpha  + 254) / 255) << 8 |

             ((color.blue() * alpha  + 254) / 255);

    } else

         pixelColor = color.rgb();

 

    return pixelColor;

}

 

2)将预乘后的数据进行非预乘还原

Color colorFromPremultipliedARGB(unsigned pixelColor)

{

    RGBA32 rgba;

 

    if (unsigned alpha = (pixelColor & 0xFF000000) >> 24) {

        rgba = makeRGBA(((pixelColor & 0x00FF0000) >> 16) * 255 / alpha,

                        ((pixelColor & 0x0000FF00) >> 8) * 255 / alpha,

                         (pixelColor & 0x000000FF) * 255 / alpha,

                          alpha);

    } else

        rgba = pixelColor;

 

    return Color(rgba);

}

 

3)预乘后的数据采用的混合算法

static inline Color blendFunc(const AnimationBase* anim, const Color& from, const Color& to, double progress)

{

    // We need to preserve the state of the valid flag at the end of the animation

    if (progress == 1 && !to.isValid())

        return Color();

 

    // Contrary to the name, RGBA32 actually stores ARGB, so we can initialize Color directly from premultipliedARGBFromColor().

    // Also, premultipliedARGBFromColor() bails on zero alpha, so special-case that.

    Color premultFrom = from.alpha() ? premultipliedARGBFromColor(from) : 0;

    Color premultTo = to.alpha() ? premultipliedARGBFromColor(to) : 0;

 

    Color premultBlended(blendFunc(anim, premultFrom.red(), premultTo.red(), progress),

                 blendFunc(anim, premultFrom.green(), premultTo.green(), progress),

                 blendFunc(anim, premultFrom.blue(), premultTo.blue(), progress),

                 blendFunc(anim, premultFrom.alpha(), premultTo.alpha(), progress));

 

    return Color(colorFromPremultipliedARGB(premultBlended.rgb()));

}

你可能感兴趣的:(alpha混合算法资料整理)