Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


Must know for Game developers.<-Part 6

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming
View previous topic :: View next topic  
Author Message
benlue
Moderator
Reputation: 0

Joined: 09 Oct 2006
Posts: 2142

PostPosted: Tue Apr 03, 2007 3:05 am    Post subject: Must know for Game developers.<-Part 6 Reply with quote

Loading Bitmaps

Believe it or not, you already know almost everything you need to load a bitmap onto a DirectDraw surface. How can that be? Well, the method you used under Windows GDI will work in DirectDraw as well, with only one little change. To refresh your memory a little bit, what we did was to retrieve a handle to the bitmap using LoadImage(), select the bitmap into a memory device context, and then use BitBlt() to copy the image from the memory DC to another DC, which was a display device context we had gotten with a call to GetDC(). If that destination DC was a device context to a DirectDraw surface, we'd have our DirectX bitmap loader all done! Thankfully, the IDirectDrawSurface7 interface provides a simple function for retrieving a device context:

Code:
HRESULT GetDC(HDC FAR *lphDC);


The return type is the same as for any DirectDraw function, and the parameter is just a pointer to an HDC which will be initialized with the device context handle if the function succeeds. Is that cool or what? This article just started and we can already load a bitmap onto one of our surfaces! Just remember that when you're all done with the surface device context, you need to release it. As you've probably guessed, this is achieved with the ReleaseDC() method of the surface interface:

Code:
HRESULT ReleaseDC(HDC hDC);


Just so you don't have to go rooting through the old articles, looking for the GDI bitmap loader, I'll show the modified version to you here. The only difference is that instead of taking a device context as a parameter, it takes a pointer to a DirectDraw surface. Then the function gets the device context from the surface, uses it to copy the image, and releases the device context.

Code:
int LoadBitmapResource(LPDIRECTDRAWSURFACE7 lpdds, int xDest, int yDest, int nResID)
{
  HDC hSrcDC;           // source DC - memory device context
  HDC hDestDC;          // destination DC - surface device context
  HBITMAP hbitmap;      // handle to the bitmap resource
  BITMAP bmp;           // structure for bitmap info
  int nHeight, nWidth;  // bitmap dimensions

  // first load the bitmap resource
  if ((hbitmap = (HBITMAP)LoadImage(hinstance, MAKEINTRESOURCE(nResID),
                                    IMAGE_BITMAP, 0, 0,
                                    LR_CREATEDIBSECTION)) == NULL)
    return(FALSE);

  // create a DC for the bitmap to use
  if ((hSrcDC = CreateCompatibleDC(NULL)) == NULL)
    return(FALSE);

  // select the bitmap into the DC
  if (SelectObject(hSrcDC, hbitmap) == NULL)
  {
    DeleteDC(hSrcDC);
    return(FALSE);
  }

  // get image dimensions
  if (GetObject(hbitmap, sizeof(BITMAP), &bmp) == 0)
  {
    DeleteDC(hSrcDC);
    return(FALSE);
  }

  nWidth = bmp.bmWidth;
  nHeight = bmp.bmHeight;

  // retrieve surface DC
  if (FAILED(lpdds->GetDC(&hDestDC)))
  {
    DeleteDC(hSrcDC);
    return(FALSE);
  }

  // copy image from one DC to the other
  if (BitBlt(hDestDC, xDest, yDest, nWidth, nHeight, hSrcDC, 0, 0,
             SRCCOPY) == NULL)
  {
    lpdds->ReleaseDC(hDestDC);
    DeleteDC(hSrcDC);
    return(FALSE);
  }

  // kill the device contexts
  lpdds->ReleaseDC(hDestDC);
  DeleteDC(hSrcDC);

  // return success
  return(TRUE);
}


This function is set to load from a resource, but you can easily modify it to load from an external file. Or better still, you could have it try to load a resource, and if the call fails, it retries as a file. Just remember to include the LR_LOADFROMFILE flag in the call to LoadImage(). The nicest thing about this function is that BitBlt() performs all the conversions on the pixel formats. That is, you can load a 24-bit bitmap into the memory device context, and blit it to a 16-bit surface, and all the colors will show up correctly, regardless of whether the pixel format is 565 or 555. Convenient, hey?

