执行alpha混合的正确方法是什么? (C)

时间:2010-06-30 21:03:35

标签: c graphics transparency 2d alphablending

我正在写一个非常简单的图形库,我正在试图弄清楚如何进行alpha混合。我尝了几次,但结果不尽如人意。根据维基百科,我应该这样做:

值=(1-alpha) Value0 + alpha value1

然而,这根本不起作用。也许我做错了什么?

我所包含的代码绘制了一幅彩色图片(即“邻近”功能),然后尝试在(100,100)处绘制一个部分透明的框。然而,我没有一个白色的半透明盒子,而是对图像产生了一种奇怪的失真(我会尝试将它们放在我的帖子的底部)。有什么建议?这是我的代码:

#include "hgl.h"

void proximity()
{
    int x = 0, y = 0, d1, d2, d3, dcenter;

    while(x < WIDTH){
        while(y < HEIGHT){
            d1 = distance(x, y, (WIDTH/2) - 200, (HEIGHT/2) + 200);
            d2 = distance(x, y, (WIDTH/2) + 200, (HEIGHT/2) + 200);
            d3 = distance(x, y, (WIDTH/2), (HEIGHT/2) - 150);
            dcenter = distance(x, y, WIDTH/2, HEIGHT/2);
            putpixel(x, y, d1, d2, d3);
            y++;
        }
        y = 0;
        x++;
    }
}

int alpha_transparency(float alpha, float value1, float value2)
{
    return (1-alpha) * value1 + alpha * value2;
}

void transparent_box(int pos_x, int pos_y, int width, int height, float alpha, char r, char g, char b)
{
    int x = 0, y = 0;
    while(x < width)
    {
        while(y < height)
        {
            int rr, rg, rb;
            rr = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].r, r);
            rg = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].g, g);
            rb = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].b, b);
            putpixel(pos_x + x, pos_y + y, rr, rg, rb);
            y++;
        }
        x++;
        y = 0;
    }
}

int main()
{
    fp = fopen("out.bmp","wb");

    set_dimensions(1440, 900);
    insert_header();

    white_screen();

    proximity();
    transparent_box(100, 100, 500, 500, .9, 255, 255, 255);

    insert_image();
    fclose(fp);
    return 0;
}

抱歉,我无法包含输出,因为我是新用户。但是,这里有链接:

Original Picture

Picture with "transparent" box

3 个答案:

答案 0 :(得分:2)

你的alpha混音功能是正确的;另一种思考alpha混合的方法是它在基于alpha的两个颜色值之间进行插值,因此它应该是[0,1]中的值。

但是,您不应将颜色分量传递为char,默认情况下会将其标记为unsigned char。您应该将它们作为255或更宽的整数类型传递。发生的事情是,不是像你期望的那样传递-1,而是传递unsigned char

换句话说,将您的颜色组件存储为char,以确保您没有签名恶作剧(参见EDIT2)。

编辑:请注意,如果您的alpha在[0,255]中,则应将其标准化为[0,1]以执行Alpha混合操作。

EDIT2:另外,如果您将像素存储为unsigned char而不是alpha_transparency(0.9, (char)255, (char)255) == alpha_transparency(0.9f, -1.0f, -1.0f) == -1.0f == 0xff (cast to char) alpha_transparency(0.9, (char)128, (char)255) == alpha_transparency(0.9f, -128.0f, -1.0f) == -13.7f == 0xf3 alpha_transparency(0.9, (char)127, (char)255) == alpha_transparency(0.9f, 127.0f, -1.0f) == -11.80f == 0x0b alpha_transparency(0.9, (char)0, (char)255) == alpha_transparency(0.9f, 0.0f, -1.0f) == -0.9f == 0x00 ,这可以解释我看到的奇数钳位:

{{1}}

答案 1 :(得分:0)

我认为问题在于你处理颜色的方式。维基百科中使用的方法假设0为黑色,1为白色,0.5为中间。但是你的代码是使用整数,所以我假设你将0定义为黑色,将255定义为白色。

所以正确的代码是:

return (255-alpha)*value1+alpha*value2;

您可能也会遇到编译器舍入,而您认为它不会。我会将函数中的代码更改为:

float result = (255.0f-alpha)*value1+alpha*value2;
return (int) result;

通常,使用浮点数而不是整数来处理图像是很常见的。今天许多程序将整个图像转换为浮点数,处理它然后将其转换回来。你可以通过这种方式避免一些错误。

答案 2 :(得分:0)

最好坚持使用单一数据类型:全部浮点数或全部整数;这减少了混淆的可能性,并避免了一类性能陷阱。

对于所有整数,您需要记住重新缩放整数,以便结果适合原始范围:

int alpha_transparency(int alpha, int value1, int value2) {
  int unscaled= (255-alpha)*value1 + alpha*value2;
  return unscaled / 255;  /* integer division */
}

对于所有浮点数,你需要记住将整数输入从[0..255](或其他)中的原始值标准化为[0.0 .. 1.0]中的浮点数,进行所有处理,然后转换回仅在结尾处为整数。

float input_raw_value(unsigned char value)  { return value/255.0; }
...
float alpha_transparency(float alpha, float value1, float value2) {
  return (1.0-alpha)*value1 + alpha*value2;
}
...
unsigned char output_raw_value(float value) { return value*255.0; }

请注意,我忽略了每种方法中的舍入问题;一旦你掌握了基本的数学运算,就应该注意这一点。此外,还有各种技巧可以通过乘法或比特来替换分区(可能相对较慢)。