Flutter Engine
physical_shape_layer.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "flutter/flow/layers/physical_shape_layer.h"
6 
7 #include "flutter/flow/paint_utils.h"
8 #include "third_party/skia/include/utils/SkShadowUtils.h"
9 
10 namespace flutter {
11 
12 const SkScalar kLightHeight = 600;
13 const SkScalar kLightRadius = 800;
14 
16  SkColor shadow_color,
17  float elevation,
18  const SkPath& path,
19  Clip clip_behavior)
20  : color_(color),
21  shadow_color_(shadow_color),
22  elevation_(elevation),
23  path_(path),
24  clip_behavior_(clip_behavior) {}
25 
27  const SkMatrix& matrix) {
28  TRACE_EVENT0("flutter", "PhysicalShapeLayer::Preroll");
31 
32  SkRect child_paint_bounds;
33  PrerollChildren(context, matrix, &child_paint_bounds);
34 
35  if (elevation_ == 0) {
36  set_paint_bounds(path_.getBounds());
37  } else {
38  // We will draw the shadow in Paint(), so add some margin to the paint
39  // bounds to leave space for the shadow. We fill this whole region and clip
40  // children to it so we don't need to join the child paint bounds.
41  set_paint_bounds(ComputeShadowBounds(path_.getBounds(), elevation_,
42  context->frame_device_pixel_ratio));
43  }
44 }
45 
47  TRACE_EVENT0("flutter", "PhysicalShapeLayer::Paint");
49 
50  if (elevation_ != 0) {
51  DrawShadow(context.leaf_nodes_canvas, path_, shadow_color_, elevation_,
52  SkColorGetA(color_) != 0xff, context.frame_device_pixel_ratio);
53  }
54 
55  // Call drawPath without clip if possible for better performance.
56  SkPaint paint;
57  paint.setColor(color_);
58  paint.setAntiAlias(true);
59  if (clip_behavior_ != Clip::antiAliasWithSaveLayer) {
60  context.leaf_nodes_canvas->drawPath(path_, paint);
61  }
62 
63  int saveCount = context.internal_nodes_canvas->save();
64  switch (clip_behavior_) {
65  case Clip::hardEdge:
66  context.internal_nodes_canvas->clipPath(path_, false);
67  break;
68  case Clip::antiAlias:
69  context.internal_nodes_canvas->clipPath(path_, true);
70  break;
72  context.internal_nodes_canvas->clipPath(path_, true);
73  context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr);
74  break;
75  case Clip::none:
76  break;
77  }
78 
79  if (UsesSaveLayer()) {
80  // If we want to avoid the bleeding edge artifact
81  // (https://github.com/flutter/flutter/issues/18057#issue-328003931)
82  // using saveLayer, we have to call drawPaint instead of drawPath as
83  // anti-aliased drawPath will always have such artifacts.
84  context.leaf_nodes_canvas->drawPaint(paint);
85  }
86 
87  PaintChildren(context);
88 
89  context.internal_nodes_canvas->restoreToCount(saveCount);
90 
91  if (UsesSaveLayer()) {
92  if (context.checkerboard_offscreen_layers) {
94  }
95  }
96 }
97 
98 SkRect PhysicalShapeLayer::ComputeShadowBounds(const SkRect& bounds,
99  float elevation,
100  float pixel_ratio) {
101  // The shadow offset is calculated as follows:
102  // .--- (kLightRadius)
103  // -------/ (light)
104  // | /
105  // | /
106  // |/
107  // |O
108  // /| (kLightHeight)
109  // / |
110  // / |
111  // / |
112  // / |
113  // ------------- (layer)
114  // /| |
115  // / | | (elevation)
116  // A / | |B
117  // ------------------------------------------------ (canvas)
118  // --- (extent of shadow)
119  //
120  // E = lt } t = (r + w/2)/h
121  // } =>
122  // r + w/2 = ht } E = (l/h)(r + w/2)
123  //
124  // Where: E = extent of shadow
125  // l = elevation of layer
126  // r = radius of the light source
127  // w = width of the layer
128  // h = light height
129  // t = tangent of AOB, i.e., multiplier for elevation to extent
130  // tangent for x
131  double tx =
132  (kLightRadius * pixel_ratio + bounds.width() * 0.5) / kLightHeight;
133  // tangent for y
134  double ty =
135  (kLightRadius * pixel_ratio + bounds.height() * 0.5) / kLightHeight;
136  SkRect shadow_bounds(bounds);
137  shadow_bounds.outset(elevation * tx, elevation * ty);
138 
139  return shadow_bounds;
140 }
141 
142 void PhysicalShapeLayer::DrawShadow(SkCanvas* canvas,
143  const SkPath& path,
144  SkColor color,
145  float elevation,
146  bool transparentOccluder,
147  SkScalar dpr) {
148  const SkScalar kAmbientAlpha = 0.039f;
149  const SkScalar kSpotAlpha = 0.25f;
150 
151  SkShadowFlags flags = transparentOccluder
152  ? SkShadowFlags::kTransparentOccluder_ShadowFlag
153  : SkShadowFlags::kNone_ShadowFlag;
154  const SkRect& bounds = path.getBounds();
155  SkScalar shadow_x = (bounds.left() + bounds.right()) / 2;
156  SkScalar shadow_y = bounds.top() - 600.0f;
157  SkColor inAmbient = SkColorSetA(color, kAmbientAlpha * SkColorGetA(color));
158  SkColor inSpot = SkColorSetA(color, kSpotAlpha * SkColorGetA(color));
159  SkColor ambientColor, spotColor;
160  SkShadowUtils::ComputeTonalColors(inAmbient, inSpot, &ambientColor,
161  &spotColor);
162  SkShadowUtils::DrawShadow(
163  canvas, path, SkPoint3::Make(0, 0, dpr * elevation),
164  SkPoint3::Make(shadow_x, shadow_y, dpr * kLightHeight),
165  dpr * kLightRadius, ambientColor, spotColor, flags);
166 }
167 
168 } // namespace flutter
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
const bool checkerboard_offscreen_layers
Definition: layer.h:123
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:75
#define FML_DCHECK(condition)
Definition: logging.h:86
static SkRect ComputeShadowBounds(const SkRect &bounds, float elevation, float pixel_ratio)
const SkScalar kLightHeight
void PrerollChildren(PrerollContext *context, const SkMatrix &child_matrix, SkRect *child_paint_bounds)
void Preroll(PrerollContext *context, const SkMatrix &matrix) override
const float frame_device_pixel_ratio
Definition: layer.h:124
bool needs_painting() const
Definition: layer.h:174
void Paint(PaintContext &context) const override
static AutoPrerollSaveLayerState Create(PrerollContext *preroll_context, bool save_layer_is_active=true, bool layer_itself_performs_readback=false)
Definition: layer.cc:43
const SkRect & paint_bounds() const
Definition: layer.h:166
const float frame_device_pixel_ratio
Definition: layer.h:57
SkCanvas * leaf_nodes_canvas
Definition: layer.h:116
void set_paint_bounds(const SkRect &paint_bounds)
Definition: layer.h:170
const SkScalar kLightRadius
static void DrawShadow(SkCanvas *canvas, const SkPath &path, SkColor color, float elevation, bool transparentOccluder, SkScalar dpr)
Clip
Definition: layer.h:41
SkCanvas * internal_nodes_canvas
Definition: layer.h:115
PhysicalShapeLayer(SkColor color, SkColor shadow_color, float elevation, const SkPath &path, Clip clip_behavior)
tonic::Float32List getBounds()
Definition: path.cc:284
void DrawCheckerboard(SkCanvas *canvas, SkColor c1, SkColor c2, int size)
Definition: paint_utils.cc:28
DEF_SWITCHES_START snapshot asset Path to the directory containing the four files specified by VmSnapshotInstructions and IsolateSnapshotInstructions vm snapshot The VM instructions snapshot that will be memory mapped as read and executable SnapshotAssetPath must be present isolate snapshot The isolate instructions snapshot that will be memory mapped as read and executable SnapshotAssetPath must be present icu symbol Prefix for the symbols representing ICU data linked into the Flutter library dart flags
Definition: switches.h:66
void PaintChildren(PaintContext &context) const