Flutter Impeller
shadow_path_geometry.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 
6 
10 
11 namespace {
12 
14 using impeller::Matrix;
16 using impeller::Point;
17 using impeller::Scalar;
21 using impeller::Trig;
22 using impeller::Vector2;
23 
24 /// Each point in the polygon form of the path is turned into a structure
25 /// that tracks the gradient of the shadow at that point in the path. The
26 /// shape is turned into a sort of pin cushion where each struct acts
27 /// like a pin pushed into that cushion in the direction of the shadow
28 /// gradient at that location.
29 ///
30 /// Each entry contains the direction of the pin at that location and the
31 /// depth to which the pin is inserted, expressed as a fraction of the full
32 /// umbra size indicated by the shadow parameters. A depth of 1.0 means
33 /// the pin was inserted all the way to the depth of the shadow gradient
34 /// and didn't collide with any other pins. A fraction less than 1.0 can
35 /// occur if either the shape was too small and the pins intersected with
36 /// other pins across the shape from them, or if the curvature in a given
37 /// area was so tight that adjacent pins started bumping into their neighbors
38 /// even if the overall size of the shape was larger than the shadow.
39 ///
40 /// Different pins will be shortened by different amounts in the same shape
41 /// depending on their local geometry (tight curves or narrow cross section).
42 struct UmbraPin {
43  /// An initial value for the pin fraction that indicates that we have
44  /// not yet visited this pin during the clipping process.
45  static constexpr Scalar kFractionUninitialized = -1.0f;
46 
47  /// The point on the original path that generated this entry into the
48  /// umbra geometry.
49  ///
50  /// AKA the point on the path at which this pin was stabbed.
51  Point path_vertex;
52 
53  /// The relative vector from this path segment to the next.
54  Vector2 path_delta;
55 
56  /// The vector from the path_vertex to the head of the pin (the part
57  /// outside the shape).
58  Vector2 penumbra_delta;
59 
60  /// The location of the end of this pin, taking into account the reduction
61  /// of the umbra_size due to minimum distance to centroid, but ignoring
62  /// clipping against other pins.
63  Point pin_tip;
64 
65  /// The location that this pin confers to the umbra polygon. Initially,
66  /// this is the same as the pin_tip, but can be reduced by intersecting
67  /// and clipping against other pins and even eliminated if the other
68  /// nearby pins make it redundant for defining the umbra polygon.
69  ///
70  /// Redundant or "removed" pins are indicated by no longer being a part
71  /// of the linked list formed by the |p_next| and |p_prev| pointers.
72  ///
73  /// Eventually, if this pin's umbra_vertex was eliminated, this location
74  /// will be overwritten by the surviving umbra vertex that best servies
75  /// this pin's path_vertex in a follow-on step.
76  Point umbra_vertex;
77 
78  /// The index in the vertices vector where the umbra_vertex is eventually
79  /// inserted. Used to enter triangles into the indices vector.
80  uint16_t umbra_index = 0u;
81 
82  /// The interior penetration of the umbra starts out at the full blur
83  /// radius as modified by the global distance of the path segments to
84  /// the centroid, but can be shortened when pins are too crowded and start
85  /// intersecting each other due to tight curvature.
86  ///
87  /// It's initial value is actually the uninitialized constant so that the
88  /// algorithm can treat it specially the first time it is encountered.
89  Scalar umbra_fraction = kFractionUninitialized;
90 
91  /// Pointers used to create a circular linked list while pruning the umbra
92  /// polygon. The final list of vertices that remain in the umbra polygon
93  /// are the vertices that remain on this linked list from a "head" pin.
94  UmbraPin* p_next = nullptr;
95  UmbraPin* p_prev = nullptr;
96 
97  /// Returns true after the umbra_fraction is first initialized to a real
98  /// value representing its potential intersections with other pins. At
99  /// that point it will be a number from 0 to 1.
100  bool IsFractionInitialized() const {
101  return umbra_fraction > kFractionUninitialized;
102  }
103 };
104 
105 /// Simple cross products of nearby vertices don't catch all cases of
106 /// non-convexity so we count the number of times that the sign of the
107 /// dx/dy of the edges change. It must be <= 3 times for the path to
108 /// be convex. Think of drawing a circle from the top. First you head
109 /// to the right, then reverse to the left as you round the bottom of
110 /// the circle, then back near the top you head to the right again,
111 /// totalling 3 changes in direction.
112 struct DirectionDetector {
113  Scalar last_direction_ = 0.0f;
114  size_t change_count = 0u;
115 
116  /// Check the coordinate delta for a new polygon edge to see if it
117  /// represents another change in direction for the path on this axis.
118  void AccumulateDirection(Scalar new_direction) {
119  if (last_direction_ == 0.0f || last_direction_ * new_direction < 0.0f) {
120  last_direction_ = std::copysign(1.0f, new_direction);
121  change_count++;
122  }
123  }
124 
125  /// Returns true if the path must be concave.
126  bool IsConcave() const {
127  // See comment above on the struct for why 3 changes is the most you
128  // should see in a convex path.
129  return change_count > 3u;
130  }
131 };
132 
133 /// Utility class to receive the vertices of a path and turn them into
134 /// a vector of UmbraPins along with a centroid Point.
135 ///
136 /// The class will immediately flag and stop processing any path that
137 /// has more than one contour since algorithms of the nature implemented
138 /// here won't be able to process such paths.
139 ///
140 /// The class will also flag and stop processing any path that has a
141 /// non-convex section because the current algorithm only works for convex
142 /// paths. Though it is possible to improve the algorithm to handle
143 /// concave single-contour paths in the future as the Skia utilities
144 /// provide a solution for those paths.
145 class UmbraPinAccumulator : public PathTessellator::VertexWriter {
146  public:
147  /// Parameters that determine the sub-pixel grid we will use to simplify
148  /// the contours to avoid degenerate differences in the vertices.
149  /// These 2 constants are a pair used in the implementation of the
150  /// ToDeviceGrid method and must be reciprocals of each other.
151  ///
152  /// @see ToPixelGrid
153  static constexpr Scalar kSubPixelCount = 16.0f;
154  static constexpr Scalar kSubPixelScale = (1.0f / kSubPixelCount);
155 
156  /// The classification status of the path after all of the points are
157  /// accumulated.
158  enum class PathStatus {
159  /// The path was empty either because it contained no points or
160  /// because they enclosed no area.
161  kEmpty,
162 
163  /// The path was complete, a single contour, and convex all around.
164  kConvex,
165 
166  /// The path violated one of the conditions of convexity. Either it
167  /// had points that turned different ways along its perimeter, or it
168  /// turned more than 360 degrees, or it self-intersected.
169  kNonConvex,
170 
171  /// The path had multiple contours.
172  kMultipleContours,
173  };
174 
175  explicit UmbraPinAccumulator(Vector2 device_scale);
176 
177  ~UmbraPinAccumulator() = default;
178 
179  /// Reserve enough pins for the indicated number of path vertices to
180  /// avoid having to grow the vector during processing.
181  void Reserve(size_t vertex_count) { pins_.reserve(vertex_count); }
182 
183  /// Return the status properties of the path.
184  /// see |PathStatus|
185  PathStatus GetStatus() { return GetResults().status; }
186 
187  /// Returns a reference to the accumulated vector of UmbraPin structs.
188  /// Only valid if the status is kConvex.
189  std::vector<UmbraPin>& GetPins() { return pins_; }
190 
191  /// Returns the centroid of the path.
192  /// Only valid if the status is kConvex.
193  Point GetCentroid() { return GetResults().centroid; }
194 
195  /// Returns the turning direction of the path.
196  /// Only valid if the status is kConvex.
197  Scalar GetDirection() { return GetResults().path_direction; }
198 
199  private:
200  /// The data computed when completing (finalizing) the analysis of the
201  /// path.
202  struct PathResults {
203  /// The type of path determined during the final analysis.
204  PathStatus status;
205 
206  /// The centroid ("center of mass") of the path around which we will
207  /// build the shadow mesh.
208  Point centroid;
209 
210  /// The direction of the path as determined by cross products. This
211  /// value is important to further processing to know when pins are
212  /// intersecting each other as the calculations for that condition
213  /// depend on the direction of the path.
214  Scalar path_direction = 0.0f;
215  };
216 
217  // |VertexWriter|
218  void Write(Point point) override;
219 
220  // |VertexWriter|
221  void EndContour() override;
222 
223  /// Rounds the device coordinate to the sub-pixel grid.
224  Point ToDeviceGrid(Point point);
225 
226  const Point device_scale_;
227 
228  /// The list of pins being accumulated for further processing by the
229  /// mesh generation code.
230  std::vector<UmbraPin> pins_;
231 
232  /// Internal state variable used by the VertexWriter callbacks to know
233  /// if the path contained multiple contours. It is set to true when the
234  /// first contour is ended by a call to EndContour().
235  bool first_contour_ended_ = false;
236 
237  /// Internal state variable used by the VertexWriter callbacks to know
238  /// if the path contained multiple contours. It is set to true if additional
239  /// path points are delivered after the first contour is ended.
240  bool has_multiple_contours_ = false;
241 
242  /// The results of finalizing the analysis of the path, set only after
243  /// the final analysis method is run.
244  std::optional<PathResults> results_;
245 
246  /// Finalize the path analysis if necessary and return the structure with
247  /// the results of the analysis.
248  PathResults& GetResults() {
249  if (results_.has_value()) {
250  return results_.value();
251  }
252  return (results_ = FinalizePath()).value();
253  }
254 
255  /// Run through the accumulated, de-duplicated, de-collinearized points
256  /// and check for a convex, non-self-intersecting path.
257  PathResults FinalizePath();
258 };
259 
260 /// The |PolygonInfo| class does most of the work of generating a mesh from
261 /// a path, including transforming it into device space, computing new vertices
262 /// by applying the inset and outset for the indicated occluder_height, and
263 /// then stitching all of those vertices together into a mesh that can be
264 /// used to render the shadow complete with gaussian coefficients for the
265 /// location of the mesh points within the shadow.
266 class PolygonInfo {
267  public:
268  /// Return the radius of the rounded corners of the shadow for the
269  /// indicated occluder_height.
270  static constexpr Scalar GetTrigRadiusForHeight(Scalar occluder_height) {
271  return GetPenumbraSizeForHeight(occluder_height);
272  }
273 
274  /// Construct a PolygonInfo that will accept a path and compute a shadow
275  /// mesh at the indicated occluder_height.
276  explicit PolygonInfo(Scalar occluder_height);
277 
278  /// Computes a shadow mesh for the indicated path (source) under the
279  /// given matrix with the associated trigs. If the algorithm is successful,
280  /// it will return the resulting mesh (which may be empty if the path
281  /// contained no area) or nullptr if it was unable to process the path.
282  ///
283  /// @param source The PathSource object that delivers the path segments
284  /// that define the path being shadowed.
285  /// @param matrix The transform matrix under which the shadow is being
286  /// viewed.
287  /// @param trigs The Trigs array that contains precomputed sin and cos
288  /// values for a flattened arc at the required radius for
289  /// rounding out the edges of the shadow as we turn corners
290  /// in the path.
291  ///
292  /// @see GetTrigRadiusForHeight
293  const std::shared_ptr<ShadowVertices> CalculateConvexShadowMesh(
294  const impeller::PathSource& source,
295  const impeller::Matrix& matrix,
296  const Tessellator::Trigs& trigs);
297 
298  private:
299  /// Compute the size of the penumbra for a given occluder_height which
300  /// can vary depending on the type of shadow. Here we are only processing
301  /// ambient shadows.
302  static constexpr Scalar GetPenumbraSizeForHeight(Scalar occluder_height) {
303  return occluder_height;
304  }
305 
306  /// Compute the size of the umbra for a given occluder_height which
307  /// can vary depending on the type of shadow. Here we are only processing
308  /// ambient shadows.
309  static constexpr Scalar GetUmbraSizeForHeight(Scalar occluder_height) {
310  return occluder_height;
311  }
312 
313  /// The minimum distance (squared) between points on the mesh before we
314  /// eliminate them as redundant.
315  static constexpr Scalar kMinSubPixelDistanceSquared =
316  UmbraPinAccumulator::kSubPixelScale * UmbraPinAccumulator::kSubPixelScale;
317 
318  /// The occluder_height for which we are processing this shadow.
319  const Scalar occluder_height_;
320 
321  /// The maximum gaussian of the umbra part of the shadow, usually 1.0f
322  /// but can be reduced if the umbra size was clipped.
323  Scalar umbra_gaussian_ = 1.0f;
324 
325  /// The vertex mesh result that represents the shadow, to be rendered
326  /// using a modified indexed variant of DrawVertices that also adjusts
327  /// the alpha of the colors on a per-pixel basis by mapping their linear
328  /// gaussian coefficients into the associated gaussian integral values.
329 
330  /// vertices_ stores all of the points in the mesh.
331  std::vector<Point> vertices_;
332 
333  /// indices_ stores the indexes of the triangles in the mesh, in a
334  /// raw triangle format (i.e. not a triangle fan or strip).
335  std::vector<uint16_t> indices_;
336 
337  /// gaussians_ stores the gaussian values associated with each vertex
338  /// in the mesh, the values being 1:1 with the equivalent vertex in
339  /// the vertices_ vedtor.
340  std::vector<Scalar> gaussians_;
341 
342  /// Run through the pins and determine the closest pin to the centroid
343  /// and, in particular, adjust the umbra_gaussian value if the closest pin
344  /// is less than the required umbra distance.
345  void ComputePinDirectionsAndMinDistanceToCentroid(std::vector<UmbraPin>& pins,
346  const Point& centroid,
347  Scalar direction);
348 
349  /// The head and count for the list of UmbraPins that contribute to the
350  /// umbra vertex ring.
351  ///
352  /// The forward and backward pointers for the linked list are stored
353  /// in the UmbraPin struct as p_next, p_prev.
354  struct UmbraPinLinkedList {
355  UmbraPin* p_head_pin = nullptr;
356  size_t pin_count = 0u;
357 
358  bool IsNull() { return p_head_pin == nullptr; }
359  };
360 
361  /// Run through the pins and determine if they intersect each other
362  /// internally, whether they are completely obscured by other pins,
363  /// their new relative lengths if they defer to another pin at some
364  /// depth, and which remaining pins are part of the umbra polygon,
365  /// and then return the pointer to the first pin in the "umbra polygon".
366  UmbraPinLinkedList ResolveUmbraIntersections(std::vector<UmbraPin>& pins,
367  Scalar direction);
368 
369  /// Structure to store the result of computing the intersection between
370  /// 2 pins, pin0 and pin1, containing the point of intersection and the
371  /// relative fractions at which the 2 pins intersected (expressed as a
372  /// ratio of 0 to 1 where 0 represents intersecting at the path outline
373  /// and 1 represents intersecting at the tip of the pin where the umbra
374  /// is darkest.
375  struct PinIntersection {
376  // The Point of the intersection between the pins.
377  Point intersection;
378  // The fraction along pin0 of the intersection.
379  Scalar fraction0;
380  // The fraction along pin1 of the intersection
381  Scalar fraction1;
382  };
383 
384  /// Return the intersection between the 2 pins pin0 and pin1 if there
385  /// is an intersection, otherwise a nullopt to indicate that there is
386  /// no intersection.
387  static std::optional<PinIntersection> ComputeIntersection(UmbraPin& pin0,
388  UmbraPin& pin1);
389 
390  /// Constants used to resolve pin intersections, adopted from the Skia
391  /// version of the algorithm.
392  static constexpr Scalar kCrossTolerance = 1.0f / 2048.0f;
393  static constexpr Scalar kIntersectionTolerance = 1.0e-6f;
394 
395  /// Compute the squared length of a vector or a special out of bounds
396  /// value if the vector becomes infinite.
397  static constexpr Scalar FiniteVectorLengthSquared(Vector2 v) {
398  return !v.IsFinite() ? -1.0f : v.Dot(v);
399  }
400 
401  /// Determine if the numerator and denominator are outside of the
402  /// interval that makes sense for an umbra intersection.
403  ///
404  /// Note calculation borrowed from Skia's SkPathUtils.
405  static constexpr inline bool OutsideInterval(Scalar numer,
406  Scalar denom,
407  bool denom_positive) {
408  return (denom_positive && (numer < 0 || numer > denom)) ||
409  (!denom_positive && (numer > 0 || numer < denom));
410  }
411 
412  /// Remove the pin at p_pin from the linked list of pins when the caller
413  /// determines that it should not contribute to the final umbra polygon.
414  /// The pointer to the head pin at *p_head will also be adjusted if we've
415  /// eliminated the head pin itself and it will be additionally set to
416  /// nullptr if that was the last pin in the list.
417  ///
418  /// @param p_pin The pin to be eliminated from the list.
419  /// @param p_head The pointer to the head of the list which might also
420  /// need adjustment depending on which pin is removed.
421  static void RemovePin(UmbraPin* p_pin, UmbraPin** p_head);
422 
423  /// A helper method for resolving pin conflicts, adopted directly from the
424  /// associated Skia algorithm.
425  ///
426  /// Note calculation borrowed from Skia's SkPathUtils.
427  static int ComputeSide(const Point& p0, const Vector2& v, const Point& p);
428 
429  /// Run through the path calculating the outset vertices for the penumbra
430  /// and connecting them to the inset vertices of the umbra and then to
431  /// the centroid in a system of triangles with the appropriate alpha values
432  /// representing the intensity of the (non-gamma-adjusted) shadow at those
433  /// points. The resulting mesh should consist of 2 rings of triangles, an
434  /// inner ring connecting the centroid to the umbra polygon, and another
435  /// outer ring connecting vertices in the umbra polygon to vertices on the
436  /// outer edge of the penumbra.
437  ///
438  /// @param pins The list of pins, one for each edge of the polygon.
439  /// @param centroid The centroid ("center of mass") of the polygon.
440  /// @param list The linked list of the subset of pins that have
441  /// umbra vertices which appear in the umbra polygon.
442  /// @param trigs The vector of sin and cos for subdivided arcs that
443  /// can round the penumbra corner at each polygon corner.
444  /// @param direction The overall direction of the path as determined by
445  /// the consistent cross products of each edge turn.
446  void ComputeMesh(std::vector<UmbraPin>& pins,
447  const Point& centroid,
448  UmbraPinLinkedList& list,
449  const impeller::Tessellator::Trigs& trigs,
450  Scalar direction);
451 
452  /// After the umbra_vertices of the pins are accumulated and linked into a
453  /// ring using their p_prev/p_next pointers, compute the best surviving umbra
454  /// vertex for each pin and set its location and index into the UmbraPin.
455  ///
456  /// @param pins The list of pins, one for each edge of the polygon.
457  /// @param list The linked list of the subset of pins that have
458  /// umbra vertices which appear in the umbra polygon.
459  /// @param centroid The centroid ("center of mass") of the polygon.
460  void PopulateUmbraVertices(std::vector<UmbraPin>& pins,
461  UmbraPinLinkedList& list,
462  const Point centroid);
463 
464  /// Appends a fan of penumbra vertices centered on the path vertex of the
465  /// |p_curr_pin| starting from the absolute point |fan_start| and ending
466  /// at the absolute point |fan_end|, both of which should be equi-distant
467  /// from the path vertex. The index of the vertex at |fan_start| should
468  /// already be in the vector of vertices at an index given by |start_index|.
469  ///
470  /// @param p_curr_pin The pin at the corner around which the penumbra is
471  /// rotating.
472  /// @param fan_start The point on the penumbra where the fan starts.
473  /// @param fan_start The point on the penumbra where the fan ends.
474  /// @param start_index The index in the vector of vertices where the
475  /// fan_start vertex has already been inserted.
476  /// @param trigs The vector of sin and cos for subdivided arcs that
477  /// can round the penumbra corner at each polygon corner.
478  /// @param direction The overall direction of the path as determined by
479  /// the consistent cross products of each edge turn.
480  uint16_t AppendFan(const UmbraPin* p_curr_pin,
481  const Point& fan_start,
482  const Point& fan_end,
483  uint16_t start_index,
484  const impeller::Tessellator::Trigs& trigs,
485  Scalar direction);
486 
487  /// Append a vertex and its associated gaussian coefficient to the lists
488  /// of vertices and guassians and return their (shared) index.
489  uint16_t AppendVertex(const Point& vertex, Scalar gaussian);
490 
491  /// Append 3 indices to the indices vector to form a new triangle in the mesh.
492  void AddTriangle(uint16_t v0, uint16_t v1, uint16_t v2);
493 };
494 
495 PolygonInfo::PolygonInfo(Scalar occluder_height)
496  : occluder_height_(occluder_height) {}
497 
498 const std::shared_ptr<ShadowVertices> PolygonInfo::CalculateConvexShadowMesh(
499  const impeller::PathSource& source,
500  const Matrix& matrix,
501  const Tessellator::Trigs& trigs) {
502  // We are operating in "2D device space" with respect to scale and there
503  // is no need to involve translations or Z or perspective in the various
504  // calculations, so we extract the incoming matrix's 2D scales on X and Y
505  // and just use those measurements to guide our tessellation.
506  Vector2 scale_2d = matrix.GetBasisScaleXY();
507 
508  FML_DCHECK(scale_2d.x >= 0.0f && scale_2d.y >= 0.0f);
509  if (!(scale_2d.x * scale_2d.y > 0.0f)) {
510  // NaN should fall in here as well.
511  return ShadowVertices::kEmpty;
512  }
513 
514  UmbraPinAccumulator pin_accumulator(scale_2d);
515 
516  Scalar scale = std::max(scale_2d.x, scale_2d.y);
517  auto [point_count, contour_count] =
519  pin_accumulator.Reserve(point_count);
520 
521  PathTessellator::PathToFilledVertices(source, pin_accumulator, scale);
522 
523  switch (pin_accumulator.GetStatus()) {
524  case UmbraPinAccumulator::PathStatus::kEmpty:
525  return ShadowVertices::kEmpty;
526  case UmbraPinAccumulator::PathStatus::kNonConvex:
527  case UmbraPinAccumulator::PathStatus::kMultipleContours:
528  return nullptr;
529  case UmbraPinAccumulator::PathStatus::kConvex:
530  break;
531  }
532 
533  std::vector<UmbraPin>& pins = pin_accumulator.GetPins();
534  const Point& centroid = pin_accumulator.GetCentroid();
535  Scalar direction = pin_accumulator.GetDirection();
536 
537  ComputePinDirectionsAndMinDistanceToCentroid(pins, centroid, direction);
538 
539  UmbraPinLinkedList list = ResolveUmbraIntersections(pins, direction);
540  if (list.IsNull()) {
541  // Ideally the Resolve algorithm will always be able to create an
542  // inner loop of umbra vertices, but it is not perfect.
543  //
544  // The Skia algorithm from which this was taken tries to fake an
545  // umbra polygon that is 95% from the path polygon to the centroid,
546  // but that result does not resemble a proper shadow. If we run into
547  // this case a lot we should either beef up the ResolveIntersections
548  // algorithm or find a better approximation than "95% to the centroid".
549  return nullptr;
550  }
551 
552  ComputeMesh(pins, centroid, list, trigs, direction);
553 
554  // Finally, we were working in a device space scaled by scale_2d
555  // so we need to un-scale the outgoing vertices by the same amount.
556  Vector2 inverted_scale_2d = 1.0f / scale_2d;
557  for (Point& vertex : vertices_) {
558  vertex = vertex * inverted_scale_2d;
559  }
560 
561  return ShadowVertices::Make(std::move(vertices_), std::move(indices_),
562  std::move(gaussians_));
563 }
564 
565 UmbraPinAccumulator::UmbraPinAccumulator(Vector2 device_scale)
566  : device_scale_(device_scale * kSubPixelCount) {}
567 
568 // Enter a new point for the polygon approximation of the shape. Points are
569 // normalized to a device subpixel grid based on |kSubPixelCount|, duplicates
570 // at that sub-pixel grid are ignored, collinear points are reduced to just
571 // the endpoints, and the centroid is updated from the remaining non-duplicate
572 // grid points.
573 void UmbraPinAccumulator::Write(Point point) {
574  // This type of algorithm will never be able to handle multiple contours.
575  if (first_contour_ended_) {
576  has_multiple_contours_ = true;
577  return;
578  }
579  FML_DCHECK(!has_multiple_contours_);
580 
581  point = ToDeviceGrid(point);
582 
583  if (!pins_.empty()) {
584  // If this isn't the first point then we need to perform de-duplication
585  // and possibly convexity checking and centroid updates.
586  Point prev = pins_.back().path_vertex;
587 
588  // Adjusted points are rounded so == testing is OK here even for floating
589  // point coordinates.
590  if (point == prev) {
591  // Ignore this point as a duplicate
592  return;
593  }
594 
595  if (pins_.size() >= 2u) {
596  // A quick collinear check to avoid extra processing later.
597  Point prev_prev = pins_.end()[-2].path_vertex;
598  Vector2 v0 = prev - prev_prev;
599  Vector2 v1 = point - prev_prev;
600  Scalar cross = v0.Cross(v1);
601  if (cross == 0) {
602  // This point is on the same line as the line between the last
603  // 2 points, so skip the intermediate point. Points that are
604  // collinear only contribute to the edge of the shape the vector
605  // from the first to the last of them.
606  pins_.pop_back();
607  if (point == prev_prev) {
608  // Not only do we eliminate the previous point as collinear, but
609  // we also eliminate this point as a duplicate.
610  // This point would tend to be eliminated anyway because it would
611  // automatically be collinear with whatever the next point would
612  // be, but we just avoid inserting it anyway to reduce processing.
613  return;
614  }
615  }
616  }
617  }
618 
619  pins_.emplace_back(point);
620 }
621 
622 // Called at the end of every contour of which we hope there is only one.
623 // If we detect more than one contour then the shadow tessellation becomes
624 // invalid.
625 //
626 // Each contour will have exactly one point at the beginning and end which
627 // are duplicates. The extra repeat of the first point actually helped the
628 // centroid accumulation do its math for ever segment in the path, but
629 // going forward we don't need the extra pin in the shape so we verify that
630 // it is a duplicate and then we delete it.
631 void UmbraPinAccumulator::EndContour() {
632  // This type of algorithm will never be able to handle multiple contours.
633  if (first_contour_ended_) {
634  has_multiple_contours_ = true;
635  return;
636  }
637  FML_DCHECK(!has_multiple_contours_);
638 
639  // PathTessellator always ensures the path is closed back to the origin
640  // by an extra call to Write(Point).
641  FML_DCHECK(pins_.front().path_vertex == pins_.back().path_vertex);
642  pins_.pop_back();
643  first_contour_ended_ = true;
644 }
645 
646 // Adjust the device point to its nearest sub-pixel grid location.
647 Point UmbraPinAccumulator::ToDeviceGrid(Point point) {
648  return (point * device_scale_).Round() * kSubPixelScale;
649 }
650 
651 // This method assumes that the pins have been accumulated by the PathVertex
652 // methods which ensure that no adjacent points are identical or collinear.
653 // It returns a PathResults that contains all of the relevant information
654 // depending on the geometric state of the path itself (ignoring whether
655 // the rest of the shadow processing will succeed).
656 //
657 // It performs 4 functions:
658 // - Normalizes empty paths (either too few vertices, or no turning directin)
659 // to an empty pins vector.
660 // - Accumulates and sets the centroid of the path
661 // - Accumulates and sets the overall direction of the path as determined by
662 // the sign of the cross products which must all agree.
663 // - Checks for convexity, including:
664 // - The direction vector determined above.
665 // - The turning direction of every triplet of points.
666 // - The signs of the area accumulated using cross products.
667 // - The number of times that the path edges change sign in X or Y.
668 UmbraPinAccumulator::PathResults UmbraPinAccumulator::FinalizePath() {
669  FML_DCHECK(!results_.has_value());
670 
671  if (has_multiple_contours_) {
672  return {.status = PathStatus::kMultipleContours};
673  }
674 
675  if (pins_.size() < 3u) {
676  return {.status = PathStatus::kEmpty};
677  }
678 
679  DirectionDetector x_direction_detector;
680  DirectionDetector y_direction_detector;
681 
682  Point relative_centroid;
683  Scalar path_direction = 0.0f;
684  Scalar path_area = 0.0f;
685 
686  Point prev = pins_.back().path_vertex;
687  Point prev_prev = pins_.end()[-2].path_vertex;
688  Point first = pins_.front().path_vertex;
689  for (UmbraPin& pin : pins_) {
690  Point new_point = pin.path_vertex;
691 
692  // Check for going around more than once in the same direction.
693  {
694  Vector2 delta = new_point - prev;
695  x_direction_detector.AccumulateDirection(delta.x);
696  y_direction_detector.AccumulateDirection(delta.y);
697  if (x_direction_detector.IsConcave() ||
698  y_direction_detector.IsConcave()) {
699  return {.status = PathStatus::kNonConvex};
700  }
701  }
702 
703  // Check if the path is locally convex over the most recent 3 vertices.
704  if (path_direction != 0.0f) {
705  Vector2 v0 = prev - prev_prev;
706  Vector2 v1 = new_point - prev_prev;
707  Scalar cross = v0.Cross(v1);
708  // We should have eliminated adjacent collinear points in the first pass.
709  FML_DCHECK(cross != 0.0f);
710  if (cross * path_direction < 0.0f) {
711  return {.status = PathStatus::kNonConvex};
712  }
713  }
714 
715  // Check if the path is globally convex with respect to the first vertex.
716  {
717  Vector2 v0 = prev - first;
718  Vector2 v1 = new_point - first;
719  Scalar quad_area = v0.Cross(v1);
720  if (quad_area != 0) {
721  // convexity check for whole path which can detect if we turn more than
722  // 360 degrees and start going the other way wrt the start point, but
723  // does not detect if any pair of points are concave (checked above).
724  if (path_direction == 0) {
725  path_direction = std::copysign(1.0f, quad_area);
726  } else if (quad_area * path_direction < 0) {
727  return {.status = PathStatus::kNonConvex};
728  }
729 
730  relative_centroid += (v0 + v1) * quad_area;
731  path_area += quad_area;
732  }
733  }
734 
735  prev_prev = prev;
736  prev = new_point;
737  }
738 
739  if (path_direction == 0.0f) {
740  // We never changed direction, indicate emptiness.
741  return {.status = PathStatus::kEmpty};
742  }
743 
744  // We are computing the centroid using a weighted average of all of the
745  // centroids of the triangles in a tessellation of the polygon, in this
746  // case a triangle fan tessellation relative to the first point in the
747  // polygon. We could use any point, but since we had to compute the cross
748  // product above relative to the initial point in order to detect if the
749  // path turned more than once, we already have values available relative
750  // to that first point here.
751  //
752  // The centroid of each triangle is the 3-way average of the corners of
753  // that triangle. Since the triangles are all relative to the first point,
754  // one of those corners is (0, 0) in this relative triangle and so we can
755  // simply add up the x,y of the two relative points and divide by 3.0.
756  // Since all values in the sum are divided by 3.0, we can save that
757  // constant division until the end when we finalize the average computation.
758  //
759  // We also weight these centroids by the area of the triangle so that we
760  // adjust for the parts of the polygon that are represented more densely
761  // and the parts that span a larger part of its circumference. A simple
762  // average would bias the centroid towards parts of the polygon where the
763  // points are denser. If we are rendering a polygonal representation of
764  // a round rect with only one round corner, all of the many approximating
765  // segments of the flattened round corner would overwhelm the handful of
766  // other simple segments for the flat sides. A weighted average places the
767  // centroid back at the "center of mass" of the polygon.
768  //
769  // Luckily, the same cross product used above that helps us determine the
770  // turning and convexity of the polygon also provides us with the area of
771  // the parallelogram projected from the 3 points in the triangle. That
772  // area is exactly double the area of the triangle itself. We could divide
773  // by 2 here, but since we are also accumulating these cross product values
774  // for the final weighted division, the factors of 2 all cancel out.
775  //
776  // path_area is (2 * triangle area).
777  // relative_centroid is accumulating sum(3 * triangle centroid * quad area).
778  // path_area_ is accumulating sum(quad area).
779  //
780  // The final combined average weight factor will be (3 * sum(quad area)).
781  relative_centroid /= 3.0f * path_area;
782 
783  // The centroid accumulation was relative to the first point in the
784  // polygon so we make it absolute here.
785  return {
786  .status = PathStatus::kConvex,
787  .centroid = pins_[0].path_vertex + relative_centroid,
788  .path_direction = path_direction,
789  };
790 }
791 
792 void PolygonInfo::ComputePinDirectionsAndMinDistanceToCentroid(
793  std::vector<UmbraPin>& pins,
794  const Point& centroid,
795  Scalar direction) {
796  Scalar desired_umbra_size = GetUmbraSizeForHeight(occluder_height_);
797  Scalar min_umbra_squared = desired_umbra_size * desired_umbra_size;
798  FML_DCHECK(direction == 1.0f || direction == -1.0f);
799 
800  // For simplicity of iteration, we start with the last vertex as the
801  // "previous" pin and then iterate once over the vector of pins,
802  // performing these calculations on the path segment from the previous
803  // pin to the current pin. In the end, all pins and therefore all path
804  // segments are processed once even if we start with the last pin.
805 
806  // First pass, compute the smallest distance to the centroid.
807  UmbraPin* p_prev_pin = &pins.back();
808  for (UmbraPin& pin : pins) {
809  UmbraPin* p_curr_pin = &pin;
810 
811  // Accumulate (min) the distance from the centroid to "this" segment.
812  Scalar distance_squared = centroid.GetDistanceToSegmentSquared(
813  p_prev_pin->path_vertex, p_curr_pin->path_vertex);
814  min_umbra_squared = std::min(min_umbra_squared, distance_squared);
815 
816  p_prev_pin = p_curr_pin;
817  }
818 
819  static constexpr auto kTolerance = 1.0e-2f;
820  Scalar umbra_size = std::sqrt(min_umbra_squared);
821  if (umbra_size < desired_umbra_size + kTolerance) {
822  // if the umbra would collapse, we back off a bit on the inner blur and
823  // adjust the alpha
824  auto newInset = umbra_size - kTolerance;
825  auto ratio = 0.5f * (newInset / desired_umbra_size + 1);
826  FML_DCHECK(std::isfinite(ratio));
827 
828  umbra_gaussian_ = ratio;
829  umbra_size = newInset;
830  } else {
831  FML_DCHECK(umbra_gaussian_ == 1.0f);
832  }
833 
834  // Second pass, fill out the pin data with the final umbra size.
835  //
836  // We also link all of the pins into a circular linked list so they can be
837  // quickly eliminated in the method that resolves intersections of the pins.
838  Scalar penumbra_scale = -GetPenumbraSizeForHeight(occluder_height_);
839  p_prev_pin = &pins.back();
840  for (UmbraPin& pin : pins) {
841  UmbraPin* p_curr_pin = &pin;
842  p_curr_pin->p_prev = p_prev_pin;
843  p_prev_pin->p_next = p_curr_pin;
844 
845  // We compute the vector along the path segment from the previous
846  // path vertex to this one as well as the unit direction vector
847  // that points from that pin towards the center of the shape,
848  // perpendicular to that segment.
849  p_prev_pin->path_delta = p_curr_pin->path_vertex - p_prev_pin->path_vertex;
850  Vector2 pin_direction = p_prev_pin
851  ->path_delta //
852  .Normalize()
853  .PerpendicularRight() *
854  direction;
855 
856  p_prev_pin->penumbra_delta = pin_direction * penumbra_scale;
857  p_prev_pin->umbra_vertex = //
858  p_prev_pin->pin_tip =
859  p_prev_pin->path_vertex + pin_direction * umbra_size;
860 
861  p_prev_pin = p_curr_pin;
862  }
863 }
864 
865 // Compute the intersection 'p' between the two pins pin 0 and pin 1, if any.
866 // The intersection structure will contain the fractional distances along the
867 // pins of the intersection and the intersection point itself if there is an
868 // intersection.
869 //
870 // The intersection structure will be reset to empty otherwise.
871 //
872 // This method was converted nearly verbatim from the Skia source files
873 // SkShadowTessellator.cpp and SkPolyUtils.cpp, except for variable
874 // naming and differences in the methods on Point and Vertex2.
875 std::optional<PolygonInfo::PinIntersection> PolygonInfo::ComputeIntersection(
876  UmbraPin& pin0,
877  UmbraPin& pin1) {
878  Vector2 v0 = pin0.path_delta;
879  Vector2 v1 = pin1.path_delta;
880  Vector2 tip_delta = pin1.pin_tip - pin0.pin_tip;
881  Vector2 w = tip_delta;
882  Scalar denom = pin0.path_delta.Cross(pin1.path_delta);
883  bool denom_positive = (denom > 0);
884  Scalar numerator0, numerator1;
885 
886  if (ScalarNearlyZero(denom, kCrossTolerance)) {
887  // This code also exists in the Skia version of this method, but it is
888  // not clear that we can ever enter here. In particular, since points
889  // were normalized to a grid (1/16th of a pixel), de-duplicated, and
890  // collinear points eliminated, denom can never be 0. And since the
891  // denom value was computed from a cross product of non-normalized
892  // delta vectors, its magnitude must exceed 1/256 which is far greater
893  // than the tolerance value.
894  //
895  // Note that in the Skia code, this method lived in a general polygon
896  // module that was unaware that it was being fed de-duplicated vertices
897  // from the Shadow module, so this code might be possible to trigger
898  // for "unfiltered" polygons, but not the normalized polygons that our
899  // (and Skia's) shadow code uses.
900  //
901  // Though entering here seems unlikely, we include the code until we can
902  // perform more due diligence in vetting that this is truly dead code.
903 
904  // segments are parallel, but not collinear
905  if (!ScalarNearlyZero(tip_delta.Cross(pin0.path_delta), kCrossTolerance) ||
906  !ScalarNearlyZero(tip_delta.Cross(pin1.path_delta), kCrossTolerance)) {
907  return std::nullopt;
908  }
909 
910  // Check for zero-length segments
911  Scalar v0_length_squared = FiniteVectorLengthSquared(v0);
912  if (v0_length_squared <= 0.0f) {
913  // Both are zero-length
914  Scalar v1_length_squared = FiniteVectorLengthSquared(v1);
915  if (v1_length_squared <= 0.0f) {
916  // Check if they're the same point
917  if (w.IsFinite() && !w.IsZero()) {
918  return {{
919  .intersection = pin0.pin_tip,
920  .fraction0 = 0.0f,
921  .fraction1 = 0.0f,
922  }};
923  } else {
924  // Intersection is indeterminate
925  return std::nullopt;
926  }
927  }
928  // Otherwise project segment0's origin onto segment1
929  numerator1 = v1.Dot(-w);
930  denom = v1_length_squared;
931  if (OutsideInterval(numerator1, denom, true)) {
932  return std::nullopt;
933  }
934  numerator0 = 0;
935  } else {
936  // Project segment1's endpoints onto segment0
937  numerator0 = v0.Dot(w);
938  denom = v0_length_squared;
939  numerator1 = 0;
940  if (OutsideInterval(numerator0, denom, true)) {
941  // The first endpoint doesn't lie on segment0
942  // If segment1 is degenerate, then there's no collision
943  Scalar v1_length_squared = FiniteVectorLengthSquared(v1);
944  if (v1_length_squared <= 0.0f) {
945  return std::nullopt;
946  }
947 
948  // Otherwise try the other one
949  Scalar old_numerator0 = numerator0;
950  numerator0 = v0.Dot(w + v1);
951  numerator1 = denom;
952  if (OutsideInterval(numerator0, denom, true)) {
953  // it's possible that segment1's interval surrounds segment0
954  // this is false if params have the same signs, and in that case
955  // no collision
956  if (numerator0 * old_numerator0 > 0) {
957  return std::nullopt;
958  }
959  // otherwise project segment0's endpoint onto segment1 instead
960  numerator0 = 0;
961  numerator1 = v1.Dot(-w);
962  denom = v1_length_squared;
963  }
964  }
965  }
966  } else {
967  numerator0 = w.Cross(v1);
968  if (OutsideInterval(numerator0, denom, denom_positive)) {
969  return std::nullopt;
970  }
971  numerator1 = w.Cross(v0);
972  if (OutsideInterval(numerator1, denom, denom_positive)) {
973  return std::nullopt;
974  }
975  }
976 
977  Scalar fraction0 = numerator0 / denom;
978  Scalar fraction1 = numerator1 / denom;
979 
980  return {{
981  .intersection = pin0.pin_tip + v0 * fraction0,
982  .fraction0 = fraction0,
983  .fraction1 = fraction1,
984  }};
985 }
986 
987 void PolygonInfo::RemovePin(UmbraPin* p_pin, UmbraPin** p_head) {
988  UmbraPin* p_next = p_pin->p_next;
989  UmbraPin* p_prev = p_pin->p_prev;
990  p_prev->p_next = p_next;
991  p_next->p_prev = p_prev;
992  if (*p_head == p_pin) {
993  *p_head = (p_next == p_pin) ? nullptr : p_next;
994  }
995 }
996 
997 // Computes the relative direction for point p compared to segment defined
998 // by origin p0 and vector v. A positive value means the point is to the
999 // left of the segment, negative is to the right, 0 is collinear.
1000 int PolygonInfo::ComputeSide(const Point& p0,
1001  const Vector2& v,
1002  const Point& p) {
1003  Vector2 w = p - p0;
1004  Scalar cross = v.Cross(w);
1005  if (!impeller::ScalarNearlyZero(cross, kCrossTolerance)) {
1006  return ((cross > 0) ? 1 : -1);
1007  }
1008 
1009  return 0;
1010 }
1011 
1012 // This method was converted nearly verbatim from the Skia source files
1013 // SkShadowTessellator.cpp and SkPolyUtils.cpp, except for variable
1014 // naming and differences in the methods on Point and Vertex2.
1015 PolygonInfo::UmbraPinLinkedList PolygonInfo::ResolveUmbraIntersections(
1016  std::vector<UmbraPin>& pins,
1017  Scalar direction) {
1018  UmbraPin* p_head_pin = &pins.front();
1019  UmbraPin* p_curr_pin = p_head_pin;
1020  UmbraPin* p_prev_pin = p_curr_pin->p_prev;
1021  size_t umbra_vertex_count = pins.size();
1022 
1023  // we should check each edge against each other edge at most once
1024  size_t allowed_iterations = pins.size() * pins.size() + 1u;
1025 
1026  while (p_head_pin && p_prev_pin != p_curr_pin) {
1027  if (--allowed_iterations == 0) {
1028  return {};
1029  }
1030 
1031  std::optional<PinIntersection> intersection =
1032  ComputeIntersection(*p_prev_pin, *p_curr_pin);
1033  if (intersection.has_value()) {
1034  // If the new intersection is further back on previous inset from the
1035  // prior intersection...
1036  if (intersection->fraction0 < p_prev_pin->umbra_fraction) {
1037  // no point in considering this one again
1038  RemovePin(p_prev_pin, &p_head_pin);
1039  --umbra_vertex_count;
1040  // go back one segment
1041  p_prev_pin = p_prev_pin->p_prev;
1042  } else if (p_curr_pin->IsFractionInitialized() &&
1043  p_curr_pin->umbra_vertex.GetDistanceSquared(
1044  intersection->intersection) < kIntersectionTolerance) {
1045  // We've already considered this intersection and come to the same
1046  // result, we're done.
1047  break;
1048  } else {
1049  // Add intersection.
1050  p_curr_pin->umbra_vertex = intersection->intersection;
1051  p_curr_pin->umbra_fraction = intersection->fraction1;
1052 
1053  // go to next segment
1054  p_prev_pin = p_curr_pin;
1055  p_curr_pin = p_curr_pin->p_next;
1056  }
1057  } else {
1058  // if previous pin is to right side of the current pin...
1059  int side = direction * ComputeSide(p_curr_pin->pin_tip, //
1060  p_curr_pin->path_delta, //
1061  p_prev_pin->pin_tip);
1062  if (side < 0 &&
1063  side == direction * ComputeSide(p_curr_pin->pin_tip, //
1064  p_curr_pin->path_delta, //
1065  p_prev_pin->pin_tip +
1066  p_prev_pin->path_delta)) {
1067  // no point in considering this one again
1068  RemovePin(p_prev_pin, &p_head_pin);
1069  --umbra_vertex_count;
1070  // go back one segment
1071  p_prev_pin = p_prev_pin->p_prev;
1072  } else {
1073  // move to next segment
1074  RemovePin(p_curr_pin, &p_head_pin);
1075  --umbra_vertex_count;
1076  p_curr_pin = p_curr_pin->p_next;
1077  }
1078  }
1079  }
1080 
1081  if (!p_head_pin) {
1082  return {};
1083  }
1084 
1085  // Now remove any duplicates from the umbra polygon. The head pin is
1086  // automatically included as the first point of the umbra polygon.
1087  p_prev_pin = p_head_pin;
1088  p_curr_pin = p_head_pin->p_next;
1089  size_t umbra_vertices = 1u;
1090  while (p_curr_pin != p_head_pin) {
1091  if (p_prev_pin->umbra_vertex.GetDistanceSquared(p_curr_pin->umbra_vertex) <
1092  kMinSubPixelDistanceSquared) {
1093  RemovePin(p_curr_pin, &p_head_pin);
1094  p_curr_pin = p_curr_pin->p_next;
1095  } else {
1096  umbra_vertices++;
1097  p_prev_pin = p_curr_pin;
1098  p_curr_pin = p_curr_pin->p_next;
1099  }
1100  FML_DCHECK(p_curr_pin == p_prev_pin->p_next);
1101  FML_DCHECK(p_prev_pin == p_curr_pin->p_prev);
1102  }
1103 
1104  if (umbra_vertices < 3u) {
1105  return {};
1106  }
1107 
1108  return {p_head_pin, umbra_vertices};
1109 }
1110 
1111 // The mesh computed connects all of the points in two rings. The outermost
1112 // ring represents the point where the shadow disappears and those points
1113 // are associated with an alpha of 0. The umbra polygon represents the ring
1114 // where the shadow is its darkest, usually fully "opaque" (potentially
1115 // modulated by a non-opaque shadow color, but opaque with respect to the
1116 // shadow's varying intensity). The umbra polygon may not be fully "opaque"
1117 // with respect to the shadow cast by the shape if the shadows radius is
1118 // larger than the cross-section of the shape. If the umbra polygon is pulled
1119 // back from extending the shadow distance inward due to this phenomenon,
1120 // then the umbra_gaussian will be computed to be less than fully opaque.
1121 //
1122 // The mesh will connect the centroid to the umbra (inner) polygon at a
1123 // constant level as computed in umbra_gaussian, and then the umbra polygon
1124 // is connected to the nearest points on the penumbra (outer) polygon which
1125 // is seeded with points that are fully transparent (umbra level 0).
1126 //
1127 // This creates 2 rings of triangles that are interspersed in the vertices_
1128 // and connected into triangles using indices_ both to reuse the vertices
1129 // as best we can and also because we don't generate the vertices in any
1130 // kind of useful fan or strip format. The points are reused as such:
1131 //
1132 // - The centroid vertex will be used once for each pair of umbra vertices
1133 // to make triangles for the inner ring.
1134 // - Each umbra vertex will be used in both the inner and the outer rings.
1135 // In particular, in 2 of the inner ring triangles and in an arbitrary
1136 // number of the outer ring vertices (each outer ring vertex is connected
1137 // to the neariest inner ring vertex so the mapping is not predictable).
1138 // - Each outer ring vertex is used in at least 2 outer ring triangles, the
1139 // one that links to the vertex before it and the one that links to the
1140 // vertex following it, plus we insert extra vertices on the outer ring
1141 // to turn the corners beteween the projected segments.
1142 void PolygonInfo::ComputeMesh(std::vector<UmbraPin>& pins,
1143  const Point& centroid,
1144  UmbraPinLinkedList& list,
1145  const impeller::Tessellator::Trigs& trigs,
1146  Scalar direction) {
1147  // Centroid and umbra polygon...
1148  size_t vertex_count = list.pin_count + 1u;
1149  size_t triangle_count = list.pin_count;
1150 
1151  // Penumbra corners - likely many more fan vertices than estimated...
1152  size_t penumbra_count = pins.size() * 2; // 2 perp at each vertex.
1153  penumbra_count += trigs.size() * 4; // total 360 degrees of fans.
1154  vertex_count += penumbra_count;
1155  triangle_count += penumbra_count;
1156 
1157  vertices_.reserve(vertex_count);
1158  gaussians_.reserve(vertex_count);
1159  indices_.reserve(triangle_count * 3);
1160 
1161  // First we populate the umbra_vertex and umbra_index of each pin with its
1162  // nearest point on the umbra polygon (the linked list computed earlier).
1163  //
1164  // This step simplifies the following operations because we will always
1165  // know which umbra vertex each pin object is associated with and whether
1166  // we need to bridge between them as we progress through the pins, without
1167  // having to search through the linked list every time.
1168  //
1169  // This method will also fill in the inner part of the mesh that connects
1170  // the centroid to every vertex in the umbra polygon with triangles that
1171  // are all at the maximum umbra gaussian coefficient.
1172  PopulateUmbraVertices(pins, list, centroid);
1173 
1174  // We now run through the list of all pins and append points and triangles
1175  // to our internal vectors to cover the part of the mesh that extends
1176  // out from the umbra polygon to the outer penumbra points.
1177  //
1178  // Each pin assumes that the previous pin contributed some points to the
1179  // penumbra polygon that ended with the point that is perpendicular to
1180  // the side between that previous path vertex and its own path vertex.
1181  // This pin will then contribute any number of the following points to
1182  // the penumbra polygon:
1183  //
1184  // - If this pin uses a different umbra vertex than the previous pin
1185  // (common for simple large polygons that have no clipping of their
1186  // inner umbra points) then it inserts a bridging quad that connects
1187  // from the ending segment of the previous pin to the starting segment
1188  // of this pin. If both are based on the same umbra vertex then the
1189  // end of the previous pin is identical to the start of this one.
1190  // - Possibly a fan of extra vertices to round the corner from the
1191  // last segment added, which is perpendicular to the previous path
1192  // segment, to the final segmet of this pin, which will be perpendicular
1193  // to the following path segment.
1194  // - The last penumbra point added will be the penumbra point that is
1195  // perpendicular to the following segment, which prepares for the
1196  // initial conditions that the next pin will expect.
1197  const UmbraPin* p_prev_pin = &pins.back();
1198 
1199  // This point may be duplicated at the end of the path. We can try to
1200  // avoid adding it twice with some bookkeeping, but it is simpler to
1201  // just add it here for the pre-conditions of the start of the first
1202  // pin and allow the duplication to happen naturally as we process the
1203  // final pin later. One extra point should not be very noticeable in
1204  // the long list of mesh vertices.
1205  Point last_penumbra_point =
1206  p_prev_pin->path_vertex + p_prev_pin->penumbra_delta;
1207  uint16_t last_penumbra_index = AppendVertex(last_penumbra_point, 0.0f);
1208 
1209  for (const UmbraPin& pin : pins) {
1210  const UmbraPin* p_curr_pin = &pin;
1211 
1212  // Preconditions:
1213  // - last_penumbra_point was the last outer vertex added by the
1214  // previous pin
1215  // - last_penumbra_index is its index in the vertices to be used
1216  // for creating indexed triangles.
1217 
1218  if (p_prev_pin->umbra_index != p_curr_pin->umbra_index) {
1219  // We've moved on to a new umbra index to anchor our penumbra triangles.
1220  // We need to bridge the gap so that we are now building a new fan from
1221  // a point that has the same relative angle from the current pin's
1222  // path vertex as the previous penumbra point had from the previous
1223  // pin's path vertex.
1224  //
1225  // Our previous penumbra fan vector would have gone from the previous
1226  // pin's umbra point to the previous pen's final penumbra point:
1227  // - prev->umbra_vertex
1228  // => prev->path_vertex + prev->penumbra_delta
1229  // We will connect to a parallel vector that extends from the new
1230  // (current pin's) umbra index in the same direction:
1231  // - curr->umbra_vertex
1232  // => curr->path_vertex + prev->penumbra_delta
1233 
1234  // First we pivot about the old penumbra point to bridge from the old
1235  // umbra vertex to our new umbra point.
1236  AddTriangle(last_penumbra_index, //
1237  p_prev_pin->umbra_index, p_curr_pin->umbra_index);
1238  }
1239 
1240  // Then we bridge from the old penumbra point to the new parallel
1241  // penumbra point, pivoting around the new umbra index.
1242  Point new_penumbra_point =
1243  p_curr_pin->path_vertex + p_prev_pin->penumbra_delta;
1244  uint16_t new_penumbra_index = AppendVertex(new_penumbra_point, 0.0f);
1245 
1246  if (last_penumbra_index != new_penumbra_index) {
1247  AddTriangle(p_curr_pin->umbra_index, last_penumbra_index,
1248  new_penumbra_index);
1249  }
1250 
1251  last_penumbra_point = new_penumbra_point;
1252  last_penumbra_index = new_penumbra_index;
1253 
1254  // Now draw a fan from the current pin's umbra vertex to all of the
1255  // penumbra points associated with this pin's path vertex, ending at
1256  // our new final penumbra point associated with this pin.
1257  new_penumbra_point = p_curr_pin->path_vertex + p_curr_pin->penumbra_delta;
1258  new_penumbra_index =
1259  AppendFan(p_curr_pin, last_penumbra_point, new_penumbra_point,
1260  last_penumbra_index, trigs, direction);
1261 
1262  last_penumbra_point = new_penumbra_point;
1263  last_penumbra_index = new_penumbra_index;
1264  p_prev_pin = p_curr_pin;
1265  }
1266 }
1267 
1268 // Visit each pin and find the nearest umbra_vertex from the linked list of
1269 // surviving umbra pins so we don't have to constantly find this as we stitch
1270 // together the mesh.
1271 void PolygonInfo::PopulateUmbraVertices(std::vector<UmbraPin>& pins,
1272  UmbraPinLinkedList& list,
1273  const Point centroid) {
1274  // We should be having the first crack at the vertex list, filling it with
1275  // the centroid, the umbra vertices, and the mesh connecting those into the
1276  // central core of the shadow.
1277  FML_DCHECK(list.p_head_pin != nullptr);
1278  FML_DCHECK(vertices_.empty());
1279  FML_DCHECK(gaussians_.empty());
1280  FML_DCHECK(indices_.empty());
1281 
1282  // Always start with the centroid.
1283  uint16_t last_umbra_index = AppendVertex(centroid, umbra_gaussian_);
1284  FML_DCHECK(last_umbra_index == 0u);
1285 
1286  // curr_umbra_pin is the most recently matched umbra vertex pin.
1287  // next_umbra_pin is the next umbra vertex pin to consider.
1288  // These pointers will always point to one of the pins that is on the
1289  // linked list of surviving umbra pins, possibly jumping over many
1290  // other umbra pins that were eliminated when we inset the polygon.
1291  UmbraPin* p_next_umbra_pin = list.p_head_pin;
1292  UmbraPin* p_curr_umbra_pin = p_next_umbra_pin->p_prev;
1293  for (UmbraPin& pin : pins) {
1294  if (p_next_umbra_pin == &pin ||
1295  (pin.path_vertex.GetDistanceSquared(p_curr_umbra_pin->umbra_vertex) >
1296  pin.path_vertex.GetDistanceSquared(p_next_umbra_pin->umbra_vertex))) {
1297  // We always bump to the next vertex when it was generated from this
1298  // pin, and also when it is closer to this path_vertex than the last
1299  // matched pin (curr).
1300  p_curr_umbra_pin = p_next_umbra_pin;
1301  p_next_umbra_pin = p_next_umbra_pin->p_next;
1302 
1303  // New umbra vertex - append it and remember its index.
1304  uint16_t new_umbra_index =
1305  AppendVertex(p_curr_umbra_pin->umbra_vertex, umbra_gaussian_);
1306  p_curr_umbra_pin->umbra_index = new_umbra_index;
1307  if (last_umbra_index != 0u) {
1308  AddTriangle(0u, last_umbra_index, new_umbra_index);
1309  }
1310  last_umbra_index = new_umbra_index;
1311  }
1312  if (p_curr_umbra_pin != &pin) {
1313  pin.umbra_vertex = p_curr_umbra_pin->umbra_vertex;
1314  pin.umbra_index = last_umbra_index;
1315  }
1316  FML_DCHECK(pin.umbra_index != 0u);
1317  }
1318  if (last_umbra_index != pins.front().umbra_index) {
1319  AddTriangle(0u, last_umbra_index, pins.front().umbra_index);
1320  }
1321 }
1322 
1323 // Appends a fan based on center from the relative point in start_delta to
1324 // the relative point in end_delta, potentially adding additional relative
1325 // vectors if the turning rate is faster than the trig values in trigs_.
1326 uint16_t PolygonInfo::AppendFan(const UmbraPin* p_curr_pin,
1327  const Vector2& start,
1328  const Vector2& end,
1329  uint16_t start_index,
1330  const impeller::Tessellator::Trigs& trigs,
1331  Scalar direction) {
1332  Point center = p_curr_pin->path_vertex;
1333  uint16_t center_index = p_curr_pin->umbra_index;
1334  uint16_t prev_index = start_index;
1335 
1336  Vector2 start_delta = start - center;
1337  Vector2 end_delta = end - center;
1338  size_t trig_count = trigs.size();
1339  for (size_t i = 1u; i < trig_count; i++) {
1340  Trig trig = trigs[i];
1341  Point fan_delta = (direction >= 0 ? trig : -trig) * start_delta;
1342  if (fan_delta.Cross(end_delta) * direction <= 0) {
1343  break;
1344  }
1345  uint16_t cur_index = AppendVertex(center + fan_delta, 0.0f);
1346  if (prev_index != cur_index) {
1347  AddTriangle(center_index, prev_index, cur_index);
1348  prev_index = cur_index;
1349  }
1350  if (i == trig_count - 1) {
1351  // This corner was >90 degrees so we start the loop over in case there
1352  // are more intermediate angles to emit.
1353  //
1354  // We set the loop variable to 0u which looks like it might apply a
1355  // 0 rotation to the new start_delta, but the for loop is about to
1356  // auto-incrment the variable to 1u, which will start at the next
1357  // non-0 rotation angle.
1358  i = 0u;
1359  start_delta = fan_delta;
1360  }
1361  }
1362  uint16_t cur_index = AppendVertex(center + end_delta, 0.0f);
1363  if (prev_index != cur_index) {
1364  AddTriangle(center_index, prev_index, cur_index);
1365  }
1366  return cur_index;
1367 }
1368 
1369 // Appends a vertex and gaussian value into the associated std::vectors
1370 // and returns the index at which the point was inserted.
1371 uint16_t PolygonInfo::AppendVertex(const Point& vertex, Scalar gaussian) {
1372  FML_DCHECK(gaussian >= 0.0f && gaussian <= 1.0f);
1373  uint16_t index = vertices_.size();
1374  FML_DCHECK(index == gaussians_.size());
1375  // TODO(jimgraham): Turn this condition into a failure of the tessellation
1376  FML_DCHECK(index <= std::numeric_limits<uint16_t>::max());
1377  if (index > 0u) {
1378  FML_DCHECK(!gaussians_.empty() && !vertices_.empty());
1379  if (gaussian == gaussians_.back() && vertex == vertices_.back()) {
1380  return index - 1;
1381  }
1382  }
1383  vertices_.push_back(vertex);
1384  gaussians_.push_back(gaussian);
1385  return index;
1386 }
1387 
1388 // Appends a triangle of the 3 indices into the indices_ vector.
1389 void PolygonInfo::AddTriangle(uint16_t v0, uint16_t v1, uint16_t v2) {
1390  FML_DCHECK(std::max(std::max(v0, v1), v2) < vertices_.size());
1391  indices_.push_back(v0);
1392  indices_.push_back(v1);
1393  indices_.push_back(v2);
1394 }
1395 
1396 } // namespace
1397 
1398 namespace impeller {
1399 
1400 const std::shared_ptr<ShadowVertices> ShadowVertices::kEmpty =
1401  std::make_shared<ShadowVertices>();
1402 
1403 std::optional<Rect> ShadowVertices::GetBounds() const {
1404  return Rect::MakePointBounds(vertices_);
1405 }
1406 
1408  const Matrix& matrix,
1409  const PathSource& source,
1410  Scalar occluder_height)
1411  : shadow_vertices_(MakeAmbientShadowVertices(tessellator,
1412  source,
1413  occluder_height,
1414  matrix)) {}
1415 
1417  return shadow_vertices_ != nullptr;
1418 }
1419 
1421  return shadow_vertices_ != nullptr && shadow_vertices_->IsEmpty();
1422 }
1423 
1424 const std::shared_ptr<ShadowVertices>& ShadowPathGeometry::GetShadowVertices()
1425  const {
1426  return shadow_vertices_;
1427 }
1428 
1429 const std::shared_ptr<ShadowVertices> ShadowPathGeometry::TakeShadowVertices() {
1430  return std::move(shadow_vertices_);
1431 }
1432 
1434  const Entity& entity,
1435  RenderPass& pass) const {
1436  using VS = ShadowVerticesVertexShader;
1437 
1438  size_t vertex_count = GetVertexCount();
1439 
1440  BufferView vertex_buffer = renderer.GetTransientsDataBuffer().Emplace(
1441  vertex_count * sizeof(VS::PerVertexData), alignof(VS::PerVertexData),
1442  [&](uint8_t* data) {
1443  VS::PerVertexData* vtx_contents =
1444  reinterpret_cast<VS::PerVertexData*>(data);
1445  for (size_t i = 0u; i < vertex_count; i++) {
1446  vtx_contents[i] = {
1447  .position = vertices_[i],
1448  .gaussian = gaussians_[i],
1449  };
1450  }
1451  });
1452 
1453  size_t index_count = GetIndexCount();
1454  const uint16_t* indices_data = GetIndices().data();
1455  BufferView index_buffer = {};
1456  index_buffer = renderer.GetTransientsIndexesBuffer().Emplace(
1457  indices_data, index_count * sizeof(uint16_t), alignof(uint16_t));
1458 
1459  return GeometryResult{
1461  .vertex_buffer =
1462  {
1463  .vertex_buffer = vertex_buffer,
1464  .index_buffer = index_buffer,
1465  .vertex_count = index_count,
1466  .index_type = IndexType::k16bit,
1467  },
1468  .transform = entity.GetShaderTransform(pass),
1469  };
1470 }
1471 
1472 std::shared_ptr<ShadowVertices> ShadowPathGeometry::MakeAmbientShadowVertices(
1473  Tessellator& tessellator,
1474  const PathSource& source,
1475  Scalar occluder_height,
1476  const Matrix& matrix) {
1477  Scalar trig_radius = PolygonInfo::GetTrigRadiusForHeight(occluder_height);
1478  Tessellator::Trigs trigs = tessellator.GetTrigsForDeviceRadius(trig_radius);
1479 
1480  PolygonInfo polygon(occluder_height);
1481 
1482  return polygon.CalculateConvexShadowMesh(source, matrix, trigs);
1483 }
1484 
1485 } // namespace impeller
HostBuffer & GetTransientsIndexesBuffer() const
Retrieve the current host buffer for transient storage of indexes used for indexed draws.
HostBuffer & GetTransientsDataBuffer() const
Retrieve the current host buffer for transient storage of other non-index data.
Matrix GetShaderTransform(const RenderPass &pass) const
Definition: entity.cc:50
BufferView Emplace(const BufferType &buffer, size_t alignment=0)
Emplace non-uniform data (like contiguous vertices) onto the host buffer.
Definition: host_buffer.h:92
static std::pair< size_t, size_t > CountFillStorage(const PathSource &source, Scalar scale)
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:30
const std::shared_ptr< ShadowVertices > & GetShadowVertices() const
ShadowPathGeometry(Tessellator &tessellator, const Matrix &matrix, const PathSource &source, Scalar occluder_height)
bool IsEmpty() const
Returns true if this shadow has no effect, is not visible.
const std::shared_ptr< ShadowVertices > TakeShadowVertices()
static std::shared_ptr< ShadowVertices > MakeAmbientShadowVertices(Tessellator &tessellator, const PathSource &source, Scalar occluder_height, const Matrix &matrix)
const std::vector< uint16_t > & GetIndices() const
std::optional< Rect > GetBounds() const
static const std::shared_ptr< ShadowVertices > kEmpty
GeometryResult GetPositionBuffer(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const
size_t GetIndexCount() const
The count of the indices that define the mesh.
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Definition: tessellator.h:37
Trigs GetTrigsForDeviceRadius(Scalar pixel_radius)
Definition: tessellator.cc:425
Point Vector2
Definition: point.h:430
float Scalar
Definition: scalar.h:19
constexpr float kEhCloseEnough
Definition: constants.h:57
constexpr bool ScalarNearlyZero(Scalar x, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:31
TPoint< Scalar > Point
Definition: point.h:426
LinePipeline::VertexShader VS
PrimitiveType type
Definition: geometry.h:37
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr TPoint Round(const TPoint< U > &other)
Definition: point.h:50
constexpr TPoint Normalize() const
Definition: point.h:286
constexpr TPoint PerpendicularRight() const
Definition: point.h:324
constexpr static std::optional< TRect > MakePointBounds(const U &value)
Definition: rect.h:165
A structure to store the sine and cosine of an angle.
Definition: trig.h:16
const size_t start
const size_t end