Random Post: Naming Private Fields
RSS .92| RSS 2.0| ATOM 0.3
  • Home
  • About
  • Eric’s Toolset
  • Free Software
  • Software License
  •  

    Rounded Rectangluar Regions

    November 19th, 2009 by Eric

    There are lots of examples that demonstrate how to draw a rectangle with rounded corners using GDI+ in .NET. Converting such a rectangle to a Region so that it can be filled or be used for the geometry of a window can have less than perfect results, though.

    For example, here is one of the more concise methods for getting a rounded Rectangle:

    public static GraphicsPath GetRoundedRectangle(Rectangle rect, int rad)
    {
    	int d = 2 * rad;
    	System.Drawing.Drawing2D.GraphicsPath gp =
    			new System.Drawing.Drawing2D.GraphicsPath();
     
    	gp.AddArc(rect.X, rect.Y, d, d, 180, 90);
    	gp.AddArc(rect.X + rect.Width - d, rect.Y, d, d, 270, 90);
    	gp.AddArc(rect.X + rect.Width - d, rect.Y + rect.Height - d, d, d, 0, 90);
    	gp.AddArc(rect.X, rect.Y + rect.Height - d, d, d, 90, 90);
    	gp.AddLine(rect.X, rect.Y + rect.Height - d, rect.X, rect.Y + d / 2);
     
    	return gp;
    }

    It’s trivial to convert that GraphicsPath to a Region — just call the appropriate Region constructor. You can then fill the region:

    var path = GetRoundedRectangle(rect, 3);
    e.Graphics.FillRegion(Brushes.Orange, new Region(path));

    If your corner radius is sufficiently large, you might not notice anything wrong with the result. With a value of 3, it is pretty obvious:

    Not-so-rounded rectangle

    The top left corner looks perfect:

    Top-left

    The bottom right corner is decidedly un-round:

    Bottom-right

    I started to wonder if all those round rectangle functions were wrong, but drawing the GraphicsPath itself shows that the code is correct:

    Graphics Path

    The problem is that when converting the GraphicsPath to a Region, it uses the inside of the GraphicsPath, so you lose the outside pixels on the sides, which you can see when drawing the path and then filling the region:

    rect-detail

    It turns out to be a whole lot easier and effective to do a little p/invoke here:

    [DllImport("gdi32.dll")]
    static extern IntPtr CreateRoundRectRgn(int x1, int y1, int x2, int y2, int cx, int cy);
    // ...
    var region = Region.FromHrgn(CreateRoundRectRgn(rect.X, rect.Y, rect.X + rect.Width, rect.Y + rect.Height, 3, 3));
    e.Graphics.FillRegion(Brushes.Orange, region);

    Here’s the output from that code:

    Good Region

    It looks like a little larger corner radius is needed to get the same rounding, but otherwise it is perfect.

    Leave a Reply