Using the BlitterThe 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.
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.
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.
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. |
|||||||||||||||||||||||||||||||||||||||||||||