Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions lib/web_ui/lib/src/engine/recording_canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ part of engine;
/// Enable this to print every command applied by a canvas.
const bool _debugDumpPaintCommands = false;

// Similar to [Offset.distance]
double _getDistance(double x, double y) => math.sqrt(x * x + y * y);
// Returns the squared length of the x,y (of a border radius)
// It normalizes x, y values before working with them, by
// assuming anything < 0 to be 0, because flutter may pass
// negative radii (which skia ssumes to be 0), see:
// https://skia.org/user/api/SkRRect_Reference#SkRRect_inset
double _measureBorderRadius(double x, double y) {
double clampedX = x < 0 ? 0 : x;
double clampedY = y < 0 ? 0 : y;
return clampedX * clampedX + clampedY * clampedY;
}

/// Records canvas commands to be applied to a [EngineCanvas].
///
Expand Down Expand Up @@ -231,8 +239,8 @@ class RecordingCanvas {
}

void drawDRRect(ui.RRect outer, ui.RRect inner, ui.Paint paint) {
// Ensure inner is fully contained within outer, by comparing its
// defining points (including its border radius)
// Check the inner bounds are contained within the outer bounds
// see: https://cs.chromium.org/chromium/src/third_party/skia/src/core/SkCanvas.cpp?l=1787-1789
ui.Rect innerRect = inner.outerRect;
ui.Rect outerRect = outer.outerRect;
if (outerRect == innerRect || outerRect.intersect(innerRect) != innerRect) {
Expand All @@ -243,15 +251,15 @@ class RecordingCanvas {
final ui.RRect scaledOuter = outer.scaleRadii();
final ui.RRect scaledInner = inner.scaleRadii();

final double outerTl = _getDistance(scaledOuter.tlRadiusX, scaledOuter.tlRadiusY);
final double outerTr = _getDistance(scaledOuter.trRadiusX, scaledOuter.trRadiusY);
final double outerBl = _getDistance(scaledOuter.blRadiusX, scaledOuter.blRadiusY);
final double outerBr = _getDistance(scaledOuter.brRadiusX, scaledOuter.brRadiusY);
final double outerTl = _measureBorderRadius(scaledOuter.tlRadiusX, scaledOuter.tlRadiusY);
final double outerTr = _measureBorderRadius(scaledOuter.trRadiusX, scaledOuter.trRadiusY);
final double outerBl = _measureBorderRadius(scaledOuter.blRadiusX, scaledOuter.blRadiusY);
final double outerBr = _measureBorderRadius(scaledOuter.brRadiusX, scaledOuter.brRadiusY);

final double innerTl = _getDistance(scaledInner.tlRadiusX, scaledInner.tlRadiusY);
final double innerTr = _getDistance(scaledInner.trRadiusX, scaledInner.trRadiusY);
final double innerBl = _getDistance(scaledInner.blRadiusX, scaledInner.blRadiusY);
final double innerBr = _getDistance(scaledInner.brRadiusX, scaledInner.brRadiusY);
final double innerTl = _measureBorderRadius(scaledInner.tlRadiusX, scaledInner.tlRadiusY);
final double innerTr = _measureBorderRadius(scaledInner.trRadiusX, scaledInner.trRadiusY);
final double innerBl = _measureBorderRadius(scaledInner.blRadiusX, scaledInner.blRadiusY);
final double innerBr = _measureBorderRadius(scaledInner.brRadiusX, scaledInner.brRadiusY);

if (innerTl > outerTl || innerTr > outerTr || innerBl > outerBl || innerBr > outerBr) {
return; // Some inner radius is overlapping some outer radius
Expand Down
21 changes: 21 additions & 0 deletions lib/web_ui/test/engine/recording_canvas_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,27 @@ void main() {
expect(mockCanvas.methodCallLog.length, equals(0));
});

test('negative corners in inner RRect get passed through to draw', () {
// This comes from github issue #40728
final RRect outer = RRect.fromRectAndCorners(const Rect.fromLTWH(0, 0, 88, 48),
topLeft: Radius.circular(6), bottomLeft: Radius.circular(6));
final RRect inner = outer.deflate(1);

// If these assertions fail, check [_measureBorderRadius] in recording_canvas.dart
expect(inner.brRadius, equals(Radius.circular(-1)));
expect(inner.trRadius, equals(Radius.circular(-1)));

underTest.drawDRRect(outer, inner, somePaint);
underTest.apply(mockCanvas);

// Expect to draw, even when inner has negative radii (which get ignored by canvas)
_expectDrawCall(mockCanvas, {
'outer': outer,
'inner': inner,
'paint': somePaint.webOnlyPaintData,
});
});

test('preserve old scuba test behavior', () {
final RRect outer = RRect.fromRectAndCorners(const Rect.fromLTRB(10, 20, 30, 40));
final RRect inner = RRect.fromRectAndCorners(const Rect.fromLTRB(12, 22, 28, 38));
Expand Down