• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkPath.h"
9 #include "include/core/SkPoint3.h"
10 #include "include/core/SkVertices.h"
11 #include "include/private/SkColorData.h"
12 #include "include/private/SkTPin.h"
13 #include "src/core/SkDrawShadowInfo.h"
14 #include "src/core/SkGeometry.h"
15 #include "src/core/SkPointPriv.h"
16 #include "src/core/SkRectPriv.h"
17 #include "src/utils/SkPolyUtils.h"
18 #include "src/utils/SkShadowTessellator.h"
19 
20 #if SK_SUPPORT_GPU
21 #include "src/gpu/geometry/GrPathUtils.h"
22 #endif
23 
24 
25 /**
26  * Base class
27  */
28 class SkBaseShadowTessellator {
29 public:
30     SkBaseShadowTessellator(const SkPoint3& zPlaneParams, const SkRect& bounds, bool transparent);
~SkBaseShadowTessellator()31     virtual ~SkBaseShadowTessellator() {}
32 
releaseVertices()33     sk_sp<SkVertices> releaseVertices() {
34         if (!fSucceeded) {
35             return nullptr;
36         }
37         return SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, this->vertexCount(),
38                                     fPositions.begin(), nullptr, fColors.begin(),
39                                     this->indexCount(), fIndices.begin());
40     }
41 
42 protected:
43     inline static constexpr auto kMinHeight = 0.1f;
44     inline static constexpr auto kPenumbraColor = SK_ColorTRANSPARENT;
45     inline static constexpr auto kUmbraColor = SK_ColorBLACK;
46 
vertexCount() const47     int vertexCount() const { return fPositions.count(); }
indexCount() const48     int indexCount() const { return fIndices.count(); }
49 
50     // initialization methods
51     bool accumulateCentroid(const SkPoint& c, const SkPoint& n);
52     bool checkConvexity(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2);
53     void finishPathPolygon();
54 
55     // convex shadow methods
56     bool computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip);
57     void computeClipVectorsAndTestCentroid();
58     bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
59     void addEdge(const SkVector& nextPoint, const SkVector& nextNormal, SkColor umbraColor,
60                  const SkTDArray<SkPoint>& umbraPolygon, bool lastEdge, bool doClip);
61     bool addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor,
62                        const SkTDArray<SkPoint>& umbraPolygon, int* currUmbraIndex);
63     int getClosestUmbraIndex(const SkPoint& point, const SkTDArray<SkPoint>& umbraPolygon);
64 
65     // concave shadow methods
66     bool computeConcaveShadow(SkScalar inset, SkScalar outset);
67     void stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
68                             SkTDArray<int>* umbraIndices,
69                             const SkTDArray<SkPoint>& penumbraPolygon,
70                             SkTDArray<int>* penumbraIndices);
71 
72     void handleLine(const SkPoint& p);
73     void handleLine(const SkMatrix& m, SkPoint* p);
74 
75     void handleQuad(const SkPoint pts[3]);
76     void handleQuad(const SkMatrix& m, SkPoint pts[3]);
77 
78     void handleCubic(const SkMatrix& m, SkPoint pts[4]);
79 
80     void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
81 
82     bool addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc);
83 
84     void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
85     void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
86 
heightFunc(SkScalar x,SkScalar y)87     SkScalar heightFunc(SkScalar x, SkScalar y) {
88         return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
89     }
90 
91     SkPoint3            fZPlaneParams;
92 
93     // temporary buffer
94     SkTDArray<SkPoint>  fPointBuffer;
95 
96     SkTDArray<SkPoint>  fPositions;
97     SkTDArray<SkColor>  fColors;
98     SkTDArray<uint16_t> fIndices;
99 
100     SkTDArray<SkPoint>   fPathPolygon;
101     SkTDArray<SkPoint>   fClipPolygon;
102     SkTDArray<SkVector>  fClipVectors;
103 
104     SkRect              fPathBounds;
105     SkPoint             fCentroid;
106     SkScalar            fArea;
107     SkScalar            fLastArea;
108     SkScalar            fLastCross;
109 
110     int                 fFirstVertexIndex;
111     SkVector            fFirstOutset;
112     SkPoint             fFirstPoint;
113 
114     bool                fSucceeded;
115     bool                fTransparent;
116     bool                fIsConvex;
117     bool                fValidUmbra;
118 
119     SkScalar            fDirection;
120     int                 fPrevUmbraIndex;
121     int                 fCurrUmbraIndex;
122     int                 fCurrClipIndex;
123     bool                fPrevUmbraOutside;
124     bool                fFirstUmbraOutside;
125     SkVector            fPrevOutset;
126     SkPoint             fPrevPoint;
127 };
128 
compute_normal(const SkPoint & p0,const SkPoint & p1,SkScalar dir,SkVector * newNormal)129 static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
130                            SkVector* newNormal) {
131     SkVector normal;
132     // compute perpendicular
133     normal.fX = p0.fY - p1.fY;
134     normal.fY = p1.fX - p0.fX;
135     normal *= dir;
136     if (!normal.normalize()) {
137         return false;
138     }
139     *newNormal = normal;
140     return true;
141 }
142 
duplicate_pt(const SkPoint & p0,const SkPoint & p1)143 static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
144     static constexpr SkScalar kClose = (SK_Scalar1 / 16);
145     static constexpr SkScalar kCloseSqd = kClose * kClose;
146 
147     SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
148     return distSq < kCloseSqd;
149 }
150 
perp_dot(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2)151 static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
152     SkVector v0 = p1 - p0;
153     SkVector v1 = p2 - p1;
154     return v0.cross(v1);
155 }
156 
SkBaseShadowTessellator(const SkPoint3 & zPlaneParams,const SkRect & bounds,bool transparent)157 SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, const SkRect& bounds,
158                                                  bool transparent)
159         : fZPlaneParams(zPlaneParams)
160         , fPathBounds(bounds)
161         , fCentroid({0, 0})
162         , fArea(0)
163         , fLastArea(0)
164         , fLastCross(0)
165         , fFirstVertexIndex(-1)
166         , fSucceeded(false)
167         , fTransparent(transparent)
168         , fIsConvex(true)
169         , fValidUmbra(true)
170         , fDirection(1)
171         , fPrevUmbraIndex(-1)
172         , fCurrUmbraIndex(0)
173         , fCurrClipIndex(0)
174         , fPrevUmbraOutside(false)
175         , fFirstUmbraOutside(false) {
176     // child classes will set reserve for positions, colors and indices
177 }
178 
accumulateCentroid(const SkPoint & curr,const SkPoint & next)179 bool SkBaseShadowTessellator::accumulateCentroid(const SkPoint& curr, const SkPoint& next) {
180     if (duplicate_pt(curr, next)) {
181         return false;
182     }
183 
184     SkASSERT(fPathPolygon.count() > 0);
185     SkVector v0 = curr - fPathPolygon[0];
186     SkVector v1 = next - fPathPolygon[0];
187     SkScalar quadArea = v0.cross(v1);
188     fCentroid.fX += (v0.fX + v1.fX) * quadArea;
189     fCentroid.fY += (v0.fY + v1.fY) * quadArea;
190     fArea += quadArea;
191     // convexity check
192     if (quadArea*fLastArea < 0) {
193         fIsConvex = false;
194     }
195     if (0 != quadArea) {
196         fLastArea = quadArea;
197     }
198 
199     return true;
200 }
201 
checkConvexity(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2)202 bool SkBaseShadowTessellator::checkConvexity(const SkPoint& p0,
203                                              const SkPoint& p1,
204                                              const SkPoint& p2) {
205     SkScalar cross = perp_dot(p0, p1, p2);
206     // skip collinear point
207     if (SkScalarNearlyZero(cross)) {
208         return false;
209     }
210 
211     // check for convexity
212     if (fLastCross*cross < 0) {
213         fIsConvex = false;
214     }
215     if (0 != cross) {
216         fLastCross = cross;
217     }
218 
219     return true;
220 }
221 
finishPathPolygon()222 void SkBaseShadowTessellator::finishPathPolygon() {
223     if (fPathPolygon.count() > 1) {
224         if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], fPathPolygon[0])) {
225             // remove coincident point
226             fPathPolygon.pop();
227         }
228     }
229 
230     if (fPathPolygon.count() > 2) {
231         // do this before the final convexity check, so we use the correct fPathPolygon[0]
232         fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
233         fCentroid += fPathPolygon[0];
234         if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
235                             fPathPolygon[fPathPolygon.count() - 1],
236                             fPathPolygon[0])) {
237             // remove collinear point
238             fPathPolygon[0] = fPathPolygon[fPathPolygon.count() - 1];
239             fPathPolygon.pop();
240         }
241     }
242 
243     // if area is positive, winding is ccw
244     fDirection = fArea > 0 ? -1 : 1;
245 }
246 
computeConvexShadow(SkScalar inset,SkScalar outset,bool doClip)247 bool SkBaseShadowTessellator::computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip) {
248     if (doClip) {
249         this->computeClipVectorsAndTestCentroid();
250     }
251 
252     // adjust inset distance and umbra color if necessary
253     auto umbraColor = kUmbraColor;
254     SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
255                                                                       fPathPolygon[0],
256                                                                       fPathPolygon[1]);
257     SkRect bounds;
258     bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
259     for (int i = 1; i < fPathPolygon.count(); ++i) {
260         int j = i + 1;
261         if (i == fPathPolygon.count() - 1) {
262             j = 0;
263         }
264         SkPoint currPoint = fPathPolygon[i];
265         SkPoint nextPoint = fPathPolygon[j];
266         SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
267                                                                        nextPoint);
268         if (distSq < minDistSq) {
269             minDistSq = distSq;
270         }
271     }
272 
273     SkTDArray<SkPoint> insetPolygon;
274     if (inset > SK_ScalarNearlyZero) {
275         static constexpr auto kTolerance = 1.0e-2f;
276         if (minDistSq < (inset + kTolerance)*(inset + kTolerance)) {
277             // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
278             auto newInset = SkScalarSqrt(minDistSq) - kTolerance;
279             auto ratio = 128 * (newInset / inset + 1);
280             SkASSERT(SkScalarIsFinite(ratio));
281             // they aren't PMColors, but the interpolation algorithm is the same
282             umbraColor = SkPMLerp(kUmbraColor, kPenumbraColor, (unsigned)ratio);
283             inset = newInset;
284         }
285 
286         // generate inner ring
287         if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), inset,
288                                   &insetPolygon)) {
289             // not ideal, but in this case we'll inset using the centroid
290             fValidUmbra = false;
291         }
292     }
293     const SkTDArray<SkPoint>& umbraPolygon = (inset > SK_ScalarNearlyZero) ? insetPolygon
294                                                                            : fPathPolygon;
295 
296     // walk around the path polygon, generate outer ring and connect to inner ring
297     if (fTransparent) {
298         fPositions.push_back(fCentroid);
299         fColors.push_back(umbraColor);
300     }
301     fCurrUmbraIndex = 0;
302 
303     // initial setup
304     // add first quad
305     int polyCount = fPathPolygon.count();
306     if (!compute_normal(fPathPolygon[polyCount - 1], fPathPolygon[0], fDirection, &fFirstOutset)) {
307         // polygon should be sanitized by this point, so this is unrecoverable
308         return false;
309     }
310 
311     fFirstOutset *= outset;
312     fFirstPoint = fPathPolygon[polyCount - 1];
313     fFirstVertexIndex = fPositions.count();
314     fPrevOutset = fFirstOutset;
315     fPrevPoint = fFirstPoint;
316     fPrevUmbraIndex = -1;
317 
318     this->addInnerPoint(fFirstPoint, umbraColor, umbraPolygon, &fPrevUmbraIndex);
319 
320     if (!fTransparent && doClip) {
321         SkPoint clipPoint;
322         bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
323                                               fCentroid, &clipPoint);
324         if (isOutside) {
325             fPositions.push_back(clipPoint);
326             fColors.push_back(umbraColor);
327         }
328         fPrevUmbraOutside = isOutside;
329         fFirstUmbraOutside = isOutside;
330     }
331 
332     SkPoint newPoint = fFirstPoint + fFirstOutset;
333     fPositions.push_back(newPoint);
334     fColors.push_back(kPenumbraColor);
335     this->addEdge(fPathPolygon[0], fFirstOutset, umbraColor, umbraPolygon, false, doClip);
336 
337     for (int i = 1; i < polyCount; ++i) {
338         SkVector normal;
339         if (!compute_normal(fPrevPoint, fPathPolygon[i], fDirection, &normal)) {
340             return false;
341         }
342         normal *= outset;
343         this->addArc(normal, outset, true);
344         this->addEdge(fPathPolygon[i], normal, umbraColor, umbraPolygon,
345                       i == polyCount - 1, doClip);
346     }
347     SkASSERT(this->indexCount());
348 
349     // final fan
350     SkASSERT(fPositions.count() >= 3);
351     if (this->addArc(fFirstOutset, outset, false)) {
352         if (fFirstUmbraOutside) {
353             this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
354                                  fFirstVertexIndex + 2);
355         } else {
356             this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
357                                  fFirstVertexIndex + 1);
358         }
359     } else {
360         // no arc added, fix up by setting first penumbra point position to last one
361         if (fFirstUmbraOutside) {
362             fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
363         } else {
364             fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
365         }
366     }
367 
368     return true;
369 }
370 
computeClipVectorsAndTestCentroid()371 void SkBaseShadowTessellator::computeClipVectorsAndTestCentroid() {
372     SkASSERT(fClipPolygon.count() >= 3);
373     fCurrClipIndex = fClipPolygon.count() - 1;
374 
375     // init clip vectors
376     SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
377     SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
378     fClipVectors.push_back(v0);
379 
380     // init centroid check
381     bool hiddenCentroid = true;
382     v1 = fCentroid - fClipPolygon[0];
383     SkScalar initCross = v0.cross(v1);
384 
385     for (int p = 1; p < fClipPolygon.count(); ++p) {
386         // add to clip vectors
387         v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
388         fClipVectors.push_back(v0);
389         // Determine if transformed centroid is inside clipPolygon.
390         v1 = fCentroid - fClipPolygon[p];
391         if (initCross*v0.cross(v1) <= 0) {
392             hiddenCentroid = false;
393         }
394     }
395     SkASSERT(fClipVectors.count() == fClipPolygon.count());
396 
397     fTransparent = fTransparent || !hiddenCentroid;
398 }
399 
addEdge(const SkPoint & nextPoint,const SkVector & nextNormal,SkColor umbraColor,const SkTDArray<SkPoint> & umbraPolygon,bool lastEdge,bool doClip)400 void SkBaseShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
401                                       SkColor umbraColor, const SkTDArray<SkPoint>& umbraPolygon,
402                                       bool lastEdge, bool doClip) {
403     // add next umbra point
404     int currUmbraIndex;
405     bool duplicate;
406     if (lastEdge) {
407         duplicate = false;
408         currUmbraIndex = fFirstVertexIndex;
409         fPrevPoint = nextPoint;
410     } else {
411         duplicate = this->addInnerPoint(nextPoint, umbraColor, umbraPolygon, &currUmbraIndex);
412     }
413     int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
414         ? fPositions.count() - 1
415         : fPositions.count() - 2;
416     if (!duplicate) {
417         // add to center fan if transparent or centroid showing
418         if (fTransparent) {
419             this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
420             // otherwise add to clip ring
421         } else if (doClip) {
422             SkPoint clipPoint;
423             bool isOutside = lastEdge ? fFirstUmbraOutside
424                 : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
425                                        &clipPoint);
426             if (isOutside) {
427                 if (!lastEdge) {
428                     fPositions.push_back(clipPoint);
429                     fColors.push_back(umbraColor);
430                 }
431                 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
432                 if (fPrevUmbraOutside) {
433                     // fill out quad
434                     this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
435                                          fPrevUmbraIndex + 1);
436                 }
437             } else if (fPrevUmbraOutside) {
438                 // add tri
439                 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
440             }
441 
442             fPrevUmbraOutside = isOutside;
443         }
444     }
445 
446     // add next penumbra point and quad
447     SkPoint newPoint = nextPoint + nextNormal;
448     fPositions.push_back(newPoint);
449     fColors.push_back(kPenumbraColor);
450 
451     if (!duplicate) {
452         this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
453     }
454     this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
455 
456     fPrevUmbraIndex = currUmbraIndex;
457     fPrevOutset = nextNormal;
458 }
459 
clipUmbraPoint(const SkPoint & umbraPoint,const SkPoint & centroid,SkPoint * clipPoint)460 bool SkBaseShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
461                                              SkPoint* clipPoint) {
462     SkVector segmentVector = centroid - umbraPoint;
463 
464     int startClipPoint = fCurrClipIndex;
465     do {
466         SkVector dp = umbraPoint - fClipPolygon[fCurrClipIndex];
467         SkScalar denom = fClipVectors[fCurrClipIndex].cross(segmentVector);
468         SkScalar t_num = dp.cross(segmentVector);
469         // if line segments are nearly parallel
470         if (SkScalarNearlyZero(denom)) {
471             // and collinear
472             if (SkScalarNearlyZero(t_num)) {
473                 return false;
474             }
475             // otherwise are separate, will try the next poly segment
476             // else if crossing lies within poly segment
477         } else if (t_num >= 0 && t_num <= denom) {
478             SkScalar s_num = dp.cross(fClipVectors[fCurrClipIndex]);
479             // if umbra point is inside the clip polygon
480             if (s_num >= 0 && s_num <= denom) {
481                 segmentVector *= s_num / denom;
482                 *clipPoint = umbraPoint + segmentVector;
483                 return true;
484             }
485         }
486         fCurrClipIndex = (fCurrClipIndex + 1) % fClipPolygon.count();
487     } while (fCurrClipIndex != startClipPoint);
488 
489     return false;
490 }
491 
addInnerPoint(const SkPoint & pathPoint,SkColor umbraColor,const SkTDArray<SkPoint> & umbraPolygon,int * currUmbraIndex)492 bool SkBaseShadowTessellator::addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor,
493                                             const SkTDArray<SkPoint>& umbraPolygon,
494                                             int* currUmbraIndex) {
495     SkPoint umbraPoint;
496     if (!fValidUmbra) {
497         SkVector v = fCentroid - pathPoint;
498         v *= 0.95f;
499         umbraPoint = pathPoint + v;
500     } else {
501         umbraPoint = umbraPolygon[this->getClosestUmbraIndex(pathPoint, umbraPolygon)];
502     }
503 
504     fPrevPoint = pathPoint;
505 
506     // merge "close" points
507     if (fPrevUmbraIndex == -1 ||
508         !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
509         // if we've wrapped around, don't add a new point
510         if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
511             *currUmbraIndex = fFirstVertexIndex;
512         } else {
513             *currUmbraIndex = fPositions.count();
514             fPositions.push_back(umbraPoint);
515             fColors.push_back(umbraColor);
516         }
517         return false;
518     } else {
519         *currUmbraIndex = fPrevUmbraIndex;
520         return true;
521     }
522 }
523 
getClosestUmbraIndex(const SkPoint & p,const SkTDArray<SkPoint> & umbraPolygon)524 int SkBaseShadowTessellator::getClosestUmbraIndex(const SkPoint& p,
525                                                   const SkTDArray<SkPoint>& umbraPolygon) {
526     SkScalar minDistance = SkPointPriv::DistanceToSqd(p, umbraPolygon[fCurrUmbraIndex]);
527     int index = fCurrUmbraIndex;
528     int dir = 1;
529     int next = (index + dir) % umbraPolygon.count();
530 
531     // init travel direction
532     SkScalar distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
533     if (distance < minDistance) {
534         index = next;
535         minDistance = distance;
536     } else {
537         dir = umbraPolygon.count() - 1;
538     }
539 
540     // iterate until we find a point that increases the distance
541     next = (index + dir) % umbraPolygon.count();
542     distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
543     while (distance < minDistance) {
544         index = next;
545         minDistance = distance;
546         next = (index + dir) % umbraPolygon.count();
547         distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
548     }
549 
550     fCurrUmbraIndex = index;
551     return index;
552 }
553 
computeConcaveShadow(SkScalar inset,SkScalar outset)554 bool SkBaseShadowTessellator::computeConcaveShadow(SkScalar inset, SkScalar outset) {
555     if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
556         return false;
557     }
558 
559     // shouldn't inset more than the half bounds of the polygon
560     inset = std::min(inset, std::min(SkTAbs(SkRectPriv::HalfWidth(fPathBounds)),
561                                      SkTAbs(SkRectPriv::HalfHeight(fPathBounds))));
562     // generate inner ring
563     SkTDArray<SkPoint> umbraPolygon;
564     SkTDArray<int> umbraIndices;
565     umbraIndices.setReserve(fPathPolygon.count());
566     if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), fPathBounds, inset,
567                                &umbraPolygon, &umbraIndices)) {
568         // TODO: figure out how to handle this case
569         return false;
570     }
571 
572     // generate outer ring
573     SkTDArray<SkPoint> penumbraPolygon;
574     SkTDArray<int> penumbraIndices;
575     penumbraPolygon.setReserve(umbraPolygon.count());
576     penumbraIndices.setReserve(umbraPolygon.count());
577     if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), fPathBounds, -outset,
578                                &penumbraPolygon, &penumbraIndices)) {
579         // TODO: figure out how to handle this case
580         return false;
581     }
582 
583     if (!umbraPolygon.count() || !penumbraPolygon.count()) {
584         return false;
585     }
586 
587     // attach the rings together
588     this->stitchConcaveRings(umbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
589 
590     return true;
591 }
592 
stitchConcaveRings(const SkTDArray<SkPoint> & umbraPolygon,SkTDArray<int> * umbraIndices,const SkTDArray<SkPoint> & penumbraPolygon,SkTDArray<int> * penumbraIndices)593 void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
594                                                  SkTDArray<int>* umbraIndices,
595                                                  const SkTDArray<SkPoint>& penumbraPolygon,
596                                                  SkTDArray<int>* penumbraIndices) {
597     // TODO: only create and fill indexMap when fTransparent is true?
598     SkAutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.count());
599 
600     // find minimum indices
601     int minIndex = 0;
602     int min = (*penumbraIndices)[0];
603     for (int i = 1; i < (*penumbraIndices).count(); ++i) {
604         if ((*penumbraIndices)[i] < min) {
605             min = (*penumbraIndices)[i];
606             minIndex = i;
607         }
608     }
609     int currPenumbra = minIndex;
610 
611     minIndex = 0;
612     min = (*umbraIndices)[0];
613     for (int i = 1; i < (*umbraIndices).count(); ++i) {
614         if ((*umbraIndices)[i] < min) {
615             min = (*umbraIndices)[i];
616             minIndex = i;
617         }
618     }
619     int currUmbra = minIndex;
620 
621     // now find a case where the indices are equal (there should be at least one)
622     int maxPenumbraIndex = fPathPolygon.count() - 1;
623     int maxUmbraIndex = fPathPolygon.count() - 1;
624     while ((*penumbraIndices)[currPenumbra] != (*umbraIndices)[currUmbra]) {
625         if ((*penumbraIndices)[currPenumbra] < (*umbraIndices)[currUmbra]) {
626             (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
627             maxPenumbraIndex = (*penumbraIndices)[currPenumbra];
628             currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
629         } else {
630             (*umbraIndices)[currUmbra] += fPathPolygon.count();
631             maxUmbraIndex = (*umbraIndices)[currUmbra];
632             currUmbra = (currUmbra + 1) % umbraPolygon.count();
633         }
634     }
635 
636     fPositions.push_back(penumbraPolygon[currPenumbra]);
637     fColors.push_back(kPenumbraColor);
638     int prevPenumbraIndex = 0;
639     fPositions.push_back(umbraPolygon[currUmbra]);
640     fColors.push_back(kUmbraColor);
641     fPrevUmbraIndex = 1;
642     indexMap[currUmbra] = 1;
643 
644     int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
645     int nextUmbra = (currUmbra + 1) % umbraPolygon.count();
646     while ((*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex ||
647            (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
648 
649         if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) {
650             // advance both one step
651             fPositions.push_back(penumbraPolygon[nextPenumbra]);
652             fColors.push_back(kPenumbraColor);
653             int currPenumbraIndex = fPositions.count() - 1;
654 
655             fPositions.push_back(umbraPolygon[nextUmbra]);
656             fColors.push_back(kUmbraColor);
657             int currUmbraIndex = fPositions.count() - 1;
658             indexMap[nextUmbra] = currUmbraIndex;
659 
660             this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
661                              fPrevUmbraIndex, currUmbraIndex);
662 
663             prevPenumbraIndex = currPenumbraIndex;
664             (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
665             currPenumbra = nextPenumbra;
666             nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
667 
668             fPrevUmbraIndex = currUmbraIndex;
669             (*umbraIndices)[currUmbra] += fPathPolygon.count();
670             currUmbra = nextUmbra;
671             nextUmbra = (currUmbra + 1) % umbraPolygon.count();
672         }
673 
674         while ((*penumbraIndices)[nextPenumbra] < (*umbraIndices)[nextUmbra] &&
675                (*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex) {
676             // fill out penumbra arc
677             fPositions.push_back(penumbraPolygon[nextPenumbra]);
678             fColors.push_back(kPenumbraColor);
679             int currPenumbraIndex = fPositions.count() - 1;
680 
681             this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
682 
683             prevPenumbraIndex = currPenumbraIndex;
684             // this ensures the ordering when we wrap around
685             (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
686             currPenumbra = nextPenumbra;
687             nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
688         }
689 
690         while ((*umbraIndices)[nextUmbra] < (*penumbraIndices)[nextPenumbra] &&
691                (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
692             // fill out umbra arc
693             fPositions.push_back(umbraPolygon[nextUmbra]);
694             fColors.push_back(kUmbraColor);
695             int currUmbraIndex = fPositions.count() - 1;
696             indexMap[nextUmbra] = currUmbraIndex;
697 
698             this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
699 
700             fPrevUmbraIndex = currUmbraIndex;
701             // this ensures the ordering when we wrap around
702             (*umbraIndices)[currUmbra] += fPathPolygon.count();
703             currUmbra = nextUmbra;
704             nextUmbra = (currUmbra + 1) % umbraPolygon.count();
705         }
706     }
707     // finish up by advancing both one step
708     fPositions.push_back(penumbraPolygon[nextPenumbra]);
709     fColors.push_back(kPenumbraColor);
710     int currPenumbraIndex = fPositions.count() - 1;
711 
712     fPositions.push_back(umbraPolygon[nextUmbra]);
713     fColors.push_back(kUmbraColor);
714     int currUmbraIndex = fPositions.count() - 1;
715     indexMap[nextUmbra] = currUmbraIndex;
716 
717     this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
718                      fPrevUmbraIndex, currUmbraIndex);
719 
720     if (fTransparent) {
721         SkTriangulateSimplePolygon(umbraPolygon.begin(), indexMap, umbraPolygon.count(),
722                                    &fIndices);
723     }
724 }
725 
726 
727 // tesselation tolerance values, in device space pixels
728 #if SK_SUPPORT_GPU
729 static constexpr SkScalar kQuadTolerance = 0.2f;
730 static constexpr SkScalar kCubicTolerance = 0.2f;
731 static constexpr SkScalar kQuadToleranceSqd = kQuadTolerance * kQuadTolerance;
732 static constexpr SkScalar kCubicToleranceSqd = kCubicTolerance * kCubicTolerance;
733 #endif
734 static constexpr SkScalar kConicTolerance = 0.25f;
735 
736 // clamps the point to the nearest 16th of a pixel
sanitize_point(const SkPoint & in,SkPoint * out)737 static void sanitize_point(const SkPoint& in, SkPoint* out) {
738     out->fX = SkScalarRoundToScalar(16.f*in.fX)*0.0625f;
739     out->fY = SkScalarRoundToScalar(16.f*in.fY)*0.0625f;
740 }
741 
handleLine(const SkPoint & p)742 void SkBaseShadowTessellator::handleLine(const SkPoint& p) {
743     SkPoint pSanitized;
744     sanitize_point(p, &pSanitized);
745 
746     if (fPathPolygon.count() > 0) {
747         if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) {
748             // skip coincident point
749             return;
750         }
751     }
752 
753     if (fPathPolygon.count() > 1) {
754         if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
755                             fPathPolygon[fPathPolygon.count() - 1],
756                             pSanitized)) {
757             // remove collinear point
758             fPathPolygon.pop();
759             // it's possible that the previous point is coincident with the new one now
760             if (duplicate_pt(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) {
761                 fPathPolygon.pop();
762             }
763         }
764     }
765 
766     fPathPolygon.push_back(pSanitized);
767 }
768 
handleLine(const SkMatrix & m,SkPoint * p)769 void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
770     m.mapPoints(p, 1);
771 
772     this->handleLine(*p);
773 }
774 
handleQuad(const SkPoint pts[3])775 void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
776 #if SK_SUPPORT_GPU
777     // check for degeneracy
778     SkVector v0 = pts[1] - pts[0];
779     SkVector v1 = pts[2] - pts[0];
780     if (SkScalarNearlyZero(v0.cross(v1))) {
781         return;
782     }
783     // TODO: Pull PathUtils out of Ganesh?
784     int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
785     fPointBuffer.setCount(maxCount);
786     SkPoint* target = fPointBuffer.begin();
787     int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
788                                                      kQuadToleranceSqd, &target, maxCount);
789     fPointBuffer.setCount(count);
790     for (int i = 0; i < count; i++) {
791         this->handleLine(fPointBuffer[i]);
792     }
793 #else
794     // for now, just to draw something
795     this->handleLine(pts[1]);
796     this->handleLine(pts[2]);
797 #endif
798 }
799 
handleQuad(const SkMatrix & m,SkPoint pts[3])800 void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
801     m.mapPoints(pts, 3);
802     this->handleQuad(pts);
803 }
804 
handleCubic(const SkMatrix & m,SkPoint pts[4])805 void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
806     m.mapPoints(pts, 4);
807 #if SK_SUPPORT_GPU
808     // TODO: Pull PathUtils out of Ganesh?
809     int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
810     fPointBuffer.setCount(maxCount);
811     SkPoint* target = fPointBuffer.begin();
812     int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
813                                                  kCubicToleranceSqd, &target, maxCount);
814     fPointBuffer.setCount(count);
815     for (int i = 0; i < count; i++) {
816         this->handleLine(fPointBuffer[i]);
817     }
818 #else
819     // for now, just to draw something
820     this->handleLine(pts[1]);
821     this->handleLine(pts[2]);
822     this->handleLine(pts[3]);
823 #endif
824 }
825 
handleConic(const SkMatrix & m,SkPoint pts[3],SkScalar w)826 void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
827     if (m.hasPerspective()) {
828         w = SkConic::TransformW(pts, w, m);
829     }
830     m.mapPoints(pts, 3);
831     SkAutoConicToQuads quadder;
832     const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
833     SkPoint lastPoint = *(quads++);
834     int count = quadder.countQuads();
835     for (int i = 0; i < count; ++i) {
836         SkPoint quadPts[3];
837         quadPts[0] = lastPoint;
838         quadPts[1] = quads[0];
839         quadPts[2] = i == count - 1 ? pts[2] : quads[1];
840         this->handleQuad(quadPts);
841         lastPoint = quadPts[2];
842         quads += 2;
843     }
844 }
845 
addArc(const SkVector & nextNormal,SkScalar offset,bool finishArc)846 bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc) {
847     // fill in fan from previous quad
848     SkScalar rotSin, rotCos;
849     int numSteps;
850     if (!SkComputeRadialSteps(fPrevOutset, nextNormal, offset, &rotSin, &rotCos, &numSteps)) {
851         // recover as best we can
852         numSteps = 0;
853     }
854     SkVector prevNormal = fPrevOutset;
855     for (int i = 0; i < numSteps-1; ++i) {
856         SkVector currNormal;
857         currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
858         currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
859         fPositions.push_back(fPrevPoint + currNormal);
860         fColors.push_back(kPenumbraColor);
861         this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
862 
863         prevNormal = currNormal;
864     }
865     if (finishArc && numSteps) {
866         fPositions.push_back(fPrevPoint + nextNormal);
867         fColors.push_back(kPenumbraColor);
868         this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
869     }
870     fPrevOutset = nextNormal;
871 
872     return (numSteps > 0);
873 }
874 
appendTriangle(uint16_t index0,uint16_t index1,uint16_t index2)875 void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
876     auto indices = fIndices.append(3);
877 
878     indices[0] = index0;
879     indices[1] = index1;
880     indices[2] = index2;
881 }
882 
appendQuad(uint16_t index0,uint16_t index1,uint16_t index2,uint16_t index3)883 void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
884                                          uint16_t index2, uint16_t index3) {
885     auto indices = fIndices.append(6);
886 
887     indices[0] = index0;
888     indices[1] = index1;
889     indices[2] = index2;
890 
891     indices[3] = index2;
892     indices[4] = index1;
893     indices[5] = index3;
894 }
895 
896 //////////////////////////////////////////////////////////////////////////////////////////////////
897 
898 class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
899 public:
900     SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
901                                const SkPoint3& zPlaneParams, bool transparent);
902 
903 private:
904     bool computePathPolygon(const SkPath& path, const SkMatrix& ctm);
905 
906     using INHERITED = SkBaseShadowTessellator;
907 };
908 
SkAmbientShadowTessellator(const SkPath & path,const SkMatrix & ctm,const SkPoint3 & zPlaneParams,bool transparent)909 SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
910                                                        const SkMatrix& ctm,
911                                                        const SkPoint3& zPlaneParams,
912                                                        bool transparent)
913         : INHERITED(zPlaneParams, path.getBounds(), transparent) {
914     // Set base colors
915     auto baseZ = heightFunc(fPathBounds.centerX(), fPathBounds.centerY());
916     // umbraColor is the interior value, penumbraColor the exterior value.
917     auto outset = SkDrawShadowMetrics::AmbientBlurRadius(baseZ);
918     auto inset = outset * SkDrawShadowMetrics::AmbientRecipAlpha(baseZ) - outset;
919 
920     if (!this->computePathPolygon(path, ctm)) {
921         return;
922     }
923     if (fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
924         fSucceeded = true; // We don't want to try to blur these cases, so we will
925                            // return an empty SkVertices instead.
926         return;
927     }
928 
929     // Outer ring: 3*numPts
930     // Middle ring: numPts
931     fPositions.setReserve(4 * path.countPoints());
932     fColors.setReserve(4 * path.countPoints());
933     // Outer ring: 12*numPts
934     // Middle ring: 0
935     fIndices.setReserve(12 * path.countPoints());
936 
937     if (fIsConvex) {
938         fSucceeded = this->computeConvexShadow(inset, outset, false);
939     } else {
940         fSucceeded = this->computeConcaveShadow(inset, outset);
941     }
942 }
943 
computePathPolygon(const SkPath & path,const SkMatrix & ctm)944 bool SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) {
945     fPathPolygon.setReserve(path.countPoints());
946 
947     // walk around the path, tessellate and generate outer ring
948     // if original path is transparent, will accumulate sum of points for centroid
949     SkPath::Iter iter(path, true);
950     SkPoint pts[4];
951     SkPath::Verb verb;
952     bool verbSeen = false;
953     bool closeSeen = false;
954     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
955         if (closeSeen) {
956             return false;
957         }
958         switch (verb) {
959             case SkPath::kLine_Verb:
960                 this->handleLine(ctm, &pts[1]);
961                 break;
962             case SkPath::kQuad_Verb:
963                 this->handleQuad(ctm, pts);
964                 break;
965             case SkPath::kCubic_Verb:
966                 this->handleCubic(ctm, pts);
967                 break;
968             case SkPath::kConic_Verb:
969                 this->handleConic(ctm, pts, iter.conicWeight());
970                 break;
971             case SkPath::kMove_Verb:
972                 if (verbSeen) {
973                     return false;
974                 }
975                 break;
976             case SkPath::kClose_Verb:
977             case SkPath::kDone_Verb:
978                 closeSeen = true;
979                 break;
980         }
981         verbSeen = true;
982     }
983 
984     this->finishPathPolygon();
985     return true;
986 }
987 
988 ///////////////////////////////////////////////////////////////////////////////////////////////////
989 
990 class SkSpotShadowTessellator : public SkBaseShadowTessellator {
991 public:
992     SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
993                             const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
994                             SkScalar lightRadius, bool transparent, bool directional);
995 
996 private:
997     bool computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
998                                     const SkMatrix& shadowTransform);
999     void addToClip(const SkVector& nextPoint);
1000 
1001     using INHERITED = SkBaseShadowTessellator;
1002 };
1003 
SkSpotShadowTessellator(const SkPath & path,const SkMatrix & ctm,const SkPoint3 & zPlaneParams,const SkPoint3 & lightPos,SkScalar lightRadius,bool transparent,bool directional)1004 SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
1005                                                  const SkPoint3& zPlaneParams,
1006                                                  const SkPoint3& lightPos, SkScalar lightRadius,
1007                                                  bool transparent, bool directional)
1008     : INHERITED(zPlaneParams, path.getBounds(), transparent) {
1009 
1010     // Compute the blur radius, scale and translation for the spot shadow.
1011     SkMatrix shadowTransform;
1012     SkScalar outset;
1013     if (!SkDrawShadowMetrics::GetSpotShadowTransform(lightPos, lightRadius, ctm, zPlaneParams,
1014                                                      path.getBounds(), directional,
1015                                                      &shadowTransform, &outset)) {
1016         return;
1017     }
1018     SkScalar inset = outset;
1019 
1020     // compute rough clip bounds for umbra, plus offset polygon, plus centroid
1021     if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) {
1022         return;
1023     }
1024     if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
1025         fSucceeded = true; // We don't want to try to blur these cases, so we will
1026                            // return an empty SkVertices instead.
1027         return;
1028     }
1029 
1030     // TODO: calculate these reserves better
1031     // Penumbra ring: 3*numPts
1032     // Umbra ring: numPts
1033     // Inner ring: numPts
1034     fPositions.setReserve(5 * path.countPoints());
1035     fColors.setReserve(5 * path.countPoints());
1036     // Penumbra ring: 12*numPts
1037     // Umbra ring: 3*numPts
1038     fIndices.setReserve(15 * path.countPoints());
1039 
1040     if (fIsConvex) {
1041         fSucceeded = this->computeConvexShadow(inset, outset, true);
1042     } else {
1043         fSucceeded = this->computeConcaveShadow(inset, outset);
1044     }
1045 
1046     if (!fSucceeded) {
1047         return;
1048     }
1049 
1050     fSucceeded = true;
1051 }
1052 
computeClipAndPathPolygons(const SkPath & path,const SkMatrix & ctm,const SkMatrix & shadowTransform)1053 bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
1054                                                          const SkMatrix& shadowTransform) {
1055 
1056     fPathPolygon.setReserve(path.countPoints());
1057     fClipPolygon.setReserve(path.countPoints());
1058 
1059     // Walk around the path and compute clip polygon and path polygon.
1060     // Will also accumulate sum of areas for centroid.
1061     // For Bezier curves, we compute additional interior points on curve.
1062     SkPath::Iter iter(path, true);
1063     SkPoint pts[4];
1064     SkPoint clipPts[4];
1065     SkPath::Verb verb;
1066 
1067     // coefficients to compute cubic Bezier at t = 5/16
1068     static constexpr SkScalar kA = 0.32495117187f;
1069     static constexpr SkScalar kB = 0.44311523437f;
1070     static constexpr SkScalar kC = 0.20141601562f;
1071     static constexpr SkScalar kD = 0.03051757812f;
1072 
1073     SkPoint curvePoint;
1074     SkScalar w;
1075     bool closeSeen = false;
1076     bool verbSeen = false;
1077     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1078         if (closeSeen) {
1079             return false;
1080         }
1081         switch (verb) {
1082             case SkPath::kLine_Verb:
1083                 ctm.mapPoints(clipPts, &pts[1], 1);
1084                 this->addToClip(clipPts[0]);
1085                 this->handleLine(shadowTransform, &pts[1]);
1086                 break;
1087             case SkPath::kQuad_Verb:
1088                 ctm.mapPoints(clipPts, pts, 3);
1089                 // point at t = 1/2
1090                 curvePoint.fX = 0.25f*clipPts[0].fX + 0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1091                 curvePoint.fY = 0.25f*clipPts[0].fY + 0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY;
1092                 this->addToClip(curvePoint);
1093                 this->addToClip(clipPts[2]);
1094                 this->handleQuad(shadowTransform, pts);
1095                 break;
1096             case SkPath::kConic_Verb:
1097                 ctm.mapPoints(clipPts, pts, 3);
1098                 w = iter.conicWeight();
1099                 // point at t = 1/2
1100                 curvePoint.fX = 0.25f*clipPts[0].fX + w*0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1101                 curvePoint.fY = 0.25f*clipPts[0].fY + w*0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY;
1102                 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
1103                 this->addToClip(curvePoint);
1104                 this->addToClip(clipPts[2]);
1105                 this->handleConic(shadowTransform, pts, w);
1106                 break;
1107             case SkPath::kCubic_Verb:
1108                 ctm.mapPoints(clipPts, pts, 4);
1109                 // point at t = 5/16
1110                 curvePoint.fX = kA*clipPts[0].fX + kB*clipPts[1].fX
1111                               + kC*clipPts[2].fX + kD*clipPts[3].fX;
1112                 curvePoint.fY = kA*clipPts[0].fY + kB*clipPts[1].fY
1113                               + kC*clipPts[2].fY + kD*clipPts[3].fY;
1114                 this->addToClip(curvePoint);
1115                 // point at t = 11/16
1116                 curvePoint.fX = kD*clipPts[0].fX + kC*clipPts[1].fX
1117                               + kB*clipPts[2].fX + kA*clipPts[3].fX;
1118                 curvePoint.fY = kD*clipPts[0].fY + kC*clipPts[1].fY
1119                               + kB*clipPts[2].fY + kA*clipPts[3].fY;
1120                 this->addToClip(curvePoint);
1121                 this->addToClip(clipPts[3]);
1122                 this->handleCubic(shadowTransform, pts);
1123                 break;
1124             case SkPath::kMove_Verb:
1125                 if (verbSeen) {
1126                     return false;
1127                 }
1128                 break;
1129             case SkPath::kClose_Verb:
1130             case SkPath::kDone_Verb:
1131                 closeSeen = true;
1132                 break;
1133             default:
1134                 SkDEBUGFAIL("unknown verb");
1135         }
1136         verbSeen = true;
1137     }
1138 
1139     this->finishPathPolygon();
1140     return true;
1141 }
1142 
addToClip(const SkPoint & point)1143 void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
1144     if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count() - 1])) {
1145         fClipPolygon.push_back(point);
1146     }
1147 }
1148 
1149 ///////////////////////////////////////////////////////////////////////////////////////////////////
1150 
MakeAmbient(const SkPath & path,const SkMatrix & ctm,const SkPoint3 & zPlane,bool transparent)1151 sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
1152                                                    const SkPoint3& zPlane, bool transparent) {
1153     if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) {
1154         return nullptr;
1155     }
1156     SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
1157     return ambientTess.releaseVertices();
1158 }
1159 
MakeSpot(const SkPath & path,const SkMatrix & ctm,const SkPoint3 & zPlane,const SkPoint3 & lightPos,SkScalar lightRadius,bool transparent,bool directional)1160 sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
1161                                                 const SkPoint3& zPlane, const SkPoint3& lightPos,
1162                                                 SkScalar lightRadius,  bool transparent,
1163                                                 bool directional) {
1164     if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() ||
1165         !lightPos.isFinite() || !(lightPos.fZ >= SK_ScalarNearlyZero) ||
1166         !SkScalarIsFinite(lightRadius) || !(lightRadius >= SK_ScalarNearlyZero)) {
1167         return nullptr;
1168     }
1169     SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent,
1170                                      directional);
1171     return spotTess.releaseVertices();
1172 }
1173