Skip to content

Commit

Permalink
[canvaskit] Fix incorrect clipping with Opacity scene layer (#55751)
Browse files Browse the repository at this point in the history
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: flutter/flutter#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
  • Loading branch information
harryterkelsen authored Oct 8, 2024
1 parent be2bc34 commit 2d0eac0
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 6 deletions.
8 changes: 2 additions & 6 deletions lib/web_ui/lib/src/engine/canvaskit/layer_visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
64 changes: 64 additions & 0 deletions lib/web_ui/test/ui/scene_builder_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -403,6 +404,69 @@ Future<void> 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);
});
});
}

Expand Down

0 comments on commit 2d0eac0

Please sign in to comment.