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

Commit 79f960a

Browse files
[canvaskit] Size the PictureRecorder when calling Scene.toImage (#48142)
When calling `flatten()` to produce a `Picture` of a `Scene`, size the `PictureRecorder` to be the same size as the final image. Fixes flutter/flutter#138009 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I signed the [CLA]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent 5064aef commit 79f960a

File tree

3 files changed

+53
-18
lines changed

3 files changed

+53
-18
lines changed

lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,19 @@ class LayerScene implements ui.Scene {
2222

2323
@override
2424
Future<ui.Image> toImage(int width, int height) {
25-
final ui.Picture picture = layerTree.flatten();
25+
final ui.Picture picture = layerTree.flatten(ui.Size(
26+
width.toDouble(),
27+
height.toDouble(),
28+
));
2629
return picture.toImage(width, height);
2730
}
2831

2932
@override
3033
ui.Image toImageSync(int width, int height) {
31-
final ui.Picture picture = layerTree.flatten();
34+
final ui.Picture picture = layerTree.flatten(ui.Size(
35+
width.toDouble(),
36+
height.toDouble(),
37+
));
3238
return picture.toImageSync(width, height);
3339
}
3440
}

lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ class LayerTree {
6565
/// Flattens the tree into a single [ui.Picture].
6666
///
6767
/// This picture does not contain any platform views.
68-
ui.Picture flatten() {
68+
ui.Picture flatten(ui.Size size) {
6969
final CkPictureRecorder recorder = CkPictureRecorder();
70-
final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest);
70+
final CkCanvas canvas = recorder.beginRecording(ui.Offset.zero & size);
7171
final PrerollContext prerollContext = PrerollContext(null, null);
7272
rootLayer.preroll(prerollContext, Matrix4.identity());
7373

lib/web_ui/test/canvaskit/layer_test.dart

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,19 @@ void testMain() {
3232

3333
// Intentionally use a perspective transform, which triggered the
3434
// https://github.com/flutter/flutter/issues/63715 bug.
35-
sb.pushTransform(
36-
Float64List.fromList(Matrix4.identity().storage
37-
..[15] = 2,
35+
sb.pushTransform(Float64List.fromList(
36+
Matrix4.identity().storage..[15] = 2,
3837
));
3938

4039
sb.addPicture(ui.Offset.zero, picture);
4140
final LayerTree layerTree = sb.build().layerTree;
4241
CanvasKitRenderer.instance.rasterizer.draw(layerTree);
43-
final ClipRectEngineLayer clipRect = layerTree.rootLayer.debugLayers.single as ClipRectEngineLayer;
42+
final ClipRectEngineLayer clipRect =
43+
layerTree.rootLayer.debugLayers.single as ClipRectEngineLayer;
4444
expect(clipRect.paintBounds, const ui.Rect.fromLTRB(15, 15, 30, 30));
4545

46-
final TransformEngineLayer transform = clipRect.debugLayers.single as TransformEngineLayer;
46+
final TransformEngineLayer transform =
47+
clipRect.debugLayers.single as TransformEngineLayer;
4748
expect(transform.paintBounds, const ui.Rect.fromLTRB(0, 0, 30, 30));
4849
});
4950

@@ -74,29 +75,57 @@ void testMain() {
7475
});
7576

7677
test('ImageFilter layer applies matrix in preroll', () async {
77-
final CkPicture picture =
78-
paintPicture(const ui.Rect.fromLTRB(0, 0, 100, 100), (CkCanvas canvas) {
78+
final CkPicture picture = paintPicture(
79+
const ui.Rect.fromLTRB(0, 0, 100, 100), (CkCanvas canvas) {
7980
canvas.drawRect(const ui.Rect.fromLTRB(0, 0, 100, 100),
8081
CkPaint()..style = ui.PaintingStyle.fill);
8182
});
8283

8384
final LayerSceneBuilder sb = LayerSceneBuilder();
8485
sb.pushImageFilter(
8586
ui.ImageFilter.matrix(
86-
(
87-
Matrix4.identity()
88-
..scale(0.5, 0.5)
89-
..translate(20)
90-
).toFloat64(),
87+
(Matrix4.identity()
88+
..scale(0.5, 0.5)
89+
..translate(20))
90+
.toFloat64(),
9191
),
9292
);
9393
sb.addPicture(ui.Offset.zero, picture);
9494

9595
final LayerTree layerTree = sb.build().layerTree;
9696
CanvasKitRenderer.instance.rasterizer.draw(layerTree);
9797

98-
final ImageFilterEngineLayer imageFilterLayer = layerTree.rootLayer.debugLayers.single as ImageFilterEngineLayer;
99-
expect(imageFilterLayer.paintBounds, const ui.Rect.fromLTRB(10, 0, 60, 50));
98+
final ImageFilterEngineLayer imageFilterLayer =
99+
layerTree.rootLayer.debugLayers.single as ImageFilterEngineLayer;
100+
expect(
101+
imageFilterLayer.paintBounds, const ui.Rect.fromLTRB(10, 0, 60, 50));
102+
});
103+
104+
test('Opacity layer works correctly with Scene.toImage', () async {
105+
// This is a regression test for https://github.com/flutter/flutter/issues/138009
106+
final CkPicture picture = paintPicture(
107+
const ui.Rect.fromLTRB(0, 0, 100, 100), (CkCanvas canvas) {
108+
canvas.drawRect(const ui.Rect.fromLTRB(0, 0, 100, 100),
109+
CkPaint()..style = ui.PaintingStyle.fill);
110+
});
111+
112+
final LayerSceneBuilder sb = LayerSceneBuilder();
113+
sb.pushTransform(Matrix4.identity().toFloat64());
114+
sb.pushOpacity(97, offset: const ui.Offset(20, 20));
115+
sb.addPicture(ui.Offset.zero, picture);
116+
117+
final LayerScene scene = sb.build();
118+
final ui.Image testImage = await scene.toImage(200, 200);
119+
120+
final CkPictureRecorder recorder = CkPictureRecorder();
121+
final CkCanvas canvas =
122+
recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 200, 200));
123+
canvas.drawImage(testImage as CkImage, ui.Offset.zero, CkPaint());
124+
await matchPictureGolden(
125+
'canvaskit_scene_toimage_opacity_layer.png',
126+
recorder.endRecording(),
127+
region: const ui.Rect.fromLTRB(0, 0, 200, 200),
128+
);
100129
});
101130
});
102131
}

0 commit comments

Comments
 (0)