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
 Loading Bitmaps
 Using the Blitter
 Color Keys
 IDirectDrawClipper
 Interface


 Get the source
 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

Using the Blitter

The blitter is a part of the video card hardware that is used for manipulating bitmap data. You can also use it to do color fills, as we'll see in a minute, and any number of other cool tricks if the hardware supports them. Having easy access to hardware acceleration is one of the best parts about DirectX. Also, remember that most of the things we'll be doing will be taken care of in the HEL if there's no hardware support. There are some things that don't have counterparts in the HEL though, which is why you have to be careful to always check whether your calls succeed or not.

There are two main functions for accessing the blitter in DirectDraw: Blt() and BltFast(). Both are methods of the IDirectDrawSurface7 interface. The difference is that BltFast() doesn't do clipping, scaling, or any of the other interesting things that Blt() does. The advantage is about a 10% speed increase over Blt() if the HEL is being used. If hardware acceleration is supported, though -- and it almost always is for blitting operations -- there is no speed difference for average blits, so I use Blt() for just about everything. Let's take a look at it:

HRESULT Blt(
  LPRECT lpDestRect,
  LPDIRECTDRAWSURFACE7 lpDDSrcSurface,
  LPRECT lpSrcRect,
  DWORD dwFlags,
  LPDDBLTFX lpDDBltFx
);

Because Blt() can do all sorts of special effects, as evidenced by that last parameter, it's got a few long lists of flags associated with it. I'll show you what I've found to be the most useful ones. Also, note that when you're blitting from one surface to another, you call the Blt() method of the destination surface, not the source surface. All right? Here are the function's parameters:

LPRECT lpDestRect: This is the destination RECT to blit to. If it differs in size from the source RECT, Blt() will automatically scale the image in the source RECT to fit the destination RECT! If the destination is the entire surface, you can set this to NULL.

LPDIRECTDRAWSURFACE7 lpDDSrcSurface: This is the source surface of the blit. If you're using the blitter to do a color fill on the destination surface, you can set this parameter to NULL.

LPRECT lpSrcRect: This is the source RECT to blit from. If you mean to blit the entire contents of the surface, set this to NULL.

DWORD dwFlags: There's a huge list of flags for this parameter, which can be logically combined with the | operator. Quite a few of them have to do with Direct3D stuff (like alpha information), so I'll show you a partial list here.

DDBLT_ASYNCPerforms the blit asynchronously through the FIFO (first in, first out) in the order received. If no room is available in the FIFO hardware, the call fails, so be careful using this one.
DDBLT_COLORFILLFills the destination rectangle with the color denoted in the dwFillColor member of the DDBLTFX structure.
DDBLT_DDFXUses the dwDDFX member of the DDBLTFX structure to specify effects used for the blit.
DDBLT_DDROPSUses the dwDDROPS member of the DDBLTFX structure to specify raster operations (ROPs) that are not part of the Win32 API.
DDBLT_KEYDESTUses the color key associated with the destination surface.
DDCLT_KEYDESTOVERRIDEUses the dckDestColorKey member of the DDBLTFX structure as the color key for the destination surface.
DDBLT_KEYSRCUses the color key associated with the source surface.
DDCLT_KEYSRCOVERRIDEUses the dckDestColorKey member of the DDBLTFX structure as the color key for the source surface.
DDBLT_ROPUses the dwROP member of the DDBLTFX structure for the ROP for the blit. These ROPs are those defined in the Win32 API.
DDBLT_ROTATIONANGLEUses the dwRotationAngle member of the DDBLTFX structure as the rotation angle (specified in hundreths of a degree) for the surface.
DDBLT_WAITWaits for the blitter to be available instead of returning an error, if the blitter was drawing at the time this blit was called. The function returns when the blit is complete, or another error occurs.

I almost always use DDBLT_WAIT. The color key flags are also important; we'll get to them in just a bit. Now, though, here's the last parameter to Blt():

LPDDBLTFX lpDDBltFx: A pointer to a DDBLTFX structure, which can contain all sorts of special effects information. If no effects are specified using this structure, you can pass NULL. Let's take a look at the structure. I'm warning you, it's massive!

