在 C/C++ 中用 alpha 值覆盖像素

Posted

技术标签:

【中文标题】在 C/C++ 中用 alpha 值覆盖像素【英文标题】:Overlaying pixels with alpha value in C/C++ 【发布时间】:2014-11-16 11:21:04 【问题描述】:

我正在尝试创建一种算法,以在完全不透明的图像之上覆盖具有透明度的图像。 在下一个示例中,我有一个完全不透明的背面图像,以及一个带有漫射边缘的蓝色框架的正面图像。 我遇到的问题是我的实现错误地覆盖了产生暗像素的半透明区域。

这是我的实现:

#define OPAQUE 0xFF
#define TRANSPARENT 0
#define ALPHA(argb)  (uint8_t)(argb >> 24)
#define RED(argb)    (uint8_t)(argb >> 16)
#define GREEN(argb)  (uint8_t)(argb >> 8)
#define BLUE(argb)   (uint8_t)(argb)
#define ARGB(a, r, g, b) (a << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)
#define BLEND(a, b, alpha) ((a * alpha) + (b * (255 - alpha))) / 255

void ImageUtil::overlay(const uint32_t* front, uint32_t* back, const unsigned int width, const unsigned int height)

    const size_t totalPixels = width * height;

    for (unsigned long index = 0; index < totalPixels; index++)
    
        const uint32_t alpha = ALPHA(*front);

        const uint32_t R = BLEND(RED(*front), RED(*back), alpha);
        const uint32_t G = BLEND(GREEN(*front), GREEN(*back), alpha);
        const uint32_t B = BLEND(BLUE(*front), BLUE(*back), alpha);

        *backPixels++ = ARGB(OPAQUE, R , G, B);
        *frontPixels++;
    

更新:

测试图像文件

DOWNLOAD

【问题讨论】:

调试时发现了什么? 您是如何加载图像的?您的图像加载器是否可以预乘该图像? 图片加载正确。我已经尝试过分别加载和保存它们而不合并,这会产生相同的原始图像。我认为问题可能出在混合像素的公式中,但我无法发现问题。 标准的 alphablend 算法是((RGB1 * alpha) + (RGB2 * (255 - alpha))) / 255 也许你的正面图像有预乘 alpha。如果确实如此,并且您再次将其乘以 alpha,它将导致您得到的结果(alpha 不是 0 或 255 的较暗结果)。 【参考方案1】:

按照 gmaninterjay 的 cmets 提示,我进行了进一步调查,是的,数据正在加载预乘 alpha。 这会在混合时产生变暗。解决方案是取消前面的像素相乘,终于得到了预期的结果。

不相乘公式:

((0xFF * color) / alpha)

最终代码:

#define OPAQUE 0xFF;
#define TRANSPARENT 0;

#define ALPHA(rgb) (uint8_t)(rgb >> 24)
#define RED(rgb)   (uint8_t)(rgb >> 16)
#define GREEN(rgb) (uint8_t)(rgb >> 8)
#define BLUE(rgb)  (uint8_t)(rgb)

#define UNMULTIPLY(color, alpha) ((0xFF * color) / alpha)
#define BLEND(back, front, alpha) ((front * alpha) + (back * (255 - alpha))) / 255
#define ARGB(a, r, g, b) (a << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF)

void ImageUtil::overlay(const uint32_t* front, uint32_t* back, const unsigned int width, const unsigned int height)

    const size_t totalPixels = width * height;

    for (unsigned long index = 0; index < totalPixels; index++)
    
        const uint32_t frontAlpha = ALPHA(*front);

        if (frontAlpha == TRANSPARENT)
        
            *back++;
            *front++;
            continue;
        

        if (frontAlpha == OPAQUE)
        
            *back++ = *front++;
            continue;
        

        const uint8_t backR = RED(*back);
        const uint8_t backG = GREEN(*back);
        const uint8_t backB = BLUE(*back);

        const uint8_t frontR = UNMULTIPLY(RED(*front), frontAlpha);
        const uint8_t frontG = UNMULTIPLY(GREEN(*front), frontAlpha);
        const uint8_t frontB = UNMULTIPLY(BLUE(*front), frontAlpha);

        const uint32_t R = BLEND(backR, frontR, frontAlpha);
        const uint32_t G = BLEND(backG, frontG, frontAlpha);
        const uint32_t B = BLEND(backB, frontB, frontAlpha);

        *back++ = ARGB(OPAQUE, R , G, B);
        *front++;
    

【讨论】:

以上是关于在 C/C++ 中用 alpha 值覆盖像素的主要内容,如果未能解决你的问题,请参考以下文章

获取BMP文件的像素值

Opengl,仅当目标像素的 alpha 值为正时才混合

用于绘制图像的 Java Graphics2D 方法,其中使用了像素 alpha 值但颜色值被替换为给定的颜色

渲染管道像素阶段“Alpha混合”

渲染管道像素阶段“Alpha混合”

渲染管道像素阶段“透明度测试”