Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Improve check to render (or not) a DRRect when inner falls outside of outer on RecordingCanvas #12229

Merged
merged 4 commits into from
Sep 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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: 27 additions & 5 deletions lib/web_ui/lib/src/engine/recording_canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ 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);

/// Records canvas commands to be applied to a [EngineCanvas].
///
/// See [Canvas] for docs for these methods.
Expand Down Expand Up @@ -228,12 +231,31 @@ class RecordingCanvas {
}

void drawDRRect(ui.RRect outer, ui.RRect inner, ui.Paint paint) {
// If inner rect is not contained inside outer, flutter engine skips
// painting rectangle.
if (!(outer.contains(ui.Offset(inner.left, inner.top)) &&
outer.contains(ui.Offset(inner.right, inner.bottom)))) {
return;
// Ensure inner is fully contained within outer, by comparing its
// defining points (including its border radius)
ui.Rect innerRect = inner.outerRect;
if (outer.outerRect.intersect(innerRect) != innerRect) {
return; // inner is not fully contained within outer
}

// Compare radius "length" of the rectangles that are going to be actually drawn
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 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);

if (innerTl >= outerTl || innerTr >= outerTr || innerBl >= outerBl || innerBr >= outerBr) {
return; // Some inner radius is overlapping some outer radius
}

_hasArbitraryPaint = true;
_didDraw = true;
final double strokeWidth =
Expand Down
60 changes: 60 additions & 0 deletions lib/web_ui/test/engine/recording_canvas_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:ui/ui.dart';
import 'package:ui/src/engine.dart';
import 'package:test/test.dart';

import '../mock_engine_canvas.dart';

void main() {

RecordingCanvas underTest;
MockEngineCanvas mockCanvas;

setUp(() {
underTest = RecordingCanvas(Rect.largest);
mockCanvas = MockEngineCanvas();
});

group('drawDRRect', () {
final RRect rrect = RRect.fromLTRBR(10, 10, 50, 50, Radius.circular(3));
final Paint somePaint = Paint()..color = const Color(0xFFFF0000);

test('Happy case', () {
underTest.drawDRRect(rrect, rrect.deflate(1), somePaint);
underTest.apply(mockCanvas);
// Expect drawDRRect to be called
expect(mockCanvas.methodCallLog.length, equals(1));
MockCanvasCall mockCall = mockCanvas.methodCallLog[0];
expect(mockCall.methodName, equals('drawDRRect'));
expect(mockCall.arguments, equals({
'outer': rrect,
'inner': rrect.deflate(1),
'paint': somePaint.webOnlyPaintData,
}));
});

test('Inner RRect > Outer RRect', () {
underTest.drawDRRect(rrect, rrect.inflate(1), somePaint);
underTest.apply(mockCanvas);
// Expect nothing to be called
expect(mockCanvas.methodCallLog.length, equals(0));
});

test('Inner RRect not completely inside Outer RRect', () {
underTest.drawDRRect(rrect, rrect.deflate(1).shift(const Offset(0.0, 10)), somePaint);
underTest.apply(mockCanvas);
// Expect nothing to be called
expect(mockCanvas.methodCallLog.length, equals(0));
});

test('Inner RRect same as Outer RRect', () {
underTest.drawDRRect(rrect, rrect, somePaint);
underTest.apply(mockCanvas);
// Expect nothing to be called
expect(mockCanvas.methodCallLog.length, equals(0));
});
});
}