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
79 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

Plotting Pixels

The first thing is to typecast the pointer we got from the Lock() function. Logically, we're going to want a pointer that's the same size as the pixels we're writing. So we want a UCHAR* for 8-bit color depth, a USHORT* for 16-bit, and a UINT* for 32-bit. But what about 24-bit? Since there's no 24-bit data type, we'll need to use a UCHAR* for this also, and do things a little differently.

We should also convert the lPitch member so it's in the same units as our pointer. Remember, when we first retrieve lPitch from the DDSURFACEDESC2 structure, it's in bytes. For 16-bit mode, we should divide it by 2 to get the pitch in terms of USHORTs, and for 32-bit mode, we divide by 4 to get it in terms of UINTs.

Let's stop for a second and look at some example code. Suppose we are in 32-bit mode and want to lock the primary surface for plotting pixels. Here's what we would do:

// declare and initialize structure
DDSURFACEDESC2 ddsd;
INIT_DXSTRUCT(ddsd);

// lock the surface
lpddsPrimary->Lock(NULL, &ddsd, DDLOCK_WAIT |
                    DDLOCK_SURFACEMEMORYPTR, NULL);

// now convert the pointer and the pitch
UINT* buffer = (UINT*)ddsd.lpSurface;
UINT nPitch = ddsd.lPitch >> 2;

Now let me go ahead and show you a pixel-plotting function, and then I'll explain it in detail:

inline void PlotPixel32(int x, int y, UINT color,
                        UINT *buffer, int nPitch)
{
  buffer[y*nPitch + x] = color;
}

All right, let's take it apart. First of all, notice that I have declared it as an inline function. This is to eliminate the overhead of passing all the parameters every time we want to do something as simple as plotting one pixel. Now, the only line in the function locates the pixel of interest and sets it to the color we want. Note that the color is just one value, not one each for red, green, and blue, so we'll need to use our RGB_32BIT() macro to create the color for this function.

The formula used to locate the pixel at location (x, y) is y*nPitch + x. This makes sense because nPitch is the number of UINTs in a line. Multiplying that by the y value yields the correct row, and then we just need to add x to locate the correct column. That's all you need to know! Pretty simple, hey? Let me show you a couple of functions for plotting pixels in 16-bit and 8-bit modes, because they're very similar:

inline void PlotPixel8(int x, int y, UCHAR color,
                       UCHAR* buffer, int byPitch)
{
  buffer[y*byPitch + x] = color;
}

inline void PlotPixel16(int x, int y, USHORT color,
                        USHORT* buffer, int nPitch)
{
  buffer[y*nPitch + x] = color;
}

The only things different about these functions are the data types used for the parameters. Also remember that for the 8-bit version, the pitch is in bytes, and for the 16-bit version, the pitch is in USHORTs. That just leaves one mode left, and that's 24-bit. Since there's no data type that's 24 bits, we need to pass and write values for red, green, and blue individually. The function might look like this:

inline void PlotPixel24(int x, int y, UCHAR r, UCHAR g,
                        UCHAR b, UCHAR* buffer, int byPitch)
{
  int index = y*byPitch + x*3;

  buffer[index] = r;
  buffer[index+1] = g;
  buffer[index+2] = b;
}

As you can see, this is going to be a bit slower because we have an extra multiply, and three memory writes. You can speed it up a little bit by replacing x*3 with (x+x+x) or (x<<1)+x, but that probably won't do a lot for you. Still, it can't hurt to throw it in for good measure. Now you can see why working with 24-bit color is a bit of a pain.




Next : Notes on Speed