预乘的原因在于它实际上会使目标图像的 alpha 值平方后再将源图像添加到目标图像中。
例如,如果没有进行预乘,我们得到的源图像数据如下:
srcA = origA
srcR = origR
srcG = origG
srcB = origB
当应用于目标时,我们得到了以下结果图像:
a = ((srcA * srcA) >> 8) + ((tgtA * (255 - srcA)) >> 8)
r = ((srcR * srcA) >> 8) + ((tgtR * (255 - srcA)) >> 8)
g = ((srcG * srcA) >> 8) + ((tgtG * (255 - srcA)) >> 8)
b = ((srcB * srcA) >> 8) + ((tgtB * (255 - srcA)) >> 8)
扩展开来,我们得到:
a = ((origA * origA) >> 8) + ((tgtA * (255 - origA)) >> 8)
r = ((origR * origA) >> 8) + ((tgtR * (255 - origA)) >> 8)
g = ((origG * origA) >> 8) + ((tgtG * (255 - origA)) >> 8)
b = ((origB * origA) >> 8) + ((tgtB * (255 - origA)) >> 8)
毫不意外...
现在来看预乘的源图像数据:
srcA = (origA * origA) >> 8
srcR = (origR * origA) >> 8
srcG = (origG * origA) >> 8
srcB = (origB * origA) >> 8
应用于目标时,它是:
a = (srcA >> 8) + ((tgtA * (255 - srcA)) >> 8);
r = (srcR >> 8) + ((tgtR * (255 - srcA)) >> 8);
g = (srcG >> 8) + ((tgtG * (255 - srcA)) >> 8);
b = (srcB >> 8) + ((tgtB * (255 - srcA)) >> 8);
好的,我们已经知道这个了,但是如果我们扩展一下,你会看到其中的区别:
a = (origA * origA) >> 8 + ((tgtA * (255 – ((origA * origA) >> 8))) >> 8);
r = (origR * origA) >> 8 + ((tgtR * (255 - ((origA * origA) >> 8))) >> 8);
g = (origG * origA) >> 8 + ((tgtG * (255 – ((origA * origA) >> 8))) >> 8);
b = (origB * origA) >> 8 + ((tgtB * (255 – ((origA * origA) >> 8))) >> 8);
与非预乘扩展相比,可以进行如下对比:
a = ((origA * origA) >> 8) + ((tgtA * (255 - origA)) >> 8)
r = ((origR * origA) >> 8) + ((tgtR * (255 - origA)) >> 8)
g = ((origG * origA) >> 8) + ((tgtG * (255 - origA)) >> 8)
b = ((origB * origA) >> 8) + ((tgtB * (255 - origA)) >> 8)
您可以立即看到,当应用origA值到目标时,我们会对其进行平方处理,这意味着更多的目标将传递到结果颜色值中。
通过平方处理,您表达了这样一种意思:我希望更多的目标能够传递过来。
这就是为什么预乘可以消除透明块周围的色带数量,因为那些Alpha值较低的像素会比不预乘时获得更多的目标像素,而这是按指数比例发生的。
希望这能让您明白。
srcA * srcA
应该只是srcA
。您不需要将 alpha 乘以自身。 - user180326