TCanvas

From Free Pascal wiki

English (en) русский (ru)

A TCanvas is the primary tool to draw graphics.

To draw lines, boxes and text a TPen, TBrush and TFont are to be used to determine pen size and color, brush style and color, font style and color.

text TextOut( txt ); TextOutXY( x, y, txt )
lines Line( x1, y1, x2, y2 ); LineTo( x, y ) with MoveTo(x, y )
box Rectangle( l, t, r, b ); Rectangle( R: TRect ); FillRect()
circle Ellipse( l, t, r, b ); Ellipse( R: TRect )


Drawing a rectangle

Many controls expose their canvas as a public property or in an OnPaint event, e.g. TForm, TPanel and TPaintBox. Let's use TForm as an example to demonstrate how to paint on a canvas.

Suppose we want to draw a red rectangle with a 5-pixel-thick blue border in the center of the form; the size of the rectangle should be half of the size of the form. For this purpose we must add code to the OnPaint event of the form. Don't paint in an OnClick handler because this painting is not persistent and will be erased whenever the operating system requests a repaint, always paint int he OnPaint event!

The TCanvas method for painting a rectangle is called exactly like that: Rectangle(). It gets the coordinates of the rectangle edges either separately or as a TRect record. The fill color is determined by the color of the canvas's Brush, and the border color is given by the color of the canvas's Pen:

procedure TForm1.FormPaint(Sender: TObject);
var
  w, h: Integer;    // Width and height of the rectangle
  cx, cy: Integer;  // center of the form
  R: TRect;         // record containing the coordinates of the rectangle's left, top, right, bottom corners
begin
  // Calculate form center
  cx := Width div 2;
  cy := Height div 2;

  // Calculate the size of the rectangle
  w := Width div 2;
  h := Height div 2;

  // Calculate the corner points of the rectangle
  R.Left := cx - w div 2;
  R.Top := cy - h div 2;
  R.Right := cx + w div 2;
  R.Bottom := cy + h div 2;

  // Set the fill color
  Canvas.Brush.Color := clRed;
  Canvas.Brush.Style := bsSolid;

  // Set the border color
  Canvas.Pen.Color := clBlue;
  Canvas.Pen.Width := 5;
  Canvas.Pen.Style := psSolid;

  // Draw the rectangle
  Canvas.Rectangle(R);
end;

Using the default GUI font

This can be done with the following simple code:

SelectObject(Canvas.Handle, GetStockObject(DEFAULT_GUI_FONT));

Drawing a text limited on the width

Use the DrawText routine, first with DT_CALCRECT and then without it.

// First calculate the text size then draw it
TextBox := Rect(0, currentPos.Y, Width, High(Integer));
DrawText(ACanvas.Handle, PChar(Text), Length(Text),
  TextBox, DT_WORDBREAK or DT_INTERNAL or DT_CALCRECT);

DrawText(ACanvas.Handle, PChar(Text), Length(Text),
  TextBox, DT_WORDBREAK or DT_INTERNAL);

Drawing text with sharp edges (non antialiased)

Some widgetsets support this via

Canvas.Font.Quality := fqNonAntialiased;

Some widgetsets like the gtk2 do not support this and always paint antialiased. Here is a simple procedure to draw text with sharp edges under gtk2. It does not consider all cases, but it should give an idea:

procedure PaintAliased(Canvas: TCanvas; x, y: integer; const TheText: string);
var
  w, h, dx, dy: Integer;
  IntfImg:      TLazIntfImage;
  Img:          TBitmap;
  col:          TFPColor;
  FontColor, c: TColor;
begin
  w := 0;
  h := 0;
  Canvas.GetTextSize(TheText, w, h);
  if (w <= 0) or (h <= 0) then exit;
  Img := TBitmap.Create;
  IntfImg := nil;
  try
    // paint text to a bitmap
    Img.Masked := true;
    Img.SetSize(w,h);
    Img.Canvas.Brush.Style := bsSolid;
    Img.Canvas.Brush.Color := clWhite;
    Img.Canvas.FillRect(0, 0, w, h);
    Img.Canvas.Font := Canvas.Font;
    Img.Canvas.TextOut(0, 0, TheText);
    // get memory image
    IntfImg := Img.CreateIntfImage;
    // replace gray pixels
    FontColor := ColorToRGB(Canvas.Font.Color);
    for dy := 0 to h - 1 do begin
      for dx := 0 to w - 1 do begin
        col := IntfImg.Colors[dx, dy];
        c := FPColorToTColor(col);
        if c <> FontColor then
          IntfImg.Colors[dx, dy] := colTransparent;
      end;
    end;
    // create bitmap
    Img.LoadFromIntfImage(IntfImg);
    // paint
    Canvas.Draw(x, y, Img);
  finally
    IntfImg.Free;
    Img.Free;
  end;
end;