Take devicePixelRatio into account when drawing shadows (#13786)

* Take devicePixelRatio into account when drawing shadows

This fixes a bug where shadows were offset in CanvasKit

* Respond to review comments
This commit is contained in:
Harry Terkelsen 2019-11-11 18:20:08 -08:00 committed by GitHub
parent 63e8c6275e
commit e150bf3305
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 7 deletions

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -193,12 +193,17 @@ js.JsArray<double> 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', <dynamic>[
path._skPath,
js.JsArray<double>.from(<double>[0, 0, elevation]),
js.JsArray<double>.from(<double>[shadowX, shadowY, 600]),
800,
js.JsArray<double>.from(<double>[0, 0, devicePixelRatio * elevation]),
js.JsArray<double>.from(
<double>[shadowX, shadowY, devicePixelRatio * kLightHeight]),
devicePixelRatio * kLightRadius,
tonalColors['ambient'],
tonalColors['spot'],
flags,