3

I created a small function that draws a Rectangle with finer edges. (you can call it a rounded rectangle)

Here is how I do it:

private void    DrawRoundedRectangle(Graphics G, int X1, int Y1, int X2, int Y2)
{
    GraphicsPath    GP  =new GraphicsPath();
    GP.AddLine(X1+1,Y1  ,  X2-1,Y1  );
    GP.AddLine(X2-1,Y1  ,  X2  ,Y1+1);
    GP.AddLine(X2  ,Y1+1,  X2  ,Y2-1);
    GP.AddLine(X2  ,Y2-1,  X2-1,Y2  );
    GP.AddLine(X2-1,Y2  ,  X1+1,Y2  );
    GP.AddLine(X1+1,Y2  ,  X1  ,Y2-1);
    GP.AddLine(X1  ,Y2-1,  X1  ,Y1+1);
    GP.AddLine(X1  ,Y1+1,  X1+1,Y1  );

    G.DrawPath(Pens.Blue,GP);
}

And here is the Paint event handler that calls this function:

private void Form1_Paint(object sender, PaintEventArgs e)
{
    this.DrawRoundedRectangle(e.Graphics,50,50,60,55);
}

Running it, indeed gives the desired result, which is this:

A good result as I wished.

However If I change the
G.DrawPath(Pens.Blue,GP);
line to be:
G.FillPath(Brushes.Blue,GP);
then what I get is this:

Not the result I wanted..
The bottom part of the rectangle is sharp, and not rounded as needed, like it was with the DrawPath() method.

Anyone knows what I should do to make the FillPath() method work well too?
If It matters, I am using .NET Framework 2.0.

leppie
  • 115,091
  • 17
  • 196
  • 297
spaceman
  • 1,061
  • 1
  • 11
  • 31
  • Possible duplicate of [Oddly drawn GraphicsPath with Graphics.FillPath](http://stackoverflow.com/questions/19167463/oddly-drawn-graphicspath-with-graphics-fillpath) – blas3nik Dec 11 '15 at 15:22
  • Hi blas3nik. The problem in the other question is indeed similar to here, yet I don't see how the workaround that is given there can help here, with a small shape like this.. If anyone does understand the workaround there and how to use it here too, please write.. Thank you – spaceman Dec 11 '15 at 16:30
  • Looks like the GraphicsPath got the old Fill- vs DrawRectanlge bug the other way round. Since you can't make the brush fill on the inside it is either modify the coordinates as in the link /and why would it not work with small sizes; it is only 1 pixel after all) or go for FillPolygon instead.. (it that gets it right../?) – TaW Dec 11 '15 at 19:50
  • 1
    GDI+ uses floating point math internally, troubleshooting the off-by-one errors produced by having to round to integral pixels is *very* little fun. You invariably get somewhat ahead by setting the Graphics.PixelOffsetMode to Half, the default is only a good choice for rendering bitmaps. That affects your shape pretty noticeably, getting it pixel-perfect is still an elusive goal. GDI+ was designed 15+ years ago with the assumption that resolution-independent graphics would soon be a practical reality. That it still is not is a fair tragedy. – Hans Passant Dec 12 '15 at 08:28
  • TaW: Regarding FillPolygon(), I checked now and unfortunately it doesn't give any different result.. Regarding "why would it not work with small sizes; it is only 1 pixel after all)" - I don't understand what to change in my code to try what they did there. – spaceman Dec 12 '15 at 09:36
  • Hans Passant: Thank you very much, it's interesting and definitely makes sense now. floating point math has a tendency to "move" a little bit from the accurate value.. BTW, I heard that in WPF this problem does not exist.. But I haven't tried it yet – spaceman Dec 12 '15 at 09:39
  • Same problem in WPF, less because it uses *double* instead of *float* and it interpolates more aggressively. The inconsistent SnapToDevicePixels is very little fun the same way. It only *really* looks good when you step back at least three feet or spend the money. – Hans Passant Dec 13 '15 at 07:07

1 Answers1

1

If you want a real rounded-rect implementation, you should use the code in the question referenced by commenter blas3nik, at Oddly drawn GraphicsPath with Graphics.FillPath.

Your implementation mainly just removes each of the pixels at the four corners. As such, there's no need to use GraphicsPath to draw this. Just fill a couple of overlapping rectangles that exclude those pixels:

    private void FillRoundedRectangle(Graphics G, int X1, int Y1, int X2, int Y2)
    {
        int width = X2 - X1, height = Y2 - Y1;

        G.FillRectangle(Brushes.Blue, X1 + 1, Y1, width - 2, height);
        G.FillRectangle(Brushes.Blue, X1, Y1 + 1, width, height - 2);
    }
Community
  • 1
  • 1
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • Thank you Peter, very nice workaround. Another workaround that I implemented yesterday, is to use DrawPath() to draw the outer GraphicsPath, which does it correctly, and then, to do FillRectangle, for the internal rectangle inside the outer graphicspath.. But I am switching now to your workaround, since it is achieved with less code, and should be faster due to that too. – spaceman Dec 12 '15 at 10:15