Microsoft.Maui.Graphics
Microsoft.Maui.Graphics copied to clipboard
ICanvas.FillRoundedRectangle/DrawRoundedRectangle does not support separate x-radius and y-radius
In SkiaSharp it is possible to set separate x and y radius for rounded rectangle.
public void AddRoundRect (SkiaSharp.SKRect rect, float rx, float ry, SkiaSharp.SKPathDirection dir = SkiaSharp.SKPathDirection.Clockwise);
https://docs.microsoft.com/en-us/dotnet/api/skiasharp.skpath.addroundrect?view=skiasharp-2.80.2
Hi @wieslawsoltes,
I think this is possible using the current library. Here's a .NET 6 console app I made to demonstrate:
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Graphics.Skia;
// create a new image with a blue background
using SkiaBitmapExportContext context = new(400, 300, 1.0f);
ICanvas canvas = context.Canvas;
canvas.FillColor = Colors.Navy;
canvas.FillRectangle(0, 0, context.Width, context.Height);
// draw a test pattern
canvas.StrokeColor = Colors.Yellow;
canvas.StrokeSize = 3;
canvas.DrawRoundedRectangle(
x: 50,
y: 50,
width: 300,
height: 200,
topLeftCornerRadius: 5,
topRightCornerRadius: 20,
bottomLeftCornerRadius: 40,
bottomRightCornerRadius: 60);
// save output
string filePath = Path.GetFullPath("test.png");
using FileStream fs = new(filePath, FileMode.Create);
context.WriteToStream(fs);
Console.WriteLine(filePath);
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Graphics" Version="6.0.200-preview.12.852" />
<PackageReference Include="Microsoft.Maui.Graphics.Skia" Version="6.0.200-preview.12.852" />
</ItemGroup>
</Project>
It looks like those rounded corner methods are canvas extensions defined here: https://github.com/dotnet/Microsoft.Maui.Graphics/blob/20e0e9e8c78d37575c7c0e1a5c64170616085f0a/src/Microsoft.Maui.Graphics/CanvasExtensions.cs#L40-L45
Related: #38
@swharden This is not the same. I want different radius along x-axis and different along y-axis.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="100" width="100">
<g>
<rect id="rounded-rectangle" x="0" y="0" height="100" width="100" stroke="red" rx="10" ry="30" stroke-width="1" fill="transparent" />
</g>
</svg>
I want [individual corners to have a] different radius along x-axis and different along y-axis
Okay, I understand now. Thanks for clarifying this with a picture!
Hi @wieslawsoltes, I created a PR to extend ICanvas to do this (#269). You may want to comment there if you have any suggestions about how to improve the API.
In the mean time this code can be used to get a rounded rectangle with custom horizontal and vertical radii:
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Graphics.Skia;
public static class Program
{
public static void Main()
{
// create a new image with a blue background
using SkiaBitmapExportContext context = new(400, 300, 1.0f);
ICanvas canvas = context.Canvas;
canvas.FillColor = Colors.White;
canvas.FillRectangle(0, 0, context.Width, context.Height);
// draw a test pattern
InspectRoundedPath(canvas, new RectangleF(50, 50, 100, 100), xRadius: 20, yRadius: 40);
InspectRoundedPath(canvas, new RectangleF(160, 50, 10, 100), xRadius: 20, yRadius: 40);
InspectRoundedPath(canvas, new RectangleF(50, 160, 100, 10), xRadius: 20, yRadius: 40);
// save output
string filePath = Path.GetFullPath("test.png");
using FileStream fs = new(filePath, FileMode.Create);
context.WriteToStream(fs);
Console.WriteLine(filePath);
}
private static void InspectRoundedPath(ICanvas canvas, RectangleF rect, float xRadius, float yRadius)
{
canvas.StrokeColor = Colors.Green;
canvas.StrokeSize = 1;
canvas.DrawRectangle(rect);
canvas.StrokeColor = Colors.Magenta;
canvas.StrokeSize = 1;
PathF roundedRectanglePath = GetRoundedRectangle(rect, xRadius, yRadius);
canvas.DrawPath(roundedRectanglePath);
}
public static PathF GetRoundedRectangle(RectangleF rect, float xRadius, float yRadius)
{
xRadius = Math.Min(xRadius, rect.Width / 2);
yRadius = Math.Min(yRadius, rect.Height / 2);
float minX = Math.Min(rect.X, rect.X + rect.Width);
float minY = Math.Min(rect.Y, rect.Y + rect.Height);
float maxX = Math.Max(rect.X, rect.X + rect.Width);
float maxY = Math.Max(rect.Y, rect.Y + rect.Height);
PathF path = new();
path.MoveTo(minX, minY + yRadius);
path.CurveTo(minX, minY + yRadius, minX, minY, minX + xRadius, minY);
path.LineTo(maxX - xRadius, minY);
path.CurveTo(maxX - xRadius, minY, maxX, minY, maxX, minY + yRadius);
path.LineTo(maxX, maxY - yRadius);
path.CurveTo(maxX, maxY - yRadius, maxX, maxY, maxX - xRadius, maxY);
path.LineTo(minX + xRadius, maxY);
path.CurveTo(minX + xRadius, maxY, minX, maxY, minX, maxY - yRadius);
path.LineTo(minX, minY + yRadius);
return path;
}
}