diff --git a/lib/web_ui/lib/src/engine/compositor/canvas.dart b/lib/web_ui/lib/src/engine/compositor/canvas.dart index b224faa0ad4..81109c047e9 100644 --- a/lib/web_ui/lib/src/engine/compositor/canvas.dart +++ b/lib/web_ui/lib/src/engine/compositor/canvas.dart @@ -108,6 +108,7 @@ class SkCanvas { void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { - drawSkShadow(skCanvas, path, color, elevation, transparentOccluder); + drawSkShadow(skCanvas, path, color, elevation, transparentOccluder, + ui.window.devicePixelRatio); } } diff --git a/lib/web_ui/lib/src/engine/compositor/layer.dart b/lib/web_ui/lib/src/engine/compositor/layer.dart index e952d090b10..f50ae53f9ef 100644 --- a/lib/web_ui/lib/src/engine/compositor/layer.dart +++ b/lib/web_ui/lib/src/engine/compositor/layer.dart @@ -341,8 +341,60 @@ class PhysicalShapeLayer extends ContainerLayer @override void preroll(PrerollContext prerollContext, Matrix4 matrix) { prerollChildren(prerollContext, matrix); - paintBounds = - ElevationShadow.computeShadowRect(_path.getBounds(), _elevation); + + paintBounds = _path.getBounds(); + if (_elevation == 0.0) { + // No need to extend the paint bounds if there is no shadow. + return; + } else { + // Add some margin to the paint bounds to leave space for the shadow. + // We fill this whole region and clip children to it so we don't need to + // join the child paint bounds. + // The offset is calculated as follows: + + // .--- (kLightRadius) + // -------/ (light) + // | / + // | / + // |/ + // |O + // /| (kLightHeight) + // / | + // / | + // / | + // / | + // ------------- (layer) + // /| | + // / | | (elevation) + // A / | |B + // ------------------------------------------------ (canvas) + // --- (extent of shadow) + // + // E = lt } t = (r + w/2)/h + // } => + // r + w/2 = ht } E = (l/h)(r + w/2) + // + // Where: E = extent of shadow + // l = elevation of layer + // r = radius of the light source + // w = width of the layer + // h = light height + // t = tangent of AOB, i.e., multiplier for elevation to extent + final double devicePixelRatio = ui.window.devicePixelRatio; + + final double radius = kLightRadius * devicePixelRatio; + // tangent for x + double tx = (radius + paintBounds.width * 0.5) / kLightHeight; + // tangent for y + double ty = (radius + paintBounds.height * 0.5) / kLightHeight; + + paintBounds = ui.Rect.fromLTRB( + paintBounds.left - tx, + paintBounds.top - ty, + paintBounds.right + tx, + paintBounds.bottom + ty, + ); + } } @override diff --git a/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart b/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart index e6ab693615f..6cb4b33ef69 100644 --- a/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart +++ b/lib/web_ui/lib/src/engine/compositor/recording_canvas.dart @@ -228,7 +228,8 @@ class SkRecordingCanvas implements RecordingCanvas { @override void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { - drawSkShadow(skCanvas, path, color, elevation, transparentOccluder); + drawSkShadow(skCanvas, path, color, elevation, transparentOccluder, + ui.window.devicePixelRatio); } @override diff --git a/lib/web_ui/lib/src/engine/compositor/util.dart b/lib/web_ui/lib/src/engine/compositor/util.dart index d97ddfc9652..6575c73692e 100644 --- a/lib/web_ui/lib/src/engine/compositor/util.dart +++ b/lib/web_ui/lib/src/engine/compositor/util.dart @@ -193,12 +193,17 @@ js.JsArray makeSkMatrix(Float64List matrix4) { return skMatrix; } +// These must be kept in sync with `flow/layers/physical_shape_layer.cc`. +const double kLightHeight = 600.0; +const double kLightRadius = 800.0; + void drawSkShadow( js.JsObject skCanvas, SkPath path, ui.Color color, double elevation, bool transparentOccluder, + double devicePixelRatio, ) { const double ambientAlpha = 0.039; const double spotAlpha = 0.25; @@ -222,9 +227,10 @@ void drawSkShadow( skCanvas.callMethod('drawShadow', [ path._skPath, - js.JsArray.from([0, 0, elevation]), - js.JsArray.from([shadowX, shadowY, 600]), - 800, + js.JsArray.from([0, 0, devicePixelRatio * elevation]), + js.JsArray.from( + [shadowX, shadowY, devicePixelRatio * kLightHeight]), + devicePixelRatio * kLightRadius, tonalColors['ambient'], tonalColors['spot'], flags,