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

Commit ecf2298

Browse files
[canvaskit] Fix incorrect calculation of ImageFilter paint bounds (#54980)
Fixes calculation of `ImageFilter` bounds by taking into account the offset. Fixes flutter/flutter#154303 ## 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 015f3b1 commit ecf2298

File tree

2 files changed

+41
-14
lines changed

2 files changed

+41
-14
lines changed

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

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -415,32 +415,34 @@ class ImageFilterEngineLayer extends ContainerLayer
415415
} else {
416416
convertible = _filter as CkManagedSkImageFilterConvertible;
417417
}
418-
final ui.Rect childPaintBounds =
418+
ui.Rect childPaintBounds =
419419
prerollChildren(prerollContext, childMatrix);
420-
if (_filter is ui.ColorFilter) {
421-
// If the filter is a ColorFilter, the extended paint bounds will be the
422-
// entire screen, which is not what we want.
423-
paintBounds = childPaintBounds;
424-
} else {
425-
convertible.withSkImageFilter((skFilter) {
426-
paintBounds = rectFromSkIRect(
427-
skFilter.getOutputBounds(toSkRect(childPaintBounds)),
428-
);
429-
});
430-
}
420+
childPaintBounds = childPaintBounds.translate(_offset.dx, _offset.dy);
421+
if (_filter is ui.ColorFilter) {
422+
// If the filter is a ColorFilter, the extended paint bounds will be the
423+
// entire screen, which is not what we want.
424+
paintBounds = childPaintBounds;
425+
} else {
426+
convertible.withSkImageFilter((skFilter) {
427+
paintBounds = rectFromSkIRect(
428+
skFilter.getOutputBounds(toSkRect(childPaintBounds)),
429+
);
430+
});
431+
}
431432
prerollContext.mutatorsStack.pop();
432433
}
433434

434435
@override
435436
void paint(PaintContext paintContext) {
436437
assert(needsPainting);
438+
final ui.Rect offsetPaintBounds = paintBounds.shift(-_offset);
437439
paintContext.internalNodesCanvas.save();
438440
paintContext.internalNodesCanvas.translate(_offset.dx, _offset.dy);
439441
paintContext.internalNodesCanvas
440-
.clipRect(paintBounds, ui.ClipOp.intersect, false);
442+
.clipRect(offsetPaintBounds, ui.ClipOp.intersect, false);
441443
final CkPaint paint = CkPaint();
442444
paint.imageFilter = _filter;
443-
paintContext.internalNodesCanvas.saveLayer(paintBounds, paint);
445+
paintContext.internalNodesCanvas.saveLayer(offsetPaintBounds, paint);
444446
paintChildren(paintContext);
445447
paintContext.internalNodesCanvas.restore();
446448
paintContext.internalNodesCanvas.restore();

lib/web_ui/test/ui/scene_builder_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,31 @@ Future<void> testMain() async {
262262
await matchGoldenFile('scene_builder_image_filter.png', region: region);
263263
});
264264

265+
// Regression test for https://github.com/flutter/flutter/issues/154303
266+
test('image filter layer with offset', () async {
267+
final ui.SceneBuilder sceneBuilder = ui.SceneBuilder();
268+
269+
sceneBuilder.pushClipRect(const ui.Rect.fromLTWH(100, 100, 100, 100));
270+
sceneBuilder.pushImageFilter(
271+
ui.ImageFilter.blur(
272+
sigmaX: 5.0,
273+
sigmaY: 5.0,
274+
),
275+
offset: const ui.Offset(100, 100),
276+
);
277+
278+
sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) {
279+
canvas.drawCircle(const ui.Offset(50, 50), 25,
280+
ui.Paint()..color = const ui.Color(0xFF00FF00));
281+
}));
282+
283+
await renderScene(sceneBuilder.build());
284+
await matchGoldenFile(
285+
'scene_builder_image_filter_with_offset.png',
286+
region: region,
287+
);
288+
});
289+
265290
test('color filter layer', () async {
266291
final ui.SceneBuilder sceneBuilder = ui.SceneBuilder();
267292
const ui.ColorFilter sepia = ui.ColorFilter.matrix(<double>[

0 commit comments

Comments
 (0)