我正在尝试实现预乘alpha混合。在此页面上What Is Color Blending?,它们确实解释了标准Alpha混合,但没有解释预乘值。
Alpha混合:(源×Blend.SourceAlpha)+(目标×Blend.InvSourceAlpha)
根据公式,它转换为:
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 = ((srcA)) + ((tgtA * (255 - srcA)) >> 8);
r = ((srcR)) + ((tgtR * (255 - srcA)) >> 8);
g = ((srcG)) + ((tgtG * (255 - srcA)) >> 8);
b = ((srcB)) + ((tgtB * (255 - srcA)) >> 8);
由于它已经被预乘,因此我在第一项中放弃了乘以...对!
但是结果是在alpha混合和加法混合之间,趋向于加法。最后,它看起来并没有太复杂。这可能是错误的,因为它看起来应该完全像经典的alpha混合。还是这种预期的行为?
谢谢。
最佳答案
预乘有效的原因是,在将源图像添加到目标之前,它实际上最终对目标的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);
将其与以下项的NON预乘展开式比较:
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值较低的那些像素相比,如果不进行预乘,这些像素将获得更多的目标像素,并且发生在指数级。
我希望这可以解决。