blob: b454e5f10329a23d7e005a6d8430a12453f3bdb5 [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 Verth41964ed2018-03-28 10:10:30 -040012#include "SkOffsetPolygon.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;
42
Brian Salomonaff27a22017-02-06 15:47:44 -050043 int vertexCount() const { return fPositions.count(); }
44 int indexCount() const { return fIndices.count(); }
45
Jim Van Verthda965502017-04-11 15:29:14 -040046 bool setZOffset(const SkRect& bounds, bool perspective);
47
Jim Van Verthcdaf6612018-06-05 15:21:13 -040048 bool accumulateCentroid(const SkPoint& c, const SkPoint& n);
49 bool checkConvexity(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2);
50 void finishPathPolygon();
Jim Van Verth8760e2f2018-06-12 14:21:38 -040051 void stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
52 SkTDArray<int>* umbraIndices,
53 const SkTDArray<SkPoint>& penumbraPolygon,
54 SkTDArray<int>* penumbraIndices);
Jim Van Verthcdaf6612018-06-05 15:21:13 -040055
56 void handleLine(const SkPoint& p);
Jim Van Vertha84898d2017-02-06 13:38:23 -050057 void handleLine(const SkMatrix& m, SkPoint* p);
Brian Salomon958fbc42017-01-30 17:01:28 -050058
59 void handleQuad(const SkPoint pts[3]);
Jim Van Vertha84898d2017-02-06 13:38:23 -050060 void handleQuad(const SkMatrix& m, SkPoint pts[3]);
Brian Salomon958fbc42017-01-30 17:01:28 -050061
Jim Van Vertha84898d2017-02-06 13:38:23 -050062 void handleCubic(const SkMatrix& m, SkPoint pts[4]);
Brian Salomon958fbc42017-01-30 17:01:28 -050063
Jim Van Vertha84898d2017-02-06 13:38:23 -050064 void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
Brian Salomon958fbc42017-01-30 17:01:28 -050065
Jim Van Verthda965502017-04-11 15:29:14 -040066 bool setTransformedHeightFunc(const SkMatrix& ctm);
Brian Salomon958fbc42017-01-30 17:01:28 -050067
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -040068 bool addArc(const SkVector& nextNormal, bool finishArc);
Jim Van Verthb4366552017-03-27 14:25:29 -040069
Jim Van Verth872da6b2018-04-10 11:24:11 -040070 void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
71 void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
72
Jim Van Verthe308a122017-05-08 14:19:30 -040073 SkScalar heightFunc(SkScalar x, SkScalar y) {
74 return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
75 }
76
77 SkPoint3 fZPlaneParams;
Jim Van Verthda965502017-04-11 15:29:14 -040078 std::function<SkScalar(const SkPoint&)> fTransformedHeightFunc;
79 SkScalar fZOffset;
80 // members for perspective height function
Jim Van Verth4c9b8932017-05-15 13:49:21 -040081 SkPoint3 fTransformedZParams;
Jim Van Verthda965502017-04-11 15:29:14 -040082 SkScalar fPartialDeterminants[3];
83
Brian Salomon958fbc42017-01-30 17:01:28 -050084 // temporary buffer
85 SkTDArray<SkPoint> fPointBuffer;
Brian Salomon0dda9cb2017-02-03 10:33:25 -050086
Jim Van Vertha84898d2017-02-06 13:38:23 -050087 SkTDArray<SkPoint> fPositions;
88 SkTDArray<SkColor> fColors;
89 SkTDArray<uint16_t> fIndices;
90
Jim Van Verthcdaf6612018-06-05 15:21:13 -040091 SkTDArray<SkPoint> fPathPolygon;
92 SkPoint fCentroid;
93 SkScalar fArea;
94 SkScalar fLastArea;
95 int fAreaSignFlips;
96 SkScalar fLastCross;
97
Jim Van Verth76387852017-05-16 16:55:16 -040098 int fFirstVertexIndex;
99 SkVector fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500100 SkPoint fFirstPoint;
101
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500102 bool fSucceeded;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500103 bool fTransparent;
Jim Van Verthf507c282018-05-11 10:48:20 -0400104 bool fIsConvex;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500105
106 SkColor fUmbraColor;
107 SkColor fPenumbraColor;
108
109 SkScalar fRadius;
110 SkScalar fDirection;
111 int fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400112 SkVector fPrevOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500113 SkPoint fPrevPoint;
Brian Salomon958fbc42017-01-30 17:01:28 -0500114};
115
Jim Van Verthda965502017-04-11 15:29:14 -0400116static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
Jim Van Verthbce74962017-01-25 09:39:46 -0500117 SkVector* newNormal) {
118 SkVector normal;
119 // compute perpendicular
120 normal.fX = p0.fY - p1.fY;
121 normal.fY = p1.fX - p0.fX;
Jim Van Verthda965502017-04-11 15:29:14 -0400122 normal *= dir;
Jim Van Verthbce74962017-01-25 09:39:46 -0500123 if (!normal.normalize()) {
124 return false;
125 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500126 *newNormal = normal;
127 return true;
128}
129
130static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
131 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400132 const SkScalar kRecipPixelsPerArcSegment = 0.125f;
Jim Van Verthbce74962017-01-25 09:39:46 -0500133
134 SkScalar rCos = v1.dot(v2);
135 SkScalar rSin = v1.cross(v2);
136 SkScalar theta = SkScalarATan2(rSin, rCos);
137
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400138 int steps = SkScalarRoundToInt(SkScalarAbs(r*theta*kRecipPixelsPerArcSegment));
Jim Van Verthbce74962017-01-25 09:39:46 -0500139
140 SkScalar dTheta = theta / steps;
141 *rotSin = SkScalarSinCos(dTheta, rotCos);
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400142 *n = steps;
Jim Van Verthbce74962017-01-25 09:39:46 -0500143}
144
Jim Van Verthf507c282018-05-11 10:48:20 -0400145static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
146 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
147 static constexpr SkScalar kCloseSqd = kClose * kClose;
148
149 SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
150 return distSq < kCloseSqd;
151}
152
153static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
154 SkVector v0 = p1 - p0;
155 SkVector v1 = p2 - p0;
156 return v0.cross(v1);
157}
158
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400159
Jim Van Verthe308a122017-05-08 14:19:30 -0400160SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
161 : fZPlaneParams(zPlaneParams)
Jim Van Verthda965502017-04-11 15:29:14 -0400162 , fZOffset(0)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400163 , fCentroid({0, 0})
164 , fArea(0)
165 , fLastArea(0)
166 , fAreaSignFlips(0)
167 , fLastCross(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400168 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500169 , fSucceeded(false)
170 , fTransparent(transparent)
Jim Van Verthf507c282018-05-11 10:48:20 -0400171 , fIsConvex(true)
Brian Salomonaff27a22017-02-06 15:47:44 -0500172 , fDirection(1)
173 , fPrevUmbraIndex(-1) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500174 // child classes will set reserve for positions, colors and indices
175}
176
Jim Van Verthda965502017-04-11 15:29:14 -0400177bool SkBaseShadowTessellator::setZOffset(const SkRect& bounds, bool perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400178 SkScalar minZ = this->heightFunc(bounds.fLeft, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400179 if (perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400180 SkScalar z = this->heightFunc(bounds.fLeft, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400181 if (z < minZ) {
182 minZ = z;
183 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400184 z = this->heightFunc(bounds.fRight, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400185 if (z < minZ) {
186 minZ = z;
187 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400188 z = this->heightFunc(bounds.fRight, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400189 if (z < minZ) {
190 minZ = z;
191 }
192 }
193
194 if (minZ < kMinHeight) {
195 fZOffset = -minZ + kMinHeight;
196 return true;
197 }
198
199 return false;
200}
201
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400202bool SkBaseShadowTessellator::accumulateCentroid(const SkPoint& curr, const SkPoint& next) {
203 if (duplicate_pt(curr, next)) {
204 return false;
205 }
206
207 SkScalar quadArea = curr.cross(next);
208 fCentroid.fX += (curr.fX + next.fX) * quadArea;
209 fCentroid.fY += (curr.fY + next.fY) * quadArea;
210 fArea += quadArea;
211 // convexity check
Jim Van Verth3645bb02018-06-26 14:58:58 -0400212 if (quadArea*fLastArea < 0) {
213 ++fAreaSignFlips;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400214 }
Jim Van Verth3645bb02018-06-26 14:58:58 -0400215 fLastArea = quadArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400216
217 return true;
218}
219
220bool SkBaseShadowTessellator::checkConvexity(const SkPoint& p0,
221 const SkPoint& p1,
222 const SkPoint& p2) {
223 SkScalar cross = perp_dot(p0, p1, p2);
224 // skip collinear point
225 if (SkScalarNearlyZero(cross)) {
226 return false;
227 }
228
229 // check for convexity
230 if (fLastCross*cross < 0) {
231 fIsConvex = false;
232 }
233 fLastCross = cross;
234
235 return true;
236}
237
238void SkBaseShadowTessellator::finishPathPolygon() {
239 if (fPathPolygon.count() > 1) {
240 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], fPathPolygon[0])) {
241 // remove coincident point
242 fPathPolygon.pop();
243 }
244 }
245
246 if (fPathPolygon.count() > 2) {
247 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
248 fPathPolygon[fPathPolygon.count() - 1],
249 fPathPolygon[0])) {
250 // remove collinear point
251 fPathPolygon[0] = fPathPolygon[fPathPolygon.count() - 1];
252 fPathPolygon.pop();
253 }
254 }
255
256 fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
257 // It's possible to have a concave path that self-intersects but also passes the
258 // cross-product check (e.g., a star). In that case, the signed area will change signs more
259 // than twice, so we check for that here.
260 if (fAreaSignFlips > 2) {
261 fIsConvex = false;
262 }
Jim Van Verth7deacf42018-06-08 15:13:25 -0400263
264 // if area is positive, winding is ccw
265 fDirection = fArea > 0 ? -1 : 1;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400266}
267
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400268void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
269 SkTDArray<int>* umbraIndices,
270 const SkTDArray<SkPoint>& penumbraPolygon,
271 SkTDArray<int>* penumbraIndices) {
272 // find minimum indices
273 int minIndex = 0;
274 int min = (*penumbraIndices)[0];
275 for (int i = 1; i < (*penumbraIndices).count(); ++i) {
276 if ((*penumbraIndices)[i] < min) {
277 min = (*penumbraIndices)[i];
278 minIndex = i;
279 }
280 }
281 int currPenumbra = minIndex;
282
283 minIndex = 0;
284 min = (*umbraIndices)[0];
285 for (int i = 1; i < (*umbraIndices).count(); ++i) {
286 if ((*umbraIndices)[i] < min) {
287 min = (*umbraIndices)[i];
288 minIndex = i;
289 }
290 }
291 int currUmbra = minIndex;
292
293 // now find a case where the indices are equal (there should be at least one)
294 int maxPenumbraIndex = fPathPolygon.count() - 1;
295 int maxUmbraIndex = fPathPolygon.count() - 1;
296 while ((*penumbraIndices)[currPenumbra] != (*umbraIndices)[currUmbra]) {
297 if ((*penumbraIndices)[currPenumbra] < (*umbraIndices)[currUmbra]) {
298 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
299 maxPenumbraIndex = (*penumbraIndices)[currPenumbra];
300 currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
301 } else {
302 (*umbraIndices)[currUmbra] += fPathPolygon.count();
303 maxUmbraIndex = (*umbraIndices)[currUmbra];
304 currUmbra = (currUmbra + 1) % umbraPolygon.count();
305 }
306 }
307
308 *fPositions.push() = penumbraPolygon[currPenumbra];
309 *fColors.push() = fPenumbraColor;
310 int prevPenumbraIndex = 0;
311 *fPositions.push() = umbraPolygon[currUmbra];
312 *fColors.push() = fUmbraColor;
313 fPrevUmbraIndex = 1;
314
315 int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
316 int nextUmbra = (currUmbra + 1) % umbraPolygon.count();
317 while ((*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex ||
318 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
319
320 if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) {
321 // advance both one step
322 *fPositions.push() = penumbraPolygon[nextPenumbra];
323 *fColors.push() = fPenumbraColor;
324 int currPenumbraIndex = fPositions.count() - 1;
325
326 *fPositions.push() = umbraPolygon[nextUmbra];
327 *fColors.push() = fUmbraColor;
328 int currUmbraIndex = fPositions.count() - 1;
329
330 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
331 fPrevUmbraIndex, currUmbraIndex);
332
333 prevPenumbraIndex = currPenumbraIndex;
334 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
335 currPenumbra = nextPenumbra;
336 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
337
338 fPrevUmbraIndex = currUmbraIndex;
339 (*umbraIndices)[currUmbra] += fPathPolygon.count();
340 currUmbra = nextUmbra;
341 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
342 }
343
344 while ((*penumbraIndices)[nextPenumbra] < (*umbraIndices)[nextUmbra] &&
345 (*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex) {
346 // fill out penumbra arc
347 *fPositions.push() = penumbraPolygon[nextPenumbra];
348 *fColors.push() = fPenumbraColor;
349 int currPenumbraIndex = fPositions.count() - 1;
350
351 this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
352
353 prevPenumbraIndex = currPenumbraIndex;
354 // this ensures the ordering when we wrap around
355 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
356 currPenumbra = nextPenumbra;
357 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
358 }
359
360 while ((*umbraIndices)[nextUmbra] < (*penumbraIndices)[nextPenumbra] &&
361 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
362 // fill out umbra arc
363 *fPositions.push() = umbraPolygon[nextUmbra];
364 *fColors.push() = fUmbraColor;
365 int currUmbraIndex = fPositions.count() - 1;
366
367 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
368
369 fPrevUmbraIndex = currUmbraIndex;
370 // this ensures the ordering when we wrap around
371 (*umbraIndices)[currUmbra] += fPathPolygon.count();
372 currUmbra = nextUmbra;
373 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
374 }
375 }
376 // finish up by advancing both one step
377 *fPositions.push() = penumbraPolygon[nextPenumbra];
378 *fColors.push() = fPenumbraColor;
379 int currPenumbraIndex = fPositions.count() - 1;
380
381 *fPositions.push() = umbraPolygon[nextUmbra];
382 *fColors.push() = fUmbraColor;
383 int currUmbraIndex = fPositions.count() - 1;
384
385 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
386 fPrevUmbraIndex, currUmbraIndex);
387
388 if (fTransparent) {
389 // TODO: fill penumbra
390 }
391}
392
393
Jim Van Vertha84898d2017-02-06 13:38:23 -0500394// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500395#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500396static const SkScalar kQuadTolerance = 0.2f;
397static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500398#endif
Jim Van Vertha84898d2017-02-06 13:38:23 -0500399static const SkScalar kConicTolerance = 0.5f;
400
Jim Van Verth3645bb02018-06-26 14:58:58 -0400401// clamps the point to the nearest 16th of a pixel
402static void sanitize_point(const SkPoint& in, SkPoint* out) {
403 out->fX = SkScalarRoundToScalar(16.f*in.fX)*0.0625f;
404 out->fY = SkScalarRoundToScalar(16.f*in.fY)*0.0625f;
405}
406
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400407void SkBaseShadowTessellator::handleLine(const SkPoint& p) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400408 SkPoint pSanitized;
409 sanitize_point(p, &pSanitized);
410
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400411 if (fPathPolygon.count() > 0) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400412 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400413 // skip coincident point
414 return;
415 }
416 }
417
418 if (fPathPolygon.count() > 1) {
419 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
420 fPathPolygon[fPathPolygon.count() - 1],
Jim Van Verth3645bb02018-06-26 14:58:58 -0400421 pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400422 // remove collinear point
423 fPathPolygon.pop();
424 }
425 }
426
Jim Van Verth3645bb02018-06-26 14:58:58 -0400427 *fPathPolygon.push() = pSanitized;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400428}
429
Brian Salomonaff27a22017-02-06 15:47:44 -0500430void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500431 m.mapPoints(p, 1);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400432
Jim Van Vertha84898d2017-02-06 13:38:23 -0500433 this->handleLine(*p);
434}
435
Brian Salomonaff27a22017-02-06 15:47:44 -0500436void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500437#if SK_SUPPORT_GPU
Jim Van Vertha947e292018-02-26 13:54:34 -0500438 // check for degeneracy
439 SkVector v0 = pts[1] - pts[0];
440 SkVector v1 = pts[2] - pts[0];
441 if (SkScalarNearlyZero(v0.cross(v1))) {
442 return;
443 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500444 // TODO: Pull PathUtils out of Ganesh?
445 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400446 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500447 SkPoint* target = fPointBuffer.begin();
448 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
449 kQuadTolerance, &target, maxCount);
450 fPointBuffer.setCount(count);
451 for (int i = 0; i < count; i++) {
452 this->handleLine(fPointBuffer[i]);
453 }
454#else
455 // for now, just to draw something
456 this->handleLine(pts[1]);
457 this->handleLine(pts[2]);
458#endif
459}
460
Brian Salomonaff27a22017-02-06 15:47:44 -0500461void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500462 m.mapPoints(pts, 3);
463 this->handleQuad(pts);
464}
465
Brian Salomonaff27a22017-02-06 15:47:44 -0500466void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500467 m.mapPoints(pts, 4);
468#if SK_SUPPORT_GPU
469 // TODO: Pull PathUtils out of Ganesh?
470 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400471 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500472 SkPoint* target = fPointBuffer.begin();
473 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
474 kCubicTolerance, &target, maxCount);
475 fPointBuffer.setCount(count);
476 for (int i = 0; i < count; i++) {
477 this->handleLine(fPointBuffer[i]);
478 }
479#else
480 // for now, just to draw something
481 this->handleLine(pts[1]);
482 this->handleLine(pts[2]);
483 this->handleLine(pts[3]);
484#endif
485}
486
Brian Salomonaff27a22017-02-06 15:47:44 -0500487void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400488 if (m.hasPerspective()) {
489 w = SkConic::TransformW(pts, w, m);
490 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500491 m.mapPoints(pts, 3);
492 SkAutoConicToQuads quadder;
493 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
494 SkPoint lastPoint = *(quads++);
495 int count = quadder.countQuads();
496 for (int i = 0; i < count; ++i) {
497 SkPoint quadPts[3];
498 quadPts[0] = lastPoint;
499 quadPts[1] = quads[0];
500 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
501 this->handleQuad(quadPts);
502 lastPoint = quadPts[2];
503 quads += 2;
504 }
505}
506
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400507bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500508 // fill in fan from previous quad
509 SkScalar rotSin, rotCos;
510 int numSteps;
Jim Van Verth76387852017-05-16 16:55:16 -0400511 compute_radial_steps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
512 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400513 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400514 SkVector currNormal;
515 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
516 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
517 *fPositions.push() = fPrevPoint + currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500518 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400519 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500520
Jim Van Verthda965502017-04-11 15:29:14 -0400521 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500522 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400523 if (finishArc && numSteps) {
Jim Van Verthda965502017-04-11 15:29:14 -0400524 *fPositions.push() = fPrevPoint + nextNormal;
525 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400526 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Verthda965502017-04-11 15:29:14 -0400527 }
Jim Van Verth76387852017-05-16 16:55:16 -0400528 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400529
530 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500531}
532
Jim Van Verth872da6b2018-04-10 11:24:11 -0400533void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
534 auto indices = fIndices.append(3);
535
536 indices[0] = index0;
537 indices[1] = index1;
538 indices[2] = index2;
539}
540
541void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
542 uint16_t index2, uint16_t index3) {
543 auto indices = fIndices.append(6);
544
545 indices[0] = index0;
546 indices[1] = index1;
547 indices[2] = index2;
548
549 indices[3] = index2;
550 indices[4] = index1;
551 indices[5] = index3;
552}
553
Jim Van Verthda965502017-04-11 15:29:14 -0400554bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400555 if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400556 fTransformedHeightFunc = [this](const SkPoint& p) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400557 return fZPlaneParams.fZ;
Jim Van Verthda965502017-04-11 15:29:14 -0400558 };
559 } else {
560 SkMatrix ctmInverse;
Jim Van Vertha947e292018-02-26 13:54:34 -0500561 if (!ctm.invert(&ctmInverse) || !ctmInverse.isFinite()) {
Jim Van Verthda965502017-04-11 15:29:14 -0400562 return false;
563 }
Jim Van Verthda965502017-04-11 15:29:14 -0400564 // multiply by transpose
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400565 fTransformedZParams = SkPoint3::Make(
Jim Van Verthe308a122017-05-08 14:19:30 -0400566 ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
567 ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
568 ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
569
570 ctmInverse[SkMatrix::kMSkewX] * fZPlaneParams.fX +
571 ctmInverse[SkMatrix::kMScaleY] * fZPlaneParams.fY +
572 ctmInverse[SkMatrix::kMPersp1] * fZPlaneParams.fZ,
573
574 ctmInverse[SkMatrix::kMTransX] * fZPlaneParams.fX +
575 ctmInverse[SkMatrix::kMTransY] * fZPlaneParams.fY +
576 ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
577 );
Jim Van Verthda965502017-04-11 15:29:14 -0400578
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400579 if (ctm.hasPerspective()) {
580 // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
581 // so pre-compute those values that are independent of X and Y.
582 // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
583 fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
584 ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
585 fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
586 ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
587 fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
588 ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
589 SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
590 ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
591 ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
Jim Van Verthda965502017-04-11 15:29:14 -0400592
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400593 // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
594 // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
595 fTransformedZParams.fX *= ctmDeterminant;
596 fTransformedZParams.fY *= ctmDeterminant;
597 fTransformedZParams.fZ *= ctmDeterminant;
Jim Van Verthda965502017-04-11 15:29:14 -0400598
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400599 fTransformedHeightFunc = [this](const SkPoint& p) {
600 SkScalar denom = p.fX * fPartialDeterminants[0] +
601 p.fY * fPartialDeterminants[1] +
602 fPartialDeterminants[2];
603 SkScalar w = SkScalarFastInvert(denom);
604 return fZOffset + w*(fTransformedZParams.fX * p.fX +
605 fTransformedZParams.fY * p.fY +
606 fTransformedZParams.fZ);
607 };
608 } else {
609 fTransformedHeightFunc = [this](const SkPoint& p) {
610 return fZOffset + fTransformedZParams.fX * p.fX +
611 fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
612 };
613 }
Jim Van Verthda965502017-04-11 15:29:14 -0400614 }
615
616 return true;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500617}
618
619
620//////////////////////////////////////////////////////////////////////////////////////////////////
621
Brian Salomonaff27a22017-02-06 15:47:44 -0500622class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500623public:
624 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400625 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500626
627private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400628 bool computePathPolygon(const SkPath& path, const SkMatrix& ctm);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400629 bool computeConvexShadow();
630 bool computeConcaveShadow();
631
Jim Van Verth7deacf42018-06-08 15:13:25 -0400632 void handlePolyPoint(const SkPoint& p, bool finalPoint);
633 void addEdge(const SkPoint& nextPoint, const SkVector& nextNormal, bool finalEdge);
634 void splitEdge(const SkPoint& nextPoint, const SkVector& insetNormal,
635 const SkPoint& penumbraPoint, const SkPoint& umbraPoint, SkColor umbraColor);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500636
Jim Van Verthda965502017-04-11 15:29:14 -0400637 static constexpr auto kMaxEdgeLenSqr = 20 * 20;
Jim Van Verth76387852017-05-16 16:55:16 -0400638 static constexpr auto kInsetFactor = -0.5f;
Jim Van Verthda965502017-04-11 15:29:14 -0400639
640 SkScalar offset(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400641 return SkDrawShadowMetrics::AmbientBlurRadius(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400642 }
643 SkColor umbraColor(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400644 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(z));
Jim Van Verth060d9822017-05-04 09:58:17 -0400645 return SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
Jim Van Verthda965502017-04-11 15:29:14 -0400646 }
647
Jim Van Verth76387852017-05-16 16:55:16 -0400648 bool fSplitFirstEdge;
649 bool fSplitPreviousEdge;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500650
Brian Salomonaff27a22017-02-06 15:47:44 -0500651 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500652};
653
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500654SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500655 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400656 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500657 bool transparent)
Jim Van Verth76387852017-05-16 16:55:16 -0400658 : INHERITED(zPlaneParams, transparent)
659 , fSplitFirstEdge(false)
660 , fSplitPreviousEdge(false) {
Jim Van Verthda965502017-04-11 15:29:14 -0400661 // Set base colors
Jim Van Verth1af03d42017-07-31 09:34:58 -0400662 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(heightFunc(0, 0)));
Jim Van Verthb4366552017-03-27 14:25:29 -0400663 // umbraColor is the interior value, penumbraColor the exterior value.
664 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
665 // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
666 // the final alpha.
Jim Van Verth060d9822017-05-04 09:58:17 -0400667 fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
668 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400669
Jim Van Verthda965502017-04-11 15:29:14 -0400670 // make sure we're not below the canvas plane
671 this->setZOffset(path.getBounds(), ctm.hasPerspective());
672
Jim Van Verth7c8008c2018-02-07 15:02:50 -0500673 if (!this->setTransformedHeightFunc(ctm)) {
674 return;
675 }
Jim Van Verthda965502017-04-11 15:29:14 -0400676
Jim Van Verth3645bb02018-06-26 14:58:58 -0400677 if (!this->computePathPolygon(path, ctm)) {
678 return;
679 }
680 if (fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
681 fSucceeded = true; // We don't want to try to blur these cases, so we will
682 // return an empty SkVertices instead.
683 return;
684 }
685
Jim Van Verthbce74962017-01-25 09:39:46 -0500686 // Outer ring: 3*numPts
687 // Middle ring: numPts
688 fPositions.setReserve(4 * path.countPoints());
689 fColors.setReserve(4 * path.countPoints());
690 // Outer ring: 12*numPts
691 // Middle ring: 0
692 fIndices.setReserve(12 * path.countPoints());
693
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400694 if (fIsConvex) {
695 fSucceeded = this->computeConvexShadow();
696 } else {
697 fSucceeded = this->computeConcaveShadow();
698 }
699}
700
Jim Van Verth3645bb02018-06-26 14:58:58 -0400701bool SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400702 fPathPolygon.setReserve(path.countPoints());
703
704 // walk around the path, tessellate and generate outer ring
705 // if original path is transparent, will accumulate sum of points for centroid
706 SkPath::Iter iter(path, true);
707 SkPoint pts[4];
708 SkPath::Verb verb;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400709 bool verbSeen = false;
710 bool closeSeen = false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400711 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400712 if (closeSeen) {
713 return false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400714 }
Jim Van Verth3645bb02018-06-26 14:58:58 -0400715 switch (verb) {
716 case SkPath::kLine_Verb:
717 this->handleLine(ctm, &pts[1]);
718 break;
719 case SkPath::kQuad_Verb:
720 this->handleQuad(ctm, pts);
721 break;
722 case SkPath::kCubic_Verb:
723 this->handleCubic(ctm, pts);
724 break;
725 case SkPath::kConic_Verb:
726 this->handleConic(ctm, pts, iter.conicWeight());
727 break;
728 case SkPath::kMove_Verb:
729 if (verbSeen) {
730 return false;
731 }
732 break;
733 case SkPath::kClose_Verb:
734 case SkPath::kDone_Verb:
735 closeSeen = true;
736 break;
737 }
738 verbSeen = true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400739 }
740
741 this->finishPathPolygon();
Jim Van Verth3645bb02018-06-26 14:58:58 -0400742 return true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400743}
744
745bool SkAmbientShadowTessellator::computeConvexShadow() {
Jim Van Verth7deacf42018-06-08 15:13:25 -0400746 int polyCount = fPathPolygon.count();
747 if (polyCount < 3) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400748 return false;
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500749 }
750
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400751 // Add center point for fan if needed
752 if (fTransparent) {
753 *fPositions.push() = fCentroid;
754 *fColors.push() = this->umbraColor(fTransformedHeightFunc(fCentroid));
755 }
756
Jim Van Verth7deacf42018-06-08 15:13:25 -0400757 // Initialize
758 SkVector normal;
759 if (!compute_normal(fPathPolygon[polyCount-1], fPathPolygon[0], fDirection, &normal)) {
760 // the polygon should be sanitized, so any issues at this point are unrecoverable
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400761 return false;
Jim Van Verthf507c282018-05-11 10:48:20 -0400762 }
Jim Van Verth7deacf42018-06-08 15:13:25 -0400763 fFirstPoint = fPathPolygon[polyCount - 1];
764 fFirstVertexIndex = fPositions.count();
765 SkScalar z = fTransformedHeightFunc(fFirstPoint);
766 fFirstOutset = normal;
767 fFirstOutset *= this->offset(z);
Jim Van Verthf507c282018-05-11 10:48:20 -0400768
Jim Van Verth7deacf42018-06-08 15:13:25 -0400769 fPrevOutset = fFirstOutset;
770 fPrevPoint = fFirstPoint;
771 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500772
Jim Van Verth7deacf42018-06-08 15:13:25 -0400773 // Add the first quad
774 *fPositions.push() = fFirstPoint;
775 *fColors.push() = this->umbraColor(z);
776 *fPositions.push() = fFirstPoint + fFirstOutset;
777 *fColors.push() = fPenumbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -0400778
Jim Van Verth7deacf42018-06-08 15:13:25 -0400779 z = fTransformedHeightFunc(fPathPolygon[0]);
780 fRadius = this->offset(z);
781 fUmbraColor = this->umbraColor(z);
782 this->addEdge(fPathPolygon[0], normal, false);
Jim Van Verthbce74962017-01-25 09:39:46 -0500783
Jim Van Verth7deacf42018-06-08 15:13:25 -0400784 // Process the remaining points
785 for (int i = 1; i < fPathPolygon.count(); ++i) {
786 this->handlePolyPoint(fPathPolygon[i], i == fPathPolygon.count()-1);
787 }
788 SkASSERT(this->indexCount());
Jim Van Verthda965502017-04-11 15:29:14 -0400789
Jim Van Verth7deacf42018-06-08 15:13:25 -0400790 // Final fan
791 SkASSERT(fPositions.count() >= 3);
792 if (this->addArc(fFirstOutset, false)) {
793 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1, fFirstVertexIndex + 1);
794 } else {
795 // arc is too small, set the first penumbra point to be the same position
796 // as the last one
797 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verthbce74962017-01-25 09:39:46 -0500798 }
799
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400800 return true;
Jim Van Verthbce74962017-01-25 09:39:46 -0500801}
802
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400803bool SkAmbientShadowTessellator::computeConcaveShadow() {
804 // TODO: remove when we support filling the penumbra
805 if (fTransparent) {
806 return false;
Jim Van Verthf507c282018-05-11 10:48:20 -0400807 }
808
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400809 // generate inner ring
810 SkTDArray<SkPoint> umbraPolygon;
811 SkTDArray<int> umbraIndices;
812 umbraIndices.setReserve(fPathPolygon.count());
813 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), 0.5f,
814 &umbraPolygon, &umbraIndices)) {
815 // TODO: figure out how to handle this case
816 return false;
817 }
818
819 // generate outer ring
820 SkTDArray<SkPoint> penumbraPolygon;
821 SkTDArray<int> penumbraIndices;
822 penumbraPolygon.setReserve(umbraPolygon.count());
823 penumbraIndices.setReserve(umbraPolygon.count());
Jim Van Verthe39bc9f2018-06-14 14:43:00 -0400824
825 auto offsetFunc = [this](const SkPoint& p) { return -this->offset(fTransformedHeightFunc(p)); };
826 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), offsetFunc,
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400827 &penumbraPolygon, &penumbraIndices)) {
828 // TODO: figure out how to handle this case
829 return false;
830 }
831
832 if (!umbraPolygon.count() || !penumbraPolygon.count()) {
833 return false;
834 }
835
836 // attach the rings together
837 this->stitchConcaveRings(umbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
838
839 return true;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400840}
841
Jim Van Verth7deacf42018-06-08 15:13:25 -0400842void SkAmbientShadowTessellator::handlePolyPoint(const SkPoint& p, bool finalPoint) {
Jim Van Verthbce74962017-01-25 09:39:46 -0500843 SkVector normal;
Jim Van Verth76387852017-05-16 16:55:16 -0400844 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400845 SkVector scaledNormal = normal;
846 scaledNormal *= fRadius;
847 this->addArc(scaledNormal, true);
848 SkScalar z = fTransformedHeightFunc(p);
849 fRadius = this->offset(z);
850 fUmbraColor = this->umbraColor(z);
Jim Van Verth7deacf42018-06-08 15:13:25 -0400851 this->addEdge(p, normal, finalPoint);
Jim Van Verthbce74962017-01-25 09:39:46 -0500852 }
853}
854
Jim Van Verth7deacf42018-06-08 15:13:25 -0400855void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
856 bool finalEdge) {
Jim Van Verth76387852017-05-16 16:55:16 -0400857 // We compute the inset in two stages: first we inset by half the current normal,
858 // then on the next addEdge() we add half of the next normal to get an average of the two
859 SkVector insetNormal = nextNormal;
860 insetNormal *= 0.5f*kInsetFactor;
861
862 // Adding the other half of the average for the previous edge
863 fPositions[fPrevUmbraIndex] += insetNormal;
864
Jim Van Verth7deacf42018-06-08 15:13:25 -0400865 SkPoint umbraPoint;
866 if (finalEdge) {
867 // Again, adding the other half of the average for the previous edge
868 fPositions[fFirstVertexIndex] += insetNormal;
869 // we multiply by another half because now we're adding to an average of an average
870 if (fSplitFirstEdge) {
871 fPositions[fFirstVertexIndex + 2] += insetNormal * 0.5f;
872 }
873 umbraPoint = fPositions[fFirstVertexIndex];
874 } else {
875 umbraPoint = nextPoint + insetNormal;
876 }
Jim Van Verth76387852017-05-16 16:55:16 -0400877 SkVector outsetNormal = nextNormal;
878 outsetNormal *= fRadius;
879 SkPoint penumbraPoint = nextPoint + outsetNormal;
880
Jim Van Verth7deacf42018-06-08 15:13:25 -0400881 // make sure we don't end up with a sharp alpha edge along the quad diagonal
882 this->splitEdge(nextPoint, insetNormal, penumbraPoint, umbraPoint, fUmbraColor);
883
884 // add next quad
885 int prevPenumbraIndex;
886 int currUmbraIndex;
887 if (finalEdge) {
888 prevPenumbraIndex = fPositions.count() - 1;
889 currUmbraIndex = fFirstVertexIndex;
890 } else {
891 prevPenumbraIndex = fPositions.count() - 1;
892 *fPositions.push() = umbraPoint;
893 *fColors.push() = fUmbraColor;
894 currUmbraIndex = fPositions.count() - 1;
895 }
896
897 *fPositions.push() = penumbraPoint;
898 *fColors.push() = fPenumbraColor;
899
900 // set triangularization to get best interpolation of color
901 if (fColors[fPrevUmbraIndex] > fUmbraColor) {
902 this->appendQuad(fPrevUmbraIndex, prevPenumbraIndex,
903 currUmbraIndex, fPositions.count() - 1);
904 } else {
905 this->appendQuad(currUmbraIndex, fPositions.count() - 1,
906 fPrevUmbraIndex, prevPenumbraIndex);
907 }
908
909 // if transparent, add to center fan
910 if (fTransparent) {
911 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
912 }
913
914 fPrevUmbraIndex = currUmbraIndex;
915 fPrevPoint = nextPoint;
916 fPrevOutset = outsetNormal;
917}
918
919void SkAmbientShadowTessellator::splitEdge(const SkPoint& nextPoint, const SkVector& insetNormal,
920 const SkPoint& penumbraPoint, const SkPoint& umbraPoint,
921 SkColor umbraColor) {
Jim Van Verth76387852017-05-16 16:55:16 -0400922 // For split edges, we're adding an average of two averages, so we multiply by another half
923 if (fSplitPreviousEdge) {
Jim Van Verth7deacf42018-06-08 15:13:25 -0400924 fPositions[fPrevUmbraIndex - 2] += insetNormal*SK_ScalarHalf;
Jim Van Verth76387852017-05-16 16:55:16 -0400925 }
926
927 // Split the edge to make sure we don't end up with a sharp alpha edge along the quad diagonal
Jim Van Verth7deacf42018-06-08 15:13:25 -0400928 if (fColors[fPrevUmbraIndex] != umbraColor &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500929 SkPointPriv::DistanceToSqd(nextPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400930
931 // This is lacking 1/4 of the next inset -- we'll add it the next time we call addEdge()
932 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + umbraPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400933 centerPoint *= 0.5f;
934 *fPositions.push() = centerPoint;
Jim Van Verth7deacf42018-06-08 15:13:25 -0400935 *fColors.push() = SkPMLerp(umbraColor, fColors[fPrevUmbraIndex], 128);
936 centerPoint = fPositions[fPositions.count() - 2] + penumbraPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400937 centerPoint *= 0.5f;
938 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400939 *fColors.push() = fPenumbraColor;
940
941 // set triangularization to get best interpolation of color
942 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400943 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
944 fPositions.count() - 2, fPositions.count() - 1);
Jim Van Verthda965502017-04-11 15:29:14 -0400945 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400946 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
947 fPrevUmbraIndex, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -0400948 }
949
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400950 // if transparent, add to center fan
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400951 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400952 this->appendTriangle(0, fPrevUmbraIndex, fPositions.count() - 2);
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400953 }
954
Jim Van Verth76387852017-05-16 16:55:16 -0400955 fSplitPreviousEdge = true;
956 if (fPrevUmbraIndex == fFirstVertexIndex) {
957 fSplitFirstEdge = true;
958 }
Jim Van Verthda965502017-04-11 15:29:14 -0400959 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Verth76387852017-05-16 16:55:16 -0400960 } else {
961 fSplitPreviousEdge = false;
Jim Van Verthda965502017-04-11 15:29:14 -0400962 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500963}
Jim Van Verth91af7272017-01-27 14:15:54 -0500964
Jim Van Verth7deacf42018-06-08 15:13:25 -0400965
Jim Van Verth91af7272017-01-27 14:15:54 -0500966///////////////////////////////////////////////////////////////////////////////////////////////////
967
Brian Salomonaff27a22017-02-06 15:47:44 -0500968class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500969public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500970 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400971 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400972 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500973
Brian Salomon958fbc42017-01-30 17:01:28 -0500974private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400975 bool computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400976 const SkMatrix& shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000977 void computeClipVectorsAndTestCentroid();
Brian Salomon66085ed2017-02-02 15:39:34 -0500978 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
Brian Salomonab664fa2017-03-24 16:07:20 +0000979 int getClosestUmbraPoint(const SkPoint& point);
Brian Salomon958fbc42017-01-30 17:01:28 -0500980
Jim Van Verth872da6b2018-04-10 11:24:11 -0400981 bool computeConvexShadow(SkScalar radius);
982 bool computeConcaveShadow(SkScalar radius);
983
Jim Van Verth7deacf42018-06-08 15:13:25 -0400984 bool handlePolyPoint(const SkPoint& p, bool lastPoint);
Brian Salomon958fbc42017-01-30 17:01:28 -0500985
986 void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
Jim Van Verth8c2de8f2018-05-14 11:20:58 -0400987 bool addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex);
Jim Van Verth7deacf42018-06-08 15:13:25 -0400988 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal, bool lastEdge);
Jim Van Verthf507c282018-05-11 10:48:20 -0400989 void addToClip(const SkVector& nextPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400990
991 SkScalar offset(SkScalar z) {
992 float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
993 return fLightRadius*zRatio;
994 }
995
996 SkScalar fLightZ;
997 SkScalar fLightRadius;
998 SkScalar fOffsetAdjust;
Brian Salomon958fbc42017-01-30 17:01:28 -0500999
Brian Salomon958fbc42017-01-30 17:01:28 -05001000 SkTDArray<SkPoint> fClipPolygon;
Brian Salomon66085ed2017-02-02 15:39:34 -05001001 SkTDArray<SkVector> fClipVectors;
Jim Van Vertha84898d2017-02-06 13:38:23 -05001002
Brian Salomonab664fa2017-03-24 16:07:20 +00001003 SkTDArray<SkPoint> fUmbraPolygon;
1004 int fCurrClipPoint;
1005 int fCurrUmbraPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001006 bool fPrevUmbraOutside;
1007 bool fFirstUmbraOutside;
Jim Van Vertha84898d2017-02-06 13:38:23 -05001008 bool fValidUmbra;
Brian Salomon958fbc42017-01-30 17:01:28 -05001009
Brian Salomonaff27a22017-02-06 15:47:44 -05001010 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -05001011};
1012
Jim Van Vertha84898d2017-02-06 13:38:23 -05001013SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001014 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -04001015 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -04001016 bool transparent)
Jim Van Verthe308a122017-05-08 14:19:30 -04001017 : INHERITED(zPlaneParams, transparent)
Jim Van Verthda965502017-04-11 15:29:14 -04001018 , fLightZ(lightPos.fZ)
1019 , fLightRadius(lightRadius)
1020 , fOffsetAdjust(0)
1021 , fCurrClipPoint(0)
1022 , fPrevUmbraOutside(false)
1023 , fFirstUmbraOutside(false)
1024 , fValidUmbra(true) {
1025
1026 // make sure we're not below the canvas plane
1027 if (this->setZOffset(path.getBounds(), ctm.hasPerspective())) {
1028 // Adjust light height and radius
1029 fLightRadius *= (fLightZ + fZOffset) / fLightZ;
1030 fLightZ += fZOffset;
1031 }
Jim Van Verthb4366552017-03-27 14:25:29 -04001032
1033 // Set radius and colors
Jim Van Verthb4366552017-03-27 14:25:29 -04001034 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verthe308a122017-05-08 14:19:30 -04001035 SkScalar occluderHeight = this->heightFunc(center.fX, center.fY) + fZOffset;
Jim Van Verth060d9822017-05-04 09:58:17 -04001036 fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
1037 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -04001038
Jim Van Verth1af03d42017-07-31 09:34:58 -04001039 // Compute the blur radius, scale and translation for the spot shadow.
1040 SkScalar radius;
Jim Van Verthda965502017-04-11 15:29:14 -04001041 SkMatrix shadowTransform;
1042 if (!ctm.hasPerspective()) {
Jim Van Verth1af03d42017-07-31 09:34:58 -04001043 SkScalar scale;
1044 SkVector translate;
1045 SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, fLightZ,
1046 lightRadius, &radius, &scale, &translate);
Jim Van Verthda965502017-04-11 15:29:14 -04001047 shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
1048 } else {
1049 // For perspective, we have a scale, a z-shear, and another projective divide --
1050 // this varies at each point so we can't use an affine transform.
1051 // We'll just apply this to each generated point in turn.
1052 shadowTransform.reset();
1053 // Also can't cull the center (for now).
1054 fTransparent = true;
Jim Van Verth1af03d42017-07-31 09:34:58 -04001055 radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
Jim Van Verthda965502017-04-11 15:29:14 -04001056 }
Jim Van Verth1af03d42017-07-31 09:34:58 -04001057 fRadius = radius;
Jim Van Verthda965502017-04-11 15:29:14 -04001058 SkMatrix fullTransform = SkMatrix::Concat(shadowTransform, ctm);
1059
1060 // Set up our reverse mapping
Jim Van Verth7c8008c2018-02-07 15:02:50 -05001061 if (!this->setTransformedHeightFunc(fullTransform)) {
1062 return;
1063 }
Jim Van Verthb4366552017-03-27 14:25:29 -04001064
Brian Salomonab664fa2017-03-24 16:07:20 +00001065 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verth3645bb02018-06-26 14:58:58 -04001066 if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) {
1067 return;
1068 }
1069 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
1070 fSucceeded = true; // We don't want to try to blur these cases, so we will
1071 // return an empty SkVertices instead.
Brian Salomon66085ed2017-02-02 15:39:34 -05001072 return;
1073 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001074
Jim Van Verthf507c282018-05-11 10:48:20 -04001075 // compute vectors for clip tests
1076 this->computeClipVectorsAndTestCentroid();
1077
Brian Salomonab664fa2017-03-24 16:07:20 +00001078 // check to see if umbra collapses
Jim Van Verthf507c282018-05-11 10:48:20 -04001079 if (fIsConvex) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001080 SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
1081 fPathPolygon[0],
1082 fPathPolygon[1]);
1083 SkRect bounds;
1084 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
1085 for (int i = 1; i < fPathPolygon.count(); ++i) {
1086 int j = i + 1;
1087 if (i == fPathPolygon.count() - 1) {
1088 j = 0;
1089 }
1090 SkPoint currPoint = fPathPolygon[i];
1091 SkPoint nextPoint = fPathPolygon[j];
1092 SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
1093 nextPoint);
1094 if (distSq < minDistSq) {
1095 minDistSq = distSq;
1096 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001097 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001098 static constexpr auto kTolerance = 1.0e-2f;
1099 if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
1100 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
1101 SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
1102 fOffsetAdjust = newRadius - radius;
1103 SkScalar ratio = 128 * (newRadius + radius) / radius;
1104 // they aren't PMColors, but the interpolation algorithm is the same
1105 fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
1106 radius = newRadius;
Brian Salomonab664fa2017-03-24 16:07:20 +00001107 }
1108 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001109
Jim Van Verth3645bb02018-06-26 14:58:58 -04001110 // TODO: calculate these reserves better
1111 // Penumbra ring: 3*numPts
1112 // Umbra ring: numPts
1113 // Inner ring: numPts
1114 fPositions.setReserve(5 * path.countPoints());
1115 fColors.setReserve(5 * path.countPoints());
1116 // Penumbra ring: 12*numPts
1117 // Umbra ring: 3*numPts
1118 fIndices.setReserve(15 * path.countPoints());
1119
Jim Van Verthf507c282018-05-11 10:48:20 -04001120 if (fIsConvex) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001121 fSucceeded = this->computeConvexShadow(radius);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001122 } else {
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001123 fSucceeded = this->computeConcaveShadow(radius);
1124 }
1125
1126 if (!fSucceeded) {
Jim Van Verthf507c282018-05-11 10:48:20 -04001127 return;
Jim Van Verth91af7272017-01-27 14:15:54 -05001128 }
Jim Van Verthda965502017-04-11 15:29:14 -04001129
1130 if (ctm.hasPerspective()) {
1131 for (int i = 0; i < fPositions.count(); ++i) {
1132 SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
1133 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
1134 fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
1135 fPositions[i].fY = (fPositions[i].fY*fLightZ - lightPos.fY*pathZ)*factor;
1136 }
1137#ifdef DRAW_CENTROID
1138 SkScalar pathZ = fTransformedHeightFunc(fCentroid);
1139 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
1140 fCentroid.fX = (fCentroid.fX*fLightZ - lightPos.fX*pathZ)*factor;
1141 fCentroid.fY = (fCentroid.fY*fLightZ - lightPos.fY*pathZ)*factor;
1142#endif
1143 }
1144#ifdef DRAW_CENTROID
1145 *fPositions.push() = fCentroid + SkVector::Make(-2, -2);
1146 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1147 *fPositions.push() = fCentroid + SkVector::Make(2, -2);
1148 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1149 *fPositions.push() = fCentroid + SkVector::Make(-2, 2);
1150 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1151 *fPositions.push() = fCentroid + SkVector::Make(2, 2);
1152 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1153
Jim Van Verth872da6b2018-04-10 11:24:11 -04001154 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
1155 fPositions.count() - 4, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -04001156#endif
1157
Brian Salomon0dda9cb2017-02-03 10:33:25 -05001158 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001159}
1160
Jim Van Verthf507c282018-05-11 10:48:20 -04001161void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
1162 if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count()-1])) {
1163 *fClipPolygon.push() = point;
1164 }
1165}
1166
Jim Van Verth3645bb02018-06-26 14:58:58 -04001167bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -04001168 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001169
1170 fPathPolygon.setReserve(path.countPoints());
Jim Van Verth3645bb02018-06-26 14:58:58 -04001171 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +00001172
1173 // Walk around the path and compute clip polygon and path polygon.
1174 // Will also accumulate sum of areas for centroid.
1175 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -05001176 SkPath::Iter iter(path, true);
1177 SkPoint pts[4];
1178 SkPath::Verb verb;
1179
Brian Salomon66085ed2017-02-02 15:39:34 -05001180 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +00001181 static constexpr SkScalar kA = 0.32495117187f;
1182 static constexpr SkScalar kB = 0.44311523437f;
1183 static constexpr SkScalar kC = 0.20141601562f;
1184 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -05001185
1186 SkPoint curvePoint;
1187 SkScalar w;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001188 bool closeSeen = false;
1189 bool verbSeen = false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001190 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001191 if (closeSeen) {
1192 return false;
1193 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001194 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001195 case SkPath::kLine_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001196 ctm.mapPoints(&pts[1], 1);
Jim Van Verthf507c282018-05-11 10:48:20 -04001197 this->addToClip(pts[1]);
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001198 this->handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -05001199 break;
1200 case SkPath::kQuad_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001201 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001202 // point at t = 1/2
1203 curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
1204 curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001205 this->addToClip(curvePoint);
1206 this->addToClip(pts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001207 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001208 break;
1209 case SkPath::kConic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001210 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001211 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -05001212 // point at t = 1/2
Brian Salomon66085ed2017-02-02 15:39:34 -05001213 curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
1214 curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
1215 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
Jim Van Verthf507c282018-05-11 10:48:20 -04001216 this->addToClip(curvePoint);
1217 this->addToClip(pts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001218 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -05001219 break;
1220 case SkPath::kCubic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001221 ctm.mapPoints(pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -05001222 // point at t = 5/16
1223 curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
1224 curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001225 this->addToClip(curvePoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001226 // point at t = 11/16
1227 curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
1228 curvePoint.fY = kD*pts[0].fY + kC*pts[1].fY + kB*pts[2].fY + kA*pts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001229 this->addToClip(curvePoint);
1230 this->addToClip(pts[3]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001231 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001232 break;
Brian Salomonab664fa2017-03-24 16:07:20 +00001233 case SkPath::kMove_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001234 if (verbSeen) {
1235 return false;
1236 }
1237 break;
Jim Van Verth91af7272017-01-27 14:15:54 -05001238 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +00001239 case SkPath::kDone_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001240 closeSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001241 break;
1242 default:
1243 SkDEBUGFAIL("unknown verb");
1244 }
Jim Van Verth3645bb02018-06-26 14:58:58 -04001245 verbSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001246 }
1247
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001248 this->finishPathPolygon();
Brian Salomonab664fa2017-03-24 16:07:20 +00001249 fCurrClipPoint = fClipPolygon.count() - 1;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001250 return true;
Brian Salomon66085ed2017-02-02 15:39:34 -05001251}
1252
Brian Salomonab664fa2017-03-24 16:07:20 +00001253void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
Brian Salomon66085ed2017-02-02 15:39:34 -05001254 SkASSERT(fClipPolygon.count() >= 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001255
Brian Salomonab664fa2017-03-24 16:07:20 +00001256 // init clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001257 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
Jim Van Verthf507c282018-05-11 10:48:20 -04001258 SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001259 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001260
1261 // init centroid check
1262 bool hiddenCentroid = true;
Jim Van Verthf507c282018-05-11 10:48:20 -04001263 v1 = fCentroid - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001264 SkScalar initCross = v0.cross(v1);
1265
1266 for (int p = 1; p < fClipPolygon.count(); ++p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001267 // add to clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001268 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
1269 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001270 // Determine if transformed centroid is inside clipPolygon.
Brian Salomonab664fa2017-03-24 16:07:20 +00001271 v1 = fCentroid - fClipPolygon[p];
Brian Salomon66085ed2017-02-02 15:39:34 -05001272 if (initCross*v0.cross(v1) <= 0) {
1273 hiddenCentroid = false;
1274 }
1275 }
1276 SkASSERT(fClipVectors.count() == fClipPolygon.count());
1277
Brian Salomonab664fa2017-03-24 16:07:20 +00001278 fTransparent = fTransparent || !hiddenCentroid;
Brian Salomon66085ed2017-02-02 15:39:34 -05001279}
1280
1281bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
1282 SkPoint* clipPoint) {
1283 SkVector segmentVector = centroid - umbraPoint;
1284
Brian Salomonab664fa2017-03-24 16:07:20 +00001285 int startClipPoint = fCurrClipPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001286 do {
Brian Salomonab664fa2017-03-24 16:07:20 +00001287 SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
1288 SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
Brian Salomon66085ed2017-02-02 15:39:34 -05001289 SkScalar t_num = dp.cross(segmentVector);
1290 // if line segments are nearly parallel
1291 if (SkScalarNearlyZero(denom)) {
1292 // and collinear
1293 if (SkScalarNearlyZero(t_num)) {
1294 return false;
1295 }
1296 // otherwise are separate, will try the next poly segment
1297 // else if crossing lies within poly segment
1298 } else if (t_num >= 0 && t_num <= denom) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001299 SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
Brian Salomon66085ed2017-02-02 15:39:34 -05001300 // if umbra point is inside the clip polygon
Jim Van Verthda965502017-04-11 15:29:14 -04001301 if (s_num >= 0 && s_num <= denom) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001302 segmentVector *= s_num/denom;
1303 *clipPoint = umbraPoint + segmentVector;
1304 return true;
1305 }
1306 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001307 fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
1308 } while (fCurrClipPoint != startClipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001309
1310 return false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001311}
1312
Brian Salomonab664fa2017-03-24 16:07:20 +00001313int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
Cary Clarkdf429f32017-11-08 11:44:31 -05001314 SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraPoint]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001315 int index = fCurrUmbraPoint;
1316 int dir = 1;
1317 int next = (index + dir) % fUmbraPolygon.count();
1318
1319 // init travel direction
Cary Clarkdf429f32017-11-08 11:44:31 -05001320 SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001321 if (distance < minDistance) {
1322 index = next;
1323 minDistance = distance;
1324 } else {
1325 dir = fUmbraPolygon.count()-1;
1326 }
1327
1328 // iterate until we find a point that increases the distance
1329 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001330 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001331 while (distance < minDistance) {
1332 index = next;
1333 minDistance = distance;
1334 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001335 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001336 }
1337
1338 fCurrUmbraPoint = index;
1339 return index;
1340}
1341
Jim Van Verth872da6b2018-04-10 11:24:11 -04001342bool SkSpotShadowTessellator::computeConvexShadow(SkScalar radius) {
1343 // generate inner ring
1344 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1345 &fUmbraPolygon)) {
1346 // this shouldn't happen, but just in case we'll inset using the centroid
1347 fValidUmbra = false;
1348 }
1349
1350 // walk around the path polygon, generate outer ring and connect to inner ring
1351 if (fTransparent) {
1352 *fPositions.push() = fCentroid;
1353 *fColors.push() = fUmbraColor;
1354 }
1355 fCurrUmbraPoint = 0;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001356
Jim Van Verth7deacf42018-06-08 15:13:25 -04001357 // initial setup
1358 // add first quad
1359 int polyCount = fPathPolygon.count();
1360 if (!compute_normal(fPathPolygon[polyCount-1], fPathPolygon[0], fDirection, &fFirstOutset)) {
1361 // polygon should be sanitized by this point, so this is unrecoverable
Jim Van Verth872da6b2018-04-10 11:24:11 -04001362 return false;
1363 }
1364
Jim Van Verth7deacf42018-06-08 15:13:25 -04001365 fFirstOutset *= fRadius;
1366 fFirstPoint = fPathPolygon[polyCount - 1];
1367 fFirstVertexIndex = fPositions.count();
1368 fPrevOutset = fFirstOutset;
1369 fPrevPoint = fFirstPoint;
1370 fPrevUmbraIndex = -1;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001371
Jim Van Verth7deacf42018-06-08 15:13:25 -04001372 this->addInnerPoint(fFirstPoint, &fPrevUmbraIndex);
1373
1374 if (!fTransparent) {
1375 SkPoint clipPoint;
1376 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
1377 fCentroid, &clipPoint);
1378 if (isOutside) {
1379 *fPositions.push() = clipPoint;
1380 *fColors.push() = fUmbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001381 }
Jim Van Verth7deacf42018-06-08 15:13:25 -04001382 fPrevUmbraOutside = isOutside;
1383 fFirstUmbraOutside = isOutside;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001384 }
1385
Jim Van Verth7deacf42018-06-08 15:13:25 -04001386 SkPoint newPoint = fFirstPoint + fFirstOutset;
1387 *fPositions.push() = newPoint;
1388 *fColors.push() = fPenumbraColor;
1389 this->addEdge(fPathPolygon[0], fFirstOutset, false);
1390
1391 for (int i = 1; i < polyCount; ++i) {
1392 if (!this->handlePolyPoint(fPathPolygon[i], i == polyCount-1)) {
1393 return false;
1394 }
1395 }
1396 SkASSERT(this->indexCount());
1397
Jim Van Verth872da6b2018-04-10 11:24:11 -04001398 // final fan
Jim Van Verth7deacf42018-06-08 15:13:25 -04001399 SkASSERT(fPositions.count() >= 3);
1400 if (this->addArc(fFirstOutset, false)) {
1401 if (fFirstUmbraOutside) {
1402 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1403 fFirstVertexIndex + 2);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001404 } else {
Jim Van Verth7deacf42018-06-08 15:13:25 -04001405 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1406 fFirstVertexIndex + 1);
1407 }
1408 } else {
1409 // no arc added, fix up by setting first penumbra point position to last one
1410 if (fFirstUmbraOutside) {
1411 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
1412 } else {
1413 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verth872da6b2018-04-10 11:24:11 -04001414 }
1415 }
1416
1417 return true;
1418}
1419
1420bool SkSpotShadowTessellator::computeConcaveShadow(SkScalar radius) {
1421 // TODO: remove when we support filling the penumbra
1422 if (fTransparent) {
1423 return false;
1424 }
1425
1426 // generate inner ring
1427 SkTDArray<int> umbraIndices;
1428 umbraIndices.setReserve(fPathPolygon.count());
1429 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1430 &fUmbraPolygon, &umbraIndices)) {
1431 // TODO: figure out how to handle this case
1432 return false;
1433 }
1434
1435 // generate outer ring
1436 SkTDArray<SkPoint> penumbraPolygon;
1437 SkTDArray<int> penumbraIndices;
1438 penumbraPolygon.setReserve(fUmbraPolygon.count());
1439 penumbraIndices.setReserve(fUmbraPolygon.count());
1440 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -radius,
1441 &penumbraPolygon, &penumbraIndices)) {
1442 // TODO: figure out how to handle this case
1443 return false;
1444 }
1445
1446 if (!fUmbraPolygon.count() || !penumbraPolygon.count()) {
1447 return false;
1448 }
1449
1450 // attach the rings together
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001451 this->stitchConcaveRings(fUmbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001452
1453 return true;
1454}
1455
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001456void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
Jim Van Verth91af7272017-01-27 14:15:54 -05001457 SkPoint* pts, int count) {
1458 // TODO: vectorize
1459 for (int i = 0; i < count; ++i) {
1460 pts[i] *= scale;
1461 pts[i] += xlate;
1462 }
1463}
1464
Jim Van Verth7deacf42018-06-08 15:13:25 -04001465bool SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p, bool lastPoint) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001466 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -04001467 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
1468 normal *= fRadius;
1469 this->addArc(normal, true);
Jim Van Verth7deacf42018-06-08 15:13:25 -04001470 this->addEdge(p, normal, lastPoint);
Jim Van Verth91af7272017-01-27 14:15:54 -05001471 }
Jim Van Verthb55eb282017-07-18 14:13:45 -04001472
1473 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001474}
1475
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001476bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001477 SkPoint umbraPoint;
1478 if (!fValidUmbra) {
1479 SkVector v = fCentroid - pathPoint;
1480 v *= 0.95f;
1481 umbraPoint = pathPoint + v;
Jim Van Verth91af7272017-01-27 14:15:54 -05001482 } else {
Brian Salomonab664fa2017-03-24 16:07:20 +00001483 umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
Jim Van Verth91af7272017-01-27 14:15:54 -05001484 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001485
Jim Van Verth91af7272017-01-27 14:15:54 -05001486 fPrevPoint = pathPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001487
1488 // merge "close" points
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001489 if (fPrevUmbraIndex == -1 ||
Brian Salomonab664fa2017-03-24 16:07:20 +00001490 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001491 // if we've wrapped around, don't add a new point
1492 if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
1493 *currUmbraIndex = fFirstVertexIndex;
1494 } else {
1495 *currUmbraIndex = fPositions.count();
1496 *fPositions.push() = umbraPoint;
1497 *fColors.push() = fUmbraColor;
1498 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001499 return false;
1500 } else {
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001501 *currUmbraIndex = fPrevUmbraIndex;
Brian Salomonab664fa2017-03-24 16:07:20 +00001502 return true;
1503 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001504}
1505
Jim Van Verth7deacf42018-06-08 15:13:25 -04001506void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
1507 bool lastEdge) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001508 // add next umbra point
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001509 int currUmbraIndex;
Jim Van Verth7deacf42018-06-08 15:13:25 -04001510 bool duplicate;
1511 if (lastEdge) {
1512 duplicate = false;
1513 currUmbraIndex = fFirstVertexIndex;
1514 fPrevPoint = nextPoint;
1515 } else {
1516 duplicate = this->addInnerPoint(nextPoint, &currUmbraIndex);
1517 }
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001518 int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
1519 ? fPositions.count()-1
1520 : fPositions.count()-2;
Brian Salomonab664fa2017-03-24 16:07:20 +00001521 if (!duplicate) {
1522 // add to center fan if transparent or centroid showing
1523 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001524 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001525 // otherwise add to clip ring
1526 } else {
Brian Salomon66085ed2017-02-02 15:39:34 -05001527 SkPoint clipPoint;
Jim Van Verth7deacf42018-06-08 15:13:25 -04001528 bool isOutside = lastEdge ? fFirstUmbraOutside
1529 : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
1530 &clipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001531 if (isOutside) {
Jim Van Verth7deacf42018-06-08 15:13:25 -04001532 if (!lastEdge) {
1533 *fPositions.push() = clipPoint;
1534 *fColors.push() = fUmbraColor;
1535 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001536 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001537 if (fPrevUmbraOutside) {
1538 // fill out quad
Jim Van Verth872da6b2018-04-10 11:24:11 -04001539 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
1540 fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001541 }
1542 } else if (fPrevUmbraOutside) {
1543 // add tri
Jim Van Verth872da6b2018-04-10 11:24:11 -04001544 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001545 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001546
Brian Salomon66085ed2017-02-02 15:39:34 -05001547 fPrevUmbraOutside = isOutside;
1548 }
1549 }
1550
1551 // add next penumbra point and quad
Jim Van Verth91af7272017-01-27 14:15:54 -05001552 SkPoint newPoint = nextPoint + nextNormal;
1553 *fPositions.push() = newPoint;
1554 *fColors.push() = fPenumbraColor;
1555
Brian Salomonab664fa2017-03-24 16:07:20 +00001556 if (!duplicate) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001557 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001558 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001559 this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
Jim Van Verth91af7272017-01-27 14:15:54 -05001560
Brian Salomon66085ed2017-02-02 15:39:34 -05001561 fPrevUmbraIndex = currUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -04001562 fPrevOutset = nextNormal;
Jim Van Verth91af7272017-01-27 14:15:54 -05001563}
Brian Salomon958fbc42017-01-30 17:01:28 -05001564
1565///////////////////////////////////////////////////////////////////////////////////////////////////
1566
Brian Salomonaff27a22017-02-06 15:47:44 -05001567sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001568 const SkPoint3& zPlane, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001569 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001570 return nullptr;
1571 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001572 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001573 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001574}
1575
Brian Salomonaff27a22017-02-06 15:47:44 -05001576sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001577 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001578 SkScalar lightRadius, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001579 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() ||
Jim Van Verth1989c492018-05-31 13:15:16 -04001580 !lightPos.isFinite() || !(lightPos.fZ >= SK_ScalarNearlyZero) ||
1581 !SkScalarIsFinite(lightRadius) || !(lightRadius >= SK_ScalarNearlyZero)) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001582 return nullptr;
1583 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001584 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001585 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001586}