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

[canvaskit] Size the PictureRecorder when calling Scene.toImage #48142

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ class LayerScene implements ui.Scene {

@override
Future<ui.Image> toImage(int width, int height) {
final ui.Picture picture = layerTree.flatten();
final ui.Picture picture = layerTree.flatten(ui.Size(
width.toDouble(),
height.toDouble(),
));
return picture.toImage(width, height);
}

@override
ui.Image toImageSync(int width, int height) {
final ui.Picture picture = layerTree.flatten();
final ui.Picture picture = layerTree.flatten(ui.Size(
width.toDouble(),
height.toDouble(),
));
return picture.toImageSync(width, height);
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ class LayerTree {
/// Flattens the tree into a single [ui.Picture].
///
/// This picture does not contain any platform views.
ui.Picture flatten() {
ui.Picture flatten(ui.Size size) {
final CkPictureRecorder recorder = CkPictureRecorder();
final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest);
final CkCanvas canvas = recorder.beginRecording(ui.Offset.zero & size);
final PrerollContext prerollContext = PrerollContext(null, null);
rootLayer.preroll(prerollContext, Matrix4.identity());

Expand Down
57 changes: 43 additions & 14 deletions lib/web_ui/test/canvaskit/layer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,19 @@ void testMain() {

// 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.pushTransform(Float64List.fromList(
Matrix4.identity().storage..[15] = 2,
));

sb.addPicture(ui.Offset.zero, picture);
final LayerTree layerTree = sb.build().layerTree;
CanvasKitRenderer.instance.rasterizer.draw(layerTree);
final ClipRectEngineLayer clipRect = layerTree.rootLayer.debugLayers.single as ClipRectEngineLayer;
final ClipRectEngineLayer clipRect =
layerTree.rootLayer.debugLayers.single as ClipRectEngineLayer;
expect(clipRect.paintBounds, const ui.Rect.fromLTRB(15, 15, 30, 30));

final TransformEngineLayer transform = clipRect.debugLayers.single as TransformEngineLayer;
final TransformEngineLayer transform =
clipRect.debugLayers.single as TransformEngineLayer;
expect(transform.paintBounds, const ui.Rect.fromLTRB(0, 0, 30, 30));
});

Expand Down Expand Up @@ -74,29 +75,57 @@ void testMain() {
});

test('ImageFilter layer applies matrix in preroll', () async {
final CkPicture picture =
paintPicture(const ui.Rect.fromLTRB(0, 0, 100, 100), (CkCanvas canvas) {
final CkPicture picture = paintPicture(
const ui.Rect.fromLTRB(0, 0, 100, 100), (CkCanvas canvas) {
canvas.drawRect(const ui.Rect.fromLTRB(0, 0, 100, 100),
CkPaint()..style = ui.PaintingStyle.fill);
});

final LayerSceneBuilder sb = LayerSceneBuilder();
sb.pushImageFilter(
ui.ImageFilter.matrix(
(
Matrix4.identity()
..scale(0.5, 0.5)
..translate(20)
).toFloat64(),
(Matrix4.identity()
..scale(0.5, 0.5)
..translate(20))
.toFloat64(),
),
);
sb.addPicture(ui.Offset.zero, picture);

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

final ImageFilterEngineLayer imageFilterLayer = layerTree.rootLayer.debugLayers.single as ImageFilterEngineLayer;
expect(imageFilterLayer.paintBounds, const ui.Rect.fromLTRB(10, 0, 60, 50));
final ImageFilterEngineLayer imageFilterLayer =
layerTree.rootLayer.debugLayers.single as ImageFilterEngineLayer;
expect(
imageFilterLayer.paintBounds, const ui.Rect.fromLTRB(10, 0, 60, 50));
});

test('Opacity layer works correctly with Scene.toImage', () async {
// This is a regression test for https://github.com/flutter/flutter/issues/138009
final CkPicture picture = paintPicture(
const ui.Rect.fromLTRB(0, 0, 100, 100), (CkCanvas canvas) {
canvas.drawRect(const ui.Rect.fromLTRB(0, 0, 100, 100),
CkPaint()..style = ui.PaintingStyle.fill);
});

final LayerSceneBuilder sb = LayerSceneBuilder();
sb.pushTransform(Matrix4.identity().toFloat64());
sb.pushOpacity(97, offset: const ui.Offset(20, 20));
sb.addPicture(ui.Offset.zero, picture);

final LayerScene scene = sb.build();
final ui.Image testImage = await scene.toImage(200, 200);

final CkPictureRecorder recorder = CkPictureRecorder();
final CkCanvas canvas =
recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 200, 200));
canvas.drawImage(testImage as CkImage, ui.Offset.zero, CkPaint());
await matchPictureGolden(
'canvaskit_scene_toimage_opacity_layer.png',
recorder.endRecording(),
region: const ui.Rect.fromLTRB(0, 0, 200, 200),
);
});
});
}