2

I'm trying to draw an Ellipse with a line inside it on a Bitmap, similar to this picture:

image

My app can load a bitmap picture, can be of any dimension. All I need is to draw an ellipse with line inside of it.

I know how to draw the ellipse, but my problem is the line inside of it:

Bmp.Canvas.Ellipse(0, 0, Bmp.Width, Bmp.Height);
Bmp.Canvas.MoveTo(?, ?);// Here is my problem
Bmp.Canvas.LineTo(?, ?);// here too

I try this:

Bmp.Canvas.MoveTo(0, 0);
Bmp.Canvas.LineTo(Bmp.Width, Bmp.Height);

But this will draw a line from the top-left to the bottom-right of the picture.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Ilyes
  • 14,640
  • 4
  • 29
  • 55
  • 1
    Possible duplicate of [How do I calculate a point on a circle’s circumference?](https://stackoverflow.com/questions/839899/how-do-i-calculate-a-point-on-a-circle-s-circumference) – Victoria Apr 16 '18 at 21:43

1 Answers1

5

Using Canvas.MoveTo() and Canvas.LineTo() like you are doing will work just fine. You just need to constrain the drawing of the line inside of the ellipse so anything you draw outside of the ellipse won't be seen.

You can apply an elliptical clipping region to the Canvas before drawing the line, using the Win32 API CreateEllipticRgn() and SelectClipRgn() functions, eg:

// draw the actual ellipse first...
Bmp.Canvas.Ellipse(0, 0, Bmp.Width, Bmp.Height);

// then create a region to match the ellipse...
Rgn := CreateEllipticRgn(0, 0, Bmp.Width, Bmp.Height);
try
  SelectClipRgn(Bmp.Canvas.Handle, Rgn);
  try
    // then draw the line inside the region...
    Bmp.Canvas.MoveTo(0, 0);
    Bmp.Canvas.LineTo(Bmp.Width, Bmp.Height);
  finally
    SelectClipRgn(Bmp.Canvas.Handle, 0);
  end;
finally
  DeleteObject(Rgn);
end;

Alternatively, you can apply an elliptical clipping path instead, using the Win32 API BeginPath(), EndPath(), and SelectClipPath() functions, eg:

// draw the actual ellipse first...
Bmp.Canvas.Ellipse(0, 0, Bmp.Width, Bmp.Height);

// then create a path to match the ellipse...
BeginPath(Bmp.Canvas.Handle);
try
  Bmp.Canvas.Ellipse(0, 0, Bmp.Width, Bmp.Height);
finally
  EndPath(Bmp.Canvas.Handle);
end;
SelectClipPath(Bmp.Canvas.Handle, RGN_COPY);

// then draw the line inside the path...
Bmp.Canvas.MoveTo(0, 0);
Bmp.Canvas.LineTo(Bmp.Width, Bmp.Height);

See Clipping Overview on MSDN for more details.

For example:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Image2: TImage;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  Bmp: TBitmap;
  Rgn: HRGN;
begin
  Bmp := TBitmap.Create;
  try
    Bmp.SetSize(Image1.Width, Image1.Height);

    Bmp.Canvas.Brush.Color := clWhite;
    Bmp.Canvas.FillRect(Rect(0, 0, Bmp.Width, Bmp.Height));

    Bmp.Canvas.Pen.Color := clRed;
    Bmp.Canvas.Pen.Width := 5;

    // draw the actual ellipse first...
    Bmp.Canvas.Ellipse(0, 0, Bmp.Width, Bmp.Height);

    // then create a region to match the ellipse...
    Rgn := CreateEllipticRgn(0, 0, Bmp.Width, Bmp.Height);
    try
      SelectClipRgn(Bmp.Canvas.Handle, Rgn);
      try
        // then draw the line inside the region...
        Bmp.Canvas.MoveTo(0, 0);
        Bmp.Canvas.LineTo(Bmp.Width, Bmp.Height);
      finally
        SelectClipRgn(Bmp.Canvas.Handle, 0);
      end;
    finally
      DeleteObject(Rgn);
    end;

    Image1.Picture.Assign(Bmp);
  finally
    Bmp.Free;
  end;

  Bmp := TBitmap.Create;
  try
    Bmp.SetSize(Image2.Width, Image2.Height);

    Bmp.Canvas.Brush.Color := clWhite;
    Bmp.Canvas.FillRect(Rect(0, 0, Bmp.Width, Bmp.Height));

    Bmp.Canvas.Pen.Color := clRed;
    Bmp.Canvas.Pen.Width := 5;

    // draw the actual ellipse first...
    Bmp.Canvas.Ellipse(0, 0, Bmp.Width, Bmp.Height);

    // then create a path to match the ellipse...
    BeginPath(Bmp.Canvas.Handle);
    try
      Bmp.Canvas.Ellipse(0, 0, Bmp.Width, Bmp.Height);
    finally
      EndPath(Bmp.Canvas.Handle);
    end;
    SelectClipPath(Bmp.Canvas.Handle, RGN_COPY);

    // then draw the line inside the path...
    Bmp.Canvas.MoveTo(0, 0);
    Bmp.Canvas.LineTo(Bmp.Width, Bmp.Height);

    Image2.Picture.Assign(Bmp);
  finally
    Bmp.Free;
  end;
end;

end.

image

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770