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

* Improve check to determine if a rrect is fully contained within another in RecordingCanvas.
* Add unit tests.

Fixes https://github.com/flutter/flutter/issues/37772 (and others)
This commit is contained in:
David Iglesias 2019-09-17 13:27:44 -07:00 committed by GitHub
parent 36107f11b8
commit 0d2e83cde2
2 changed files with 87 additions and 5 deletions

View File

@ -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.
@ -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 =

View File

@ -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));
});
});
}