From 2b994d5d16e2fc1898719fb136b53bfc871e01ca Mon Sep 17 00:00:00 2001 From: Ferhat Date: Wed, 20 Jan 2021 09:29:03 -0800 Subject: [PATCH] [web] Fix shadow rendering using boxshadow due to webkit repaint area bug (#23769) --- lib/web_ui/dev/goldens_lock.yaml | 2 +- lib/web_ui/lib/src/engine/dom_canvas.dart | 26 ++++++++++-- .../engine/dom_mask_filter_test.dart | 42 +++++++++++++++++++ .../engine/recording_canvas_golden_test.dart | 2 +- 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 lib/web_ui/test/golden_tests/engine/dom_mask_filter_test.dart diff --git a/lib/web_ui/dev/goldens_lock.yaml b/lib/web_ui/dev/goldens_lock.yaml index f309c77a2..5dca76788 100644 --- a/lib/web_ui/dev/goldens_lock.yaml +++ b/lib/web_ui/dev/goldens_lock.yaml @@ -1,2 +1,2 @@ repository: https://github.com/flutter/goldens.git -revision: ee03ff97af36cbf9bd2627ef4e32f5a45676f96f +revision: 510c545ee4dd94f7d620cdc51a9027fdd8e521bc diff --git a/lib/web_ui/lib/src/engine/dom_canvas.dart b/lib/web_ui/lib/src/engine/dom_canvas.dart index 4fde6d2fc..ef090078b 100644 --- a/lib/web_ui/lib/src/engine/dom_canvas.dart +++ b/lib/web_ui/lib/src/engine/dom_canvas.dart @@ -135,6 +135,17 @@ class DomCanvas extends EngineCanvas with SaveElementStackTracking { } } +/// Converts a shadow color specified by the framework to the color that should +/// actually be applied when rendering the element. +/// +/// Returns a color for box-shadow based on blur filter at sigma. +ui.Color blurColor(ui.Color color, double sigma) { + final double strength = math.min( + math.sqrt(sigma) / (math.pi * 2.0), 1.0); + final int reducedAlpha = ((1.0 - strength) * color.alpha).round(); + return ui.Color((reducedAlpha & 0xff) << 24 | (color.value & 0x00ffffff)); +} + html.HtmlElement _buildDrawRectElement(ui.Rect rect, SurfacePaintData paint, String tagName, Matrix4 transform) { assert(paint.shader == null); @@ -175,11 +186,20 @@ html.HtmlElement _buildDrawRectElement(ui.Rect rect, SurfacePaintData paint, Str ..transformOrigin = '0 0 0' ..transform = effectiveTransform; - final String cssColor = - paint.color == null ? '#000000' : colorToCssString(paint.color)!; + String cssColor = + paint.color == null ? '#000000' : colorToCssString(paint.color)!; if (paint.maskFilter != null) { - style.filter = 'blur(${paint.maskFilter!.webOnlySigma}px)'; + final double sigma = paint.maskFilter!.webOnlySigma; + if (browserEngine == BrowserEngine.webkit && !isStroke) { + // A bug in webkit leaves artifacts when this element is animated + // with filter: blur, we use boxShadow instead. + style.boxShadow = '0px 0px ${sigma * 2.0}px $cssColor'; + cssColor = colorToCssString( + blurColor(paint.color ?? const ui.Color(0xFF000000), sigma))!; + } else { + style.filter = 'blur(${sigma}px)'; + } } if (isStroke) { diff --git a/lib/web_ui/test/golden_tests/engine/dom_mask_filter_test.dart b/lib/web_ui/test/golden_tests/engine/dom_mask_filter_test.dart new file mode 100644 index 000000000..cb158bd88 --- /dev/null +++ b/lib/web_ui/test/golden_tests/engine/dom_mask_filter_test.dart @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/ui.dart' hide TextStyle; +import 'package:ui/src/engine.dart'; +import 'screenshot.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() async { + const double screenWidth = 500.0; + const double screenHeight = 500.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + test('Should blur rectangles based on sigma.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + for (int blurSigma = 1; blurSigma < 10; blurSigma += 2) { + final Paint paint = Paint() + ..color = Color(0xFF2fdfd2) + ..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSigma.toDouble()); + rc.drawRect(Rect.fromLTWH(15.0, 15.0 + blurSigma * 40, 200, 20), paint); + } + await canvasScreenshot(rc, 'dom_mask_filter_blur', + region: screenRect, + maxDiffRatePercent: 0.01); + }); +} diff --git a/lib/web_ui/test/golden_tests/engine/recording_canvas_golden_test.dart b/lib/web_ui/test/golden_tests/engine/recording_canvas_golden_test.dart index cd279413c..954cca9f1 100644 --- a/lib/web_ui/test/golden_tests/engine/recording_canvas_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/recording_canvas_golden_test.dart @@ -679,7 +679,7 @@ void testMain() async { await matchGoldenFile( 'paint_spread_bounds.png', region: const Rect.fromLTRB(0, 0, 250, 600), - maxDiffRatePercent: 0.2, + maxDiffRatePercent: 0.21, pixelComparison: PixelComparison.precise, ); } finally { -- GitLab