blob: 151f948cb735da6336b34f7185233a5c7f174621 [file] [log] [blame]
Jim Van Verthbce74962017-01-25 09:39:46 -05001/*
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
Jim Van Verthefe3ded2017-01-30 13:11:45 -05008#include "SkShadowTessellator.h"
Cary Clarka4083c92017-09-15 11:59:23 -04009#include "SkColorData.h"
Jim Van Verth1af03d42017-07-31 09:34:58 -040010#include "SkDrawShadowInfo.h"
Jim Van Verth58abc9e2017-01-25 11:05:01 -050011#include "SkGeometry.h"
Jim Van Verth8664a1d2018-06-28 16:26:50 -040012#include "SkPolyUtils.h"
Jim Van Verth85dc96b2017-01-30 14:49:21 -050013#include "SkPath.h"
Mike Reed54518ac2017-07-22 08:29:48 -040014#include "SkPoint3.h"
Cary Clarkdf429f32017-11-08 11:44:31 -050015#include "SkPointPriv.h"
Brian Salomonaff27a22017-02-06 15:47:44 -050016#include "SkVertices.h"
Jim Van Verth85dc96b2017-01-30 14:49:21 -050017
18#if SK_SUPPORT_GPU
19#include "GrPathUtils.h"
20#endif
Jim Van Verth58abc9e2017-01-25 11:05:01 -050021
Brian Salomon958fbc42017-01-30 17:01:28 -050022
Jim Van Vertha84898d2017-02-06 13:38:23 -050023/**
24 * Base class
25 */
Brian Salomonaff27a22017-02-06 15:47:44 -050026class SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -050027public:
Jim Van Verthe308a122017-05-08 14:19:30 -040028 SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -050029 virtual ~SkBaseShadowTessellator() {}
Brian Salomon958fbc42017-01-30 17:01:28 -050030
Brian Salomonaff27a22017-02-06 15:47:44 -050031 sk_sp<SkVertices> releaseVertices() {
32 if (!fSucceeded) {
33 return nullptr;
34 }
Mike Reed887cdf12017-04-03 11:11:09 -040035 return SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, this->vertexCount(),
Mike Reed97eb4fe2017-03-14 12:04:16 -040036 fPositions.begin(), nullptr, fColors.begin(),
37 this->indexCount(), fIndices.begin());
Brian Salomon1a8b79a2017-01-31 11:26:26 -050038 }
Brian Salomon958fbc42017-01-30 17:01:28 -050039
Jim Van Vertha84898d2017-02-06 13:38:23 -050040protected:
Jim Van Verthda965502017-04-11 15:29:14 -040041 static constexpr auto kMinHeight = 0.1f;
Jim Van Verthfb186392018-09-11 11:37:46 -040042 static constexpr auto kPenumbraColor = SK_ColorTRANSPARENT;
43 static constexpr auto kUmbraColor = SK_ColorBLACK;
Jim Van Verthda965502017-04-11 15:29:14 -040044
Brian Salomonaff27a22017-02-06 15:47:44 -050045 int vertexCount() const { return fPositions.count(); }
46 int indexCount() const { return fIndices.count(); }
47
Jim Van Verthd737ab92018-09-10 15:19:01 -040048 // initialization methods
Jim Van Verthcdaf6612018-06-05 15:21:13 -040049 bool accumulateCentroid(const SkPoint& c, const SkPoint& n);
50 bool checkConvexity(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2);
51 void finishPathPolygon();
Jim Van Verthd737ab92018-09-10 15:19:01 -040052
53 // convex shadow methods
54 bool computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip);
55 void computeClipVectorsAndTestCentroid();
56 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -040057 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal, SkColor umbraColor,
Jim Van Verth6bdfebe2018-09-17 11:46:50 -040058 const SkTDArray<SkPoint>& umbraPolygon, bool lastEdge, bool doClip);
59 bool addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor,
60 const SkTDArray<SkPoint>& umbraPolygon, int* currUmbraIndex);
61 int getClosestUmbraIndex(const SkPoint& point, const SkTDArray<SkPoint>& umbraPolygon);
Jim Van Verthd737ab92018-09-10 15:19:01 -040062
63 // concave shadow methods
64 bool computeConcaveShadow(SkScalar inset, SkScalar outset);
Jim Van Verth8760e2f2018-06-12 14:21:38 -040065 void stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
66 SkTDArray<int>* umbraIndices,
67 const SkTDArray<SkPoint>& penumbraPolygon,
68 SkTDArray<int>* penumbraIndices);
Jim Van Verthcdaf6612018-06-05 15:21:13 -040069
70 void handleLine(const SkPoint& p);
Jim Van Vertha84898d2017-02-06 13:38:23 -050071 void handleLine(const SkMatrix& m, SkPoint* p);
Brian Salomon958fbc42017-01-30 17:01:28 -050072
73 void handleQuad(const SkPoint pts[3]);
Jim Van Vertha84898d2017-02-06 13:38:23 -050074 void handleQuad(const SkMatrix& m, SkPoint pts[3]);
Brian Salomon958fbc42017-01-30 17:01:28 -050075
Jim Van Vertha84898d2017-02-06 13:38:23 -050076 void handleCubic(const SkMatrix& m, SkPoint pts[4]);
Brian Salomon958fbc42017-01-30 17:01:28 -050077
Jim Van Vertha84898d2017-02-06 13:38:23 -050078 void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
Brian Salomon958fbc42017-01-30 17:01:28 -050079
Jim Van Verthd737ab92018-09-10 15:19:01 -040080 bool addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc);
Jim Van Verthb4366552017-03-27 14:25:29 -040081
Jim Van Verth872da6b2018-04-10 11:24:11 -040082 void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
83 void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
84
Jim Van Verthe308a122017-05-08 14:19:30 -040085 SkScalar heightFunc(SkScalar x, SkScalar y) {
86 return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
87 }
88
Jim Van Verth0b7645f2018-08-31 12:36:52 -040089 SkPoint3 fZPlaneParams;
Jim Van Verthda965502017-04-11 15:29:14 -040090
Brian Salomon958fbc42017-01-30 17:01:28 -050091 // temporary buffer
92 SkTDArray<SkPoint> fPointBuffer;
Brian Salomon0dda9cb2017-02-03 10:33:25 -050093
Jim Van Vertha84898d2017-02-06 13:38:23 -050094 SkTDArray<SkPoint> fPositions;
95 SkTDArray<SkColor> fColors;
96 SkTDArray<uint16_t> fIndices;
97
Jim Van Verth6bdfebe2018-09-17 11:46:50 -040098 SkTDArray<SkPoint> fPathPolygon;
99 SkTDArray<SkPoint> fClipPolygon;
100 SkTDArray<SkVector> fClipVectors;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400101
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400102 SkPoint fCentroid;
103 SkScalar fArea;
104 SkScalar fLastArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400105 SkScalar fLastCross;
106
Jim Van Verth76387852017-05-16 16:55:16 -0400107 int fFirstVertexIndex;
108 SkVector fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500109 SkPoint fFirstPoint;
110
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500111 bool fSucceeded;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500112 bool fTransparent;
Jim Van Verthf507c282018-05-11 10:48:20 -0400113 bool fIsConvex;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400114 bool fValidUmbra;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500115
Jim Van Vertha84898d2017-02-06 13:38:23 -0500116 SkScalar fDirection;
117 int fPrevUmbraIndex;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400118 int fCurrUmbraIndex;
119 int fCurrClipIndex;
120 bool fPrevUmbraOutside;
121 bool fFirstUmbraOutside;
Jim Van Verth76387852017-05-16 16:55:16 -0400122 SkVector fPrevOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500123 SkPoint fPrevPoint;
Brian Salomon958fbc42017-01-30 17:01:28 -0500124};
125
Jim Van Verthfb186392018-09-11 11:37:46 -0400126// make external linkage happy
127constexpr SkColor SkBaseShadowTessellator::kUmbraColor;
128constexpr SkColor SkBaseShadowTessellator::kPenumbraColor;
129
Jim Van Verthda965502017-04-11 15:29:14 -0400130static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
Jim Van Verthbce74962017-01-25 09:39:46 -0500131 SkVector* newNormal) {
132 SkVector normal;
133 // compute perpendicular
134 normal.fX = p0.fY - p1.fY;
135 normal.fY = p1.fX - p0.fX;
Jim Van Verthda965502017-04-11 15:29:14 -0400136 normal *= dir;
Jim Van Verthbce74962017-01-25 09:39:46 -0500137 if (!normal.normalize()) {
138 return false;
139 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500140 *newNormal = normal;
141 return true;
142}
143
Jim Van Verthf507c282018-05-11 10:48:20 -0400144static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
145 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
146 static constexpr SkScalar kCloseSqd = kClose * kClose;
147
148 SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
149 return distSq < kCloseSqd;
150}
151
152static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
153 SkVector v0 = p1 - p0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400154 SkVector v1 = p2 - p1;
Jim Van Verthf507c282018-05-11 10:48:20 -0400155 return v0.cross(v1);
156}
157
Jim Van Verthe308a122017-05-08 14:19:30 -0400158SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
159 : fZPlaneParams(zPlaneParams)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400160 , fCentroid({0, 0})
161 , fArea(0)
162 , fLastArea(0)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400163 , fLastCross(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400164 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500165 , fSucceeded(false)
166 , fTransparent(transparent)
Jim Van Verthf507c282018-05-11 10:48:20 -0400167 , fIsConvex(true)
Jim Van Verthd737ab92018-09-10 15:19:01 -0400168 , fValidUmbra(true)
Brian Salomonaff27a22017-02-06 15:47:44 -0500169 , fDirection(1)
Jim Van Verthd737ab92018-09-10 15:19:01 -0400170 , fPrevUmbraIndex(-1)
171 , fCurrUmbraIndex(0)
172 , fCurrClipIndex(0)
173 , fPrevUmbraOutside(false)
174 , fFirstUmbraOutside(false) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500175 // child classes will set reserve for positions, colors and indices
176}
177
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400178bool SkBaseShadowTessellator::accumulateCentroid(const SkPoint& curr, const SkPoint& next) {
179 if (duplicate_pt(curr, next)) {
180 return false;
181 }
182
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400183 SkASSERT(fPathPolygon.count() > 0);
184 SkVector v0 = curr - fPathPolygon[0];
185 SkVector v1 = next - fPathPolygon[0];
186 SkScalar quadArea = v0.cross(v1);
187 fCentroid.fX += (v0.fX + v1.fX) * quadArea;
188 fCentroid.fY += (v0.fY + v1.fY) * quadArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400189 fArea += quadArea;
190 // convexity check
Jim Van Verth3645bb02018-06-26 14:58:58 -0400191 if (quadArea*fLastArea < 0) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400192 fIsConvex = false;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400193 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400194 if (0 != quadArea) {
195 fLastArea = quadArea;
196 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400197
198 return true;
199}
200
201bool SkBaseShadowTessellator::checkConvexity(const SkPoint& p0,
202 const SkPoint& p1,
203 const SkPoint& p2) {
204 SkScalar cross = perp_dot(p0, p1, p2);
205 // skip collinear point
206 if (SkScalarNearlyZero(cross)) {
207 return false;
208 }
209
210 // check for convexity
211 if (fLastCross*cross < 0) {
212 fIsConvex = false;
213 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400214 if (0 != cross) {
215 fLastCross = cross;
216 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400217
218 return true;
219}
220
221void SkBaseShadowTessellator::finishPathPolygon() {
222 if (fPathPolygon.count() > 1) {
223 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], fPathPolygon[0])) {
224 // remove coincident point
225 fPathPolygon.pop();
226 }
227 }
228
229 if (fPathPolygon.count() > 2) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400230 // do this before the final convexity check, so we use the correct fPathPolygon[0]
231 fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
232 fCentroid += fPathPolygon[0];
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400233 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
234 fPathPolygon[fPathPolygon.count() - 1],
235 fPathPolygon[0])) {
236 // remove collinear point
237 fPathPolygon[0] = fPathPolygon[fPathPolygon.count() - 1];
238 fPathPolygon.pop();
239 }
240 }
241
Jim Van Verth7deacf42018-06-08 15:13:25 -0400242 // if area is positive, winding is ccw
243 fDirection = fArea > 0 ? -1 : 1;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400244}
245
Jim Van Verthd737ab92018-09-10 15:19:01 -0400246bool SkBaseShadowTessellator::computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip) {
247 if (doClip) {
248 this->computeClipVectorsAndTestCentroid();
249 }
250
Jim Van Verthfb186392018-09-11 11:37:46 -0400251 // adjust inset distance and umbra color if necessary
252 auto umbraColor = kUmbraColor;
253 SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
254 fPathPolygon[0],
255 fPathPolygon[1]);
256 SkRect bounds;
257 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
258 for (int i = 1; i < fPathPolygon.count(); ++i) {
259 int j = i + 1;
260 if (i == fPathPolygon.count() - 1) {
261 j = 0;
262 }
263 SkPoint currPoint = fPathPolygon[i];
264 SkPoint nextPoint = fPathPolygon[j];
265 SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
266 nextPoint);
267 if (distSq < minDistSq) {
268 minDistSq = distSq;
269 }
270 }
Jim Van Verthfb186392018-09-11 11:37:46 -0400271
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400272 SkTDArray<SkPoint> insetPolygon;
273 if (inset > SK_ScalarNearlyZero) {
274 static constexpr auto kTolerance = 1.0e-2f;
275 if (minDistSq < (inset + kTolerance)*(inset + kTolerance)) {
276 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
277 auto newInset = SkScalarSqrt(minDistSq) - kTolerance;
278 auto ratio = 128 * (newInset / inset + 1);
279 SkASSERT(SkScalarIsFinite(ratio));
280 // they aren't PMColors, but the interpolation algorithm is the same
281 umbraColor = SkPMLerp(kUmbraColor, kPenumbraColor, (unsigned)ratio);
282 inset = newInset;
283 }
284
285 // generate inner ring
286 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), inset,
287 &insetPolygon)) {
288 // not ideal, but in this case we'll inset using the centroid
289 fValidUmbra = false;
290 }
Jim Van Verthd737ab92018-09-10 15:19:01 -0400291 }
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400292 const SkTDArray<SkPoint>& umbraPolygon = (inset > SK_ScalarNearlyZero) ? insetPolygon
293 : fPathPolygon;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400294
295 // walk around the path polygon, generate outer ring and connect to inner ring
296 if (fTransparent) {
297 fPositions.push_back(fCentroid);
Jim Van Verthfb186392018-09-11 11:37:46 -0400298 fColors.push_back(umbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400299 }
300 fCurrUmbraIndex = 0;
301
302 // initial setup
303 // add first quad
304 int polyCount = fPathPolygon.count();
305 if (!compute_normal(fPathPolygon[polyCount - 1], fPathPolygon[0], fDirection, &fFirstOutset)) {
306 // polygon should be sanitized by this point, so this is unrecoverable
307 return false;
308 }
309
310 fFirstOutset *= outset;
311 fFirstPoint = fPathPolygon[polyCount - 1];
312 fFirstVertexIndex = fPositions.count();
313 fPrevOutset = fFirstOutset;
314 fPrevPoint = fFirstPoint;
315 fPrevUmbraIndex = -1;
316
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400317 this->addInnerPoint(fFirstPoint, umbraColor, umbraPolygon, &fPrevUmbraIndex);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400318
319 if (!fTransparent && doClip) {
320 SkPoint clipPoint;
321 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
322 fCentroid, &clipPoint);
323 if (isOutside) {
324 fPositions.push_back(clipPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400325 fColors.push_back(umbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400326 }
327 fPrevUmbraOutside = isOutside;
328 fFirstUmbraOutside = isOutside;
329 }
330
331 SkPoint newPoint = fFirstPoint + fFirstOutset;
332 fPositions.push_back(newPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400333 fColors.push_back(kPenumbraColor);
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400334 this->addEdge(fPathPolygon[0], fFirstOutset, umbraColor, umbraPolygon, false, doClip);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400335
336 for (int i = 1; i < polyCount; ++i) {
337 SkVector normal;
338 if (!compute_normal(fPrevPoint, fPathPolygon[i], fDirection, &normal)) {
339 return false;
340 }
341 normal *= outset;
342 this->addArc(normal, outset, true);
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400343 this->addEdge(fPathPolygon[i], normal, umbraColor, umbraPolygon,
344 i == polyCount - 1, doClip);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400345 }
346 SkASSERT(this->indexCount());
347
348 // final fan
349 SkASSERT(fPositions.count() >= 3);
350 if (this->addArc(fFirstOutset, outset, false)) {
351 if (fFirstUmbraOutside) {
352 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
353 fFirstVertexIndex + 2);
354 } else {
355 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
356 fFirstVertexIndex + 1);
357 }
358 } else {
359 // no arc added, fix up by setting first penumbra point position to last one
360 if (fFirstUmbraOutside) {
361 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
362 } else {
363 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
364 }
365 }
366
367 return true;
368}
369
370void SkBaseShadowTessellator::computeClipVectorsAndTestCentroid() {
371 SkASSERT(fClipPolygon.count() >= 3);
372 fCurrClipIndex = fClipPolygon.count() - 1;
373
374 // init clip vectors
375 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
376 SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
377 fClipVectors.push_back(v0);
378
379 // init centroid check
380 bool hiddenCentroid = true;
381 v1 = fCentroid - fClipPolygon[0];
382 SkScalar initCross = v0.cross(v1);
383
384 for (int p = 1; p < fClipPolygon.count(); ++p) {
385 // add to clip vectors
386 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
387 fClipVectors.push_back(v0);
388 // Determine if transformed centroid is inside clipPolygon.
389 v1 = fCentroid - fClipPolygon[p];
390 if (initCross*v0.cross(v1) <= 0) {
391 hiddenCentroid = false;
392 }
393 }
394 SkASSERT(fClipVectors.count() == fClipPolygon.count());
395
396 fTransparent = fTransparent || !hiddenCentroid;
397}
398
399void SkBaseShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400400 SkColor umbraColor, const SkTDArray<SkPoint>& umbraPolygon,
401 bool lastEdge, bool doClip) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400402 // add next umbra point
403 int currUmbraIndex;
404 bool duplicate;
405 if (lastEdge) {
406 duplicate = false;
407 currUmbraIndex = fFirstVertexIndex;
408 fPrevPoint = nextPoint;
409 } else {
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400410 duplicate = this->addInnerPoint(nextPoint, umbraColor, umbraPolygon, &currUmbraIndex);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400411 }
412 int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
413 ? fPositions.count() - 1
414 : fPositions.count() - 2;
415 if (!duplicate) {
416 // add to center fan if transparent or centroid showing
417 if (fTransparent) {
418 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
419 // otherwise add to clip ring
420 } else if (doClip) {
421 SkPoint clipPoint;
422 bool isOutside = lastEdge ? fFirstUmbraOutside
423 : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
424 &clipPoint);
425 if (isOutside) {
426 if (!lastEdge) {
427 fPositions.push_back(clipPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400428 fColors.push_back(umbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400429 }
430 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
431 if (fPrevUmbraOutside) {
432 // fill out quad
433 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
434 fPrevUmbraIndex + 1);
435 }
436 } else if (fPrevUmbraOutside) {
437 // add tri
438 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
439 }
440
441 fPrevUmbraOutside = isOutside;
442 }
443 }
444
445 // add next penumbra point and quad
446 SkPoint newPoint = nextPoint + nextNormal;
447 fPositions.push_back(newPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400448 fColors.push_back(kPenumbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400449
450 if (!duplicate) {
451 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
452 }
453 this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
454
455 fPrevUmbraIndex = currUmbraIndex;
456 fPrevOutset = nextNormal;
457}
458
459bool SkBaseShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
460 SkPoint* clipPoint) {
461 SkVector segmentVector = centroid - umbraPoint;
462
463 int startClipPoint = fCurrClipIndex;
464 do {
465 SkVector dp = umbraPoint - fClipPolygon[fCurrClipIndex];
466 SkScalar denom = fClipVectors[fCurrClipIndex].cross(segmentVector);
467 SkScalar t_num = dp.cross(segmentVector);
468 // if line segments are nearly parallel
469 if (SkScalarNearlyZero(denom)) {
470 // and collinear
471 if (SkScalarNearlyZero(t_num)) {
472 return false;
473 }
474 // otherwise are separate, will try the next poly segment
475 // else if crossing lies within poly segment
476 } else if (t_num >= 0 && t_num <= denom) {
477 SkScalar s_num = dp.cross(fClipVectors[fCurrClipIndex]);
478 // if umbra point is inside the clip polygon
479 if (s_num >= 0 && s_num <= denom) {
480 segmentVector *= s_num / denom;
481 *clipPoint = umbraPoint + segmentVector;
482 return true;
483 }
484 }
485 fCurrClipIndex = (fCurrClipIndex + 1) % fClipPolygon.count();
486 } while (fCurrClipIndex != startClipPoint);
487
488 return false;
489}
490
Jim Van Verthfb186392018-09-11 11:37:46 -0400491bool SkBaseShadowTessellator::addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor,
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400492 const SkTDArray<SkPoint>& umbraPolygon,
Jim Van Verthfb186392018-09-11 11:37:46 -0400493 int* currUmbraIndex) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400494 SkPoint umbraPoint;
495 if (!fValidUmbra) {
496 SkVector v = fCentroid - pathPoint;
497 v *= 0.95f;
498 umbraPoint = pathPoint + v;
499 } else {
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400500 umbraPoint = umbraPolygon[this->getClosestUmbraIndex(pathPoint, umbraPolygon)];
Jim Van Verthd737ab92018-09-10 15:19:01 -0400501 }
502
503 fPrevPoint = pathPoint;
504
505 // merge "close" points
506 if (fPrevUmbraIndex == -1 ||
507 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
508 // if we've wrapped around, don't add a new point
509 if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
510 *currUmbraIndex = fFirstVertexIndex;
511 } else {
512 *currUmbraIndex = fPositions.count();
513 fPositions.push_back(umbraPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400514 fColors.push_back(umbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400515 }
516 return false;
517 } else {
518 *currUmbraIndex = fPrevUmbraIndex;
519 return true;
520 }
521}
522
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400523int SkBaseShadowTessellator::getClosestUmbraIndex(const SkPoint& p,
524 const SkTDArray<SkPoint>& umbraPolygon) {
525 SkScalar minDistance = SkPointPriv::DistanceToSqd(p, umbraPolygon[fCurrUmbraIndex]);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400526 int index = fCurrUmbraIndex;
527 int dir = 1;
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400528 int next = (index + dir) % umbraPolygon.count();
Jim Van Verthd737ab92018-09-10 15:19:01 -0400529
530 // init travel direction
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400531 SkScalar distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400532 if (distance < minDistance) {
533 index = next;
534 minDistance = distance;
535 } else {
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400536 dir = umbraPolygon.count() - 1;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400537 }
538
539 // iterate until we find a point that increases the distance
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400540 next = (index + dir) % umbraPolygon.count();
541 distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400542 while (distance < minDistance) {
543 index = next;
544 minDistance = distance;
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400545 next = (index + dir) % umbraPolygon.count();
546 distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400547 }
548
549 fCurrUmbraIndex = index;
550 return index;
551}
552
553bool SkBaseShadowTessellator::computeConcaveShadow(SkScalar inset, SkScalar outset) {
554 if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
555 return false;
556 }
557
558 // generate inner ring
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400559 SkTDArray<SkPoint> umbraPolygon;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400560 SkTDArray<int> umbraIndices;
561 umbraIndices.setReserve(fPathPolygon.count());
562 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), inset,
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400563 &umbraPolygon, &umbraIndices)) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400564 // TODO: figure out how to handle this case
565 return false;
566 }
567
568 // generate outer ring
569 SkTDArray<SkPoint> penumbraPolygon;
570 SkTDArray<int> penumbraIndices;
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400571 penumbraPolygon.setReserve(umbraPolygon.count());
572 penumbraIndices.setReserve(umbraPolygon.count());
Jim Van Verthd737ab92018-09-10 15:19:01 -0400573 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -outset,
574 &penumbraPolygon, &penumbraIndices)) {
575 // TODO: figure out how to handle this case
576 return false;
577 }
578
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400579 if (!umbraPolygon.count() || !penumbraPolygon.count()) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400580 return false;
581 }
582
583 // attach the rings together
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400584 this->stitchConcaveRings(umbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400585
586 return true;
587}
588
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400589void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
590 SkTDArray<int>* umbraIndices,
591 const SkTDArray<SkPoint>& penumbraPolygon,
592 SkTDArray<int>* penumbraIndices) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400593 // TODO: only create and fill indexMap when fTransparent is true?
594 SkAutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.count());
595
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400596 // find minimum indices
597 int minIndex = 0;
598 int min = (*penumbraIndices)[0];
599 for (int i = 1; i < (*penumbraIndices).count(); ++i) {
600 if ((*penumbraIndices)[i] < min) {
601 min = (*penumbraIndices)[i];
602 minIndex = i;
603 }
604 }
605 int currPenumbra = minIndex;
606
607 minIndex = 0;
608 min = (*umbraIndices)[0];
609 for (int i = 1; i < (*umbraIndices).count(); ++i) {
610 if ((*umbraIndices)[i] < min) {
611 min = (*umbraIndices)[i];
612 minIndex = i;
613 }
614 }
615 int currUmbra = minIndex;
616
617 // now find a case where the indices are equal (there should be at least one)
618 int maxPenumbraIndex = fPathPolygon.count() - 1;
619 int maxUmbraIndex = fPathPolygon.count() - 1;
620 while ((*penumbraIndices)[currPenumbra] != (*umbraIndices)[currUmbra]) {
621 if ((*penumbraIndices)[currPenumbra] < (*umbraIndices)[currUmbra]) {
622 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
623 maxPenumbraIndex = (*penumbraIndices)[currPenumbra];
624 currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
625 } else {
626 (*umbraIndices)[currUmbra] += fPathPolygon.count();
627 maxUmbraIndex = (*umbraIndices)[currUmbra];
628 currUmbra = (currUmbra + 1) % umbraPolygon.count();
629 }
630 }
631
Jim Van Verthd737ab92018-09-10 15:19:01 -0400632 fPositions.push_back(penumbraPolygon[currPenumbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400633 fColors.push_back(kPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400634 int prevPenumbraIndex = 0;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400635 fPositions.push_back(umbraPolygon[currUmbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400636 fColors.push_back(kUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400637 fPrevUmbraIndex = 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400638 indexMap[currUmbra] = 1;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400639
640 int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
641 int nextUmbra = (currUmbra + 1) % umbraPolygon.count();
642 while ((*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex ||
643 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
644
645 if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) {
646 // advance both one step
Jim Van Verthd737ab92018-09-10 15:19:01 -0400647 fPositions.push_back(penumbraPolygon[nextPenumbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400648 fColors.push_back(kPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400649 int currPenumbraIndex = fPositions.count() - 1;
650
Jim Van Verthd737ab92018-09-10 15:19:01 -0400651 fPositions.push_back(umbraPolygon[nextUmbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400652 fColors.push_back(kUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400653 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400654 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400655
656 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
657 fPrevUmbraIndex, currUmbraIndex);
658
659 prevPenumbraIndex = currPenumbraIndex;
660 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
661 currPenumbra = nextPenumbra;
662 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
663
664 fPrevUmbraIndex = currUmbraIndex;
665 (*umbraIndices)[currUmbra] += fPathPolygon.count();
666 currUmbra = nextUmbra;
667 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
668 }
669
670 while ((*penumbraIndices)[nextPenumbra] < (*umbraIndices)[nextUmbra] &&
671 (*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex) {
672 // fill out penumbra arc
Jim Van Verthd737ab92018-09-10 15:19:01 -0400673 fPositions.push_back(penumbraPolygon[nextPenumbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400674 fColors.push_back(kPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400675 int currPenumbraIndex = fPositions.count() - 1;
676
677 this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
678
679 prevPenumbraIndex = currPenumbraIndex;
680 // this ensures the ordering when we wrap around
681 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
682 currPenumbra = nextPenumbra;
683 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
684 }
685
686 while ((*umbraIndices)[nextUmbra] < (*penumbraIndices)[nextPenumbra] &&
687 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
688 // fill out umbra arc
Jim Van Verthd737ab92018-09-10 15:19:01 -0400689 fPositions.push_back(umbraPolygon[nextUmbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400690 fColors.push_back(kUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400691 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400692 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400693
694 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
695
696 fPrevUmbraIndex = currUmbraIndex;
697 // this ensures the ordering when we wrap around
698 (*umbraIndices)[currUmbra] += fPathPolygon.count();
699 currUmbra = nextUmbra;
700 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
701 }
702 }
703 // finish up by advancing both one step
Jim Van Verthd737ab92018-09-10 15:19:01 -0400704 fPositions.push_back(penumbraPolygon[nextPenumbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400705 fColors.push_back(kPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400706 int currPenumbraIndex = fPositions.count() - 1;
707
Jim Van Verthd737ab92018-09-10 15:19:01 -0400708 fPositions.push_back(umbraPolygon[nextUmbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400709 fColors.push_back(kUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400710 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400711 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400712
713 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
714 fPrevUmbraIndex, currUmbraIndex);
715
716 if (fTransparent) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400717 SkTriangulateSimplePolygon(umbraPolygon.begin(), indexMap, umbraPolygon.count(),
718 &fIndices);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400719 }
720}
721
722
Jim Van Vertha84898d2017-02-06 13:38:23 -0500723// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500724#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500725static const SkScalar kQuadTolerance = 0.2f;
726static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500727#endif
Jim Van Vertha84898d2017-02-06 13:38:23 -0500728static const SkScalar kConicTolerance = 0.5f;
729
Jim Van Verth3645bb02018-06-26 14:58:58 -0400730// clamps the point to the nearest 16th of a pixel
731static void sanitize_point(const SkPoint& in, SkPoint* out) {
732 out->fX = SkScalarRoundToScalar(16.f*in.fX)*0.0625f;
733 out->fY = SkScalarRoundToScalar(16.f*in.fY)*0.0625f;
734}
735
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400736void SkBaseShadowTessellator::handleLine(const SkPoint& p) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400737 SkPoint pSanitized;
738 sanitize_point(p, &pSanitized);
739
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400740 if (fPathPolygon.count() > 0) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400741 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400742 // skip coincident point
743 return;
744 }
745 }
746
747 if (fPathPolygon.count() > 1) {
748 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
749 fPathPolygon[fPathPolygon.count() - 1],
Jim Van Verth3645bb02018-06-26 14:58:58 -0400750 pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400751 // remove collinear point
752 fPathPolygon.pop();
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400753 // it's possible that the previous point is coincident with the new one now
754 if (duplicate_pt(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) {
755 fPathPolygon.pop();
756 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400757 }
758 }
759
Jim Van Verthd737ab92018-09-10 15:19:01 -0400760 fPathPolygon.push_back(pSanitized);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400761}
762
Brian Salomonaff27a22017-02-06 15:47:44 -0500763void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500764 m.mapPoints(p, 1);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400765
Jim Van Vertha84898d2017-02-06 13:38:23 -0500766 this->handleLine(*p);
767}
768
Brian Salomonaff27a22017-02-06 15:47:44 -0500769void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500770#if SK_SUPPORT_GPU
Jim Van Vertha947e292018-02-26 13:54:34 -0500771 // check for degeneracy
772 SkVector v0 = pts[1] - pts[0];
773 SkVector v1 = pts[2] - pts[0];
774 if (SkScalarNearlyZero(v0.cross(v1))) {
775 return;
776 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500777 // TODO: Pull PathUtils out of Ganesh?
778 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400779 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500780 SkPoint* target = fPointBuffer.begin();
781 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
782 kQuadTolerance, &target, maxCount);
783 fPointBuffer.setCount(count);
784 for (int i = 0; i < count; i++) {
785 this->handleLine(fPointBuffer[i]);
786 }
787#else
788 // for now, just to draw something
789 this->handleLine(pts[1]);
790 this->handleLine(pts[2]);
791#endif
792}
793
Brian Salomonaff27a22017-02-06 15:47:44 -0500794void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500795 m.mapPoints(pts, 3);
796 this->handleQuad(pts);
797}
798
Brian Salomonaff27a22017-02-06 15:47:44 -0500799void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500800 m.mapPoints(pts, 4);
801#if SK_SUPPORT_GPU
802 // TODO: Pull PathUtils out of Ganesh?
803 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400804 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500805 SkPoint* target = fPointBuffer.begin();
806 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
807 kCubicTolerance, &target, maxCount);
808 fPointBuffer.setCount(count);
809 for (int i = 0; i < count; i++) {
810 this->handleLine(fPointBuffer[i]);
811 }
812#else
813 // for now, just to draw something
814 this->handleLine(pts[1]);
815 this->handleLine(pts[2]);
816 this->handleLine(pts[3]);
817#endif
818}
819
Brian Salomonaff27a22017-02-06 15:47:44 -0500820void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400821 if (m.hasPerspective()) {
822 w = SkConic::TransformW(pts, w, m);
823 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500824 m.mapPoints(pts, 3);
825 SkAutoConicToQuads quadder;
826 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
827 SkPoint lastPoint = *(quads++);
828 int count = quadder.countQuads();
829 for (int i = 0; i < count; ++i) {
830 SkPoint quadPts[3];
831 quadPts[0] = lastPoint;
832 quadPts[1] = quads[0];
833 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
834 this->handleQuad(quadPts);
835 lastPoint = quadPts[2];
836 quads += 2;
837 }
838}
839
Jim Van Verthd737ab92018-09-10 15:19:01 -0400840bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500841 // fill in fan from previous quad
842 SkScalar rotSin, rotCos;
843 int numSteps;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400844 if (!SkComputeRadialSteps(fPrevOutset, nextNormal, offset, &rotSin, &rotCos, &numSteps)) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400845 // recover as best we can
846 numSteps = 0;
847 }
Jim Van Verth76387852017-05-16 16:55:16 -0400848 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400849 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400850 SkVector currNormal;
851 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
852 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400853 fPositions.push_back(fPrevPoint + currNormal);
Jim Van Verthfb186392018-09-11 11:37:46 -0400854 fColors.push_back(kPenumbraColor);
Jim Van Verth872da6b2018-04-10 11:24:11 -0400855 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500856
Jim Van Verthda965502017-04-11 15:29:14 -0400857 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500858 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400859 if (finishArc && numSteps) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400860 fPositions.push_back(fPrevPoint + nextNormal);
Jim Van Verthfb186392018-09-11 11:37:46 -0400861 fColors.push_back(kPenumbraColor);
Jim Van Verth872da6b2018-04-10 11:24:11 -0400862 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Verthda965502017-04-11 15:29:14 -0400863 }
Jim Van Verth76387852017-05-16 16:55:16 -0400864 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400865
866 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500867}
868
Jim Van Verth872da6b2018-04-10 11:24:11 -0400869void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
870 auto indices = fIndices.append(3);
871
872 indices[0] = index0;
873 indices[1] = index1;
874 indices[2] = index2;
875}
876
877void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
878 uint16_t index2, uint16_t index3) {
879 auto indices = fIndices.append(6);
880
881 indices[0] = index0;
882 indices[1] = index1;
883 indices[2] = index2;
884
885 indices[3] = index2;
886 indices[4] = index1;
887 indices[5] = index3;
888}
889
Jim Van Vertha84898d2017-02-06 13:38:23 -0500890//////////////////////////////////////////////////////////////////////////////////////////////////
891
Brian Salomonaff27a22017-02-06 15:47:44 -0500892class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500893public:
894 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400895 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500896
897private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400898 bool computePathPolygon(const SkPath& path, const SkMatrix& ctm);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400899
Brian Salomonaff27a22017-02-06 15:47:44 -0500900 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500901};
902
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500903SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500904 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400905 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500906 bool transparent)
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400907 : INHERITED(zPlaneParams, transparent) {
Jim Van Verthda965502017-04-11 15:29:14 -0400908 // Set base colors
Jim Van Verthfb186392018-09-11 11:37:46 -0400909 auto baseZ = heightFunc(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verthb4366552017-03-27 14:25:29 -0400910 // umbraColor is the interior value, penumbraColor the exterior value.
Jim Van Verthfb186392018-09-11 11:37:46 -0400911 auto outset = SkDrawShadowMetrics::AmbientBlurRadius(baseZ);
912 auto inset = outset * SkDrawShadowMetrics::AmbientRecipAlpha(baseZ) - outset;
Jim Van Verthda965502017-04-11 15:29:14 -0400913
Jim Van Verth3645bb02018-06-26 14:58:58 -0400914 if (!this->computePathPolygon(path, ctm)) {
915 return;
916 }
917 if (fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
918 fSucceeded = true; // We don't want to try to blur these cases, so we will
919 // return an empty SkVertices instead.
920 return;
921 }
922
Jim Van Verthbce74962017-01-25 09:39:46 -0500923 // Outer ring: 3*numPts
924 // Middle ring: numPts
925 fPositions.setReserve(4 * path.countPoints());
926 fColors.setReserve(4 * path.countPoints());
927 // Outer ring: 12*numPts
928 // Middle ring: 0
929 fIndices.setReserve(12 * path.countPoints());
930
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400931 if (fIsConvex) {
Jim Van Verthfb186392018-09-11 11:37:46 -0400932 fSucceeded = this->computeConvexShadow(inset, outset, false);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400933 } else {
Jim Van Verthfb186392018-09-11 11:37:46 -0400934 fSucceeded = this->computeConcaveShadow(inset, outset);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400935 }
936}
937
Jim Van Verth3645bb02018-06-26 14:58:58 -0400938bool SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400939 fPathPolygon.setReserve(path.countPoints());
940
941 // walk around the path, tessellate and generate outer ring
942 // if original path is transparent, will accumulate sum of points for centroid
943 SkPath::Iter iter(path, true);
944 SkPoint pts[4];
945 SkPath::Verb verb;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400946 bool verbSeen = false;
947 bool closeSeen = false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400948 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400949 if (closeSeen) {
950 return false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400951 }
Jim Van Verth3645bb02018-06-26 14:58:58 -0400952 switch (verb) {
953 case SkPath::kLine_Verb:
954 this->handleLine(ctm, &pts[1]);
955 break;
956 case SkPath::kQuad_Verb:
957 this->handleQuad(ctm, pts);
958 break;
959 case SkPath::kCubic_Verb:
960 this->handleCubic(ctm, pts);
961 break;
962 case SkPath::kConic_Verb:
963 this->handleConic(ctm, pts, iter.conicWeight());
964 break;
965 case SkPath::kMove_Verb:
966 if (verbSeen) {
967 return false;
968 }
969 break;
970 case SkPath::kClose_Verb:
971 case SkPath::kDone_Verb:
972 closeSeen = true;
973 break;
974 }
975 verbSeen = true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400976 }
977
978 this->finishPathPolygon();
Jim Van Verth3645bb02018-06-26 14:58:58 -0400979 return true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400980}
981
Jim Van Verth91af7272017-01-27 14:15:54 -0500982///////////////////////////////////////////////////////////////////////////////////////////////////
983
Brian Salomonaff27a22017-02-06 15:47:44 -0500984class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500985public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500986 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400987 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400988 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500989
Brian Salomon958fbc42017-01-30 17:01:28 -0500990private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400991 bool computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400992 const SkMatrix& shadowTransform);
Jim Van Verthf507c282018-05-11 10:48:20 -0400993 void addToClip(const SkVector& nextPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400994
Brian Salomonaff27a22017-02-06 15:47:44 -0500995 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -0500996};
997
Jim Van Vertha84898d2017-02-06 13:38:23 -0500998SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400999 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -04001000 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -04001001 bool transparent)
Jim Van Verthd737ab92018-09-10 15:19:01 -04001002 : INHERITED(zPlaneParams, transparent) {
Jim Van Verthda965502017-04-11 15:29:14 -04001003
Jim Van Verth1af03d42017-07-31 09:34:58 -04001004 // Compute the blur radius, scale and translation for the spot shadow.
Jim Van Verthda965502017-04-11 15:29:14 -04001005 SkMatrix shadowTransform;
Jim Van Verth3a039d52018-09-14 17:14:47 -04001006 SkScalar outset;
1007 if (!SkDrawShadowMetrics::GetSpotShadowTransform(lightPos, lightRadius,
1008 ctm, zPlaneParams, path.getBounds(),
1009 &shadowTransform, &outset)) {
1010 return;
Jim Van Verthda965502017-04-11 15:29:14 -04001011 }
Jim Van Verthd737ab92018-09-10 15:19:01 -04001012 SkScalar inset = outset;
Jim Van Verthb4366552017-03-27 14:25:29 -04001013
Brian Salomonab664fa2017-03-24 16:07:20 +00001014 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verth3645bb02018-06-26 14:58:58 -04001015 if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) {
1016 return;
1017 }
1018 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
1019 fSucceeded = true; // We don't want to try to blur these cases, so we will
1020 // return an empty SkVertices instead.
Brian Salomon66085ed2017-02-02 15:39:34 -05001021 return;
1022 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001023
Jim Van Verthd737ab92018-09-10 15:19:01 -04001024 // TODO: calculate these reserves better
1025 // Penumbra ring: 3*numPts
1026 // Umbra ring: numPts
1027 // Inner ring: numPts
1028 fPositions.setReserve(5 * path.countPoints());
1029 fColors.setReserve(5 * path.countPoints());
1030 // Penumbra ring: 12*numPts
1031 // Umbra ring: 3*numPts
1032 fIndices.setReserve(15 * path.countPoints());
Jim Van Verthf507c282018-05-11 10:48:20 -04001033
Jim Van Verthf507c282018-05-11 10:48:20 -04001034 if (fIsConvex) {
Jim Van Verthd737ab92018-09-10 15:19:01 -04001035 fSucceeded = this->computeConvexShadow(inset, outset, true);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001036 } else {
Jim Van Verthd737ab92018-09-10 15:19:01 -04001037 fSucceeded = this->computeConcaveShadow(inset, outset);
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001038 }
1039
1040 if (!fSucceeded) {
Jim Van Verthf507c282018-05-11 10:48:20 -04001041 return;
Jim Van Verth91af7272017-01-27 14:15:54 -05001042 }
Jim Van Verthda965502017-04-11 15:29:14 -04001043
Brian Salomon0dda9cb2017-02-03 10:33:25 -05001044 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001045}
1046
Jim Van Verth3645bb02018-06-26 14:58:58 -04001047bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -04001048 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001049
1050 fPathPolygon.setReserve(path.countPoints());
Jim Van Verth3645bb02018-06-26 14:58:58 -04001051 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +00001052
1053 // Walk around the path and compute clip polygon and path polygon.
1054 // Will also accumulate sum of areas for centroid.
1055 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -05001056 SkPath::Iter iter(path, true);
1057 SkPoint pts[4];
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001058 SkPoint clipPts[4];
Jim Van Verth91af7272017-01-27 14:15:54 -05001059 SkPath::Verb verb;
1060
Brian Salomon66085ed2017-02-02 15:39:34 -05001061 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +00001062 static constexpr SkScalar kA = 0.32495117187f;
1063 static constexpr SkScalar kB = 0.44311523437f;
1064 static constexpr SkScalar kC = 0.20141601562f;
1065 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -05001066
1067 SkPoint curvePoint;
1068 SkScalar w;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001069 bool closeSeen = false;
1070 bool verbSeen = false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001071 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001072 if (closeSeen) {
1073 return false;
1074 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001075 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001076 case SkPath::kLine_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001077 ctm.mapPoints(clipPts, &pts[1], 1);
1078 this->addToClip(clipPts[0]);
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001079 this->handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -05001080 break;
1081 case SkPath::kQuad_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001082 ctm.mapPoints(clipPts, pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001083 // point at t = 1/2
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001084 curvePoint.fX = 0.25f*clipPts[0].fX + 0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1085 curvePoint.fY = 0.25f*clipPts[0].fY + 0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001086 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001087 this->addToClip(clipPts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001088 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001089 break;
1090 case SkPath::kConic_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001091 ctm.mapPoints(clipPts, pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001092 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -05001093 // point at t = 1/2
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001094 curvePoint.fX = 0.25f*clipPts[0].fX + w*0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1095 curvePoint.fY = 0.25f*clipPts[0].fY + w*0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY;
Brian Salomon66085ed2017-02-02 15:39:34 -05001096 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
Jim Van Verthf507c282018-05-11 10:48:20 -04001097 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001098 this->addToClip(clipPts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001099 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -05001100 break;
1101 case SkPath::kCubic_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001102 ctm.mapPoints(clipPts, pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -05001103 // point at t = 5/16
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001104 curvePoint.fX = kA*clipPts[0].fX + kB*clipPts[1].fX
1105 + kC*clipPts[2].fX + kD*clipPts[3].fX;
1106 curvePoint.fY = kA*clipPts[0].fY + kB*clipPts[1].fY
1107 + kC*clipPts[2].fY + kD*clipPts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001108 this->addToClip(curvePoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001109 // point at t = 11/16
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001110 curvePoint.fX = kD*clipPts[0].fX + kC*clipPts[1].fX
1111 + kB*clipPts[2].fX + kA*clipPts[3].fX;
1112 curvePoint.fY = kD*clipPts[0].fY + kC*clipPts[1].fY
1113 + kB*clipPts[2].fY + kA*clipPts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001114 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001115 this->addToClip(clipPts[3]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001116 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001117 break;
Brian Salomonab664fa2017-03-24 16:07:20 +00001118 case SkPath::kMove_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001119 if (verbSeen) {
1120 return false;
1121 }
1122 break;
Jim Van Verth91af7272017-01-27 14:15:54 -05001123 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +00001124 case SkPath::kDone_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001125 closeSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001126 break;
1127 default:
1128 SkDEBUGFAIL("unknown verb");
1129 }
Jim Van Verth3645bb02018-06-26 14:58:58 -04001130 verbSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001131 }
1132
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001133 this->finishPathPolygon();
Jim Van Verth3645bb02018-06-26 14:58:58 -04001134 return true;
Brian Salomon66085ed2017-02-02 15:39:34 -05001135}
1136
Jim Van Verthd737ab92018-09-10 15:19:01 -04001137void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
1138 if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count() - 1])) {
1139 fClipPolygon.push_back(point);
Brian Salomon66085ed2017-02-02 15:39:34 -05001140 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001141}
Brian Salomon958fbc42017-01-30 17:01:28 -05001142
1143///////////////////////////////////////////////////////////////////////////////////////////////////
1144
Brian Salomonaff27a22017-02-06 15:47:44 -05001145sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001146 const SkPoint3& zPlane, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001147 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001148 return nullptr;
1149 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001150 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001151 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001152}
1153
Brian Salomonaff27a22017-02-06 15:47:44 -05001154sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001155 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001156 SkScalar lightRadius, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001157 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() ||
Jim Van Verth1989c492018-05-31 13:15:16 -04001158 !lightPos.isFinite() || !(lightPos.fZ >= SK_ScalarNearlyZero) ||
1159 !SkScalarIsFinite(lightRadius) || !(lightRadius >= SK_ScalarNearlyZero)) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001160 return nullptr;
1161 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001162 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001163 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001164}