typedef struct _DDBLTFX{ 
  DWORD dwSize; 
  DWORD dwDDFX; 
  DWORD dwROP; 
  DWORD dwDDROP; 
  DWORD dwRotationAngle; 
  DWORD dwZBufferOpCode; 
  DWORD dwZBufferLow; 
  DWORD dwZBufferHigh; 
  DWORD dwZBufferBaseDest; 
  DWORD dwZDestConstBitDepth;
  union { 
    DWORD               dwZDestConst; 
    LPDIRECTDRAWSURFACE lpDDSZBufferDest;
  };
  DWORD dwZSrcConstBitDepth; 
  union {
    DWORD               dwZSrcConst; 
    LPDIRECTDRAWSURFACE lpDDSZBufferSrc;
  }; 
  DWORD dwAlphaEdgeBlendBitDepth; 
  DWORD dwAlphaEdgeBlend; 
  DWORD dwReserved; 
  DWORD dwAlphaDestConstBitDepth;
  union { 
    DWORD               dwAlphaDestConst; 
    LPDIRECTDRAWSURFACE lpDDSAlphaDest;
  }; 
  DWORD dwAlphaSrcConstBitDepth;
  union { 
    DWORD               dwAlphaSrcConst; 
    LPDIRECTDRAWSURFACE lpDDSAlphaSrc;
  };
  union { 
    DWORD               dwFillColor; 
    DWORD               dwFillDepth; 
    DWORD               dwFillPixel; 
    LPDIRECTDRAWSURFACE lpDDSPattern;
  };
  DDCOLORKEY ddckDestColorkey; 
  DDCOLORKEY ddckSrcColorkey;
} DDBLTFX, FAR* LPDDBLTFX;

If I went through this whole structure, we'd all be standing in line for our AARP cards by the time I got finished. So I'll just go over the important ones. Thankfully, most of this structure is for z-buffers and alpha information, which don't concern us. It makes my job a little easier. :)

DWORD dwSize: Like all DirectX structures, this member has to be set to the size of the structure when you initialize it.

DWORD dwDDFX: These are kinds of effects that can be applied to the blit. The list isn't too long, don't worry.

DDBLTFX_ARITHSTRETCHYThe blit uses arithmetic stretching along the y-axis.
DDBLTFX_MIRRORLEFTRIGHTThe blit mirrors the surface from left to right.
DDBLTFX_MIRRORUPDOWNThe blit mirrors the surface from top to bottom.
DDBLTFX_NOTEARINGSchedules the blit to avoid tearing.
DDBLTFX_ROTATE180Rotates the surface 180 degrees clockwise during the blit.
DDBLTFX_ROTATE270Rotates the surface 270 degrees clockwise during the blit.
DDBLTFX_ROTATE90Rotates the surface 90 degrees clockwise during the blit.

The only one that might need some explanation is DDBLTFX_NOTEARING. Tearing is what can happen when you blit a surface out of sync with the vertical blank. If you blit to the entire primary surface while it's being drawn, you can see the top half of the old frame along with the bottom half of your updated frame. In my experience, this has almost never been a problem. On with the DDBLTFX structure...

DWORD dwROP: You use this flag to specify Win32 raster operations, like those that can be used with the GDI functions BitBlt() and StretchBlt(). Most of them have to do with combining source and destination images using Boolean operators. You can call IDirectDraw7::GetCaps() to retrieve a list of supported raster ops, among other things.

DWORD dwRotationAngle: This is an angle by which to rotate the bitmap, specified in hundredths of a degree. This is pretty cool, but unfortunately, rotation is only supported in the HAL, which means that if the user's video card doesn't support accelerated rotation, it won't work at all. Hardware support for this is not too common, either, so the bottom line is that you shouldn't use this in a program you're going to distribute. If you really need rotation, you'll have to write your own code for it. Doing that is a topic for another entire tutorial, though, so we'll just pass it by for now. But note that rotations by multiples of ninety degrees are easy, so using DDBLTFX_ROTATE90, etc. instead of using dwRotationAngle will work no matter how lousy the user's video card is.

DWORD dwFillColor: When you're using the blitter to perform a color fill, you must set the color to be used in this parameter.

