Jump to content
c0d3r

Issue about calling Canvas.Polygon

Recommended Posts

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.

 

 

DrawTriangle.thumb.PNG.6dc35b19d4ba2bd4f07621d6839ff546.PNG

 

 

I just can't figure out what I was doing wrong.

 

Edited by c0d3r

Share this post


Link to post

if I use clBlack instead:

 

image.thumb.png.dce8766dc4e149e17813c027a324b2ac.png

Share this post


Link to post
2 hours ago, c0d3r said:

ACanvas.Brush.Color := ColorToRGB(clRed); //ColorToRGB(AColor);

You should not pass thru ColorToRGB.

Share this post


Link to post

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

@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
6 hours ago, FPiette said:

You should not pass thru ColorToRGB.

Tried without, it doesn't work either.

Share this post


Link to post
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 by Anders Melander
  • Thanks 1

Share this post


Link to post

@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;

 

image.thumb.png.810ea1b34fbad1c20bd867f31b97c51a.png

Edited by c0d3r

Share this post


Link to post

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
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

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 by c0d3r

Share this post


Link to post
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

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 by balabuev
  • Like 1

Share this post


Link to post
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 by balabuev
  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×