diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer.dart b/lib/web_ui/lib/src/engine/canvaskit/layer.dart index b4d419a926548..010fc012786d3 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer.dart @@ -415,32 +415,34 @@ class ImageFilterEngineLayer extends ContainerLayer } else { convertible = _filter as CkManagedSkImageFilterConvertible; } - final ui.Rect childPaintBounds = + ui.Rect childPaintBounds = prerollChildren(prerollContext, childMatrix); - if (_filter is ui.ColorFilter) { - // If the filter is a ColorFilter, the extended paint bounds will be the - // entire screen, which is not what we want. - paintBounds = childPaintBounds; - } else { - convertible.withSkImageFilter((skFilter) { - paintBounds = rectFromSkIRect( - skFilter.getOutputBounds(toSkRect(childPaintBounds)), - ); - }); - } + childPaintBounds = childPaintBounds.translate(_offset.dx, _offset.dy); + if (_filter is ui.ColorFilter) { + // If the filter is a ColorFilter, the extended paint bounds will be the + // entire screen, which is not what we want. + paintBounds = childPaintBounds; + } else { + convertible.withSkImageFilter((skFilter) { + paintBounds = rectFromSkIRect( + skFilter.getOutputBounds(toSkRect(childPaintBounds)), + ); + }); + } prerollContext.mutatorsStack.pop(); } @override void paint(PaintContext paintContext) { assert(needsPainting); + final ui.Rect offsetPaintBounds = paintBounds.shift(-_offset); paintContext.internalNodesCanvas.save(); paintContext.internalNodesCanvas.translate(_offset.dx, _offset.dy); paintContext.internalNodesCanvas - .clipRect(paintBounds, ui.ClipOp.intersect, false); + .clipRect(offsetPaintBounds, ui.ClipOp.intersect, false); final CkPaint paint = CkPaint(); paint.imageFilter = _filter; - paintContext.internalNodesCanvas.saveLayer(paintBounds, paint); + paintContext.internalNodesCanvas.saveLayer(offsetPaintBounds, paint); paintChildren(paintContext); paintContext.internalNodesCanvas.restore(); paintContext.internalNodesCanvas.restore(); diff --git a/lib/web_ui/test/ui/scene_builder_test.dart b/lib/web_ui/test/ui/scene_builder_test.dart index fc77fd98c2d39..aeaef07ad0c5a 100644 --- a/lib/web_ui/test/ui/scene_builder_test.dart +++ b/lib/web_ui/test/ui/scene_builder_test.dart @@ -262,6 +262,31 @@ Future testMain() async { await matchGoldenFile('scene_builder_image_filter.png', region: region); }); + // Regression test for https://github.com/flutter/flutter/issues/154303 + test('image filter layer with offset', () async { + final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); + + sceneBuilder.pushClipRect(const ui.Rect.fromLTWH(100, 100, 100, 100)); + sceneBuilder.pushImageFilter( + ui.ImageFilter.blur( + sigmaX: 5.0, + sigmaY: 5.0, + ), + offset: const ui.Offset(100, 100), + ); + + sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { + canvas.drawCircle(const ui.Offset(50, 50), 25, + ui.Paint()..color = const ui.Color(0xFF00FF00)); + })); + + await renderScene(sceneBuilder.build()); + await matchGoldenFile( + 'scene_builder_image_filter_with_offset.png', + region: region, + ); + }); + test('color filter layer', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); const ui.ColorFilter sepia = ui.ColorFilter.matrix([