Drawing Shapes

Now let's look at an Image component example that will paint on a bitmap. The idea is simple. I've basically written a simplified version of the Shape example, by placing an Image component on its form and redirecting all the output operations to the canvas of this Image component.

In this example, ShapeBmp, I've also added some new menu items to save the image to a file and to load an existing bitmap. To accomplish this, I've added to the form a couple of default dialog components, OpenDialog and SaveDialog. One of the properties I had to change was the background color of the form. In fact, when you perform the first graphical operation on the image, it creates a bitmap, which has a white background by default. If the form has a gray background, each time the window is repainted, some flickering occurs. For this reason, I've chosen a white background for the form, too.

The code of this example is still quite simple, considering the number of operations and menu commands. The drawing portion is linear and very close to Mouse1, except that the mouse events now relate to the image instead of the form; I've used the NormalizeRect function during the dragging; and the program uses the canvas of the image. Here is the OnMouseMove event handler, which reintro-duces the drawing of points when moving the mouse with the Shift key pressed:

procedure TShapesForm.Image1MouseMove(Sender: TObject;

Shift: TShiftState; X, Y: Integer); var

ARect: TRect; begin

  • display the position of the mouse in the caption Caption := Format ('ShapeBmp (x=%d, y=%d)', [X, Y]); if fDragging then begin
  • remove and redraw the dragging rectangle

ARect := NormalizeRect (fRect); Canvas.DrawFocusRect (ARect); fRect.Right := X; fRect.Bottom := Y; ARect := NormalizeRect (fRect); Canvas.DrawFocusRect (ARect); end else if ssShift in Shift then // mark point in red Image1.Canvas.Pixels [X, Y] := clRed;


Notice that the temporary focus rectangle is painted directly on the form, over the image (and thus not stored in the bitmap). What is different is that at the end of the dragging operation, the program paints the rectangle on the image, storing it in the bitmap. This time the program doesn't call Invalidate and has no OnPaint event handler:

procedure TShapesForm.Image1MouseUp(Sender: TObject;

Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if fDragging then begin

ReleaseCapture; fDragging := False;

Image1.Canvas.Rectangle (fRect.Left, fRect.Top, fRect.Right, fRect.Bottom);

To avoid overly complex file support, I decided to implement the File ^ Load and File ^ Save As commands and not handle the Save command, which is generally more complex. I've simply added an fChanged field to the form to know when an image has changed, and I've included code that checks this value a number of times (before asking the user to confirm).

The OnClick event handler of the File ^ New menu item calls the FillArea method to paint a big white rectangle over the whole bitmap. In this code you can also see how the Changed field is used:

procedure TShapesForm.New1Click(Sender: TObject); var

Area: TRect; OldColor: TColor;

begin if not fChanged or (MessageDlg (

  • Are you sure you want to delete the current image?', mtConfirmation, [mbYes, mbNo], 0) = idYes) then begin
  • repaint the surface, covering the whole area, and resetting the old brush}

Image1.Picture.Height); OldColor := Image1.Canvas.Brush.Color; Image1.Canvas.Brush.Color := clWhite; Image1.Canvas.FillRect (Area); Image1.Canvas.Brush.Color := OldColor; fChanged := False; end; end;

Of course, the code has to save the original color and restore it later on. A realignment of the colors is also required by the File ^ Load command-response method. When you load a new bitmap, in fact, the Image component creates a new canvas with the default attributes. For this reason, the program saves the pen's colors and size and copies them later to the new canvas:

procedure TShapesForm.Load1Click(Sender: TObject); var

PenCol, BrushCol: TColor; PenSize: Integer; begin if not fChanged or (MessageDlg (

'Are you sure you want to delete the current image?', mtConfirmation, [mbYes, mbNo], 0) = idYes) then if OpenDialog1.Execute then begin

PenCol := Image1.Canvas.Pen.Color; BrushCol := Image1.Canvas.Brush.Color; PenSize := Image1.Canvas.Pen.Width; Image1.Picture.LoadFromFile (OpenDialog1.Filename); Image1.Canvas.Pen.Color := PenCol; Image1.Canvas.Brush.Color := BrushCol; Image1.Canvas.Pen.Width := PenSize; fChanged := False; end;


Saving the current image is much simpler:

procedure TShapesForm.Saveas1Click(Sender: TObject); begin if SaveDialog1.Execute then begin

  1. Picture.SaveToFile (
  2. Filename); fChanged := False; end; end;

Finally, here is the code of the OnCloseQuery event of the form, which uses the Changed field:

procedure TShapesForm.FormCloseQuery(Sender: TObject;

var CanClose: Boolean); begin if not fChanged or (MessageDlg (

'Are you sure you want to delete the current image?', mtConfirmation, [mbYes, mbNo], 0) = idYes) then CanClose := True else

CanClose := False;

+1 0

Post a comment