🐛 Fix inner and outer rect strokes ignoring corner radius (#11926)

This commit is contained in:
Elena Torró 2026-09-25 14:12:01 +02:00 committed by GitHub
parent 98daf1592d
commit 4943285e63
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 27 additions and 3 deletions

View File

@ -40,9 +40,12 @@ pub(super) fn draw_stroke_on_rect(
return;
}
// Radii as Skia fits them to the shape, so the stroke follows the fill.
let shape_radii = corners.map(|radii| *RRect::new_rect_radii(*rect, &radii).radii_ref());
// By default just draw the rect. Only dotted inner/outer strokes need
// clipping to prevent the dotted pattern from appearing in wrong areas.
let draw_stroke = || match corners {
let draw_stroke = || match &shape_radii {
Some(radii) => {
let radii = stroke.outer_corners(radii);
let rrect = RRect::new_rect_radii(stroke_rect, &radii);
@ -73,10 +76,13 @@ pub(super) fn draw_stroke_on_rect(
draw_stroke();
canvas.restore();
} else if stroke.kind == StrokeKind::Inner
&& (stroke.width >= rect.width() || stroke.width >= rect.height())
&& (stroke.width >= rect.width()
|| stroke.width >= rect.height()
|| inner_corners_collapse(stroke, rect, &shape_radii))
{
// When the inner stroke width exceeds a shape dimension, the inset
// rect goes negative and the stroke overflows outside the shape.
// Same when the inset radii can't follow the shape corners.
// Fall back to the same approach as the SVG renderer: draw with
// doubled width centered on the original shape and clip to it.
canvas.save();
@ -106,6 +112,22 @@ pub(super) fn draw_stroke_on_rect(
}
}
/// Whether the inset radii clamp to zero or get rescaled by Skia to fit.
fn inner_corners_collapse(stroke: &Stroke, rect: &Rect, radii: &Option<Corners>) -> bool {
let Some(radii) = radii else {
return false;
};
if matches!(stroke.style, StrokeStyle::Dotted | StrokeStyle::Dashed) {
return false;
}
let wanted = stroke.outer_corners(radii);
let inset = RRect::new_rect_radii(stroke.outer_rect(rect), &wanted);
radii
.iter()
.zip(wanted.iter().zip(inset.radii_ref()))
.any(|(shape, (want, got))| shape.x > 0.0 && (want.x <= 0.0 || want != got))
}
/// Draws a rect/frame stroke whose sides have different widths as the area
/// between an outer and an inner (rounded) rect, mitered like CSS borders.
/// Dashed/dotted styles cut that band per side, each side fitting a whole

View File

@ -358,7 +358,9 @@ impl Stroke {
let mut outer = *corners;
for corner in outer.iter_mut() {
corner.offset((offset, offset))
if corner.x > 0.0 {
corner.offset((offset, offset))
}
}
outer
}