Howto: Create a rounded rectangle path #196
-
Hi all, I recently needed to render a rectangle with rounded corners. Although it is a bit hacky and probably not the most performant implementation ever, I thought it might be of some help. Here it goes (Gist here): using System.Collections.Generic;
using System.Linq;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing;
namespace RoundedRectangleTest
{
public static class RoundedRectangleExtension
{
public static IPath ToRoundedRectangle(this RectangleF rectangle, float cornerRadius)
{
IEnumerable<PointF> makeTopLeftCorner()
{
var ox = rectangle.Left + cornerRadius;
var oy = rectangle.Top + cornerRadius;
var clip = new RectangleF(rectangle.Left, rectangle.Top, cornerRadius, cornerRadius);
var ellipse = new EllipsePolygon(ox, oy, cornerRadius);
return ellipse.ClipCorner(clip);
}
IEnumerable<PointF> makeTopRightCorner()
{
var ox = rectangle.Right - cornerRadius;
var oy = rectangle.Top + cornerRadius;
var clip = new RectangleF(ox, rectangle.Top, cornerRadius, cornerRadius);
var ellipse = new EllipsePolygon(ox, oy, cornerRadius);
return ellipse.ClipCorner(clip);
}
IEnumerable<PointF> makeBottomRightCorner()
{
var ox = rectangle.Right - cornerRadius;
var oy = rectangle.Bottom - cornerRadius;
var clip = new RectangleF(ox, oy, cornerRadius, cornerRadius);
var ellipse = new EllipsePolygon(ox, oy, cornerRadius);
return ellipse.ClipCorner(clip);
}
IEnumerable<PointF> makeBottomLeftCorner()
{
var ox = rectangle.Left + cornerRadius;
var oy = rectangle.Bottom - cornerRadius;
var clip = new RectangleF(rectangle.Left, oy, cornerRadius, cornerRadius);
var ellipse = new EllipsePolygon(ox, oy, cornerRadius);
// Special case here: the first point should be returned last; other ones are good
var clipped = ellipse.ClipCorner(clip);
var first = clipped.First();
foreach (var point in clipped.Skip(1))
yield return point;
yield return first;
}
IEnumerable<PointF> getPathPoints()
{
foreach (var point in makeTopLeftCorner())
yield return point;
foreach (var point in makeTopRightCorner())
yield return point;
foreach (var point in makeBottomRightCorner())
yield return point;
foreach (var point in makeBottomLeftCorner())
yield return point;
}
var points = getPathPoints().ToArray();
var segments = new List<LinearLineSegment>();
var previous = points[0];
for (var i = 1; i < points.Length; i++)
{
var current = points[i];
var segment = new LinearLineSegment(previous, current);
segments.Add(segment);
previous = current;
}
return new Path(segments).AsClosedPath();
}
private static bool IsInRect(this PointF point, RectangleF rectangle) =>
point.X >= rectangle.Left && point.X <= rectangle.Right &&
point.Y >= rectangle.Top && point.Y <= rectangle.Bottom;
private static IEnumerable<PointF> ClipCorner(this EllipsePolygon ellipse, RectangleF clip) =>
ellipse.Points.ToArray().Where(p => p.IsInRect(clip));
}
} Here is a screenshot of how I used it (the path is used both to draw the border and to clip the interior): |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
Hey @odalet, thanks for sharing this! A more performant and easier way to construct your path is to use The elliptical arc feature is not present in our NuGet releases yet, you will have to use our MyGet feed. |
Beta Was this translation helpful? Give feedback.
Hey @odalet, thanks for sharing this! A more performant and easier way to construct your path is to use
PathBuilder
. Additionally, instead of clipping theEllipsePolygon
, you can use elliptical arcs which have been added in #144. This way you will be able to avoid the expensive point-by-point addition of the ellipse segments. You can use one of thePathBuilder.AddEllipticalArc
overloads for insertions.The elliptical arc feature is not present in our NuGet releases yet, you will have to use our MyGet feed.