🐛 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; 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 // By default just draw the rect. Only dotted inner/outer strokes need
// clipping to prevent the dotted pattern from appearing in wrong areas. // clipping to prevent the dotted pattern from appearing in wrong areas.
let draw_stroke = || match corners { let draw_stroke = || match &shape_radii {
Some(radii) => { Some(radii) => {
let radii = stroke.outer_corners(radii); let radii = stroke.outer_corners(radii);
let rrect = RRect::new_rect_radii(stroke_rect, &radii); let rrect = RRect::new_rect_radii(stroke_rect, &radii);
@ -73,10 +76,13 @@ pub(super) fn draw_stroke_on_rect(
draw_stroke(); draw_stroke();
canvas.restore(); canvas.restore();
} else if stroke.kind == StrokeKind::Inner } 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 // When the inner stroke width exceeds a shape dimension, the inset
// rect goes negative and the stroke overflows outside the shape. // 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 // Fall back to the same approach as the SVG renderer: draw with
// doubled width centered on the original shape and clip to it. // doubled width centered on the original shape and clip to it.
canvas.save(); 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 /// 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. /// 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 /// 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; let mut outer = *corners;
for corner in outer.iter_mut() { for corner in outer.iter_mut() {
corner.offset((offset, offset)) if corner.x > 0.0 {
corner.offset((offset, offset))
}
} }
outer outer
} }