[size="5"]Pixel Formats
First, I'd like to say a few words about 16-bit directdraw surfaces. Each 16-bit pixel on the surface holds red, green, and blue values. Since 16 is not evenly divided by 3, the most common way to hold pixel information is in the 5-6-5 format. This means the red value gets the first 5 bits, green the next 6 bits, and blue the next 5 bits. Using the 5-6-5 format, we have a total of 65,536 different colors (2^16). We have 32 shades of red, 64 shades of green, and 32 shades of blue. If the 5-6-5 format was universal, things would be much easier. However, we have to accomodate for the possibility that their is a different pixel format.
[size="5"]A Word About Bitmasks and Shifting
Many of you may already know about bitmasks and shifting. If so, skip to the next section. If you are confused by bitmasks, or have no idea what I'm talking about, read on. A bitmask is a variable that will allow us to extract the red, green, or blue component from our 16 bit value. We use logical operations such as OR (|) or AND (&) on the two variables to create a new variable with the color component. For example, suppose we have a 5-6-5 pixel encoded like this:
red: 5 bits, 00011
green: 6 bits, 001011
blue: 5 bits, 00101
0001 1001 0110 0101 (note the spacing is just to make it easier to read)
1111 1000 0000 0000
0001 1001 0110 0101 &
1111 1000 0000 0000
---------------------
0001 1000 0000 0000
0001 1000 0000 0000 >> 11 =
0000 0000 0000 0011
// suppose "pixel" contains the value above
BYTE red;
red = ((pixel & 0xF800) >> 11);
The important thing to see here, is that we can use bitmasks and shifting to decode a pixel, and to encode a pixel. Sample code for both of these operations is given below.
[size="5"]How Do We Find Out The Pixel Format?
Chris Barnes suggests in his article to use a GetRGB16 function. I've modified this function in a way that I think is simpler to understand. Basically, this procedure takes as input a directdraw surface and uses the directdraw function GetSurfaceDesc() to get a surface description, and returns the pixel format to us. An important note: the procedures shown here and below ASSUME that the structure of the pixel is red-green-blue. While the majority of video cards will use the RGB format, it is also possible that a video card could use any possible combination of RGB (ie, BGR, GRB, BRG, etc). If you want to make your code work on 100% of the video cards out there, you will have to modify the GetRGB16() function and the Plot_Pixel() function.
Here is my GetRGB16 function:
// you need this struct for the getrgb16 proc
// note: you could even yank the bitmask variables out of this
// if you find you aren't using them in any functions of your own
typedef struct
{
DWORD dwRBitMask,
dwGBitMask,
dwBBitMask;
RGBQUAD Position; // RGBQUAD is a data structure included with MSVC++5.0
} RGB16;
BOOL GetRGB16 ( LPDIRECTDRAWSURFACE Surface, RGB16 *rgb16)
{
DDSURFACEDESC ddsd;
//get a surface description
ddsd.dwSize = sizeof( ddsd );
ddsd.dwFlags = DDSD_PIXELFORMAT;
if (Surface->GetSurfaceDesc ( &ddsd ) != DD_OK )
return FALSE;
// will increment r,g, or b for each 1 in it's bitmask, which will give
// us the number of bits used for each color
char r=0, g=0, b=0;
for(int n = 1; n<65536; N<<=1)
{
IF(ddsd.ddpfPixelFormat.dwRBitMask & N)
++R;
IF(ddsd.ddpfPixelFormat.dwRBitMask & N)
++G;
IF(ddsd.ddpfPixelFormat.dwRBitMask & N)
++B;
}
// THIS IS THE PART THAT ASSUMES THE FORMAT IS RGB
RGB16->dwRBitMask = ddsd.ddpfPixelFormat.dwRBitMask;
rgb16->Position.rgbRed = g + b;
rgb16->dwGBitMask = ddsd.ddpfPixelFormat.dwGBitMask;
rgb16->Position.rgbGreen = b;
rgb16->dwBBitMask = ddsd.ddpfPixelFormat.dwBBitMask;
rgb16->Position.rgbBlue = 0;
return TRUE;
}//GetRBG16
// Must have the surface locked, pass the surface description
// that gets returned from the lock call
void PlotRGB(int x, int y, int r, int g, int b, DDSURFACEDESC *ddsd, RGB16 *rgb16)
{
WORD Pixel;
Pixel = (r << RGB16->Position.rgbRed) |
(g << RGB16->Position.rgbGreen) |
(b); // once again, assuming format is RGB
WORD *pixels = (WORD *)ddsd->lpSurface;
DWORD pitch = ddsd->dwWidth;
pixels[y*pitch + x] = Pixel;
}
DWORD pitch = ddsd->lPitch >> 1;
DWORD pitch = ddsd->dwWidth;
[size="5"]Optimizations?
How do we speed up this function? Well, the function takes the RGB values and encodes them into a 16-bit value. Suppose we have already encoded a 16-bit value in the pixel format. For example, suppose in your particle engine, the pixels you are plotting don't change color. Then it may definitely be worth it to encode the color once and store it as a 16-bit value, rather than sending RGB values to the PlotPixel function. Here's the function:
// Must have the surface locked, pass the surface description
// that gets returned from the lock call
void PlotPixel(int x, int y, WORD RGB, DDSURFACEDESC *ddsd)
{
WORD *pixels = (WORD *)ddsd->lpSurface;
DWORD pitch = ddsd->dwWidth;
pixels[y*pitch + x] = RGB;
}
int ytable[480];
for (int i=0;i<480;I++) YTABLE="640" * I;
pixels[ytable[y] + x] = RGB;
(C) 1998 Sam Christiansen
Reprinted with permission