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