If you want to manipulate the actual bitmap data manually instead of simply using a function to copy, you have two options. First, you can use a modified version of the function above, and use the bmBits member of the BITMAP structure, which is an LPVOID pointing to the bits making up the image. Second, if you really want to have control of how the load is performed, you can write a function that opens the file and reads it in manually, using standard file I/O functions. To do that, you need to know the structure of a bitmap file. I'm not going to go through developing the whole function, since we already have the functionality we need, but I'll show you everything you need to do so.

The Bitmap File Format

The nice thing about writing a bitmap loader is that there are Win32 structures designed to hold the bitmap headers, so loading all the header info as simple as making a few calls to fread(). First up in a bitmap file is the bitmap file header, which contains general information about the bitmap. Not surprisingly, the structure that holds this header is called BITMAPFILEHEADER. Here's what it looks like:

Code:
typedef struct tagBITMAPFILEHEADER { // bmfh
  WORD    bfType;       // file type - must be "BM" for bitmap
  DWORD   bfSize;       // size in bytes of the bitmap file
  WORD    bfReserved1;  // must be zero
  WORD    bfReserved2;  // must be zero
  DWORD   bfOffBits;    // offset in bytes from the BITMAPFILEHEADER
                          // structure to the bitmap bits
} BITMAPFILEHEADER;


I'm not going to detail all the members; I've commented the structure to give you an idea of what everything is. Just use a call to fread() to read this in, and check the bfType member to make sure it is equal to the characters "BM" to ensure you're dealing with a valid bitmap. After that, there's another header file to read, called the info header. It contains image data like the dimensions, compression type, etc. Here's the structure:

Code:
typedef struct tagBITMAPINFOHEADER{ // bmih
  DWORD  biSize;           // number of bytes required by the structure
  LONG   biWidth;          // width of the image in pixels
  LONG   biHeight;         // height of the image in pixels
  WORD   biPlanes;         // number of planes for target device - must be 1
  WORD   biBitCount;       // bits per pixel - 1, 4, 8, 16, 24, or 32
  DWORD  biCompression;    // type of compression - BI_RGB for uncompressed
  DWORD  biSizeImage;      // size in bytes of the image
  LONG   biXPelsPerMeter;  // horizontal resolution in pixels per meter
  LONG   biYPelsPerMeter;  // vertical resolution in pixels per meter
  DWORD  biClrUsed;        // number of colors used
  DWORD  biClrImportant;   // number of colors that are important
} BITMAPINFOHEADER;


A few of the fields need some explanation. First, a note on compression. Most bitmaps that you'll be dealing with will be uncompressed. The most common type of compression for bitmaps is run-length encoding (RLE), but this is only used for 4-bit or 8-bit images, in which case the biCompression member will be BI_RLE4 or BI_RLE8, respectively. I'm not going to go into run-length encoding, but it's a fairly straightforward method of compression, so it's not too hard to deal with if you come across it.

Second, biClrUsed and biClrImportant will usually be set to zero in high-color bitmaps, so don't worry about them too much. The biSizeImage field may also be set to zero in some BI_RGB uncompressed bitmaps. Finally, the resolution fields are also unimportant for our purposes. Mainly the only things you're interested in from this structure are the width, height, and color depth of the image.

After you've read in the info header, if the bitmap has eight bits per pixel or less, it is palettized, and the palette information immediately follows the info header. The palette information, however, is not stored in PALETTEENTRY structures, but rather in RGBQUAD structures. An RGBQUAD looks like this:

Code:
typedef struct tagRGBQUAD { // rgbq
  BYTE    rgbBlue;
  BYTE    rgbGreen;
  BYTE    rgbRed;
  BYTE    rgbReserved;
} RGBQUAD;


After the palette, or immediately following the info header if there is no palette, you'll find the actual image bits. You'll probably want to simply create a pointer and allocate enough memory to it to hold the image data, and then read it in. Just to be clear, I'll show you the code for doing this, assuming the info header is stored in a BITMAPINFOHEADER structure called info, and your file pointer is called fptr:

Code:
UCHAR* buffer = (UCHAR*)malloc(info.biSizeImage);
fread(buffer, sizeof(UCHAR), info.biSizeImage, fptr);


Just remember that Microsoft warns that biSizeImage may be set to zero in some cases, so check it before running code like the above. If it is set to zero, you'll have to calculate the size of the image by figuring out how many pixels comprise the image, and how many bytes are required for each pixel.

