From 86642a856cb8ead0b50c4b16eaf641d4e6dafc6a Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 4 Jul 2024 02:22:56 +0800 Subject: [PATCH] fix: mask disappeared when having nested mask filter on Flutter web HTML (flutter/engine#45166) Hi from [Dora team](https://www.dora.run/), which powers web developers to build their 3d websites in just a few seconds. This PR fixes: https://github.com/flutter/flutter/issues/133443, related: https://github.com/flutter/flutter/issues/58546 The original codes attempts to cache the css string but it causes bugs when encountering nested the same mask filter blur. We should also set `filter` properties when currentFilter == incoming mask filter using the cached css string, not just ignore it. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style --- .../web_ui/lib/src/engine/canvas_pool.dart | 28 ++++++++-------- .../canvas_mask_filter_golden_test.dart | 32 +++++++++++++++++++ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvas_pool.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvas_pool.dart index 2fb4ad478b1..4fbc1eecf21 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvas_pool.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvas_pool.dart @@ -934,6 +934,7 @@ class ContextStateHandle { } ui.MaskFilter? _currentFilter; + String? _currentFilterCss; SurfacePaintData? _lastUsedPaint; /// Currently active shader bounds. @@ -1003,19 +1004,20 @@ class ContextStateHandle { } final ui.MaskFilter? maskFilter = paint.maskFilter; - if (!_renderMaskFilterForWebkit) { - if (_currentFilter != maskFilter) { - _currentFilter = maskFilter; - context.filter = maskFilterToCanvasFilter(maskFilter); - } - } else { - // WebKit does not support the `filter` property. Instead we apply a - // shadow to the shape of the same color as the paint and the same blur - // as the mask filter. - // - // Note that on WebKit the cached value of _currentFilter is not useful. - // Instead we destructure it into the shadow properties and cache those. - if (maskFilter != null) { + if (maskFilter != null) { + if (!_renderMaskFilterForWebkit) { + if (_currentFilter != maskFilter) { + _currentFilter = maskFilter; + _currentFilterCss = maskFilterToCanvasFilter(maskFilter); + } + context.filter = _currentFilterCss; + } else { + // WebKit does not support the `filter` property. Instead we apply a + // shadow to the shape of the same color as the paint and the same blur + // as the mask filter. + // + // Note that on WebKit the cached value of _currentFilter is not useful. + // Instead we destructure it into the shadow properties and cache those. context.save(); context.shadowBlur = convertSigmaToRadius(maskFilter.webOnlySigma); // Shadow color must be fully opaque. diff --git a/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart index 840bb0c2333..e38aa597268 100644 --- a/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart +++ b/engine/src/flutter/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart @@ -150,6 +150,38 @@ Future testMain() async { await canvasScreenshot(rc, 'mask_filter_transformed_$browser', region: screenRect); }); + + test('multiple MaskFilter.blur in $browser', () async { + const double screenWidth = 300.0; + const double screenHeight = 300.0; + const ui.Rect screenRect = + ui.Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + ContextStateHandle.debugEmulateWebKitMaskFilter = isWebkit; + final RecordingCanvas rc = RecordingCanvas(screenRect); + + final SurfacePaint paint = SurfacePaint() + ..maskFilter = const ui.MaskFilter.blur(ui.BlurStyle.normal, 5); + rc.save(); + rc.drawCircle(const ui.Offset(150, 150), 100, + paint..color = const ui.Color(0xFFC8C800)); + rc.restore(); + rc.save(); + rc.drawCircle(const ui.Offset(150, 150), 50, + paint..color = const ui.Color(0xFFC800C8)); + rc.restore(); + rc.save(); + rc.drawCircle( + const ui.Offset(150, 150), + 20, + paint + ..color = const ui.Color(0xFF00C8C8) + ..maskFilter = const ui.MaskFilter.blur(ui.BlurStyle.normal, 10)); + rc.restore(); + + await canvasScreenshot(rc, 'multiple_mask_filter_$browser', + region: screenRect); + }); } testMaskFilterBlur();