![]() |
|
Bitmaps, Device Contexts and BitBltExample: bmp_one ![]() GDIThe really great thing about MS Windows is that unlike DOS, you don't need to know anything about what video hardware you are using to display graphics. Instead, windows provides an API called the Graphics Device Interface, or GDI. The GDI uses a set of generic graphics objects that can be used to draw to the screen, to memory, or even to printers. Device ContextsThe GDI revolves around an object called the Device Context (DC), represented by the data type
An HDC like most GDI objects is opaque, meaning that you can't access it's data directly... but you can pass it to various GDI functions that will operate on it, either to draw something, get information about it, or change the object in some way.
For example, if you wanted to draw on a window, first you would retreive an BitmapsBitmaps can be loaded much like icons in earlier examples, there is
One of the quirks of GDI is that you can't draw to bitmap objects ( You do have the option of manipulating the bitmap data in memory yourself. You can do this with Device Independant Bitmaps (DIB), and you can even combine GDI and manual operations on the DIB. However for the time being, this is beyond the scope of the basic tutorial and for now we're just cover the simpler GDI operations on their own.
WINX module: In this tutorial, we will not use the original Win32 API LoadBitmap, SelectObject etc.,
but we will use the corresponding procedures from the WINX module: LoadBitmapID, SelectBitmap, etc.
The reason for this is that a function like GDI Leaks
Once you're finished with an If your program runs fine for a few minutes and then starts drawing strangely or not at all, it's a good sign that you're leaking GDI resources. HDCs aren't the only GDI objects you need to be careful about releasing, but generally it's ok to keep things like bitmaps and fonts around for the entire lifetime of your program, since it's much more efficiant than reloading them each time you need them.
Also, an HDC can only contain one of each type of object (bitmap, font, pen...) at a time, and when you
select a new one in it will return the last one. It's very important that you deal with this object
properly. If you ignore it completely, it will be lost and they will pile up in memory causing GDI
leaks. When an HDC is created, it's also created with some default objects selected
into it... it's a good idea to store these when they are returned to you, and then when you are completed
drawing with the Displaying BitmapsOK, down to business. The simplest drawing operations on a window occure by handling VAR g_hbmBall : HBITMAP; | WM_CREATE : g_hbmBall := LoadBitmapId(Instance, IDB_BALL); IF g_hbmBall = NIL THEN FUNC MessageBox(hwnd, "Could not load IDB_BALL!", "Error", MB_OK BOR MB_ICONEXCLAMATION); END; The first step is of course loading the bitmap, this is quite simple with a bitmap resource, there are no significant differences from loading other resource types. Then we can get down to drawing... VAR bm : BITMAP; hbmOld : HBITMAP; hdc : HDC; hdcMem : HDC; ps : PAINTSTRUCT; | WM_PAINT : hdc := BeginPaint(hwnd, ps); hdcMem := CreateCompatibleDC(hdc); hbmOld := SelectBitmap(hdcMem, g_hbmBall); FUNC GetBITMAP(g_hbmBall, bm); FUNC BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); FUNC SelectBitmap(hdcMem, hbmOld); FUNC DeleteDC(hdcMem); FUNC EndPaint(hwnd, ps); (* ... *) Getting the Window DC
To start off we declare a couple of variables we need. Notice that the first one is a
The
Setting up a Memory DC for the Bitmap
As I mention above, in order to draw on or with bitmaps, we need to create a DC in memory... the easiest way to do that
here is to
Now we call
Drawing
Once we've got the dimentions of the bitmap filled into the
Cleanup
At this point the bitmap should be on the screen, and we need to clean up after ourselves. The first thing
to do is restore the Memory DC to the state it was when we got it, which means replacing our bitmap with the
default one that we saved. Next we can delete it altogether with
Finally we release the Window DC we got from
Destroying an
And finally, at the termination of our program, we want to free any resources that we allocated. Technically speaking this isn't absolutely required, since modern Windows platforms are pretty good at freeing everything when your program exists, but it's always a good idea to keep track of your own objects because if get lazy and don't delete them they have a habit of getting loose. And no doubt, there are still bugs in windows especially older versions that won't clean up all of your GDI objects if you don't do a thorough job. | WM_DESTROY : FUNC DeleteBitmap(g_hbmBall); PostQuitMessage(0); Copyright © 1998-2011, Brook Miles. All rights reserved. Adapted for Modula-2 by Frank Schoonjans, with permission. |