Fix rounded-rectangle rendering artifacts with high-quality inset stroke - #14805
Conversation
Graphics.DrawRoundedRectangle and Graphics.FillRoundedRectangle now render under SmoothingMode.AntiAlias + PixelOffsetMode.HighQuality (saved and restored around the call), and DrawRoundedRectangle insets the stroke by half the pen width so the whole border sits inside the requested bounds on a consistent sub-pixel grid. This realigns the corner arcs with the straight edges and removes the reported artifacts. Public API signatures are unchanged. Fixes dotnet#14804 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 19883d86-6d5f-4d51-94c2-81d0bf3fc3ff
|
@Olina-Zhang: Can you take a look? This should fix the anti-alias artifacts and make it look really clean. |
| { | ||
| this.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias; | ||
| this.PixelOffsetMode = PixelOffsetMode.HighQuality; | ||
| DrawPath(pen, path); |
|
Hi @KlausLoeffelmann, the rendering quality looks much better overall after this change, especially the rounded corners on larger rectangles. While testing the PR, we found two possible regressions: 1. Exception when the adjusted radius becomes 0 This throws 2. Rendering regression for very small rectangles With Could you please take a look? Both seem to be edge cases related to the new inset logic. |
| if (rect.Width > pen.Width && rect.Height > pen.Width) | ||
| { | ||
| float inset = pen.Width / 2f; | ||
| strokeRect = RectangleF.Inflate(rect, -inset, -inset); | ||
| strokeRadius = new( | ||
| Math.Max(0f, radius.Width - inset), | ||
| Math.Max(0f, radius.Height - inset)); | ||
| } |
There was a problem hiding this comment.
Maybe we can drop the inset logic entirely and keep just the AntiAlias + PixelOffsetMode.HighQuality change.
- It crashes on valid input. When the pen.Width = 8 and radius = 4, the strokeRadius will be 0, which causes an exception: System.ArgumentException: 'Parameter is not valid.'
- It deforms small shapes. The
rect > pen.Widthguard is a hard threshold, so geometry jumps discontinuously as size crosses it. With a 10px pen, a 12×12 rect insets down to a 2×2 path (radius 3) → visibly malformed next to the 8×8/10×10 ones that aren't inset at all. - It breaks alignment with
DrawRectangle. Every otherDraw*centers the pen on the bounds, insetting makesDrawRoundedRectanglethe odd one out, so mixing it withDrawRectangleat the same bounds no longer lines up.
Summary
Fixes the anti-aliasing artifacts on rounded-rectangle corners rendered by
Graphics.DrawRoundedRectangleandGraphics.FillRoundedRectangle(System.Drawing.Common,
NET9_0_OR_GREATER).Both methods now render under
SmoothingMode.AntiAlias+PixelOffsetMode.HighQuality(saved and restored so the caller'sGraphicsstate is not permanently changed).
DrawRoundedRectangleadditionally insetsthe stroke by half the pen width so the whole border sits inside the requested
bounds on a consistent sub-pixel grid. This realigns the corner arcs with the
straight edges and removes the artifact. Public API signatures are unchanged.
Changes
Graphics.DrawRoundedRectangle(Pen, RectangleF, SizeF)— high-quality inset stroke.Graphics.FillRoundedRectangle(Brush, RectangleF, SizeF)— anti-aliased, high-quality fill.Test
System.Drawing.Common.Tests— all*RoundedRectangle*tests pass (9/9).Fixes #14804
Microsoft Reviewers: Open in CodeFlow