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

Fading Out

One of the most commonly used screen transitions in games is the fade to black, or the fade in from black. Each one is achieved in the same manner. You simply draw your frame, then apply some sort of transformation that alters the brightness of the image. To fade out, you decrease the brightness from 100% to 0%, and to fade in, you increase it from 0% to 100%. If you're working in a palettized mode, you've got it easy! You just change the colors in your palette, and the result shows up onscreen. If you're working in RGB mode, you have to consider other methods.

Now, I'll say right here that manually fading the screen has some better alternatives. You can use Direct3D, which supports alpha-blending, to set each frame as a texture, then set the transparency level. Or, even easier, you can use the DirectDraw color/gamma controls. But if you only want to fade part of the screen, or fade to a color besides black, and you're not a Direct3D expert -- I'm certainly not! -- then knowing how to do the manual transform might come in handy.

Basically all you do is to read each pixel and break it up into red, green, and blue. Then you multiply each of the three values by the level of fading to apply, recombine the RGB values, and write the new color back out to the buffer. Sound complicated? It's not too bad. Take a look at this (highly unoptimized) sample code. It applies a fade to a viewport of size 200x200 in the upper-left corner of the display, in a 16-bit color depth using the 565 pixel format.

void ApplyFade16_565(float pct, USHORT* buffer, int pitch)
{
  int x, y;
  UCHAR r, g, b;
  USHORT color;

  for (y=0; y<200; y++)
  {
    for (x=0; x<200; x++)
    {
      // first, get the pixel
      color = buffer[y*pitch + x];

      // now extract red, green, and blue
      r = (color & 0xF800) >> 11;
      g = (color & 0x0730) >> 5;
      b = (color & 0x001F);

      // apply the fade
      r = (UCHAR)((float)r * pct);
      g = (UCHAR)((float)g * pct);
      b = (UCHAR)((float)b * pct);

      // write the new color back to the buffer
      buffer[y*pitch + x] = RGB_16BIT565(r, g, b);
    }
  }
}

Now, there are a number of things wrong with this function. First, not only is the calculation of the pixel location inside the inner loop, it's in there twice! You can get away with calculating it once in the entire function, but in this example it's calculated 80,000 times. :) Here's what you should do. At the very top of the function, you should declare another USHORT*, and set it equal to buffer. Inside the inner loop, increment the pointer by one to get to the next pixel. And inside the outer loop, increment it by whatever it takes to get down to the next line. Here's the considerably better example:

void ApplyFade16_565(float pct, USHORT* buffer, int pitch)
{
  int x, y;
  UCHAR r, g, b;
  USHORT color;
  USHORT* temp = buffer;
  int jump = pitch - 200;

  for (y=0; y<200; y++)
  {
    for (x=0; x<200; x++, temp++) // move pointer to next pixel each time
    {
      // first, get the pixel
      color = *temp;

      // now extract red, green, and blue
      r = (color & 0xF800) >> 11;
      g = (color & 0x0730) >> 5;
      b = (color & 0x001F);

      // apply the fade
      r = (UCHAR)((float)r * pct);
      g = (UCHAR)((float)g * pct);
      b = (UCHAR)((float)b * pct);

      // write the new color back to the buffer
      *temp = RGB_16BIT565(r, g, b);
    }

    // move pointer to beginning of next line
    temp+=jump;
  }
}

This is better. The value of jump is the number of USHORTs from the end of a line on the 200-pixel-wide viewport to the beginning of the next line. Still, there's not much you can do to speed up those floating-point multiplies and all the extracting/recombining of colors. Or is there? Take a look at this:

USHORT clut[65536][20];

If you asked a DOS programmer to put an array that size into one of his programs, he'd probably cry out in pain. He might even drop dead on the spot, or spontaneously combust. But in Windows, it's not really a problem if you need to do it, because you have all the system's free RAM available to you. Wouldn't it be nice if you could replace the entire inner loop with this one line?

*temp = clut[*temp][index];

That would speed things up a bit, right? :) Instead of passing a float to the function, you could pass an integer that's between 0 and 100. If the value is 100, no fade is needed, so return without doing anything. If the value is 0, just use a ZeroMemory() call to do all the work. Otherwise, divide the value by 5, and use it for the second subscript into the giant lookup table.

If you're wondering where I got the dimensions for the lookup table, 65536 is 2^16, so that's how many colors there are in a 16-bit color depth. Since our color values are unsigned values, they range from 0 to 65535. The 20 is just the number of increments you'd be using for a fade. It seemed a decent choice to me, considering the memory involved.

For 24-bit and 32-bit color, you obviously can't create a lookup table to span every color combination, so what you could do is use three smaller tables:

UCHAR red[256];
UCHAR green[256];
UCHAR blue[256];

Then, when you read each pixel, split it up into its color components, look each component up in its table, and recombine them into the resulting color. There are all sorts of ways to do things like this. The best way to determine what works well for your purposes is just to throw some code together, and then play around with it for awhile. With that in mind, I will briefly go over one other transformation you might find useful.




Next : Basic Transparency