美文网首页
iOS开发遇到的图片预乘透明度问题

iOS开发遇到的图片预乘透明度问题

作者: little_ma | 来源:发表于2019-04-29 15:27 被阅读0次

    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);
        }
    }
    

    相关文章

      网友评论

          本文标题:iOS开发遇到的图片预乘透明度问题

          本文链接:https://www.haomeiwen.com/subject/ykvynqtx.html