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 2

 
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 2:26 am    Post subject: Must know for Game developers.<-Part 2 Reply with quote

PART 2 . No description to not waste space .

Device Contexts
When you're finished with a device context, you have to release it. This frees up any memory that was being used by the object -- you'll come across the concept of releasing objects a lot more in the future. Once again, this is done by using a simple function call:

Code:
int ReleaseDC(
     HWND hWnd,  // handle to window
     HDC hDC     // handle to device context );


Tracking the Status of Your Window
The first two are relatively simple. WM_MOVE is called whenever the window is moved by the user. The new window coordinates are stored in lparam. (Remember, messages are further specified by the contents of lparam and wparam, which are parameters received by your message-handling function.) The low word of lparam is the x-coordinate of the upper-left corner of the window's client area. The high word of lparam is the y-coordinate.

The WM_SIZE message is sent when the window is resized. Like the WM_MOVE message, its parameterization is held in lparam. The low word is the client area's width, and the high word is its height. But unlike WM_MOVE, the wparam parameter also holds some significant.

When I'm writing windowed applications, I usually like to keep a few global variables that give the window's current position and size. If these variables were called xPos, yPos, xSize, and ySize, you'd handle the WM_SIZE and WM_MOVE messages something like this:

Code:
if (msg == WM_SIZE)
{
  xSize = LOWORD(lparam);
  ySize = HIWORD(lparam);
}

if (msg == WM_MOVE)
{
  xPos = LOWORD(lparam);
  yPos = HIWORD(lparam);
}


For dealing with this message, I'll keep another global variable called bFocus, and change its value when a WM_ACTIVATE message is received. The code would look something like this:

Code:
if (msg == WM_ACTIVATE)
{
  if (LOWORD(wparam) == WA_INACTIVE)
    focus = FALSE;
  else
    focus = TRUE;

  // tell Windows we handled it
  return(0);
}


The WM_PAINT Message

A window receives this important message when part of its client area has become invalidated. Suppose your program doesn't have the focus, and the active window is on top of your window. If the user moves that active window, it's going to reveal a part of your window. Since that part of the window needs to be refreshed, it is said to be invalidated. To handle this, there are a couple of things you can do. The first involves a pair of functions designed exclusively for use with the WM_PAINT message. The first is BeginPaint(). Here's the prototype:

Code:
HDC BeginPaint(
     HWND hwnd,             // handle to window
     LPPAINTSTRUCT lpPaint  // pointer to structure for paint information


Before I tell you exactly what the return value is, let's look at the parameters:

HWND hwnd: This is a handle to the window which needs repainting. You should be used to seeing this parameter by now, right?

LPPAINTSTRUCT lpPaint: Here's the important one. This is a pointer to a PAINTSTRUCT structure, which contains all sorts of information about the area to be painted.

And before we go on, I should show you exactly what a PAINTSTRUCT looks like...

Code:
typedef struct tagPAINTSTRUCT { // ps
     HDC  hdc;
     BOOL fErase;
     RECT rcPaint;
     BOOL fRestore;
     BOOL fIncUpdate;
     BYTE rgbReserved[32];
} PAINTSTRUCT;


Wrapping up , (global variable)
Code:
if (msg == WM_PAINT) {
   PAINTSTRUCT ps;  // declare a PAINTSTRUCT for use with this message
   HDC hdc;         // display device context for graphics calls
   hdc = BeginPaint(hMainWindow, &ps);  // validate the window

   // your painting goes here!

   EndPaint(hMainWindow, &ps);  // release the DC

  // tell Windows we took care of it
  return(0);
}


Closing Your Application
There are three messages that seem to be pratically identical, and all deal with closing things out. They are WM_DESTROY, WM_CLOSE, and WM_QUIT. They're similar, but you need to know the difference! WM_CLOSE is sent when a window or application should be closing. When you receive a WM_CLOSE message, it's a good place to ask the user if they're sure they want to quit, if you want to do it. You know those little message boxes that are always popping up on your screen when errors or notifications occur? Well, they're easy to create. In addition to serving many functions in the final program, they're also handy for reporting debug information. The call to create your very own message box is pretty simple:

Code:
int MessageBox(
     HWND hWnd,          // handle of owner window
     LPCTSTR lpText,     // address of text in message box
     LPCTSTR lpCaption,  // address of title of message box
     UINT uType          // style of message box );


The parameter is an exit code that your application returns to Windows. Remember, WinMain() returns an int, not a void. The nExitCode parameter also becomes the wparam member of the WM_QUIT message that results. WM_QUIT represents a request to close the application, so when you get one, you should end your main loop and return wparam to Windows. Here's an example of what a simplified WinMain() function might look like with this in place:

Code:
int WinMain(HINSTANCE hinstance,
            HINSTANCE hPrevInstance,
            LPSTR     lpCmdLine,
            int       nCmdShow)
{
  // initialization stuff goes here

  // main loop - infinite!
  while (TRUE)
  {
    // check the message queue
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
      if (msg.message == WM_QUIT)  // exit main loop on WM_QUIT
        break;

      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

    // main program logic goes here
  }

  // perform any shutdown functions here - releasing objects and such

  return(msg.wparam);  // return exit code to Windows
}


Plotting Pixels

At last! Plotting pixels with GDI is a cinch as long as you've got a display device context to work with. Remember, calling GetDC() does this for you. To plot a pixel, not surprisingly, you call SetPixel():

Code:
COLORREF SetPixel(
     HDC hdc,           // handle to device context
     int X,             // x-coordinate of pixel
     int Y,             // y-coordinate of pixel
     COLORREF crColor   // pixel color );


The return type is something we haven't encountered yet, a COLORREF. This is not a structure, but a 32-bit value in the form 0x00bbggrr, where bb is an 8-bit value for the blue component, gg is green, and rr is red. The high byte is unused and is always set to zero. Let's take a look at the parameters for SetPixel():

HDC hdc: This is a device context for your window that you should obtain with a call to GetDC(). You only need to call GetDC() once, and then you can use it for any number of these functions. Don't get a new DC every time you want to plot a pixel!

int X, Y: The x- and y-coordinates of the pixel. These are in client coordinates, meaning that (0, 0) represents the upper-left corner of your window's client area, not the upper-left corner of the screen.

COLORREF crColor: This is the color you want to set the pixel to. To do this, it's easiest to use the RGB() macro, which takes values for red, green, and blue -- in that order -- between 0 and 255. SetPixel() will choose the closest available color to the one you have specified.

If the function succeeds, the return value is the color that the pixel was set to. This may not always be exactly the COLORREF you pass if you're working in less than 24-bit color; Windows will choose the closest match. If the function fails, it returns -1. As an example, if you want to set the upper-left corner of your client area to white, you'd use the following call:

Code:
SetPixel(hdc, 0, 0, RGB(255, 255, 255));


This call assumes you've gotten a display device context named hdc. Pretty easy, hey? There's one other way to do it that's just a tad faster. Here's the function:

Code:
BOOL SetPixelV(
     HDC hdc,           // handle to device context
     int X,             // x-coordinate of pixel
     int Y,             // y-coordinate of pixel
     COLORREF crColor   // new pixel color );


The parameters are all the same. The return value is simply TRUE or FALSE for success or failure. SetPixelV() is slightly faster since it doesn't need to return the actual color that was used to plot. You'll probably never even notice the difference, unless you're using it thousands of times per frame, but if you don't need the extra information SetPixel() provides, there's no reason not to take the slightly increased performance, right?

The only other thing you need to know about plotting pixels is how to read the value of a pixel that's already been plotted. It's no problem; a quick call to GetPixel() does the job for you:

Code:
COLORREF GetPixel(
     HDC hdc,   // handle to device context
     int XPos,  // x-coordinate of pixel
     int nYPos  // y-coordinate of pixel );




The return value is obviously the color of the pixel at the given coordinates. If the coordinates specified are outside the clipping region (the area represented by the device context), the return value is CLR_INVALID. The parameters are the same as for SetPixel(): a device context to use, and the coordinates to operate on. That's it for plotting pixels. Now let's have a look at GDI's text-rendering functions.

GDI Text Functions

There are two functions for actually plotting text that you need to be concerned with. The simpler of the two is TextOut(), as shown here:

Code:
BOOL TextOut(
     HDC hdc,           // handle to device context
     int nXStart,       // x-coordinate of starting position
     int nYStart,       // y-coordinate of starting position
     LPCTSTR lpString,  // pointer to string
     int cbString       // number of characters in string );


By now we've seen enough BOOL-returning functions to know what that means: TRUE for success, FALSE for failure. The parameters are:

HDC hdc: The device context to use.

int nXStart, nYStart: These are the coordinates of the starting point for the text, called the reference point. By default, this is the upper-left corner of the rectangular area occupied by the string. You can change this setting, as we'll see in just a bit.

LPCTSTR lpString: The text to print out. Since the number of characters is given in the final parameter, this string does not need to be null-terminated.

int cbString: This is the length of the string, in characters.

TextOut() uses the current settings for text color, background color, and background type. Before looking at the other, more complicated text-rendering function, let's take a look at the functions you can use to control the colors being used.

Code:
COLORREF SetTextColor(
     HDC hdc,           // handle to device context
     COLORREF crColor   // text color );

COLORREF SetBkColor(
     HDC hdc,           // handle of device context
     COLORREF crColor   // background color value );


SetTextColor() sets the active text color, and SetBkColor() sets the active background color. The parameters are obviously the device context to apply the settings to, and the colors to use. Since these are COLORREFs, remember that you can use the RGB() macro for specifying your colors. Each function returns the previous value of the attribute it deals with. For instance, if you call SetTextColor(hdc, RGB(255, 0, 0)), the return value will be the active color that was being used before you turned it red. Finally, to set the background type, use SetBkType() as shown:

Code:
int SetBkMode(
     HDC hdc,      // handle of device context
     int iBkMode   // flag specifying background mode );


The default setting is TA_LEFT | TA_TOP | TA_NOUPDATECP. If you set TA_UPDATECP, subsequent calls to TextOut() will ignore the nXStart and nYStart parameters, and render the text where the last call left off. Now that that's out of the way, let's look at the bells-and-whistles version of TextOut(), called DrawText():

Code:
int DrawText(
     HDC hDC,          // handle to device context
     LPCTSTR lpString, // pointer to string to draw
     int nCount,       // string length, in characters
     LPRECT lpRect,    // pointer to struct with formatting dimensions
     UINT uFormat      // text-drawing flags );


Displaying Bitmaps With GDI

You already know how to do the first step. I alluded to the second one last time, but didn't go over it. I said that there was a function called LoadBitmap() that retrieves a handle to a bitmap resource. However, this function is obsolete now; it has been superseded by LoadImage(), which is much more flexible. So that's what we'll be using. Here she is:

Code:
HANDLE LoadImage(
     HINSTANCE hinst,   // handle of the instance containing the image
     LPCTSTR lpszName,  // name or identifier of image
     UINT uType,        // type of image
     int cxDesired,     // desired width
     int cyDesired,     // desired height
     UINT fuLoad        // load flags );


The parameter is a DC with which to make the new DC compatible. If you pass NULL, the DC will be made compatiable with the display screen, which is what we want. The return value is a handle to a memory device context -- not a display device context! This means that the contents of this DC won't be visible. If the function fails, the return value is NULL. Now, to get the bitmap into the memory device context, we use this:

Code:
HGDIOBJ SelectObject(
     HDC hdc,          // handle to device context
     HGDIOBJ hgdiobj   // handle to object );


The type HGDIOBJ is more general than our HBITMAP, so never fear, they're compatible without any tricks on our part. Here are the parameters:

HDC hdc: This is a handle to the device context which we want to fill with an object. For loading bitmaps, this must be a memory device context.

HGDIOBJ hgdiobj: And this is a handle to that object. This function is used with bitmaps, brushes, fonts, pens, and regions; but the only one that concerns us is bitmaps.

The return value is a handle to the object that is being replaced in the DC, or NULL if an error occurs. The return values are different for regions, but like I said, we don't care about regions. Smile

Now you've got a bitmap loaded into a DC, and you need only take the last step: copying the contents of the memory device context to our display device context. However, it's necessary to obtain some information about the bitmap, such as its dimensions, which must be used in the function call that will display the image. For that, we need the GetObject() function, which is used for obtaining information about graphical objects such as bitmaps.

Code:
int GetObject(
     HGDIOBJ hgdiobj,  // handle to graphics object of interest
     int cbBuffer,     // size of buffer for object information
     LPVOID lpvObject  // pointer to buffer for object information );


The return value is the number of bytes successfully obtained, or 0 for function failure. The parameters for the function are the following:

HGDIOBJ hgdiobj: The handle to the graphics object we want information on. In this case, pass the handle to the bitmap we loaded.

int cbBuffer: This is the size of the structure receiving the information. In the case of loading bitmaps, the receiving structure is of type BITMAP, so set this to sizeof(BITMAP).

LPVOID lpvObject: Pass the address of the structure receiving the information.

You need to define a variable of type BITMAP, and with a quick call to the GetObject() function, you'll have the information you need. Since the BITMAP structure is new to us, I'll show you what it looks like:

Code:
typedef struct tagBITMAP {  // bm
     LONG   bmType;
     LONG   bmWidth;
     LONG   bmHeight;
     LONG   bmWidthBytes;
     WORD   bmPlanes;
     WORD   bmBitsPixel;
     LPVOID bmBits;
} BITMAP;


All right, almost done! Now we have the bitmap in a memory device context, and we know its dimensions. All we have to do is copy it from one DC to the other, and that only takes a single function call. See, I told you bitmaps were easy to deal with. There are actually two options you can use here. I'll show you both of them.

Code:
BOOL BitBlt(
     HDC hdcDest, // handle to destination device context
     int nXDest,  // x-coordinate of destination rectangle's upper-left corner
     int nYDest,  // y-coordinate of destination rectangle's upper-left corner
     int nWidth,  // width of destination rectangle
     int nHeight, // height of destination rectangle
     HDC hdcSrc,  // handle to source device context
     int nXSrc,   // x-coordinate of source rectangle's upper-left corner
     int nYSrc,   // y-coordinate of source rectangle's upper-left corner
     DWORD dwRop  // raster operation code );


The return value is TRUE or FALSE based on whether the function succeeds. You've seen that plenty of times before. There are a lot of parameters, but most of them are pretty easy to figure out.

HDC hdcDest: The destination device context handle. In our case, this will be the display device context for our window.



int nXDest, nYDest: The coordinates of the upper-left hand corner of the region where the bitmap will end up. Remember that for our DC, these coordinates are client coordinates -- relative to the client area of our window.

int nWidth, nHeight: These are the width and height of the destination and source rectangles, since this function doesn't perform scaling. Pass the dimensions of the bitmap.

HDC hdcSrc: This is the source device context handle. In our case, this is the memory device context that our bitmap is currently residing in.

int nXSrc, nYSrc: The x- and y- coordinates of the source rectangle's upper-left corner. In this case you would use (0, 0), but this may not always be the case, depending on what you're using the source DC for, or if you only want to copy a part of the image.

DWORD dwRop: There are a lot of operation codes you can use here, most of them dealing with Boolean operations on the data in the two device contexts. The only one we're interested in is SRCCOPY, which copies the contents of the source DC directly to the destination DC.

That's about all there is to it! The other option you have is to use StretchBlt(), which requires you to specify the width and height for both the source and destination rectangles. StretchBlt() then scales the image in the source DC to fit in the rectangle specified on the destination DC. This can be useful for scaling images, but as always, since BitBlt() has fewer capabilities to worry about, it's faster. Here is the prototype for StretchBlt():

Code:
BOOL StretchBlt(
     HDC hdcDest,      // handle to destination device context
     int nXOriginDest, // x-coordinate of upper-left corner of dest. rectangle
     int nYOriginDest, // y-coordinate of upper-left corner of dest. rectangle
     int nWidthDest,   // width of destination rectangle
     int nHeightDest,  // height of destination rectangle
     HDC hdcSrc,       // handle to source device context
     int nXOriginSrc,  // x-coordinate of upper-left corner of source rectangle
     int nYOriginSrc,  // y-coordinate of upper-left corner of source rectangle
     int nWidthSrc,    // width of source rectangle
     int nHeightSrc,   // height of source rectangle
     DWORD dwRop       // raster operation code );


The parameter is simply the device context to delete, and the return value is a BOOL again, and we all know what that means, right? All right, are you feeling pretty good about all this stuff? Just for the sake of tying all these steps together, I'll show you a function you can use for loading and displaying a bitmap resource using all GDI functions. For this example, I'm assuming you have saved the handle to the instance of the application in a global called hinstance.

Code:
int ShowBitmapResource(HDC hDestDC, int xDest, int yDest, int nResID)
{
  HDC hSrcDC;           // source DC - memory 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)
    return(FALSE);

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

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

  // copy image from one DC to the other
  if (BitBlt(hDestDC, xDest, yDest, nWidth, nHeight, hSrcDC, 0, 0,
             SRCCOPY) == NULL)
    return(FALSE);

  // kill the memory DC
  DeleteDC(hSrcDC);

  // return success!
  return(TRUE);
}


GO TO PART 3 PLEASE .
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