iOS开发的小伙伴,可能会经常遇到图片解码后图像的RGB信息是预乘过的。
如果需要对接Engine的库,有可能他们需要非预乘的,那就比较麻烦了,编辑过的颜色效果肯定是不正确的。
那么,为什么iOS平台解码后的图像信息是预乘透明度的呢?
熟悉图片解码的应该了解,对于RGB图像,苹果只给我们提供了两种透明度信息参数:1、不保留透明度信息(如:kCGImageAlphaNoneSkipFirst)2、保留透明度信息(如,kCGImageAlphaPremultipliedFirst).
所以在保留透明度信息时,Apple只给我们提供了预乘透明度的参数,那么解码后的图片信息肯定是预乘后的了;
什么是预乘呢?
很简单,就是把每个颜色分量都乘以他的Alpha值,如RGB,预乘后为(r * a, g * a, b * a)
问题来了,为什么要预乘呢?
这样的做法可以加速图片的渲染时间,因为它避免了渲染时的额外乘法运算。
网上有一个老哥这么简单的解释了一下:
传统的后乘alpha算法在shader或者混合配置中应该是这样的。
blend(source, dest) = (source.rgb * source.a) + (dest.rgb * (1 – source.a))
而预乘算法是这样的
blend(source, dest) = source.rgb + (dest.rgb * (1 – source.a))
从感受上来说,只是程序员为了效率引入的东西,而实际上他还解决了以下几个问题。
a、解决纹理比例缩放映射产生的颜色错误问题
b、可以和其他纹理一起正常混合而不打破批次渲染
本文最重要的目的是讲解第一点,为什么会出现颜色的错误问题。当一个图片素材被引擎放置在屏幕上时,往往不是1:1映射的,有可能因为在世界坐标的不同位置,映射到屏幕可能有不同的缩放比例。比如有linear,nearest等算法可以将缺失的像素补全。这时候这些缺失像素的计算,会因为alpha因子的本身也会被平均而导致RGB因子被缩放两次,进而会产生一个黑色的边缘。
而实际上我们希望的结果应该是alpha因子RGB值只被平均一次,进而有需求就是将RGB值在素材中就被预乘。
如何恢复预乘前的RGB信息呢?
同样也是很简单的,就是每个颜色分量除以Alpha值
代码如下:
+ (void)doAlphaUnMultiplyForPixelBuffer:(CVPixelBufferRef)srcImg
{
if (srcImg != nil) {
size_t width = (int) CVPixelBufferGetWidth(srcImg);
size_t height = (int) CVPixelBufferGetHeight(srcImg);
size_t stride = (int) CVPixelBufferGetBytesPerRow(srcImg);
CVReturn lockRes = CVPixelBufferLockBaseAddress(srcImg, 0);
if (lockRes != kCVReturnSuccess) {
return;
}else {
unsigned char* pPixelBuffer = (unsigned char *)CVPixelBufferGetBaseAddress(srcImg);
for(int row = 0; row < height; row++) {
unsigned char* curLine = pPixelBuffer + row * stride;
for(int col = 0; col < width; col++) {
unsigned char* curPixel = curLine + col * 4;
unsigned int alpha = curPixel[3];
if(alpha != 0) {
curPixel[0] = (curPixel[0] * 255) / alpha;
curPixel[1] = (curPixel[1] * 255) / alpha;
curPixel[2] = (curPixel[2] * 255) / alpha;
}
}
}
}
CVPixelBufferUnlockBaseAddress(srcImg, 0);
}
}
网友评论