Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
80 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Creating a
 DirectDraw Palette

 Pixel Formats
 Locking Surfaces
 Plotting Pixels
 Notes on Speed
 Fading Out
 Basic Transparency

 Printable version
 Discuss this article
 in the forums



The Series
 Beginning Windows
 Programming

 Using Resources
 in Win32 Programs

 Tracking Your
 Window/Using GDI

 Introduction
 to DirectX

 Palettes and Pixels
 in DirectDraw

 Bitmapped Graphics
 in DirectDraw

 Developing the
 Game Structure

 Basic Tile Engines
 Adding Characters
 Tips and Tricks

Basic Transparency

Overlaying a transparent image on an opaque one is not something you can use a lookup table for, because it would have to have 65,536 entries in both dimensions. It's going to be awhile before the average computer has the 8.6 GB of RAM it takes to hold that monster. :) So you'll have to do all the calculations for each pixel. I'll give you the basic idea. Suppose you want to place image A on top of image B, and image A is to have a transparency percentage of pct, which is a floating-point number between 0 and 1, where 0 is fully transparent (invisible) and 1 is fully opaque. Then, let's call a pixel in image A pixelA, and its counterpart in image B we'll call pixelB. You would apply the following equation:

color = (pixelA * pct) + (pixelB * (1-pct));

Basically, this is just a weighted average of the two pixel colors. By multiplying the colors, I mean to multiply the intensities for red, green, and blue. So you're actually looking at six floating-point multiplications per pixel. You can use some small lookup tables to lighten the workload a little. Experiment with it!

The other thing you might want to do is to create a window of a solid color that's partially transparent. If you've seen a demo or screenshots of my upcoming RPG, Terran, you know what I mean. An effect like that can be done entirely with a lookup table, because in Terran's case, I just needed to provide a color of blue for every possible color on the screen. In fact, a lookup table is exactly how I do the effect. I'll show you exactly what I mean.

void Init_CLUT(void)
{
  int x, y, bright;
  UCHAR r, g, b;

  // calculate textbox transparency CLUT
  for (x=0; x<65536; x++)
  {
    // transform RGB data
    if (color_depth == 15)
    {
      r = (UCHAR)((x & 0x7C00) >> 10);
      g = (UCHAR)((x & 0x03E0) >> 5);
      b = (UCHAR)(x & 0x001F);

    }
    else  // color_depth must be 16
    {
      r = (UCHAR)((x & 0xF800) >> 11);
      g = (UCHAR)((x & 0x07E0) >> 6);   // shifting 6 bits instead of 5 to put
      b = (UCHAR)(x & 0x001F);          // green on a 0-31 scale instead of 0-63
    }

    // find brightness as a weighted average
    y = (int)r + (int)g + (int)b;
    bright = (int)((float)r * ((float)r/(float)y) + 
                   (float)g * ((float)g/(float)y) + 
                   (float)b * ((float)b/(float)y) + .5f);

    // write CLUT entry as 1 + one half of brightness
    clut[x] = (USHORT)(1 + (bright>>1));
  }
}

This is the code from Terran that creates the lookup table with which the text boxes are generated. There are typecasts everywhere just for safety, and it could be sped up quite a bit, but I didn't bother because it's only called once, in the very beginning of the game. First, the values for red, green, and blue are extracted. Since this is in 16-bit color, notice that I have a variable called color_depth that takes into account whether the pixel format is 565 or 555. Then, the brightness of the pixel is calculated using this formula:

y = r + g + b;
brightness = r*(r/y) + g*(g/y) + b*(b/y);

It's just another weighted average. I'm not sure if that's really how the brightness of a color is defined, but it seems logical and it produces a nice effect. Anyway, at the end of the equation I've added .5, because when you cast a float to an int, the decimal is truncated. Adding .5 turns that truncation into rounding. Finally, I divide the brightness in half and add one. The division is so the text boxes don't get too bright, and the one is so the box is never totally black. Since the low bits of a 16-bit color descriptor are for blue, I can just set the color by setting the value of blue, instead of having to use a macro. Make sense? Finally, before I wrap this up, I'll also show you how I create a text box:

int Text_Box(USHORT *ptr, int pitch, LPRECT box)
{
    int x, y, jump;
    RECT ibox;

    // leave room for the border
    SetRect(&ibox, box->left+3, box->top+3, box->right-3, box->bottom-3);
	
    // update surface pointer and jump distance
    ptr += (ibox.top * pitch + ibox.left);
    jump = pitch - (ibox.right - ibox.left);

    // use CLUT to apply transparency
    for (y=ibox.top; y<ibox.bottom; y++)
    {
        for (x=ibox.left; x<ibox.right; x++, ptr++)
            *ptr = clut[*ptr];
        ptr += jump;
    }
    return(TRUE);
}

Now that it's just a lookup table, this looks a lot like the code for fading. The only difference is that the lookup table holds different values. And it's only one column instead of 20. :) The declaration for the lookup table, by the way, looks like this:

USHORT clut[65536];

With that, you should be able to produce some rather interesting effects. To get you started, check out the sample code that comes with this article. It's available here. You might try modifying it so that it fills the screen with pixels, then plots a transparent box over the top of them.

Closing

That does it for pixel-based graphics. Next time around, we'll be working with bitmaps! Believe it or not, working with bitmaps is easier than all this pixel stuff. Seems a little backwards, doesn't it? You'll find out. In the meantime, send me any questions you might have and I'll be happy to help you out. My E-mail address is ironblayde@aeon-software.com, and my ICQ UIN is 53210499. Oh, one other thing... the next article will be the last one covering general DirectX techniques. After that, we'll get into specific applications that you can use for developing an RPG. More details to follow. :) Later!

Copyright © 2000 by Joseph D. Farrell. All rights reserved.