3

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

值 = (1-alpha) 值0 + alpha值 1

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

我包含的代码绘制了一张彩色图片(即“接近”功能),然后尝试在 (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;
}

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

原图

带有“透明”框的图片

4

3 回答 3

2

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

但是,您不应该将颜色组件传递为char默认签名的 。您应该将它们作为unsigned char或作为更广泛的整数类型传递。正在发生的事情是,255您没有按预期传递,而是传递了-1.

换句话说,将您的颜色分量存储为unsigned chars 以确保您没有签名恶作剧(请参阅 EDIT2)。

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

EDIT2:另外,如果您将像素存储为char而不是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
于 2010-07-01T22:57:41.990 回答
0

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

所以正确的代码是:

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

您可能还会遭受编译器舍入的痛苦,而您认为它不会。我会将您函数中的代码更改为:

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

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

于 2010-06-30T21:12:05.460 回答
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; }

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

于 2010-07-01T22:13:27.687 回答