Slightly confused about Rectangles! lazarus / 39-Graphics ---------------------------------------------------------------------- OldAndTired · 2021-08-26 15:05 Hi I was having problems with copying bitmaps, so I decided to start a new test project just to draw some lines & rectangles. I have a form with an Image sized at 200x200 pixels. So my understanding is the pixel numbering is 0..199 in this case. If so to draw/select/fill a rectangle the co-ordinates should be 0,0,199,199. But the examples I see online are 0,0,Image.Width,Image.Height. If I try Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Image1.Canvas.FillRect(0,0,199,199); then my rectangle is 1 pix short. What am I missing?? [By the way - the actual requirement is to draw a moving 1 pix wide vertical cursor on an image, so I'm trying to save/draw/resstore a bitmap 1 pix x image height.] Here is my test Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls; type   { TForm1 }   TForm1 = class(TForm)    Image1: TImage;    procedure FormCreate(Sender: TObject);  private   public   end; var  Form1: TForm1;implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject);begin  image1.Canvas.Brush.Color:=clGray;  //This following line draws a rectangle but the RH side is one pixel short  // If I change to FillRect(0,0,200,200) it works but isn't that now 201x201 size?  image1.Canvas.FillRect(0,0,199,199);   //The following is just to draw some lines at the edges of the image  image1.Canvas.Pen.Width:=1;  image1.Canvas.Pen.Color:=clRed;  image1.Canvas.MoveTo(0,50);  image1.Canvas.LineTo(0, 150);  image1.Canvas.Pen.Color:=clYellow;  image1.Canvas.MoveTo(199,50);  image1.Canvas.LineTo(199, 150);  image1.Canvas.Pen.Color:=clTeal;  image1.Canvas.MoveTo(50,199);  image1.Canvas.LineTo(150,199);end;   end.  and here is the image it draws ---------------------------------------------------------------------- Handoko · 2021-08-26 15:45 Hello OldAndTired, Welcome to the forum. Maybe you haven't known: You should not call any drawing function in OnCreate event. If you do so, many weird things may happen. OnCreate usually is used for setting up default values for variables or components. It should not be long (like loading huge data from disk) and you should not call any drawing function there. Calling drawing function inside OnCreate event can cause unexpected things to happen because the component maybe have not been fully created yet. ---------------------------------------------------------------------- winni · 2021-08-26 16:20 Hi! And an addition to Handoko: Every painting on a canvas should be done with the onPaint event of the parent: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.FormPaint(Sender: TObject);begin   self.Canvas.TextOut(10,10,'HUHU');   ......end;      Winni ---------------------------------------------------------------------- OldAndTired · 2021-08-26 16:23 OK thanks and thanks. Understood about the OnCreate event, although actually in my applicaton things are happening in a timer event. The code was just a quick example. Exactly the same thing happens if I just put the code in some button clicks. Rather than specifying the Rectangle as X1,Y1,X2,Y2 it's behaving as if it's X,Y,W,H? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls; type   { TForm1 }   TForm1 = class(TForm)    Button1: TButton;    Button2: TButton;    Image1: TImage;    procedure Button1Click(Sender: TObject);    procedure Button2Click(Sender: TObject);   private   public   end; var  Form1: TForm1;implementation {$R *.lfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject);begin  image1.Canvas.Brush.Color:=clGray;  //This following line draws a rectangle but the RH side is one pixel short  // If I change to FillRect(0,0,200,200) it works but isn't that now 201x201 size?  image1.Canvas.FillRect(0,0,199,199);end; procedure TForm1.Button2Click(Sender: TObject);begin  //The following is just to draw some lines at the edges of the image  image1.Canvas.Pen.Width:=1;  image1.Canvas.Pen.Color:=clRed;  image1.Canvas.MoveTo(0,50);  image1.Canvas.LineTo(0, 150);  image1.Canvas.Pen.Color:=clYellow;  image1.Canvas.MoveTo(199,50);  image1.Canvas.LineTo(199, 150);  image1.Canvas.Pen.Color:=clTeal;  image1.Canvas.MoveTo(50,199);  image1.Canvas.LineTo(150,199);end;   end. ---------------------------------------------------------------------- OldAndTired · 2021-08-26 16:29 Quote from: winni on August 26, 2021, 06:20:09 pmHi! And an addition to Handoko: Every painting on a canvas should be done with the onPaint event of the parent: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.FormPaint(Sender: TObject);begin   self.Canvas.TextOut(10,10,'HUHU');   ......end;      Winni Hi. Now this bit I don't get. How does that wotk when another event is generating the line/text/whatever? In my case, it will be either a timer event or a LazSerial OnData event which will need to draw some lines. ...and it still doesn't explain why my rectangle is one pixel short ---------------------------------------------------------------------- Seenkao · 2021-08-26 17:08 Задаются начальные координаты и ширина и высота. А не начальные и конечные координаты. Google translate: Start coordinates and width and height are set. Not the start and end coordinates. Для вывода на экран можно использовать таймер, но принято использовать событие Paint. Google translate: You can use a timer to display it on the screen, but it's common to use the Paint event. ---------------------------------------------------------------------- winni · 2021-08-26 17:24 Quote from: Seenkao on August 26, 2021, 07:08:43 pmYou can use a timer to display it on the screen, but it's common to use the Paint event. Hi! Whenever you paint on a canvas you have to do it with the onPaint event. Some widgetset will allow an drawing outside this procedure, but it will crash in another widgetset or if you mix up the event qeue. And then the timer send out a refresh or invalidate which executes the onPaint event. Winni ---------------------------------------------------------------------- winni · 2021-08-26 17:29 Quote from: OldAndTired on August 26, 2021, 06:29:45 pm...and it still doesn't explain why my rectangle is one pixel short  Read more about the Off-By-One-Error: https://en.wikipedia.org/wiki/Off-by-one_error Winni ---------------------------------------------------------------------- Seenkao · 2021-08-26 18:06 Quote from: winni on August 26, 2021, 07:24:55 pmHi! Whenever you paint on a canvas you have to do it with the onPaint event. Some widgetset will allow an drawing outside this procedure, but it will crash in another widgetset or if you mix up the event qeue. And then the timer send out a refresh or invalidate which executes the onPaint event. Winni Это было изначально проблемой. В настоящее время мне (именно мне!) ни что не помешает использовать таймер для вывода, или любую процедуру, которая вообще не должна отрисовывать изображение на Canvas.  Но это уже вне темы. Google translate: This was originally a problem. For me, at the moment, nothing will prevent me from using a timer for output, or any procedure that should not render an image on a Canvas at all. But this is no longer for this topic. Google translates me wrong... ---------------------------------------------------------------------- OldAndTired · 2021-08-26 18:22 Quote from: Seenkao on August 26, 2021, 07:08:43 pmЗадаются начальные координаты и ширина и высота. А не начальные и конечные координаты. Google translate: Start coordinates and width and height are set. Not the start and end coordinates. This seems to be the crux of the matter - All the docs seem to point to calling with Left/Top/Right/Bottom, but the behaviour is Left/Top/Width/Height. Which is correct? Quotepublic procedure TCanvas.FillRect(   const ARect: TRect ); virtual; QuoteTRect Descibes a rectangle Declaration Source position: classesh.inc line 28 type TRect = Types.TRect; Description TRect describes a rectangle in space with its upper-left (in (Top,Left>)) and lower-right (in (Bottom ,Right)) corners. ---------------------------------------------------------------------- skalogryz · 2021-08-26 18:27 Quote from: OldAndTired on August 26, 2021, 05:05:19 pmIf I try Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Image1.Canvas.FillRect(0,0,199,199); then my rectangle is 1 pix short. What am I missing?? This is for the historical reasons. (Delphi compatility) and Delphi itself draws rectangles following WinAPI rules QuoteWhen an application calls the Rectangle function, the system draws the rectangle, excluding the right and lower sides if no world transformation is set for the given device context. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.FormPaint(Sender: TObject);var  p : array[0..3] of TPoint;begin  Canvas.Pen.Style := psSolid;  Canvas.Pen.Color := clLime;  Canvas.Brush.Color := clBlue;  Canvas.Brush.Style := bsSolid;   Canvas.Rectangle(10, 10, 15, 15);   p[0]:=Point(20,10);  p[1]:=Point(25,10);  p[2]:=Point(25,15);  p[3]:=Point(20,15);   Canvas.Polygon(p);end; This stackoverflow post is really good. Interesting fact: this behavior has to be emulated on all other platforms that LCL supports. ---------------------------------------------------------------------- Seenkao · 2021-08-26 18:29 image1.Canvas.FillRect(0,0,200,200); ---------------------------------------------------------------------- winni · 2021-08-26 18:44 Hi! Rectangle for example rect (10,10,110,110);; results in  width=100; height = 100 But: if you fill all pixels from 10 to 110 then you fill 101 pixels. That contradicts the computing That is why the rectangle is drawn correct. And not one pixel too long. Winni ---------------------------------------------------------------------- tetrastes · 2021-08-26 19:27 Quote from: Seenkao on August 26, 2021, 07:08:43 pmStart coordinates and width and height are set. Not the start and end coordinates. You are wrong. In that case Quote from: winni on August 26, 2021, 08:44:36 pmRectangle for example rect (10,10,110,110);; results in  width=100; height = 100 would resulted in width=110; height=110, no matter of start coordinates. ---------------------------------------------------------------------- winni · 2021-08-26 19:38 Hi! It is such a joy that you've got the source at your  fingertips. Place the cursor on TRect and type Alt-CursorUp. You see: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TRect = ....  record  ....case Longint of         0: (Left,Top,Right,Bottom : Longint);         1: (TopLeft,BottomRight : TPoint);         2: (Vector:TArray4IntegerType); end; If you need the height or width there are also       Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i        property Height: Longint read getHeight write setHeight;       property Width : Longint read getWidth  write setWidth;       property Size  : TSize   read getSize   write setSize;       property Location  : TPoint read getLocation write setLocation; Look in the code. That is often more helpful than searching for a documentation! Winni ---------------------------------------------------------------------- 440bx · 2021-08-26 20:13 @skalogryz explained the cause for the rectangle being short one pixel. if you want to see an example that is specifically created to show how the painting occurs and what affects it, see the "rectangle" example found at https://forum.lazarus.freepascal.org/index.php/topic,53690.ms g397420.html#msg397420 That post is more than just about rectangles but you might find the example and the additional explanations helpful. HTH. ---------------------------------------------------------------------- OldAndTired · 2021-08-26 20:33 Quote from: skalogryz on August 26, 2021, 08:27:44 pm This is for the historical reasons. (Delphi compatility) and Delphi itself draws rectangles following WinAPI rules QuoteWhen an application calls the Rectangle function, the system draws the rectangle, excluding the right and lower sides if no world transformation is set for the given device context. Thanks! This finally explains what I've been experiencing. Actually, I was attempting to use a Copyrect in order to copy a 1-pixel wide area from the image to a buffer bitmap and it wasn't working. After many hours of messing about not sure if my code was wrong I tried drawing some lines & rectangles and got to where I was when I posted here. It sort of became a X-Y problem for a while. Now I can hopefully move forward... I'll look to moving things to onPaint while I'm at it. I'm not too concerned about cross-platform & compatability as this is just a one-off hobby project for me. ---------------------------------------------------------------------- speter · 2021-08-27 02:18 Quote from: OldAndTired on August 26, 2021, 10:33:00 pm I was attempting to use a Copyrect in order to copy a 1-pixel wide area Are you aiming to create a crosshair (cursor)? If so, have a look at the attached project. It uses "xor" mode to create a cursor. cheers S. ---------------------------------------------------------------------- OldAndTired · 2021-08-27 12:36 Quote from: speter on August 27, 2021, 04:18:48 amAre you aiming to create a crosshair (cursor)? If so, have a look at the attached project. It uses "xor" mode to create a cursor. Thanks - I'll take a look at that, as it sounds like it might be simpler anyway. No I wasn't planning a crosshair, just a vertical line to sweep across a graph as a sort of visual progress indicator. It's for an arduino-controlled oven which will follow a defined time/temperature profile, the profile is pre-plotted and the actual temperature plotted over time. I may well decide I don't like it once I get it working  But I can't leave it until I fix it....