c0d3r 17 Posted January 29, 2021 (edited) Hi, For the last couple of days I tried figure out the issue about Canvas.Polygon wasn't working properly. Here is my routine: procedure DrawTriangle(ACanvas: TCanvas; AColor: TColor; ARect: TRect); begin ACanvas.Brush.Style := bsSolid; ACanvas.Brush.Color := ColorToRGB(clRed); //ColorToRGB(AColor); ACanvas.Pen.Color := ColorToRGB(clRed); //ColorToRGB(AColor); //ACanvas.Pen.Mode := pmCopy; ACanvas.Polygon([Point(ARect.Right - 1, ARect.Bottom - 10), Point(ARect.Right - 1, ARect.Bottom - 1), Point(ARect.Right - 10, ARect.Bottom - 1)]); end; if I passed clRed as one of the Color parameter on the red background, it drawed a white triangle, as you see from the below screenshot,. It was only painted right at the most right column. Even I hard coded to set color to clRed, it still drawed a white triangle. I just can't figure out what I was doing wrong. Edited January 29, 2021 by c0d3r Share this post Link to post
FPiette 383 Posted January 29, 2021 2 hours ago, c0d3r said: ACanvas.Brush.Color := ColorToRGB(clRed); //ColorToRGB(AColor); You should not pass thru ColorToRGB. Share this post Link to post
Anders Melander 1783 Posted January 29, 2021 I use the following to draw corners (top-right in this example): const CornerSize = 8; var Triangle: array[0..2] of TPoint; begin ACanvas.SaveDC; try ACanvas.Brush.Color := clRed; ACanvas.Brush.Style := bsSolid; ACanvas.Pen.Handle := 0; // Work around for messed up pen handle sporadically causing line not to be drawn ACanvas.Pen.Color := GetHighLightColor(ACanvas.Brush.Color); ACanvas.Pen.Style := psSolid; Triangle[0].X := AViewInfo.BoundsRect.Right-1; Triangle[0].Y := AViewInfo.BoundsRect.Top; Triangle[1].X := Triangle[0].X - CornerSize; Triangle[1].Y := Triangle[0].Y; Triangle[2].X := Triangle[0].X; Triangle[2].Y := Triangle[0].Y + CornerSize; ACanvas.Polygon(Triangle); finally ACanvas.RestoreDC; end; Share this post Link to post
MarkShark 27 Posted January 29, 2021 @Anders Melander Are your SaveDC and RestoreDC calls from a class helper for TCanvas? I don't do a lot of canvas drawing and only recently need to use the API functions for this and I don't fully understand when calling Canvas.Refresh is needed... and I'm wondering if your calls handle that. Thanks! Share this post Link to post
Anders Melander 1783 Posted January 29, 2021 Just now, MarkShark said: Are your SaveDC and RestoreDC calls from a class helper for TCanvas? They are Windows API functions: https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-savedc Share this post Link to post
MarkShark 27 Posted January 29, 2021 4 minutes ago, Anders Melander said: They are Windows API functions: https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-savedc Thanks! I notice you're calling it like ACanvas.SaveDC so I thought maybe you were encapsulating the API functions (my TCanvas doesn't seem to have those as methods.) I do use the api calls directly, but obviously your code is cleaner. Share this post Link to post
c0d3r 17 Posted January 29, 2021 6 hours ago, FPiette said: You should not pass thru ColorToRGB. Tried without, it doesn't work either. Share this post Link to post
Anders Melander 1783 Posted January 29, 2021 (edited) 1 hour ago, MarkShark said: Thanks! I notice you're calling it like ACanvas.SaveDC so I thought maybe you were encapsulating the API functions (my TCanvas doesn't seem to have those as methods.) I do use the api calls directly, but obviously your code is cleaner. My mistake. The ACanvas in my code is a DevExpress TcxCanvas - I didn't think about that. I think that if you use SaveDC/RestoreDC with a TCanvas then you'll need to lock the canvas to guard against the DC being changed. Something like this: ACanvas.Lock; try SaveDC(ACanvas.Handle); try ... finally RestoreDC(ACanvas.Handle, -1); end; finally ACanvas.Unlock; end; Edited January 29, 2021 by Anders Melander 1 Share this post Link to post
c0d3r 17 Posted January 29, 2021 (edited) @Anders Melander tried yours but still the same issue: procedure DrawTriangle(ACanvas: TCanvas; AColor: TColor; ARect: TRect); var SaveIndex: Integer; begin ACanvas.Lock; try SaveIndex := SaveDC(ACanvas.Handle); try ACanvas.Brush.Style := bsSolid; ACanvas.Brush.Color := clBlack; ACanvas.Pen.Color := clBlack; ACanvas.Polygon([Point(ARect.Right - 1, ARect.Bottom - 10), Point(ARect.Right - 1, ARect.Bottom - 1), Point(ARect.Right - 10, ARect.Bottom - 1)]); finally RestoreDC(ACanvas.Handle, SaveIndex); end; finally ACanvas.Unlock; end; end; Edited January 29, 2021 by c0d3r Share this post Link to post
Anders Melander 1783 Posted January 29, 2021 I know the SaveDC/RestoreDC doesn't solve your problem. In my code it's just there to leave the canvas in the same state as it was in when I got it. The key point from my code was the clearing of the pen handle since in my case I had problems with the pen color. Try reversing the order of the pen and brush assignments. I've had similar problems and as far as I remember that was what I did to solve it. Share this post Link to post
c0d3r 17 Posted January 29, 2021 13 minutes ago, Anders Melander said: I know the SaveDC/RestoreDC doesn't solve your problem. In my code it's just there to leave the canvas in the same state as it was in when I got it. The key point from my code was the clearing of the pen handle since in my case I had problems with the pen color. Try reversing the order of the pen and brush assignments. I've had similar problems and as far as I remember that was what I did to solve it. Tried reversing the order of pen and brush assignments, doesn't work either. Share this post Link to post
c0d3r 17 Posted January 29, 2021 (edited) Solved the issue, but I don't understand the reason why, I had like: SaveIndex := SaveDC(Canvas.Handle); try IntersectClipRect(Canvas.Handle, .....); PaintRows(Canvas, ...); finally RestoreDC(Canvas.Handle, SaveIndex); end; The codes in procedure PaintRows: SaveIndex2 := SaveDC(ACanvas.Handle); try IntersectClipRect(ACanvas.Handle, .....); ..... PaintCells(ACanvas, ...); ...... finally RestoreDC(ACanvas.Handle, SaveIndex2); end; By removing the SaveDC/RestoreDC from procedure PaintRows, it solves, but Why? can't nest call SaveDC/RestoreDC?? Edited January 29, 2021 by c0d3r Share this post Link to post
Anders Melander 1783 Posted January 29, 2021 7 minutes ago, c0d3r said: can't nest call SaveDC/RestoreDC?? Yes, they're designed to be nested. I have no idea about why it doesn't work though. Maybe TCanvas gets confused when the HDC is modified outside its control. Share this post Link to post
balabuev 102 Posted January 31, 2021 (edited) The issue happens due to the fact that you use two not fully compatible ways to deal with the device context (DC): 1) Delphi TCanvas wrapper, which have a lot of lazy initializing stuff. For example, when you change the color of the Brush, the brush object is marked as invalid internally, and its handle will be recreated later - only when needed. 2) WinAPI directly, like SaveDC/RestoreDC, which skips mentioned Delphi internal invalidation. TCanvas.Refresh method is specifically exist to resort cases like this. Try to call it just before ACanvas.Polygon. I can even describe, why red color is a kind of special color, which do not work (as opposed to any other color, like black). And I agree - ColorToRGB is not needed here. Edited January 31, 2021 by balabuev 1 Share this post Link to post
balabuev 102 Posted January 31, 2021 (edited) On 1/29/2021 at 6:07 PM, c0d3r said: Solved the issue, but I don't understand the reason why, I had like: SaveIndex := SaveDC(Canvas.Handle); try IntersectClipRect(Canvas.Handle, .....); PaintRows(Canvas, ...); finally RestoreDC(Canvas.Handle, SaveIndex); end; SaveDC/RestoreDC in such cases are usually used to restore original clipping region, since you change it in the code. Edited January 31, 2021 by balabuev 1 Share this post Link to post