Delphi Programming

  1. Home
  2. Computing & Technology
  3. Delphi Programming
RTL referenceGlossary|Tips/Tricks|FREE App/VCL|Best'O'Net|Books|Link To
 
GDI Graphics In Delphi
Page 8: GDI, The Hard Way
 More of this Feature
• Page 1: GDI Jargon
• Page 2: Drawing: Lines
• Page 3: Drawing: Shapes
• Page 4: Draw vs. Paint
• Page 5: Handles and Stuff
• Page 6: Pictures: TBitmap
• Page 7: Ways To Kill Flicker
• Page 9: API Drawings
 Join the Discussion
"Post your views, comments, questions and doubts to this article."
Discuss!
 Related Resources
• Graphics programming in Delphi
• Screen zooming
• Double buffering
• Win API in Delphi

   GDI, The Hard Way
Get the code for this tutorial here: gdiapionly.zip (3Kb)

We've explored the GDI a little bit, but only using the Delphi functions. Because the VCL gives (excellent) encapsulation of the GDI this should be enough for most cases. However, there are reasons for doing it the hard way, namely: you need to write a non-VCL (i.e. small) program; you want to learn stuff to transfer to other languages (e.g. C++); or you just like pain. So let's take a look at the basics.

Incidentally, I'll sometimes be using Win32 terminology (e.g. LOGFONT). You might find the types are renamed using the standard Delphi convention (e.g. TLogFont). If you get compiler errors this might be the cause.

   GDI Objects
We've been using TCanvas up until this point. It encapsulates the Device Context well. However, it's time to get down and dirty. Device Contexts work like this:

1. Get the DC
2. Select anything you need into the DC
3. Use the DC to draw stuff
4. Select the old values back into the DC
5. Get rid of the DC (Release it) when done

Getting the DC
Let's go through each of the stages in turn. First, you need a device context. You can get this using the function GetDC, which expects the handle of the window. If you're creating an API program you'll know what to pass in here. Otherwise, it's time to explain...

Everything in Windows is a window (little 'w'). The method for identifying a window, as well as many other objects, is the Handle. This was discussed previously. It boils down to a pointer to your object. If you're using the VCL and want this handle you can use the "Handle" property for your object. Otherwise, the handle is returned when you use CreateWindow or CreateWindowEx. You should, of course, have stored these handles as they're pretty darned important. The final option is to pass in "0", as in "someDC := GetDC(0)", which gives you a DC for the entire screen (rather than just your wanted window).

Of course, you might not have the handle for the window (maybe if it's not one of yours). The solution is simple, provided you know the caption of the window: just use FindWindow or EnumWindows. These search for a given window and return its handle. Once you have that, you can get a DC for the window. However, I'm not going to explain how they work. You'll have to look at Win32.hlp yourself.

Selecting Objects Into the DC
The next step is to select your object into the DC. The DC stores five different categories of GDI Object. These are:

OBJECT USE DATA TYPE
Fonts writing text (e.g. TextOut) HFONT
Pens drawing lines HPEN
Brushes filling areas HBRUSH
Bitmaps drawing purty things HBITMAP
Regions defining areas (e.g. for clipping) HRGN

Each object is a "GDI Object" (of type HGDIOBJ). You use the same function, SelectObject, to choose your new object. The appropriate data type for your wanted object is just "H" + the object, for example "HPEN" or "HBRUSH". The H prefix indicates this is a Handle, which we've discussed previously. This just means you've got a pointer. What do you need to do with pointers? Yes, that's right, you need to allocate memory so it points to something, and deallocate that memory when finished. There are functions to do that, so lets look at them now.

Before using GDI objects you need to call the appropriate Create function. There are either of the form CreateXXX (e.g. CreatePen) or CreateXXXIndirect (e.g. CreateBrushIndirect). The first is pretty intuitive - you just say "someObj := CreateXXX({parameters});", and it creates the object as wanted. The "indirect" functions just require one extra step: filling out a structure. You use a LogXXX structure (e.g. LOGFONT) and fill out all its values. You then pass this structure into the CreateXXXIndirect function instead of the parameters directly. The Indirect functions are handy when there are lots of parameters in the CreateXXX version of your function.

After you've finished using your GDI object, you need to deallocate it. To do this, there's only one function you need to know about: DeleteObject. You use this for any HGDIOBJ, regardless. This means you have no excuses for forgetting this step, and will always do it in every program you make. Right? An (unnecessary) example: DeleteObject(SomeObj);

When you use a GDI function (e.g. FillRect) the DC uses the currently selected objects, as appropriate. It could use the Pen to draw a line, the Brush to fill an area, the region to define the clipping area (where drawing will take place), etc. Each of these is separate. However, you use the same function to change each attribute: SelectObject.

To change an attribute of the DC do this: Select your new object, use the DC, and select the old object again. It's important you follow this process. Other windows might use the same DC. If you don't restore the old value for your changed attribute these windows will get confused - for example, if they expect a grey brush and you change it to blue things could get messy. Luckily, SelectObject gives you the old value when you change the value, which allows you to easily restore it.

Here's a long-awaited example of using SelectObject:

var
  OldPen, NewPen: HPEN;
begin
  // Create the new pen first
  NewPen := CreatePen(PS_SOLID, 1, RGB(0,0,255));

  // Select the new pen, storing previous pen value in OldPen
  OldPen := SelectObject(SomeDC, NewPen);

  // use SomeDC somehow, e.g. LineTo
  
  // we're done - restore the old pen so things don't go wrong
  SelectObject(SomeDC, OldPen);
  DeleteObject(NewPen);
end

This code, of course, assumes you have already nabbed a DC called "SomeDC". Anyway, it demonstrates the basics. The first part is to create your new object. You then select it into the DC so any drawing functions will use your new object instead of the old one. At the same time you keep the old value. You use the DC, select the old value back again then finally clear up any created objects.

Releasing the DC
The final step is to release the DC. This follows the usual process - if you create something, you delete it. If you get something, you release it. Anyway, to release the DC you use the function ReleaseDC. Make sure you do this, because you want to be nice to Windows.

   Question, Suggestions...
If you have any questions or comments to this (huge) article, please post them on the Delphi Programming Forum. Discuss!

Next page > API Drawings > Page 1, 2, 3, 4, 5, 6, 7, 8, 9

All graphics (if any) in this feature created by Zarko Gajic.

 More Delphi
· Learn another routine every day - RTL Quick Reference.
· Download free source code applications and components.
· Talk about Delphi Programming, real time.
· Link to the Delphi Programming site from your Web pages.
· Tutorials, articles, tech. tips by date: 2001|2000|1999|1998 or by TOPIC.
· NEXT ARTICLE: Articles.
More Delphi articles
 Stay informed with all new and interesting things about Delphi (for free).
Subscribe to the Newsletter
Name
Email

 Got some code to share? Got a question? Need some help?

Explore Delphi Programming

About.com Special Features

Delphi Programming

  1. Home
  2. Computing & Technology
  3. Delphi Programming

©2009 About.com, a part of The New York Times Company.

All rights reserved.