From 8ccf169df62b7dd070aa06a70644848399b013dc Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Thu, 29 Jun 2023 23:34:13 +0000 Subject: [PATCH] Apply the transform of an image filter layer to paint bounds in the CanvasKit backend Fixes https://github.com/flutter/flutter/issues/128788 --- .../src/engine/canvaskit/color_filter.dart | 4 +++ .../src/engine/canvaskit/image_filter.dart | 10 +++++++ .../lib/src/engine/canvaskit/layer.dart | 12 +++++++++ lib/web_ui/test/canvaskit/layer_test.dart | 26 +++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart b/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart index bb40093ae73b6..8114aa3cedc01 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; +import 'package:ui/src/engine/vector_math.dart'; import 'package:ui/ui.dart' as ui; import '../color_filter.dart'; @@ -79,6 +80,9 @@ abstract class CkColorFilter implements CkManagedSkImageFilterConvertible { borrow(skImageFilter); skImageFilter.delete(); } + + @override + Matrix4 get transform => Matrix4.identity(); } /// A reusable identity transform matrix. diff --git a/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart b/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart index 46eda4f2ce4be..2032ccdd88670 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart @@ -23,6 +23,8 @@ typedef SkImageFilterBorrow = void Function(SkImageFilter); /// Currently implemented by [CkImageFilter] and [CkColorFilter]. abstract class CkManagedSkImageFilterConvertible implements ui.ImageFilter { void imageFilter(SkImageFilterBorrow borrow); + + Matrix4 get transform; } /// The CanvasKit implementation of [ui.ImageFilter]. @@ -40,6 +42,9 @@ abstract class CkImageFilter implements CkManagedSkImageFilterConvertible { required ui.FilterQuality filterQuality}) = _CkMatrixImageFilter; CkImageFilter._(); + + @override + Matrix4 get transform => Matrix4.identity(); } class CkColorFilterImageFilter extends CkImageFilter { @@ -149,6 +154,7 @@ class _CkMatrixImageFilter extends CkImageFilter { _CkMatrixImageFilter( {required Float64List matrix, required this.filterQuality}) : matrix = Float64List.fromList(matrix), + _transform = Matrix4.fromFloat32List(toMatrix32(matrix)), super._() { final SkImageFilter skImageFilter = canvasKit.ImageFilter.MakeMatrixTransform( toSkMatrixFromFloat64(matrix), @@ -160,6 +166,7 @@ class _CkMatrixImageFilter extends CkImageFilter { final Float64List matrix; final ui.FilterQuality filterQuality; + final Matrix4 _transform; late final UniqueRef _ref; @@ -183,4 +190,7 @@ class _CkMatrixImageFilter extends CkImageFilter { @override String toString() => 'ImageFilter.matrix($matrix, $filterQuality)'; + + @override + Matrix4 get transform => _transform; } diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer.dart b/lib/web_ui/lib/src/engine/canvaskit/layer.dart index b357465e7668c..314aa4870b43b 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer.dart @@ -7,6 +7,7 @@ import 'package:ui/ui.dart' as ui; import '../vector_math.dart'; import 'canvas.dart'; import 'embedded_views.dart'; +import 'image_filter.dart'; import 'n_way_canvas.dart'; import 'painting.dart'; import 'path.dart'; @@ -396,6 +397,17 @@ class ImageFilterEngineLayer extends ContainerLayer final ui.Offset _offset; final ui.ImageFilter _filter; + @override + void preroll(PrerollContext prerollContext, Matrix4 matrix) { + final Matrix4 transform = (_filter as CkManagedSkImageFilterConvertible).transform; + final Matrix4 childMatrix = matrix.multiplied(transform); + prerollContext.mutatorsStack.pushTransform(transform); + final ui.Rect childPaintBounds = + prerollChildren(prerollContext, childMatrix); + paintBounds = transform.transformRect(childPaintBounds); + prerollContext.mutatorsStack.pop(); + } + @override void paint(PaintContext paintContext) { assert(needsPainting); diff --git a/lib/web_ui/test/canvaskit/layer_test.dart b/lib/web_ui/test/canvaskit/layer_test.dart index 4dae7c63addb9..7ed4051b7c930 100644 --- a/lib/web_ui/test/canvaskit/layer_test.dart +++ b/lib/web_ui/test/canvaskit/layer_test.dart @@ -72,5 +72,31 @@ void testMain() { region: kDefaultRegion, ); }); + + test('ImageFilter layer applies matrix in preroll', () async { + 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(), + ), + ); + 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)); + }); }); }