Windows provides a powerful Graphics Device Interface (GDI) for drawing device-independent graphics. The GDI, however, imposes extra requirements on the programmer, such as managing graphic resources. Delphi takes care of all the GDI drudgery, allowing you to focus on productive work instead of searching for lost handles or unreleased resources.
As with any part of the Windows API, you can call GDI functions directly from your Delphi application. But you will probably find that using Delphi's encapsulation of the graphic functions is faster and easier.
The topics in this section include
Delphi encapsulates the Windows GDI at several levels. The most important to you as a component writer is the way components display their images on the screen. When calling GDI functions directly, you need to have a handle to a device context, into which you have selected various drawing tools such as pens, brushes, and fonts. After rendering your graphic images, you must restore the device context to its original state before disposing of it.
Instead of forcing you to deal with graphics at a detailed level, Delphi provides a simple yet complete interface: your component's Canvas property. The canvas ensures that it has a valid device context, and releases the context when you are not using it. Similarly, the canvas has its own properties representing the current pen, brush, and font.
The canvas manages all these resources for you, so you need not concern yourself with creating, selecting, and releasing things like pen handles. You just tell the canvas what kind of pen it should use, and it takes care of the rest.
One of the benefits of letting Delphi manage graphic resources is that it can cache resources for later use, which can speed up repetitive operations. For example, if you have a program that repeatedly creates, uses, and disposes of a particular kind of pen tool, you need to repeat those steps each time you use it. Because Delphi caches graphic resources, chances are good that a tool you use repeatedly is still in the cache, so instead of having to recreate a tool, Delphi uses an existing one.
An example of this is an application that has dozens of forms open, with hundreds of controls. Each of these controls might have one or more TFont properties. Though this could result in hundreds or thousands of instances of TFont objects, most applications wind up using only two or three font handles thanks to the VCL font cache.
Here are two examples of how simple Delphi's graphics code can be. The first uses standard GDI functions to draw a yellow ellipse outlined in blue on a window in an application written with ObjectWindows. The second uses a canvas to draw the same ellipse in an application written with Delphi.
procedure TMyWindow.Paint(PaintDC: HDC; var PaintInfo: TPaintStruct);
var
PenHandle, OldPenHandle: HPEN;
BrushHandle, OldBrushHandle: HBRUSH;
begin
PenHandle := CreatePen(PS_SOLID, 1, RGB(0, 0, 255)); { create blue pen }
OldPenHandle := SelectObject(PaintDC, PenHandle); { tell DC to use blue pen }
BrushHandle := CreateSolidBrush(RGB(255, 255, 0)); { create a yellow brush }
OldBrushHandle := SelectObject(PaintDC, BrushHandle); { tell DC to use yellow brush }
Ellipse(HDC, 10, 10, 50, 50); { draw the ellipse }
SelectObject(OldBrushHandle); { restore original brush }
DeleteObject(BrushHandle); { delete yellow brush }
SelectObject(OldPenHandle); { restore original pen }
DeleteObject(PenHandle); { destroy blue pen }
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
with Canvas do
begin
Pen.Color := clBlue; { make the pen blue }
Brush.Color := clYellow; { make the brush yellow }
Ellipse(10, 10, 50, 50); { draw the ellipse }
end;
end;
The canvas class encapsulates Windows graphics at several levels, including high-level functions for drawing individual lines, shapes, and text; intermediate properties for manipulating the drawing capabilities of the canvas; and low-level access to the Windows GDI.
Table 36.1 summarizes the capabilities of the canvas.
For detailed information on canvas classes and their methods and properties, see online Help.
Most of the graphics work you do in Delphi is limited to drawing directly on the canvases of components and forms. Delphi also provides for handling stand-alone graphic images, such as bitmaps, metafiles, and icons, including automatic management of palettes.
There are three important aspects to working with pictures in Delphi:
There are three kinds of classes in Delphi that deal with graphics:
Keep in mind that a picture class always has a graphic, and a graphic might have a canvas. (The only standard graphic that has a canvas is TBitmap.) Normally, when dealing with a picture, you work only with the parts of the graphic class exposed through TPicture. If you need access to the specifics of the graphic class itself, you can refer to the picture's Graphic property.
All pictures and graphics in Delphi can load their images from files and store them back again (or into different files). You can load or store the image of a picture at any time.
To load an image into a picture from a file, call the picture's LoadFromFile method.
To save an image from a picture into a file, call the picture's SaveToFile method.
LoadFromFile and SaveToFile each take the name of a file as the only parameter. LoadFromFile uses the extension of the file name to determine what kind of graphic object it will create and load. SaveToFile saves whatever type of file is appropriate for the type of graphic object being saved.
To load a bitmap into an image control's picture, for example, pass the name of a bitmap file to the picture's LoadFromFile method:
procedure TForm1.LoadBitmapClick(Sender: TObject);
begin
Image1.Picture.LoadFromFile('RANDOM.BMP');
end;
The picture recognizes .BMP as the standard extension for bitmap files, so it creates its graphic as a TBitmap, then calls that graphic's LoadFromFile method. Because the graphic is a bitmap, it loads the image from the file as a bitmap.
When running on a palette-based device (typically, a 256-color video mode), Delphi controls automatically support palette realization. That is, if you have a control that has a palette, you can use two methods inherited from TControl to control how Windows accommodates that palette.
Palette support for controls has these two aspects:
Most controls have no need for a palette, but controls that contain "rich color" graphic images (such as the image control) might need to interact with Windows and the screen device driver to ensure the proper appearance of the control. Windows refers to this process as realizing palettes.
Realizing palettes is the process of ensuring that the foremost window uses its full palette, and that windows in the background use as much of their palettes as possible, then map any other colors to the closest available colors in the "real" palette. As windows move in front of one another, Windows continually realizes the palettes.
Note: Delphi itself provides no specific support for creating or maintaining palettes, other than in bitmaps. If you have a palette handle, however, Delphi controls can manage it for you.
To specify a palette for a control, override the control's GetPalette method to return the handle of the palette.
Specifying the palette for a control does these things for your application:
If your control specifies a palette by overriding GetPalette, Delphi automatically takes care of responding to palette messages from Windows. The method that handles the palette messages is PaletteChanged.
The primary role of PaletteChanged is to determine whether to realize the control's palette in the foreground or the background. Windows handles this realization of palettes by making the topmost window have a foreground palette, with other windows resolved in background palettes. Delphi goes one step further, in that it also realizes palettes for controls within a window in tab order. The only time you might need to override this default behavior is if you want a control that is not first in tab order to have the foreground palette.
When drawing complex graphic images, a common technique in Windows programming is to create an off-screen bitmap, draw the image on the bitmap, and then copy the complete image from the bitmap to the final destination onscreen. Using an off-screen image reduces flicker caused by repeated drawing directly to the screen.
The bitmap class in Delphi, which represents bitmapped images in resources and files, can also work as an off-screen image.
There are two main aspects to working with off-screen bitmaps:
When creating complex graphic images, you should avoid drawing them directly on a canvas that appears onscreen. Instead of drawing on the canvas for a form or control, you can construct a bitmap object, draw on its canvas, and then copy its completed image to the onscreen canvas.
The most common use of an off-screen bitmap is in the Paint method of a graphic control. As with any temporary object, the bitmap should be protected with a try..finally block:
type
TFancyControl = class(TGraphicControl)
protected
procedure Paint; override; { override the Paint method }
end;
procedure TFancyControl.Paint;
var
Bitmap: TBitmap; { temporary variable for the off-screen
bitmap }
begin
Bitmap := TBitmap.Create; { construct the bitmap object }
try
{ draw on the bitmap }
{ copy the result into the control's canvas }
finally
Bitmap.Free; { destroy the bitmap object }
end;
end;
Delphi provides four different ways to copy images from one canvas to another. Depending on the effect you want to create, you call different methods.
Table 36.2 summarizes the image-copying methods in canvas objects.
All graphic objects, including canvases and their owned objects (pens, brushes, and fonts) have events built into them for responding to changes in the object. By using these events, you can make your components (or the applications that use them) respond to changes by redrawing their images.
Responding to changes in graphic objects is particularly important if you publish them as part of the design-time interface of your components. The only way to ensure that the design-time appearance of the component matches the properties set in the Object Inspector is to respond to changes in the objects.
To respond to changes in a graphic object, assign a method to the class's OnChange event.
The shape component publishes properties representing the pen and brush it uses to draw its shape. The component's constructor assigns a method to the OnChange event of each, causing the component to refresh its image if either the pen or brush changes:
type
TShape = class(TGraphicControl)
public
procedure StyleChanged(Sender: TObject);
end;
...
implementation
...
constructor TShape.Create(AOwner: TComponent);
begin
inherited Create(AOwner); { always call the inherited constructor! }
Width := 65;
Height := 65;
FPen := TPen.Create; { construct the pen }
FPen.OnChange := StyleChanged; { assign method to OnChange event }
FBrush := TBrush.Create; { construct the brush }
FBrush.OnChange := StyleChanged; { assign method to OnChange event }
end;
procedure TShape.StyleChanged(Sender: TObject);
begin
Invalidate(); { erase and repaint the component }
end;
pubsweb@inprise.com
Copyright © 1999, Inprise Corporation. All rights reserved.