Writing your own bitmap loader isn't too bad, but if you want to avoid it, you can always use the code we developed back in the GDI article and modified at the beginning of this one. Now that that's over with, let's get into what DirectDraw's all about: using the blitter!

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:

Code:
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).

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.


Code:
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;


Colour Keys

A color key is a method of blitting one image onto another whereby not all of the pixels are copied. For instance, let's say you have a character sprite you want to blit onto a background image. Chances are that your hero isn't shaped precisely like a rectangle (unless your game is a celebration of "modern art"), so copying the RECT that encloses him is going to produce some unwanted effects.

Now this isn't really the way the game is drawn. In reality, the characters are blitted to the display before the background has finished being drawn, so that things like the tops of trees can obscure the player if he's standing behind them. But that's unimportant for our purposes here; we'll get to it in the next article. The important point is that without color keying, all of your non-rectangular images like this character would have boxes around them.

To solve this problem, we use a source color key. The source color key tells the blitter which colors not to copy. A color key consists of two values: a lower color value, and an upper color value. When the color key is applied to a blit, any colors lying between the two values, including the values themselves, are not copied. There's a structure in DirectX that goes along with this, called DDCOLORKEY. Take a look:

Code:
typedef struct _DDCOLORKEY{
  DWORD dwColorSpaceLowValue;
  DWORD dwColorSpaceHighValue;
} DDCOLORKEY, FAR* LPDDCOLORKEY;


Setting Color Keys

There are two ways to use a color key in DirectDraw. First, you can attach one (or two, if you're using source and destination) to a surface, and then specify the DDBLT_KEYSRC, DDBLT_KEYDEST, DDBLTFAST_SRCCOLORKEY, or DDBLTFAST_DESTCOLORKEY flag when blitting, depending on which blit function and what type of color key you're using. Second, you can create a color key and pass it to the blit operation through the DDBLTFX structure. The first method is recommended if you're going to be using a color key over and over, whereas the second method is ideal if you only want to use a certain color key once.

You can either attach a color key to a surface that has been created, or you can create the color key at the same time you create the surface. I'll show you how to do both. Let's assume you're working in a 16-bit display mode, with a 565 pixel format, and you want a source color key on your back buffer that includes black only. If your back buffer has already been created, you simply create a DDCOLORKEY structure like we saw before, and pass it to IDirectDrawSurface7::SetColorKey(), shown below:

Code:
HRESULT SetColorKey(
  DWORD dwFlags,
  LPDDCOLORKEY lpDDColorKey
);


From that point on, you just specify the appropriate flag on blits which you want to use the color key. Note that just because a surface has a color key attached to it, doesn't mean you have to use it every time. If you specify blits with only the DDBLT_WAIT or DDBLTFAST_WAIT flag and nothing else, any color keys will be ignored. So here's how you'd set up the color key we described earlier:

Code:
DDCOLORKEY ckey;
ckey.dwColorSpaceLowValue = RGB_16BIT565(0, 0, 0);  // or we could just say '0'
ckey.dwColorSpaceHighValue = RGB_16BIT565(0, 0, 0);
if (FAILED(lpddsBack->SetColorKey(DDCKEY_SRCBLT, &ckey)))
{
  // error-handling code here
}


If you want to create a surface with the color key already specified, there are just a few things you have to do. First, when you're specifying the valid fields of the DDSURFACEDESC2 structure, you need to include DDSD_CKSRCBLT or DDSD_CKDESTBLT in the dwFlags member, depending on which type of color key you want to use. Looking back at the DDSURFACEDESC2 structure, it contains two DDCOLORKEY structures. One is called ddckCKSrcBlt, and the other is called ddckCKDestBlt. Fill out the appropriate structure, create the surface, and you're all set! Here's the example code for an offscreen surface that's 640x480 in size.

Code:
// set up surface description structure
INIT_DXSTRUCT(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_CKSRCBLT;
ddsd.dwWidth = 640;                                          // width of the surface 
ddsd.dwHeight = 480;                                         // and its height
ddsd.ddckCKSrcBlt.dwColorSpaceLowValue  = RGB_16BIT(0,0,0);  // color key low value
ddsd.ddckCKSrcBlt.dwColorSpaceHighValue = RGB_16BIT(0,0,0);  // color key high value
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;                // type of surface

// now create the surface
if (FAILED(lpdd7->CreateSurface(&ddsd, &lpddsBack, NULL)))
{
  // error-handling code here
}


GO TO PART 7 .
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites