soften shadows even more; fix shadow alpha (#18029)

* soften shadows even more; fix shadow alpha
This commit is contained in:
Yegor 2020-04-29 20:54:21 -07:00 committed by GitHub
parent 32a4cc5ed4
commit 590381a00a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 26 deletions

View File

@ -1,2 +1,2 @@
repository: https://github.com/flutter/goldens.git
revision: 263d816b73df47fca2fdc0fbce7ad09f17aa30b6
revision: f64d8957ae281d1558647f0591ff9742e6135385

View File

@ -598,7 +598,21 @@ class _CanvasPool extends _SaveStackTracking {
bool transparentOccluder) {
final SurfaceShadowData shadow = computeShadow(path.getBounds(), elevation);
if (shadow != null) {
// TODO(het): Shadows with transparent occluders are not supported
// On April 2020 Web canvas 2D did not support shadow color alpha. So
// instead we apply alpha separately using globalAlpha, then paint a
// solid shadow.
final ui.Color shadowColor = toShadowColor(color);
final double opacity = shadowColor.alpha / 255;
final String solidColor = colorComponentsToCssString(
shadowColor.red,
shadowColor.green,
shadowColor.blue,
255,
);
context.save();
context.globalAlpha = opacity;
// TODO(hterkelsen): Shadows with transparent occluders are not supported
// on webkit since filter is unsupported.
if (transparentOccluder && browserEngine != BrowserEngine.webkit) {
// We paint shadows using a path and a mask filter instead of the
@ -607,17 +621,17 @@ class _CanvasPool extends _SaveStackTracking {
// paint the shadow without the path itself, but if we use a non-zero
// alpha for the paint the path is painted in addition to the shadow,
// which is undesirable.
context.save();
context.translate(shadow.offset.dx, shadow.offset.dy);
context.filter = _maskFilterToCss(
ui.MaskFilter.blur(ui.BlurStyle.normal, shadow.blurWidth));
context.strokeStyle = '';
context.fillStyle = colorToCssString(color);
_runPath(context, path);
context.fill();
context.restore();
context.fillStyle = solidColor;
} else {
// TODO(het): We fill the path with this paint, then later we clip
// TODO(yjbanov): the following comment by hterkelsen makes sense, but
// somehow we lost the implementation described in it.
// Perhaps we should revisit this and actually do what
// the comment says.
// TODO(hterkelsen): We fill the path with this paint, then later we clip
// by the same path and fill it with a fully opaque color (we know
// the color is fully opaque because `transparentOccluder` is false.
// However, due to anti-aliasing of the clip, a few pixels of the
@ -625,23 +639,20 @@ class _CanvasPool extends _SaveStackTracking {
// the opaque occluder. For that reason, we fill with the shadow color,
// and set the shadow color to fully opaque. This way, the visible
// pixels are less opaque and less noticeable.
context.save();
context.filter = 'none';
context.strokeStyle = '';
final int red = color.red;
final int green = color.green;
final int blue = color.blue;
// Multiply by 0.4 to make shadows less aggressive (https://github.com/flutter/flutter/issues/52734)
final int alpha = (0.4 * color.alpha).round();
context.fillStyle = colorComponentsToCssString(red, green, blue, alpha);
context.fillStyle = solidColor;
context.shadowBlur = shadow.blurWidth;
context.shadowColor = colorToCssString(color.withAlpha(0xff));
context.shadowColor = solidColor;
context.shadowOffsetX = shadow.offset.dx;
context.shadowOffsetY = shadow.offset.dy;
_runPath(context, path);
context.fill();
context.restore();
}
_runPath(context, path);
context.fill();
// This also resets globalAlpha and shadow attributes. See:
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save#Drawing_state
context.restore();
}
}

View File

@ -133,9 +133,24 @@ void applyCssShadow(
if (shadow == null) {
element.style.boxShadow = 'none';
} else {
// Multiply by 0.4 to make shadows less aggressive (https://github.com/flutter/flutter/issues/52734)
final double alpha = 0.4 * color.alpha / 255;
color = toShadowColor(color);
element.style.boxShadow = '${shadow.offset.dx}px ${shadow.offset.dy}px '
'${shadow.blurWidth}px 0px rgba(${color.red}, ${color.green}, ${color.blue}, $alpha)';
'${shadow.blurWidth}px 0px rgba(${color.red}, ${color.green}, ${color.blue}, ${color.alpha / 255})';
}
}
/// Converts a shadow color specified by the framework to the color that should
/// actually be applied when rendering the shadow.
///
/// Flutter shadows look softer than the color specified by the developer. For
/// example, it is common to get a solid black for a shadow and see a very soft
/// shadow. This function softens the color by reducing its alpha by a constant
/// factor.
ui.Color toShadowColor(ui.Color color) {
// Reduce alpha to make shadows less aggressive:
//
// - https://github.com/flutter/flutter/issues/52734
// - https://github.com/flutter/gallery/issues/118
final int reducedAlpha = (0.3 * color.alpha).round();
return ui.Color((reducedAlpha & 0xff) << 24 | (color.value & 0x00ffffff));
}

View File

@ -69,7 +69,7 @@ void main() async {
builder.pop(); // offset
}
void _paintBitmapCanvasShadow(double elevation, Offset offset) {
void _paintBitmapCanvasShadow(double elevation, Offset offset, bool transparentOccluder) {
final SurfacePath path = SurfacePath()
..addRect(const Rect.fromLTRB(0, 0, 20, 20));
builder.pushOffset(offset.dx, offset.dy);
@ -82,7 +82,7 @@ void main() async {
path,
_kShadowColor,
elevation,
false,
transparentOccluder,
);
builder.addPicture(Offset.zero, recorder.endRecording());
_paintShapeOutline();
@ -136,12 +136,16 @@ void main() async {
}
for (int i = 0; i < 10; i++) {
_paintBitmapCanvasShadow(i.toDouble(), Offset(50.0 * i, 60));
_paintBitmapCanvasShadow(i.toDouble(), Offset(50.0 * i, 60), false);
}
for (int i = 0; i < 10; i++) {
_paintBitmapCanvasShadow(i.toDouble(), Offset(50.0 * i, 120), true);
}
for (int i = 0; i < 10; i++) {
_paintBitmapCanvasComplexPathShadow(
i.toDouble(), Offset(50.0 * i, 120));
i.toDouble(), Offset(50.0 * i, 180));
}
builder.pop();