views:

109

answers:

3

Hi,

I'm writing a very simple graphics library, and I'm trying to figure out how to do alpha blending. I tried it a few times, but my results were less than satisfactory. According to Wikipedia, I should do:

Value = (1-alpha)*Value0 + alpha*value1

This, however is not working at all. Maybe I'm doing something wrong?

The code I've included draws a colorful picture (that's the 'proximity' function), then attempts to draw a partially transparent box at (100,100). However, instead of a white translucent box, I get a weird-looking distortion to the image (I'll try to have them at the bottom of my post). Any suggestions? Here is my code:

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

Sorry, I couldn't include the output because I'm a new user. However, here are the links:

Original Picture

Picture with "transparent" box

A: 

The issue I think is in the way you're dealing with colors. The method used in Wikipedia assumes that 0 is black and 1 is white, with 0.5 being in the middle. However your code is using ints, so I assume that you're defining 0 as black and 255 as white.

So the correct code is:

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

You may be also suffering from the compiler rounding where you don't think it would. I would change the code in your function to this:

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

In general it's very common to work with images using floats instead of ints. Many programs today convert the entire image to floats, process it then convert it back. You can avoid several bugs this way.

Timothy Baldridge
Well, one thing's for sure: you were right about the rounding. I hadn't even asked about it! Thanks!About the transparency though, that didn't seem to work very well. You were right about the colors, they do range from 0 to 255 for me. Surprisingly though, changing from (1-alpha) to (255-alpha) didn't make much of a difference. A color value of 256 is exactly the same as a color value of zero, so I think the first time it just looped around and produced the same general effect. I'll keep messing with it for now, and again thanks for the help!Hassan
Hassan
The alpha calculation is correct; in this case, alpha is a fractional value that should be interpolating between value1 and value2. The fact that the color range is [0, 255] doesn't matter.
MSN
A: 

It is probably best to stick with a single datatype: either all floats, or all integers; this reduces the potential for confusion, and avoids a class of performance pitfalls.

For all ints, you need to remember to re-scale the integer so the result fits back into the original range:

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

For all-floats, you need to remember to normalize integer inputs from a raw value in [0..255] (or whatever) to a float in [0.0 .. 1.0], do all the processing, then convert back to integer only at the end.

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

Note that I have ignored rounding issues in each method; once you've got the basic math up and running, you should pay some attention to that. Also, there are various tricks to replace the divisions (which can be relatively slow) by multiplication or bit-fiddling.

comingstorm
+1  A: 

Your alpha blend function is correct; another way to think of alpha blending is that it interpolates between two color values based on alpha, so it should be a value in [0, 1].

However, you shouldn't be passing the color components as char, which is signed by default. You should pass them either as unsigned char or as a wider integer type. What is happening is that instead of passing in 255 as you expect, you are passing in -1.

In other words, store your color components as unsigned chars to ensure you don't have signedness shenanigans (see EDIT2).

EDIT: Note that if your alpha is in [0, 255], you should normalize it to [0, 1] to perform the alpha blending operation.

EDIT2: Also, if you are storing your pixels as char instead of unsigned char, this would explain the odd clamping I saw:

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
MSN