DDCOLORKEY ddckDestColorKey, ddckSrcColorKey: Specify these members when you want to use a destination or source color key other than the one specified for the surface involved. These guys are important, but we can't talk about them just yet because we haven't covered color keys. It's coming soon enough...

That about does it for the immediately useful members of the DDBLTFX structure, which means you now have enough information to go ahead and start blitting images! If it's all rather confusing at this point, don't worry, it will become second nature once you use it a couple of times. Let's look at an example or two. Suppose you have a back buffer called lpddsBack, and you want to blit its contents to the primary surface. Here's the call:

lpddsPrimary->Blt(NULL, lpddsBack, NULL, DDBLT_WAIT, NULL);

How easy is that? To refresh your memory a bit, the first and third parameters are the destination and source RECTs for the blit, respectively. Since I have specified NULL for each, the entire surface is copied. Now, let's say you have a tile that's 16x16 in size in an offscreen surface called lpddsTileset, and you want to blit it to the back buffer, scaling it up to 32x32. Here's what you might do:

RECT dest, src;
SetRect(&src, 0, 0, 16, 16);   // the coordinates of the tile
SetRect(&dest, 0, 0, 32, 32);  // where you want it to end up on the back buffer
lpddsBack->Blt(&dest, lpddsTileset, &src, DDBLT_WAIT, NULL);

The only difference between this example and the last one is that we've specified the coordinates to be used in the blit. Since the two RECTs are of different sizes, Blt() scales the image appropriately. Finally, to illustrate using the DDBLTFX structure, let's do a color fill. Suppose you're in 16-bit color, with a 565 pixel format, and you want to fill your back buffer with blue. This is all you need:

DDBLTFX fx;
INIT_DXSTRUCT(fx);                        // zero out the structure and set dwSize
fx.dwFillColor = RGB_16BIT565(0, 0, 31);  // set fill color to blue
lpddsBack->Blt(NULL, NULL, NULL, DDBLT_WAIT | DDBLT_COLORFILL, &fx);

Note that since there is no source surface involved, the second parameter is set to NULL. Got it? All right, let's take a look at the alternative blitting function, BltFast(). It's basically a stripped-down version of Blt(), so we don't need to spend nearly as much time on it.

HRESULT BltFast(
  DWORD dwX,                            
  DWORD dwY,
  LPDIRECTDRAWSURFACE7 lpDDSrcSurface,  
  LPRECT lpSrcRect,
  DWORD dwTrans                         
);

As you can see, it's very similar to Blt(). This is also a member function of the IDirectDrawSurface7 interface, and should be called as a method of the destination surface. Let's have a look at the parameters:

DWORD dwX, dwY: Here's a difference between Blt() and BltFast(). BltFast() uses these x- and y-coordinates to specify the destination of the blit, rather than a RECT. This is because you can't do scaling with BltFast().

LPDIRECTDRAWSURFACE7 lpDDSrcSurface: This is the source surface, just like before.

LPRECT lpSrcRect: This is also what we used in Blt(), the source RECT for the blit.

DWORD dwTrans: This parameter takes one or more of the flags listed below, and specifies the type of transfer to perform. The list is very simple; there are only four possible values you can use.

DDBLTFAST_DESTCOLORKEYSpecifies a transparent blit that uses the destination's color key.
DDBLTFAST_NOCOLORKEYSpecifies a normal copy blit with no transparency.
DDBLTFAST_SRCCOLORKEYSpecifies a transparent blit that uses the source's color key.
DDBLTFAST_WAITWaits for the blitter to be available instead of returning an error, if the blitter was drawing at the time this blit was called. The function returns when the blit is complete, or another error occurs.

That's it! BltFast() supports color keys, and that's about it. Just as a quick demonstration, let's look at how you would do the first example I gave above with BltFast(). Here's how you would copy your entire back buffer onto the primary surface:

lpddsPrimary->BltFast(0, 0, lpddsBack, NULL, DDBLTFAST_WAIT);

Now that you're an expert on using the blitter, there are a few things we can cover that are very useful in any DirectDraw program: color keys and clipping. Since you've seen a million different flags concerning color keys in some way and are probably wondering how to make use of them, let's cover that first.




Next : Color Keys