From 0ed39374dfd401e558c3b04b6ff4ec5eb07374ab Mon Sep 17 00:00:00 2001 From: Harry Terkelsen <1961493+harryterkelsen@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:49:09 -0700 Subject: [PATCH] [canvaskit] Fix incorrect clipping with Opacity scene layer (flutter/engine#55751) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes opacity layer incorrectly clipping its children when there are complex transforms applied to them. Since the opacity saveLayer only applies to children which actually draw, it doesn't need to have tight bounds anyways. Pre-requisite for re-enabling tests here: https://github.com/flutter/flutter/issues/110785 BEFORE: ![Screenshot 2024-10-08 at 11 00 14 AM](https://github.com/user-attachments/assets/cf4c6296-7730-4db6-96f6-e22f32e28d94) AFTER: ![Screenshot 2024-10-08 at 11 06 22 AM](https://github.com/user-attachments/assets/6a680e2c-23d0-41e2-9de5-1c9667a785ab) BEFORE: ![failing_opacity](https://github.com/user-attachments/assets/d8acf075-c92d-4f8a-aa1a-476f46f1c931) AFTER: ![working_opacity](https://github.com/user-attachments/assets/3e589deb-d8b6-4466-9e19-95daff870bfb) [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style --- .../src/engine/canvaskit/layer_visitor.dart | 8 +-- .../web_ui/test/ui/scene_builder_test.dart | 64 +++++++++++++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/layer_visitor.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/layer_visitor.dart index 629f6164596..68b08dc5fe1 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/layer_visitor.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/layer_visitor.dart @@ -329,9 +329,7 @@ class MeasureVisitor extends LayerVisitor { measuringCanvas.save(); measuringCanvas.translate(opacity.offset.dx, opacity.offset.dy); - final ui.Rect saveLayerBounds = opacity.paintBounds.shift(-opacity.offset); - - measuringCanvas.saveLayer(saveLayerBounds, paint); + measuringCanvas.saveLayer(ui.Rect.largest, paint); measureChildren(opacity); // Restore twice: once for the translate and once for the saveLayer. measuringCanvas.restore(); @@ -567,9 +565,7 @@ class PaintVisitor extends LayerVisitor { nWayCanvas.save(); nWayCanvas.translate(opacity.offset.dx, opacity.offset.dy); - final ui.Rect saveLayerBounds = opacity.paintBounds.shift(-opacity.offset); - - nWayCanvas.saveLayer(saveLayerBounds, paint); + nWayCanvas.saveLayer(ui.Rect.largest, paint); paintChildren(opacity); // Restore twice: once for the translate and once for the saveLayer. nWayCanvas.restore(); diff --git a/engine/src/flutter/lib/web_ui/test/ui/scene_builder_test.dart b/engine/src/flutter/lib/web_ui/test/ui/scene_builder_test.dart index 8199b1fd0b3..af17bf98eb3 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/scene_builder_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/scene_builder_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:math' as math; +import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; @@ -403,6 +404,69 @@ Future testMain() async { }, skip: isFirefox && isHtml); // https://github.com/flutter/flutter/issues/86623 + + test('opacity layer with transformed children', () async { + final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); + sceneBuilder.pushOffset(0, 0); + sceneBuilder.pushOpacity(128); + // Push some complex transforms + final Float64List transform1 = Float64List.fromList([ + 1.00, + 0.00, + 0.00, + 0.00, + 0.06, + 1.00, + -0.88, + 0.00, + -0.03, + 0.60, + 0.47, + -0.00, + -4.58, + 257.03, + 63.11, + 0.81, + ]); + final Float64List transform2 = Float64List.fromList([ + 1.00, + 0.00, + 0.00, + 0.00, + 0.07, + 0.90, + -0.94, + 0.00, + -0.02, + 0.75, + 0.33, + -0.00, + -3.28, + 309.29, + 45.20, + 0.86, + ]); + sceneBuilder + .pushTransform(Matrix4.identity().scaled(0.3, 0.3).toFloat64()); + sceneBuilder.pushTransform(transform1); + sceneBuilder.pushTransform(transform2); + + sceneBuilder.addPicture(const ui.Offset(20, 20), + drawPicture((ui.Canvas canvas) { + canvas.drawCircle(const ui.Offset(25, 75), 25, + ui.Paint()..color = const ui.Color(0xFFFF0000)); + })); + sceneBuilder.pop(); + sceneBuilder.pop(); + sceneBuilder.pop(); + sceneBuilder.pop(); + sceneBuilder.pop(); + await renderScene(sceneBuilder.build()); + + await matchGoldenFile( + 'scene_builder_opacity_layer_with_transformed_children.png', + region: region); + }); }); }