From 2f6c8a98650024c3349ff9a39ee3849b6fc3bfd1 Mon Sep 17 00:00:00 2001 From: Yegor Date: Tue, 8 Dec 2020 12:04:58 -0800 Subject: [PATCH] [canvaskit] fix TransformLayer.preroll (flutter/engine#22890) --- .../lib/src/engine/canvaskit/layer.dart | 43 ++------------- .../lib/web_ui/test/canvaskit/common.dart | 10 ++++ .../lib/web_ui/test/canvaskit/layer_test.dart | 55 +++++++++++++++++++ 3 files changed, 71 insertions(+), 37 deletions(-) create mode 100644 engine/src/flutter/lib/web_ui/test/canvaskit/layer_test.dart diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/layer.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/layer.dart index 0892a70a182..b9c116ebb50 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/layer.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/layer.dart @@ -71,6 +71,11 @@ class PaintContext { abstract class ContainerLayer extends Layer { final List _layers = []; + /// The list of child layers. + /// + /// Useful in tests. + List get debugLayers => _layers; + /// Register [child] as a child of this layer. void add(Layer child) { child.parent = this; @@ -294,46 +299,10 @@ class TransformLayer extends ContainerLayer final Matrix4 childMatrix = matrix * _transform; context.mutatorsStack.pushTransform(_transform); final ui.Rect childPaintBounds = prerollChildren(context, childMatrix); - paintBounds = _transformRect(_transform, childPaintBounds); + paintBounds = transformRect(_transform, childPaintBounds); context.mutatorsStack.pop(); } - /// Applies the given matrix as a perspective transform to the given point. - /// - /// This function assumes the given point has a z-coordinate of 0.0. The - /// z-coordinate of the result is ignored. - static ui.Offset _transformPoint(Matrix4 transform, ui.Offset point) { - final Vector3 position3 = Vector3(point.dx, point.dy, 0.0); - final Vector3 transformed3 = transform.perspectiveTransform(position3); - return ui.Offset(transformed3.x, transformed3.y); - } - - /// Returns a rect that bounds the result of applying the given matrix as a - /// perspective transform to the given rect. - /// - /// This function assumes the given rect is in the plane with z equals 0.0. - /// The transformed rect is then projected back into the plane with z equals - /// 0.0 before computing its bounding rect. - static ui.Rect _transformRect(Matrix4 transform, ui.Rect rect) { - final ui.Offset point1 = _transformPoint(transform, rect.topLeft); - final ui.Offset point2 = _transformPoint(transform, rect.topRight); - final ui.Offset point3 = _transformPoint(transform, rect.bottomLeft); - final ui.Offset point4 = _transformPoint(transform, rect.bottomRight); - return ui.Rect.fromLTRB( - _min4(point1.dx, point2.dx, point3.dx, point4.dx), - _min4(point1.dy, point2.dy, point3.dy, point4.dy), - _max4(point1.dx, point2.dx, point3.dx, point4.dx), - _max4(point1.dy, point2.dy, point3.dy, point4.dy)); - } - - static double _min4(double a, double b, double c, double d) { - return math.min(a, math.min(b, math.min(c, d))); - } - - static double _max4(double a, double b, double c, double d) { - return math.max(a, math.max(b, math.max(c, d))); - } - @override void paint(PaintContext paintContext) { assert(needsPainting); diff --git a/engine/src/flutter/lib/web_ui/test/canvaskit/common.dart b/engine/src/flutter/lib/web_ui/test/canvaskit/common.dart index 1cff72b20cd..97a754779b3 100644 --- a/engine/src/flutter/lib/web_ui/test/canvaskit/common.dart +++ b/engine/src/flutter/lib/web_ui/test/canvaskit/common.dart @@ -45,6 +45,16 @@ void setUpCanvasKitTest() { }); } +/// Utility function for CanvasKit tests to draw pictures without +/// the [CkPictureRecorder] boilerplate. +CkPicture paintPicture( + ui.Rect cullRect, void Function(CkCanvas canvas) painter) { + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(cullRect); + painter(canvas); + return recorder.endRecording(); +} + class _TestFinalizerRegistration { _TestFinalizerRegistration(this.wrapper, this.deletable, this.stackTrace); diff --git a/engine/src/flutter/lib/web_ui/test/canvaskit/layer_test.dart b/engine/src/flutter/lib/web_ui/test/canvaskit/layer_test.dart new file mode 100644 index 00000000000..f5499aab53c --- /dev/null +++ b/engine/src/flutter/lib/web_ui/test/canvaskit/layer_test.dart @@ -0,0 +1,55 @@ +// 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. + +// @dart = 2.12 +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('CanvasKit', () { + setUpCanvasKitTest(); + + // Regression test for https://github.com/flutter/flutter/issues/63715 + test('TransformLayer prerolls correctly', () async { + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + + final CkPicture picture = + paintPicture(ui.Rect.fromLTRB(0, 0, 30, 30), (CkCanvas canvas) { + canvas.drawRect(ui.Rect.fromLTRB(0, 0, 30, 30), + CkPaint()..style = ui.PaintingStyle.fill); + }); + + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushClipRect(ui.Rect.fromLTRB(15, 15, 30, 30)); + + // Intentionally use a perspective transform, which triggered the + // https://github.com/flutter/flutter/issues/63715 bug. + sb.pushTransform( + Float64List.fromList(Matrix4.identity().storage + ..[15] = 2, + )); + + sb.addPicture(ui.Offset.zero, picture); + final LayerTree layerTree = sb.build().layerTree; + dispatcher.rasterizer!.draw(layerTree); + final ClipRectLayer clipRect = layerTree.rootLayer as ClipRectLayer; + expect(clipRect.paintBounds, ui.Rect.fromLTRB(15, 15, 30, 30)); + + final TransformLayer transform = clipRect.debugLayers.single as TransformLayer; + expect(transform.paintBounds, ui.Rect.fromLTRB(0, 0, 30, 30)); + }); + // TODO: https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +}