blob: 3d84e8fdadae702a3aa5b1fd159a6a4d7ce95196 [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;
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
Jim Van Verthf507c282018-05-11 10:48:20 -0400130static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
131 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
132 static constexpr SkScalar kCloseSqd = kClose * kClose;
133
134 SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
135 return distSq < kCloseSqd;
136}
137
138static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
139 SkVector v0 = p1 - p0;
140 SkVector v1 = p2 - p0;
141 return v0.cross(v1);
142}
143
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400144
Jim Van Verthe308a122017-05-08 14:19:30 -0400145SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
146 : fZPlaneParams(zPlaneParams)
Jim Van Verthda965502017-04-11 15:29:14 -0400147 , fZOffset(0)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400148 , fCentroid({0, 0})
149 , fArea(0)
150 , fLastArea(0)
151 , fAreaSignFlips(0)
152 , fLastCross(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400153 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500154 , fSucceeded(false)
155 , fTransparent(transparent)
Jim Van Verthf507c282018-05-11 10:48:20 -0400156 , fIsConvex(true)
Brian Salomonaff27a22017-02-06 15:47:44 -0500157 , fDirection(1)
158 , fPrevUmbraIndex(-1) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500159 // child classes will set reserve for positions, colors and indices
160}
161
Jim Van Verthda965502017-04-11 15:29:14 -0400162bool SkBaseShadowTessellator::setZOffset(const SkRect& bounds, bool perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400163 SkScalar minZ = this->heightFunc(bounds.fLeft, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400164 if (perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400165 SkScalar z = this->heightFunc(bounds.fLeft, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400166 if (z < minZ) {
167 minZ = z;
168 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400169 z = this->heightFunc(bounds.fRight, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400170 if (z < minZ) {
171 minZ = z;
172 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400173 z = this->heightFunc(bounds.fRight, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400174 if (z < minZ) {
175 minZ = z;
176 }
177 }
178
179 if (minZ < kMinHeight) {
180 fZOffset = -minZ + kMinHeight;
181 return true;
182 }
183
184 return false;
185}
186
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400187bool SkBaseShadowTessellator::accumulateCentroid(const SkPoint& curr, const SkPoint& next) {
188 if (duplicate_pt(curr, next)) {
189 return false;
190 }
191
192 SkScalar quadArea = curr.cross(next);
193 fCentroid.fX += (curr.fX + next.fX) * quadArea;
194 fCentroid.fY += (curr.fY + next.fY) * quadArea;
195 fArea += quadArea;
196 // convexity check
Jim Van Verth3645bb02018-06-26 14:58:58 -0400197 if (quadArea*fLastArea < 0) {
198 ++fAreaSignFlips;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400199 }
Jim Van Verth3645bb02018-06-26 14:58:58 -0400200 fLastArea = quadArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400201
202 return true;
203}
204
205bool SkBaseShadowTessellator::checkConvexity(const SkPoint& p0,
206 const SkPoint& p1,
207 const SkPoint& p2) {
208 SkScalar cross = perp_dot(p0, p1, p2);
209 // skip collinear point
210 if (SkScalarNearlyZero(cross)) {
211 return false;
212 }
213
214 // check for convexity
215 if (fLastCross*cross < 0) {
216 fIsConvex = false;
217 }
218 fLastCross = cross;
219
220 return true;
221}
222
223void SkBaseShadowTessellator::finishPathPolygon() {
224 if (fPathPolygon.count() > 1) {
225 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], fPathPolygon[0])) {
226 // remove coincident point
227 fPathPolygon.pop();
228 }
229 }
230
231 if (fPathPolygon.count() > 2) {
232 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
233 fPathPolygon[fPathPolygon.count() - 1],
234 fPathPolygon[0])) {
235 // remove collinear point
236 fPathPolygon[0] = fPathPolygon[fPathPolygon.count() - 1];
237 fPathPolygon.pop();
238 }
239 }
240
241 fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
242 // It's possible to have a concave path that self-intersects but also passes the
243 // cross-product check (e.g., a star). In that case, the signed area will change signs more
244 // than twice, so we check for that here.
245 if (fAreaSignFlips > 2) {
246 fIsConvex = false;
247 }
Jim Van Verth7deacf42018-06-08 15:13:25 -0400248
249 // if area is positive, winding is ccw
250 fDirection = fArea > 0 ? -1 : 1;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400251}
252
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400253void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
254 SkTDArray<int>* umbraIndices,
255 const SkTDArray<SkPoint>& penumbraPolygon,
256 SkTDArray<int>* penumbraIndices) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400257 // TODO: only create and fill indexMap when fTransparent is true?
258 SkAutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.count());
259
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400260 // find minimum indices
261 int minIndex = 0;
262 int min = (*penumbraIndices)[0];
263 for (int i = 1; i < (*penumbraIndices).count(); ++i) {
264 if ((*penumbraIndices)[i] < min) {
265 min = (*penumbraIndices)[i];
266 minIndex = i;
267 }
268 }
269 int currPenumbra = minIndex;
270
271 minIndex = 0;
272 min = (*umbraIndices)[0];
273 for (int i = 1; i < (*umbraIndices).count(); ++i) {
274 if ((*umbraIndices)[i] < min) {
275 min = (*umbraIndices)[i];
276 minIndex = i;
277 }
278 }
279 int currUmbra = minIndex;
280
281 // now find a case where the indices are equal (there should be at least one)
282 int maxPenumbraIndex = fPathPolygon.count() - 1;
283 int maxUmbraIndex = fPathPolygon.count() - 1;
284 while ((*penumbraIndices)[currPenumbra] != (*umbraIndices)[currUmbra]) {
285 if ((*penumbraIndices)[currPenumbra] < (*umbraIndices)[currUmbra]) {
286 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
287 maxPenumbraIndex = (*penumbraIndices)[currPenumbra];
288 currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
289 } else {
290 (*umbraIndices)[currUmbra] += fPathPolygon.count();
291 maxUmbraIndex = (*umbraIndices)[currUmbra];
292 currUmbra = (currUmbra + 1) % umbraPolygon.count();
293 }
294 }
295
296 *fPositions.push() = penumbraPolygon[currPenumbra];
297 *fColors.push() = fPenumbraColor;
298 int prevPenumbraIndex = 0;
299 *fPositions.push() = umbraPolygon[currUmbra];
300 *fColors.push() = fUmbraColor;
301 fPrevUmbraIndex = 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400302 indexMap[currUmbra] = 1;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400303
304 int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
305 int nextUmbra = (currUmbra + 1) % umbraPolygon.count();
306 while ((*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex ||
307 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
308
309 if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) {
310 // advance both one step
311 *fPositions.push() = penumbraPolygon[nextPenumbra];
312 *fColors.push() = fPenumbraColor;
313 int currPenumbraIndex = fPositions.count() - 1;
314
315 *fPositions.push() = umbraPolygon[nextUmbra];
316 *fColors.push() = fUmbraColor;
317 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400318 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400319
320 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
321 fPrevUmbraIndex, currUmbraIndex);
322
323 prevPenumbraIndex = currPenumbraIndex;
324 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
325 currPenumbra = nextPenumbra;
326 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
327
328 fPrevUmbraIndex = currUmbraIndex;
329 (*umbraIndices)[currUmbra] += fPathPolygon.count();
330 currUmbra = nextUmbra;
331 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
332 }
333
334 while ((*penumbraIndices)[nextPenumbra] < (*umbraIndices)[nextUmbra] &&
335 (*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex) {
336 // fill out penumbra arc
337 *fPositions.push() = penumbraPolygon[nextPenumbra];
338 *fColors.push() = fPenumbraColor;
339 int currPenumbraIndex = fPositions.count() - 1;
340
341 this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
342
343 prevPenumbraIndex = currPenumbraIndex;
344 // this ensures the ordering when we wrap around
345 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
346 currPenumbra = nextPenumbra;
347 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
348 }
349
350 while ((*umbraIndices)[nextUmbra] < (*penumbraIndices)[nextPenumbra] &&
351 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
352 // fill out umbra arc
353 *fPositions.push() = umbraPolygon[nextUmbra];
354 *fColors.push() = fUmbraColor;
355 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400356 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400357
358 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
359
360 fPrevUmbraIndex = currUmbraIndex;
361 // this ensures the ordering when we wrap around
362 (*umbraIndices)[currUmbra] += fPathPolygon.count();
363 currUmbra = nextUmbra;
364 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
365 }
366 }
367 // finish up by advancing both one step
368 *fPositions.push() = penumbraPolygon[nextPenumbra];
369 *fColors.push() = fPenumbraColor;
370 int currPenumbraIndex = fPositions.count() - 1;
371
372 *fPositions.push() = umbraPolygon[nextUmbra];
373 *fColors.push() = fUmbraColor;
374 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400375 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400376
377 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
378 fPrevUmbraIndex, currUmbraIndex);
379
380 if (fTransparent) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400381 SkTriangulateSimplePolygon(umbraPolygon.begin(), indexMap, umbraPolygon.count(),
382 &fIndices);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400383 }
384}
385
386
Jim Van Vertha84898d2017-02-06 13:38:23 -0500387// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500388#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500389static const SkScalar kQuadTolerance = 0.2f;
390static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500391#endif
Jim Van Vertha84898d2017-02-06 13:38:23 -0500392static const SkScalar kConicTolerance = 0.5f;
393
Jim Van Verth3645bb02018-06-26 14:58:58 -0400394// clamps the point to the nearest 16th of a pixel
395static void sanitize_point(const SkPoint& in, SkPoint* out) {
396 out->fX = SkScalarRoundToScalar(16.f*in.fX)*0.0625f;
397 out->fY = SkScalarRoundToScalar(16.f*in.fY)*0.0625f;
398}
399
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400400void SkBaseShadowTessellator::handleLine(const SkPoint& p) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400401 SkPoint pSanitized;
402 sanitize_point(p, &pSanitized);
403
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400404 if (fPathPolygon.count() > 0) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400405 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400406 // skip coincident point
407 return;
408 }
409 }
410
411 if (fPathPolygon.count() > 1) {
412 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
413 fPathPolygon[fPathPolygon.count() - 1],
Jim Van Verth3645bb02018-06-26 14:58:58 -0400414 pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400415 // remove collinear point
416 fPathPolygon.pop();
417 }
418 }
419
Jim Van Verth3645bb02018-06-26 14:58:58 -0400420 *fPathPolygon.push() = pSanitized;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400421}
422
Brian Salomonaff27a22017-02-06 15:47:44 -0500423void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500424 m.mapPoints(p, 1);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400425
Jim Van Vertha84898d2017-02-06 13:38:23 -0500426 this->handleLine(*p);
427}
428
Brian Salomonaff27a22017-02-06 15:47:44 -0500429void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500430#if SK_SUPPORT_GPU
Jim Van Vertha947e292018-02-26 13:54:34 -0500431 // check for degeneracy
432 SkVector v0 = pts[1] - pts[0];
433 SkVector v1 = pts[2] - pts[0];
434 if (SkScalarNearlyZero(v0.cross(v1))) {
435 return;
436 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500437 // TODO: Pull PathUtils out of Ganesh?
438 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400439 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500440 SkPoint* target = fPointBuffer.begin();
441 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
442 kQuadTolerance, &target, maxCount);
443 fPointBuffer.setCount(count);
444 for (int i = 0; i < count; i++) {
445 this->handleLine(fPointBuffer[i]);
446 }
447#else
448 // for now, just to draw something
449 this->handleLine(pts[1]);
450 this->handleLine(pts[2]);
451#endif
452}
453
Brian Salomonaff27a22017-02-06 15:47:44 -0500454void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500455 m.mapPoints(pts, 3);
456 this->handleQuad(pts);
457}
458
Brian Salomonaff27a22017-02-06 15:47:44 -0500459void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500460 m.mapPoints(pts, 4);
461#if SK_SUPPORT_GPU
462 // TODO: Pull PathUtils out of Ganesh?
463 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400464 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500465 SkPoint* target = fPointBuffer.begin();
466 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
467 kCubicTolerance, &target, maxCount);
468 fPointBuffer.setCount(count);
469 for (int i = 0; i < count; i++) {
470 this->handleLine(fPointBuffer[i]);
471 }
472#else
473 // for now, just to draw something
474 this->handleLine(pts[1]);
475 this->handleLine(pts[2]);
476 this->handleLine(pts[3]);
477#endif
478}
479
Brian Salomonaff27a22017-02-06 15:47:44 -0500480void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400481 if (m.hasPerspective()) {
482 w = SkConic::TransformW(pts, w, m);
483 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500484 m.mapPoints(pts, 3);
485 SkAutoConicToQuads quadder;
486 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
487 SkPoint lastPoint = *(quads++);
488 int count = quadder.countQuads();
489 for (int i = 0; i < count; ++i) {
490 SkPoint quadPts[3];
491 quadPts[0] = lastPoint;
492 quadPts[1] = quads[0];
493 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
494 this->handleQuad(quadPts);
495 lastPoint = quadPts[2];
496 quads += 2;
497 }
498}
499
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400500bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500501 // fill in fan from previous quad
502 SkScalar rotSin, rotCos;
503 int numSteps;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400504 SkComputeRadialSteps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
Jim Van Verth76387852017-05-16 16:55:16 -0400505 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400506 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400507 SkVector currNormal;
508 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
509 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
510 *fPositions.push() = fPrevPoint + currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500511 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400512 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500513
Jim Van Verthda965502017-04-11 15:29:14 -0400514 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500515 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400516 if (finishArc && numSteps) {
Jim Van Verthda965502017-04-11 15:29:14 -0400517 *fPositions.push() = fPrevPoint + nextNormal;
518 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400519 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Verthda965502017-04-11 15:29:14 -0400520 }
Jim Van Verth76387852017-05-16 16:55:16 -0400521 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400522
523 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500524}
525
Jim Van Verth872da6b2018-04-10 11:24:11 -0400526void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
527 auto indices = fIndices.append(3);
528
529 indices[0] = index0;
530 indices[1] = index1;
531 indices[2] = index2;
532}
533
534void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
535 uint16_t index2, uint16_t index3) {
536 auto indices = fIndices.append(6);
537
538 indices[0] = index0;
539 indices[1] = index1;
540 indices[2] = index2;
541
542 indices[3] = index2;
543 indices[4] = index1;
544 indices[5] = index3;
545}
546
Jim Van Verthda965502017-04-11 15:29:14 -0400547bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400548 if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400549 fTransformedHeightFunc = [this](const SkPoint& p) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400550 return fZPlaneParams.fZ;
Jim Van Verthda965502017-04-11 15:29:14 -0400551 };
552 } else {
553 SkMatrix ctmInverse;
Jim Van Vertha947e292018-02-26 13:54:34 -0500554 if (!ctm.invert(&ctmInverse) || !ctmInverse.isFinite()) {
Jim Van Verthda965502017-04-11 15:29:14 -0400555 return false;
556 }
Jim Van Verthda965502017-04-11 15:29:14 -0400557 // multiply by transpose
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400558 fTransformedZParams = SkPoint3::Make(
Jim Van Verthe308a122017-05-08 14:19:30 -0400559 ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
560 ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
561 ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
562
563 ctmInverse[SkMatrix::kMSkewX] * fZPlaneParams.fX +
564 ctmInverse[SkMatrix::kMScaleY] * fZPlaneParams.fY +
565 ctmInverse[SkMatrix::kMPersp1] * fZPlaneParams.fZ,
566
567 ctmInverse[SkMatrix::kMTransX] * fZPlaneParams.fX +
568 ctmInverse[SkMatrix::kMTransY] * fZPlaneParams.fY +
569 ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
570 );
Jim Van Verthda965502017-04-11 15:29:14 -0400571
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400572 if (ctm.hasPerspective()) {
573 // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
574 // so pre-compute those values that are independent of X and Y.
575 // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
576 fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
577 ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
578 fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
579 ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
580 fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
581 ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
582 SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
583 ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
584 ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
Jim Van Verthda965502017-04-11 15:29:14 -0400585
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400586 // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
587 // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
588 fTransformedZParams.fX *= ctmDeterminant;
589 fTransformedZParams.fY *= ctmDeterminant;
590 fTransformedZParams.fZ *= ctmDeterminant;
Jim Van Verthda965502017-04-11 15:29:14 -0400591
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400592 fTransformedHeightFunc = [this](const SkPoint& p) {
593 SkScalar denom = p.fX * fPartialDeterminants[0] +
594 p.fY * fPartialDeterminants[1] +
595 fPartialDeterminants[2];
596 SkScalar w = SkScalarFastInvert(denom);
597 return fZOffset + w*(fTransformedZParams.fX * p.fX +
598 fTransformedZParams.fY * p.fY +
599 fTransformedZParams.fZ);
600 };
601 } else {
602 fTransformedHeightFunc = [this](const SkPoint& p) {
603 return fZOffset + fTransformedZParams.fX * p.fX +
604 fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
605 };
606 }
Jim Van Verthda965502017-04-11 15:29:14 -0400607 }
608
609 return true;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500610}
611
612
613//////////////////////////////////////////////////////////////////////////////////////////////////
614
Brian Salomonaff27a22017-02-06 15:47:44 -0500615class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500616public:
617 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400618 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500619
620private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400621 bool computePathPolygon(const SkPath& path, const SkMatrix& ctm);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400622 bool computeConvexShadow();
623 bool computeConcaveShadow();
624
Jim Van Verth7deacf42018-06-08 15:13:25 -0400625 void handlePolyPoint(const SkPoint& p, bool finalPoint);
626 void addEdge(const SkPoint& nextPoint, const SkVector& nextNormal, bool finalEdge);
627 void splitEdge(const SkPoint& nextPoint, const SkVector& insetNormal,
628 const SkPoint& penumbraPoint, const SkPoint& umbraPoint, SkColor umbraColor);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500629
Jim Van Verthda965502017-04-11 15:29:14 -0400630 static constexpr auto kMaxEdgeLenSqr = 20 * 20;
Jim Van Verth76387852017-05-16 16:55:16 -0400631 static constexpr auto kInsetFactor = -0.5f;
Jim Van Verthda965502017-04-11 15:29:14 -0400632
633 SkScalar offset(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400634 return SkDrawShadowMetrics::AmbientBlurRadius(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400635 }
636 SkColor umbraColor(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400637 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(z));
Jim Van Verth060d9822017-05-04 09:58:17 -0400638 return SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
Jim Van Verthda965502017-04-11 15:29:14 -0400639 }
640
Jim Van Verth76387852017-05-16 16:55:16 -0400641 bool fSplitFirstEdge;
642 bool fSplitPreviousEdge;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500643
Brian Salomonaff27a22017-02-06 15:47:44 -0500644 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500645};
646
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500647SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500648 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400649 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500650 bool transparent)
Jim Van Verth76387852017-05-16 16:55:16 -0400651 : INHERITED(zPlaneParams, transparent)
652 , fSplitFirstEdge(false)
653 , fSplitPreviousEdge(false) {
Jim Van Verthda965502017-04-11 15:29:14 -0400654 // Set base colors
Jim Van Verth1af03d42017-07-31 09:34:58 -0400655 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(heightFunc(0, 0)));
Jim Van Verthb4366552017-03-27 14:25:29 -0400656 // umbraColor is the interior value, penumbraColor the exterior value.
657 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
658 // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
659 // the final alpha.
Jim Van Verth060d9822017-05-04 09:58:17 -0400660 fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
661 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400662
Jim Van Verthda965502017-04-11 15:29:14 -0400663 // make sure we're not below the canvas plane
664 this->setZOffset(path.getBounds(), ctm.hasPerspective());
665
Jim Van Verth7c8008c2018-02-07 15:02:50 -0500666 if (!this->setTransformedHeightFunc(ctm)) {
667 return;
668 }
Jim Van Verthda965502017-04-11 15:29:14 -0400669
Jim Van Verth3645bb02018-06-26 14:58:58 -0400670 if (!this->computePathPolygon(path, ctm)) {
671 return;
672 }
673 if (fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
674 fSucceeded = true; // We don't want to try to blur these cases, so we will
675 // return an empty SkVertices instead.
676 return;
677 }
678
Jim Van Verthbce74962017-01-25 09:39:46 -0500679 // Outer ring: 3*numPts
680 // Middle ring: numPts
681 fPositions.setReserve(4 * path.countPoints());
682 fColors.setReserve(4 * path.countPoints());
683 // Outer ring: 12*numPts
684 // Middle ring: 0
685 fIndices.setReserve(12 * path.countPoints());
686
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400687 if (fIsConvex) {
688 fSucceeded = this->computeConvexShadow();
689 } else {
690 fSucceeded = this->computeConcaveShadow();
691 }
692}
693
Jim Van Verth3645bb02018-06-26 14:58:58 -0400694bool SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400695 fPathPolygon.setReserve(path.countPoints());
696
697 // walk around the path, tessellate and generate outer ring
698 // if original path is transparent, will accumulate sum of points for centroid
699 SkPath::Iter iter(path, true);
700 SkPoint pts[4];
701 SkPath::Verb verb;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400702 bool verbSeen = false;
703 bool closeSeen = false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400704 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400705 if (closeSeen) {
706 return false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400707 }
Jim Van Verth3645bb02018-06-26 14:58:58 -0400708 switch (verb) {
709 case SkPath::kLine_Verb:
710 this->handleLine(ctm, &pts[1]);
711 break;
712 case SkPath::kQuad_Verb:
713 this->handleQuad(ctm, pts);
714 break;
715 case SkPath::kCubic_Verb:
716 this->handleCubic(ctm, pts);
717 break;
718 case SkPath::kConic_Verb:
719 this->handleConic(ctm, pts, iter.conicWeight());
720 break;
721 case SkPath::kMove_Verb:
722 if (verbSeen) {
723 return false;
724 }
725 break;
726 case SkPath::kClose_Verb:
727 case SkPath::kDone_Verb:
728 closeSeen = true;
729 break;
730 }
731 verbSeen = true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400732 }
733
734 this->finishPathPolygon();
Jim Van Verth3645bb02018-06-26 14:58:58 -0400735 return true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400736}
737
738bool SkAmbientShadowTessellator::computeConvexShadow() {
Jim Van Verth7deacf42018-06-08 15:13:25 -0400739 int polyCount = fPathPolygon.count();
740 if (polyCount < 3) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400741 return false;
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500742 }
743
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400744 // Add center point for fan if needed
745 if (fTransparent) {
746 *fPositions.push() = fCentroid;
747 *fColors.push() = this->umbraColor(fTransformedHeightFunc(fCentroid));
748 }
749
Jim Van Verth7deacf42018-06-08 15:13:25 -0400750 // Initialize
751 SkVector normal;
752 if (!compute_normal(fPathPolygon[polyCount-1], fPathPolygon[0], fDirection, &normal)) {
753 // the polygon should be sanitized, so any issues at this point are unrecoverable
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400754 return false;
Jim Van Verthf507c282018-05-11 10:48:20 -0400755 }
Jim Van Verth7deacf42018-06-08 15:13:25 -0400756 fFirstPoint = fPathPolygon[polyCount - 1];
757 fFirstVertexIndex = fPositions.count();
758 SkScalar z = fTransformedHeightFunc(fFirstPoint);
759 fFirstOutset = normal;
760 fFirstOutset *= this->offset(z);
Jim Van Verthf507c282018-05-11 10:48:20 -0400761
Jim Van Verth7deacf42018-06-08 15:13:25 -0400762 fPrevOutset = fFirstOutset;
763 fPrevPoint = fFirstPoint;
764 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500765
Jim Van Verth7deacf42018-06-08 15:13:25 -0400766 // Add the first quad
767 *fPositions.push() = fFirstPoint;
768 *fColors.push() = this->umbraColor(z);
769 *fPositions.push() = fFirstPoint + fFirstOutset;
770 *fColors.push() = fPenumbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -0400771
Jim Van Verth7deacf42018-06-08 15:13:25 -0400772 z = fTransformedHeightFunc(fPathPolygon[0]);
773 fRadius = this->offset(z);
774 fUmbraColor = this->umbraColor(z);
775 this->addEdge(fPathPolygon[0], normal, false);
Jim Van Verthbce74962017-01-25 09:39:46 -0500776
Jim Van Verth7deacf42018-06-08 15:13:25 -0400777 // Process the remaining points
778 for (int i = 1; i < fPathPolygon.count(); ++i) {
779 this->handlePolyPoint(fPathPolygon[i], i == fPathPolygon.count()-1);
780 }
781 SkASSERT(this->indexCount());
Jim Van Verthda965502017-04-11 15:29:14 -0400782
Jim Van Verth7deacf42018-06-08 15:13:25 -0400783 // Final fan
784 SkASSERT(fPositions.count() >= 3);
785 if (this->addArc(fFirstOutset, false)) {
786 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1, fFirstVertexIndex + 1);
787 } else {
788 // arc is too small, set the first penumbra point to be the same position
789 // as the last one
790 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verthbce74962017-01-25 09:39:46 -0500791 }
792
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400793 return true;
Jim Van Verthbce74962017-01-25 09:39:46 -0500794}
795
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400796bool SkAmbientShadowTessellator::computeConcaveShadow() {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400797 if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400798 return false;
Jim Van Verthf507c282018-05-11 10:48:20 -0400799 }
800
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400801 // generate inner ring
802 SkTDArray<SkPoint> umbraPolygon;
803 SkTDArray<int> umbraIndices;
804 umbraIndices.setReserve(fPathPolygon.count());
805 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), 0.5f,
806 &umbraPolygon, &umbraIndices)) {
807 // TODO: figure out how to handle this case
808 return false;
809 }
810
811 // generate outer ring
812 SkTDArray<SkPoint> penumbraPolygon;
813 SkTDArray<int> penumbraIndices;
814 penumbraPolygon.setReserve(umbraPolygon.count());
815 penumbraIndices.setReserve(umbraPolygon.count());
Jim Van Verthe39bc9f2018-06-14 14:43:00 -0400816
817 auto offsetFunc = [this](const SkPoint& p) { return -this->offset(fTransformedHeightFunc(p)); };
818 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), offsetFunc,
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400819 &penumbraPolygon, &penumbraIndices)) {
820 // TODO: figure out how to handle this case
821 return false;
822 }
823
824 if (!umbraPolygon.count() || !penumbraPolygon.count()) {
825 return false;
826 }
827
828 // attach the rings together
829 this->stitchConcaveRings(umbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
830
831 return true;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400832}
833
Jim Van Verth7deacf42018-06-08 15:13:25 -0400834void SkAmbientShadowTessellator::handlePolyPoint(const SkPoint& p, bool finalPoint) {
Jim Van Verthbce74962017-01-25 09:39:46 -0500835 SkVector normal;
Jim Van Verth76387852017-05-16 16:55:16 -0400836 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400837 SkVector scaledNormal = normal;
838 scaledNormal *= fRadius;
839 this->addArc(scaledNormal, true);
840 SkScalar z = fTransformedHeightFunc(p);
841 fRadius = this->offset(z);
842 fUmbraColor = this->umbraColor(z);
Jim Van Verth7deacf42018-06-08 15:13:25 -0400843 this->addEdge(p, normal, finalPoint);
Jim Van Verthbce74962017-01-25 09:39:46 -0500844 }
845}
846
Jim Van Verth7deacf42018-06-08 15:13:25 -0400847void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
848 bool finalEdge) {
Jim Van Verth76387852017-05-16 16:55:16 -0400849 // We compute the inset in two stages: first we inset by half the current normal,
850 // then on the next addEdge() we add half of the next normal to get an average of the two
851 SkVector insetNormal = nextNormal;
852 insetNormal *= 0.5f*kInsetFactor;
853
854 // Adding the other half of the average for the previous edge
855 fPositions[fPrevUmbraIndex] += insetNormal;
856
Jim Van Verth7deacf42018-06-08 15:13:25 -0400857 SkPoint umbraPoint;
858 if (finalEdge) {
859 // Again, adding the other half of the average for the previous edge
860 fPositions[fFirstVertexIndex] += insetNormal;
861 // we multiply by another half because now we're adding to an average of an average
862 if (fSplitFirstEdge) {
863 fPositions[fFirstVertexIndex + 2] += insetNormal * 0.5f;
864 }
865 umbraPoint = fPositions[fFirstVertexIndex];
866 } else {
867 umbraPoint = nextPoint + insetNormal;
868 }
Jim Van Verth76387852017-05-16 16:55:16 -0400869 SkVector outsetNormal = nextNormal;
870 outsetNormal *= fRadius;
871 SkPoint penumbraPoint = nextPoint + outsetNormal;
872
Jim Van Verth7deacf42018-06-08 15:13:25 -0400873 // make sure we don't end up with a sharp alpha edge along the quad diagonal
874 this->splitEdge(nextPoint, insetNormal, penumbraPoint, umbraPoint, fUmbraColor);
875
876 // add next quad
877 int prevPenumbraIndex;
878 int currUmbraIndex;
879 if (finalEdge) {
880 prevPenumbraIndex = fPositions.count() - 1;
881 currUmbraIndex = fFirstVertexIndex;
882 } else {
883 prevPenumbraIndex = fPositions.count() - 1;
884 *fPositions.push() = umbraPoint;
885 *fColors.push() = fUmbraColor;
886 currUmbraIndex = fPositions.count() - 1;
887 }
888
889 *fPositions.push() = penumbraPoint;
890 *fColors.push() = fPenumbraColor;
891
892 // set triangularization to get best interpolation of color
893 if (fColors[fPrevUmbraIndex] > fUmbraColor) {
894 this->appendQuad(fPrevUmbraIndex, prevPenumbraIndex,
895 currUmbraIndex, fPositions.count() - 1);
896 } else {
897 this->appendQuad(currUmbraIndex, fPositions.count() - 1,
898 fPrevUmbraIndex, prevPenumbraIndex);
899 }
900
901 // if transparent, add to center fan
902 if (fTransparent) {
903 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
904 }
905
906 fPrevUmbraIndex = currUmbraIndex;
907 fPrevPoint = nextPoint;
908 fPrevOutset = outsetNormal;
909}
910
911void SkAmbientShadowTessellator::splitEdge(const SkPoint& nextPoint, const SkVector& insetNormal,
912 const SkPoint& penumbraPoint, const SkPoint& umbraPoint,
913 SkColor umbraColor) {
Jim Van Verth76387852017-05-16 16:55:16 -0400914 // For split edges, we're adding an average of two averages, so we multiply by another half
915 if (fSplitPreviousEdge) {
Jim Van Verth7deacf42018-06-08 15:13:25 -0400916 fPositions[fPrevUmbraIndex - 2] += insetNormal*SK_ScalarHalf;
Jim Van Verth76387852017-05-16 16:55:16 -0400917 }
918
919 // 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 -0400920 if (fColors[fPrevUmbraIndex] != umbraColor &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500921 SkPointPriv::DistanceToSqd(nextPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400922
923 // This is lacking 1/4 of the next inset -- we'll add it the next time we call addEdge()
924 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + umbraPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400925 centerPoint *= 0.5f;
926 *fPositions.push() = centerPoint;
Jim Van Verth7deacf42018-06-08 15:13:25 -0400927 *fColors.push() = SkPMLerp(umbraColor, fColors[fPrevUmbraIndex], 128);
928 centerPoint = fPositions[fPositions.count() - 2] + penumbraPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400929 centerPoint *= 0.5f;
930 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400931 *fColors.push() = fPenumbraColor;
932
933 // set triangularization to get best interpolation of color
934 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400935 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
936 fPositions.count() - 2, fPositions.count() - 1);
Jim Van Verthda965502017-04-11 15:29:14 -0400937 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400938 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
939 fPrevUmbraIndex, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -0400940 }
941
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400942 // if transparent, add to center fan
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400943 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400944 this->appendTriangle(0, fPrevUmbraIndex, fPositions.count() - 2);
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400945 }
946
Jim Van Verth76387852017-05-16 16:55:16 -0400947 fSplitPreviousEdge = true;
948 if (fPrevUmbraIndex == fFirstVertexIndex) {
949 fSplitFirstEdge = true;
950 }
Jim Van Verthda965502017-04-11 15:29:14 -0400951 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Verth76387852017-05-16 16:55:16 -0400952 } else {
953 fSplitPreviousEdge = false;
Jim Van Verthda965502017-04-11 15:29:14 -0400954 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500955}
Jim Van Verth91af7272017-01-27 14:15:54 -0500956
Jim Van Verth7deacf42018-06-08 15:13:25 -0400957
Jim Van Verth91af7272017-01-27 14:15:54 -0500958///////////////////////////////////////////////////////////////////////////////////////////////////
959
Brian Salomonaff27a22017-02-06 15:47:44 -0500960class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500961public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500962 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400963 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400964 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500965
Brian Salomon958fbc42017-01-30 17:01:28 -0500966private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400967 bool computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400968 const SkMatrix& shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000969 void computeClipVectorsAndTestCentroid();
Brian Salomon66085ed2017-02-02 15:39:34 -0500970 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
Brian Salomonab664fa2017-03-24 16:07:20 +0000971 int getClosestUmbraPoint(const SkPoint& point);
Brian Salomon958fbc42017-01-30 17:01:28 -0500972
Jim Van Verth872da6b2018-04-10 11:24:11 -0400973 bool computeConvexShadow(SkScalar radius);
974 bool computeConcaveShadow(SkScalar radius);
975
Jim Van Verth7deacf42018-06-08 15:13:25 -0400976 bool handlePolyPoint(const SkPoint& p, bool lastPoint);
Brian Salomon958fbc42017-01-30 17:01:28 -0500977
978 void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
Jim Van Verth8c2de8f2018-05-14 11:20:58 -0400979 bool addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex);
Jim Van Verth7deacf42018-06-08 15:13:25 -0400980 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal, bool lastEdge);
Jim Van Verthf507c282018-05-11 10:48:20 -0400981 void addToClip(const SkVector& nextPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400982
983 SkScalar offset(SkScalar z) {
984 float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
985 return fLightRadius*zRatio;
986 }
987
988 SkScalar fLightZ;
989 SkScalar fLightRadius;
990 SkScalar fOffsetAdjust;
Brian Salomon958fbc42017-01-30 17:01:28 -0500991
Brian Salomon958fbc42017-01-30 17:01:28 -0500992 SkTDArray<SkPoint> fClipPolygon;
Brian Salomon66085ed2017-02-02 15:39:34 -0500993 SkTDArray<SkVector> fClipVectors;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500994
Brian Salomonab664fa2017-03-24 16:07:20 +0000995 SkTDArray<SkPoint> fUmbraPolygon;
996 int fCurrClipPoint;
997 int fCurrUmbraPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -0500998 bool fPrevUmbraOutside;
999 bool fFirstUmbraOutside;
Jim Van Vertha84898d2017-02-06 13:38:23 -05001000 bool fValidUmbra;
Brian Salomon958fbc42017-01-30 17:01:28 -05001001
Brian Salomonaff27a22017-02-06 15:47:44 -05001002 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -05001003};
1004
Jim Van Vertha84898d2017-02-06 13:38:23 -05001005SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001006 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -04001007 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -04001008 bool transparent)
Jim Van Verthe308a122017-05-08 14:19:30 -04001009 : INHERITED(zPlaneParams, transparent)
Jim Van Verthda965502017-04-11 15:29:14 -04001010 , fLightZ(lightPos.fZ)
1011 , fLightRadius(lightRadius)
1012 , fOffsetAdjust(0)
1013 , fCurrClipPoint(0)
1014 , fPrevUmbraOutside(false)
1015 , fFirstUmbraOutside(false)
1016 , fValidUmbra(true) {
1017
1018 // make sure we're not below the canvas plane
1019 if (this->setZOffset(path.getBounds(), ctm.hasPerspective())) {
1020 // Adjust light height and radius
1021 fLightRadius *= (fLightZ + fZOffset) / fLightZ;
1022 fLightZ += fZOffset;
1023 }
Jim Van Verthb4366552017-03-27 14:25:29 -04001024
1025 // Set radius and colors
Jim Van Verthb4366552017-03-27 14:25:29 -04001026 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verthe308a122017-05-08 14:19:30 -04001027 SkScalar occluderHeight = this->heightFunc(center.fX, center.fY) + fZOffset;
Jim Van Verth060d9822017-05-04 09:58:17 -04001028 fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
1029 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -04001030
Jim Van Verth1af03d42017-07-31 09:34:58 -04001031 // Compute the blur radius, scale and translation for the spot shadow.
1032 SkScalar radius;
Jim Van Verthda965502017-04-11 15:29:14 -04001033 SkMatrix shadowTransform;
1034 if (!ctm.hasPerspective()) {
Jim Van Verth1af03d42017-07-31 09:34:58 -04001035 SkScalar scale;
1036 SkVector translate;
1037 SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, fLightZ,
1038 lightRadius, &radius, &scale, &translate);
Jim Van Verthda965502017-04-11 15:29:14 -04001039 shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
1040 } else {
1041 // For perspective, we have a scale, a z-shear, and another projective divide --
1042 // this varies at each point so we can't use an affine transform.
1043 // We'll just apply this to each generated point in turn.
1044 shadowTransform.reset();
1045 // Also can't cull the center (for now).
1046 fTransparent = true;
Jim Van Verth1af03d42017-07-31 09:34:58 -04001047 radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
Jim Van Verthda965502017-04-11 15:29:14 -04001048 }
Jim Van Verth1af03d42017-07-31 09:34:58 -04001049 fRadius = radius;
Jim Van Verthda965502017-04-11 15:29:14 -04001050 SkMatrix fullTransform = SkMatrix::Concat(shadowTransform, ctm);
1051
1052 // Set up our reverse mapping
Jim Van Verth7c8008c2018-02-07 15:02:50 -05001053 if (!this->setTransformedHeightFunc(fullTransform)) {
1054 return;
1055 }
Jim Van Verthb4366552017-03-27 14:25:29 -04001056
Brian Salomonab664fa2017-03-24 16:07:20 +00001057 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verth3645bb02018-06-26 14:58:58 -04001058 if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) {
1059 return;
1060 }
1061 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
1062 fSucceeded = true; // We don't want to try to blur these cases, so we will
1063 // return an empty SkVertices instead.
Brian Salomon66085ed2017-02-02 15:39:34 -05001064 return;
1065 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001066
Jim Van Verthf507c282018-05-11 10:48:20 -04001067 // compute vectors for clip tests
1068 this->computeClipVectorsAndTestCentroid();
1069
Brian Salomonab664fa2017-03-24 16:07:20 +00001070 // check to see if umbra collapses
Jim Van Verthf507c282018-05-11 10:48:20 -04001071 if (fIsConvex) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001072 SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
1073 fPathPolygon[0],
1074 fPathPolygon[1]);
1075 SkRect bounds;
1076 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
1077 for (int i = 1; i < fPathPolygon.count(); ++i) {
1078 int j = i + 1;
1079 if (i == fPathPolygon.count() - 1) {
1080 j = 0;
1081 }
1082 SkPoint currPoint = fPathPolygon[i];
1083 SkPoint nextPoint = fPathPolygon[j];
1084 SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
1085 nextPoint);
1086 if (distSq < minDistSq) {
1087 minDistSq = distSq;
1088 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001089 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001090 static constexpr auto kTolerance = 1.0e-2f;
1091 if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
1092 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
1093 SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
1094 fOffsetAdjust = newRadius - radius;
1095 SkScalar ratio = 128 * (newRadius + radius) / radius;
1096 // they aren't PMColors, but the interpolation algorithm is the same
1097 fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
1098 radius = newRadius;
Brian Salomonab664fa2017-03-24 16:07:20 +00001099 }
1100 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001101
Jim Van Verth3645bb02018-06-26 14:58:58 -04001102 // TODO: calculate these reserves better
1103 // Penumbra ring: 3*numPts
1104 // Umbra ring: numPts
1105 // Inner ring: numPts
1106 fPositions.setReserve(5 * path.countPoints());
1107 fColors.setReserve(5 * path.countPoints());
1108 // Penumbra ring: 12*numPts
1109 // Umbra ring: 3*numPts
1110 fIndices.setReserve(15 * path.countPoints());
1111
Jim Van Verthf507c282018-05-11 10:48:20 -04001112 if (fIsConvex) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001113 fSucceeded = this->computeConvexShadow(radius);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001114 } else {
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001115 fSucceeded = this->computeConcaveShadow(radius);
1116 }
1117
1118 if (!fSucceeded) {
Jim Van Verthf507c282018-05-11 10:48:20 -04001119 return;
Jim Van Verth91af7272017-01-27 14:15:54 -05001120 }
Jim Van Verthda965502017-04-11 15:29:14 -04001121
1122 if (ctm.hasPerspective()) {
1123 for (int i = 0; i < fPositions.count(); ++i) {
1124 SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
1125 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
1126 fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
1127 fPositions[i].fY = (fPositions[i].fY*fLightZ - lightPos.fY*pathZ)*factor;
1128 }
1129#ifdef DRAW_CENTROID
1130 SkScalar pathZ = fTransformedHeightFunc(fCentroid);
1131 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
1132 fCentroid.fX = (fCentroid.fX*fLightZ - lightPos.fX*pathZ)*factor;
1133 fCentroid.fY = (fCentroid.fY*fLightZ - lightPos.fY*pathZ)*factor;
1134#endif
1135 }
1136#ifdef DRAW_CENTROID
1137 *fPositions.push() = fCentroid + SkVector::Make(-2, -2);
1138 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1139 *fPositions.push() = fCentroid + SkVector::Make(2, -2);
1140 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1141 *fPositions.push() = fCentroid + SkVector::Make(-2, 2);
1142 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1143 *fPositions.push() = fCentroid + SkVector::Make(2, 2);
1144 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1145
Jim Van Verth872da6b2018-04-10 11:24:11 -04001146 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
1147 fPositions.count() - 4, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -04001148#endif
1149
Brian Salomon0dda9cb2017-02-03 10:33:25 -05001150 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001151}
1152
Jim Van Verthf507c282018-05-11 10:48:20 -04001153void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
1154 if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count()-1])) {
1155 *fClipPolygon.push() = point;
1156 }
1157}
1158
Jim Van Verth3645bb02018-06-26 14:58:58 -04001159bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -04001160 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001161
1162 fPathPolygon.setReserve(path.countPoints());
Jim Van Verth3645bb02018-06-26 14:58:58 -04001163 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +00001164
1165 // Walk around the path and compute clip polygon and path polygon.
1166 // Will also accumulate sum of areas for centroid.
1167 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -05001168 SkPath::Iter iter(path, true);
1169 SkPoint pts[4];
1170 SkPath::Verb verb;
1171
Brian Salomon66085ed2017-02-02 15:39:34 -05001172 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +00001173 static constexpr SkScalar kA = 0.32495117187f;
1174 static constexpr SkScalar kB = 0.44311523437f;
1175 static constexpr SkScalar kC = 0.20141601562f;
1176 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -05001177
1178 SkPoint curvePoint;
1179 SkScalar w;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001180 bool closeSeen = false;
1181 bool verbSeen = false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001182 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001183 if (closeSeen) {
1184 return false;
1185 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001186 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001187 case SkPath::kLine_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001188 ctm.mapPoints(&pts[1], 1);
Jim Van Verthf507c282018-05-11 10:48:20 -04001189 this->addToClip(pts[1]);
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001190 this->handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -05001191 break;
1192 case SkPath::kQuad_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001193 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001194 // point at t = 1/2
1195 curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
1196 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 -04001197 this->addToClip(curvePoint);
1198 this->addToClip(pts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001199 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001200 break;
1201 case SkPath::kConic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001202 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001203 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -05001204 // point at t = 1/2
Brian Salomon66085ed2017-02-02 15:39:34 -05001205 curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
1206 curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
1207 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
Jim Van Verthf507c282018-05-11 10:48:20 -04001208 this->addToClip(curvePoint);
1209 this->addToClip(pts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001210 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -05001211 break;
1212 case SkPath::kCubic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001213 ctm.mapPoints(pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -05001214 // point at t = 5/16
1215 curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
1216 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 -04001217 this->addToClip(curvePoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001218 // point at t = 11/16
1219 curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
1220 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 -04001221 this->addToClip(curvePoint);
1222 this->addToClip(pts[3]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001223 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001224 break;
Brian Salomonab664fa2017-03-24 16:07:20 +00001225 case SkPath::kMove_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001226 if (verbSeen) {
1227 return false;
1228 }
1229 break;
Jim Van Verth91af7272017-01-27 14:15:54 -05001230 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +00001231 case SkPath::kDone_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001232 closeSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001233 break;
1234 default:
1235 SkDEBUGFAIL("unknown verb");
1236 }
Jim Van Verth3645bb02018-06-26 14:58:58 -04001237 verbSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001238 }
1239
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001240 this->finishPathPolygon();
Brian Salomonab664fa2017-03-24 16:07:20 +00001241 fCurrClipPoint = fClipPolygon.count() - 1;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001242 return true;
Brian Salomon66085ed2017-02-02 15:39:34 -05001243}
1244
Brian Salomonab664fa2017-03-24 16:07:20 +00001245void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
Brian Salomon66085ed2017-02-02 15:39:34 -05001246 SkASSERT(fClipPolygon.count() >= 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001247
Brian Salomonab664fa2017-03-24 16:07:20 +00001248 // init clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001249 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
Jim Van Verthf507c282018-05-11 10:48:20 -04001250 SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001251 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001252
1253 // init centroid check
1254 bool hiddenCentroid = true;
Jim Van Verthf507c282018-05-11 10:48:20 -04001255 v1 = fCentroid - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001256 SkScalar initCross = v0.cross(v1);
1257
1258 for (int p = 1; p < fClipPolygon.count(); ++p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001259 // add to clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001260 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
1261 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001262 // Determine if transformed centroid is inside clipPolygon.
Brian Salomonab664fa2017-03-24 16:07:20 +00001263 v1 = fCentroid - fClipPolygon[p];
Brian Salomon66085ed2017-02-02 15:39:34 -05001264 if (initCross*v0.cross(v1) <= 0) {
1265 hiddenCentroid = false;
1266 }
1267 }
1268 SkASSERT(fClipVectors.count() == fClipPolygon.count());
1269
Brian Salomonab664fa2017-03-24 16:07:20 +00001270 fTransparent = fTransparent || !hiddenCentroid;
Brian Salomon66085ed2017-02-02 15:39:34 -05001271}
1272
1273bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
1274 SkPoint* clipPoint) {
1275 SkVector segmentVector = centroid - umbraPoint;
1276
Brian Salomonab664fa2017-03-24 16:07:20 +00001277 int startClipPoint = fCurrClipPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001278 do {
Brian Salomonab664fa2017-03-24 16:07:20 +00001279 SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
1280 SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
Brian Salomon66085ed2017-02-02 15:39:34 -05001281 SkScalar t_num = dp.cross(segmentVector);
1282 // if line segments are nearly parallel
1283 if (SkScalarNearlyZero(denom)) {
1284 // and collinear
1285 if (SkScalarNearlyZero(t_num)) {
1286 return false;
1287 }
1288 // otherwise are separate, will try the next poly segment
1289 // else if crossing lies within poly segment
1290 } else if (t_num >= 0 && t_num <= denom) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001291 SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
Brian Salomon66085ed2017-02-02 15:39:34 -05001292 // if umbra point is inside the clip polygon
Jim Van Verthda965502017-04-11 15:29:14 -04001293 if (s_num >= 0 && s_num <= denom) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001294 segmentVector *= s_num/denom;
1295 *clipPoint = umbraPoint + segmentVector;
1296 return true;
1297 }
1298 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001299 fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
1300 } while (fCurrClipPoint != startClipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001301
1302 return false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001303}
1304
Brian Salomonab664fa2017-03-24 16:07:20 +00001305int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
Cary Clarkdf429f32017-11-08 11:44:31 -05001306 SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraPoint]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001307 int index = fCurrUmbraPoint;
1308 int dir = 1;
1309 int next = (index + dir) % fUmbraPolygon.count();
1310
1311 // init travel direction
Cary Clarkdf429f32017-11-08 11:44:31 -05001312 SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001313 if (distance < minDistance) {
1314 index = next;
1315 minDistance = distance;
1316 } else {
1317 dir = fUmbraPolygon.count()-1;
1318 }
1319
1320 // iterate until we find a point that increases the distance
1321 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001322 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001323 while (distance < minDistance) {
1324 index = next;
1325 minDistance = distance;
1326 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001327 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001328 }
1329
1330 fCurrUmbraPoint = index;
1331 return index;
1332}
1333
Jim Van Verth872da6b2018-04-10 11:24:11 -04001334bool SkSpotShadowTessellator::computeConvexShadow(SkScalar radius) {
1335 // generate inner ring
1336 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1337 &fUmbraPolygon)) {
1338 // this shouldn't happen, but just in case we'll inset using the centroid
1339 fValidUmbra = false;
1340 }
1341
1342 // walk around the path polygon, generate outer ring and connect to inner ring
1343 if (fTransparent) {
1344 *fPositions.push() = fCentroid;
1345 *fColors.push() = fUmbraColor;
1346 }
1347 fCurrUmbraPoint = 0;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001348
Jim Van Verth7deacf42018-06-08 15:13:25 -04001349 // initial setup
1350 // add first quad
1351 int polyCount = fPathPolygon.count();
1352 if (!compute_normal(fPathPolygon[polyCount-1], fPathPolygon[0], fDirection, &fFirstOutset)) {
1353 // polygon should be sanitized by this point, so this is unrecoverable
Jim Van Verth872da6b2018-04-10 11:24:11 -04001354 return false;
1355 }
1356
Jim Van Verth7deacf42018-06-08 15:13:25 -04001357 fFirstOutset *= fRadius;
1358 fFirstPoint = fPathPolygon[polyCount - 1];
1359 fFirstVertexIndex = fPositions.count();
1360 fPrevOutset = fFirstOutset;
1361 fPrevPoint = fFirstPoint;
1362 fPrevUmbraIndex = -1;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001363
Jim Van Verth7deacf42018-06-08 15:13:25 -04001364 this->addInnerPoint(fFirstPoint, &fPrevUmbraIndex);
1365
1366 if (!fTransparent) {
1367 SkPoint clipPoint;
1368 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
1369 fCentroid, &clipPoint);
1370 if (isOutside) {
1371 *fPositions.push() = clipPoint;
1372 *fColors.push() = fUmbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001373 }
Jim Van Verth7deacf42018-06-08 15:13:25 -04001374 fPrevUmbraOutside = isOutside;
1375 fFirstUmbraOutside = isOutside;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001376 }
1377
Jim Van Verth7deacf42018-06-08 15:13:25 -04001378 SkPoint newPoint = fFirstPoint + fFirstOutset;
1379 *fPositions.push() = newPoint;
1380 *fColors.push() = fPenumbraColor;
1381 this->addEdge(fPathPolygon[0], fFirstOutset, false);
1382
1383 for (int i = 1; i < polyCount; ++i) {
1384 if (!this->handlePolyPoint(fPathPolygon[i], i == polyCount-1)) {
1385 return false;
1386 }
1387 }
1388 SkASSERT(this->indexCount());
1389
Jim Van Verth872da6b2018-04-10 11:24:11 -04001390 // final fan
Jim Van Verth7deacf42018-06-08 15:13:25 -04001391 SkASSERT(fPositions.count() >= 3);
1392 if (this->addArc(fFirstOutset, false)) {
1393 if (fFirstUmbraOutside) {
1394 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1395 fFirstVertexIndex + 2);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001396 } else {
Jim Van Verth7deacf42018-06-08 15:13:25 -04001397 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1398 fFirstVertexIndex + 1);
1399 }
1400 } else {
1401 // no arc added, fix up by setting first penumbra point position to last one
1402 if (fFirstUmbraOutside) {
1403 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
1404 } else {
1405 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verth872da6b2018-04-10 11:24:11 -04001406 }
1407 }
1408
1409 return true;
1410}
1411
1412bool SkSpotShadowTessellator::computeConcaveShadow(SkScalar radius) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001413 if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001414 return false;
1415 }
1416
1417 // generate inner ring
1418 SkTDArray<int> umbraIndices;
1419 umbraIndices.setReserve(fPathPolygon.count());
1420 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1421 &fUmbraPolygon, &umbraIndices)) {
1422 // TODO: figure out how to handle this case
1423 return false;
1424 }
1425
1426 // generate outer ring
1427 SkTDArray<SkPoint> penumbraPolygon;
1428 SkTDArray<int> penumbraIndices;
1429 penumbraPolygon.setReserve(fUmbraPolygon.count());
1430 penumbraIndices.setReserve(fUmbraPolygon.count());
1431 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -radius,
1432 &penumbraPolygon, &penumbraIndices)) {
1433 // TODO: figure out how to handle this case
1434 return false;
1435 }
1436
1437 if (!fUmbraPolygon.count() || !penumbraPolygon.count()) {
1438 return false;
1439 }
1440
1441 // attach the rings together
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001442 this->stitchConcaveRings(fUmbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001443
1444 return true;
1445}
1446
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001447void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
Jim Van Verth91af7272017-01-27 14:15:54 -05001448 SkPoint* pts, int count) {
1449 // TODO: vectorize
1450 for (int i = 0; i < count; ++i) {
1451 pts[i] *= scale;
1452 pts[i] += xlate;
1453 }
1454}
1455
Jim Van Verth7deacf42018-06-08 15:13:25 -04001456bool SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p, bool lastPoint) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001457 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -04001458 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
1459 normal *= fRadius;
1460 this->addArc(normal, true);
Jim Van Verth7deacf42018-06-08 15:13:25 -04001461 this->addEdge(p, normal, lastPoint);
Jim Van Verth91af7272017-01-27 14:15:54 -05001462 }
Jim Van Verthb55eb282017-07-18 14:13:45 -04001463
1464 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001465}
1466
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001467bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001468 SkPoint umbraPoint;
1469 if (!fValidUmbra) {
1470 SkVector v = fCentroid - pathPoint;
1471 v *= 0.95f;
1472 umbraPoint = pathPoint + v;
Jim Van Verth91af7272017-01-27 14:15:54 -05001473 } else {
Brian Salomonab664fa2017-03-24 16:07:20 +00001474 umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
Jim Van Verth91af7272017-01-27 14:15:54 -05001475 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001476
Jim Van Verth91af7272017-01-27 14:15:54 -05001477 fPrevPoint = pathPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001478
1479 // merge "close" points
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001480 if (fPrevUmbraIndex == -1 ||
Brian Salomonab664fa2017-03-24 16:07:20 +00001481 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001482 // if we've wrapped around, don't add a new point
1483 if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
1484 *currUmbraIndex = fFirstVertexIndex;
1485 } else {
1486 *currUmbraIndex = fPositions.count();
1487 *fPositions.push() = umbraPoint;
1488 *fColors.push() = fUmbraColor;
1489 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001490 return false;
1491 } else {
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001492 *currUmbraIndex = fPrevUmbraIndex;
Brian Salomonab664fa2017-03-24 16:07:20 +00001493 return true;
1494 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001495}
1496
Jim Van Verth7deacf42018-06-08 15:13:25 -04001497void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
1498 bool lastEdge) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001499 // add next umbra point
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001500 int currUmbraIndex;
Jim Van Verth7deacf42018-06-08 15:13:25 -04001501 bool duplicate;
1502 if (lastEdge) {
1503 duplicate = false;
1504 currUmbraIndex = fFirstVertexIndex;
1505 fPrevPoint = nextPoint;
1506 } else {
1507 duplicate = this->addInnerPoint(nextPoint, &currUmbraIndex);
1508 }
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001509 int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
1510 ? fPositions.count()-1
1511 : fPositions.count()-2;
Brian Salomonab664fa2017-03-24 16:07:20 +00001512 if (!duplicate) {
1513 // add to center fan if transparent or centroid showing
1514 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001515 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001516 // otherwise add to clip ring
1517 } else {
Brian Salomon66085ed2017-02-02 15:39:34 -05001518 SkPoint clipPoint;
Jim Van Verth7deacf42018-06-08 15:13:25 -04001519 bool isOutside = lastEdge ? fFirstUmbraOutside
1520 : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
1521 &clipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001522 if (isOutside) {
Jim Van Verth7deacf42018-06-08 15:13:25 -04001523 if (!lastEdge) {
1524 *fPositions.push() = clipPoint;
1525 *fColors.push() = fUmbraColor;
1526 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001527 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001528 if (fPrevUmbraOutside) {
1529 // fill out quad
Jim Van Verth872da6b2018-04-10 11:24:11 -04001530 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
1531 fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001532 }
1533 } else if (fPrevUmbraOutside) {
1534 // add tri
Jim Van Verth872da6b2018-04-10 11:24:11 -04001535 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001536 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001537
Brian Salomon66085ed2017-02-02 15:39:34 -05001538 fPrevUmbraOutside = isOutside;
1539 }
1540 }
1541
1542 // add next penumbra point and quad
Jim Van Verth91af7272017-01-27 14:15:54 -05001543 SkPoint newPoint = nextPoint + nextNormal;
1544 *fPositions.push() = newPoint;
1545 *fColors.push() = fPenumbraColor;
1546
Brian Salomonab664fa2017-03-24 16:07:20 +00001547 if (!duplicate) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001548 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001549 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001550 this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
Jim Van Verth91af7272017-01-27 14:15:54 -05001551
Brian Salomon66085ed2017-02-02 15:39:34 -05001552 fPrevUmbraIndex = currUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -04001553 fPrevOutset = nextNormal;
Jim Van Verth91af7272017-01-27 14:15:54 -05001554}
Brian Salomon958fbc42017-01-30 17:01:28 -05001555
1556///////////////////////////////////////////////////////////////////////////////////////////////////
1557
Brian Salomonaff27a22017-02-06 15:47:44 -05001558sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001559 const SkPoint3& zPlane, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001560 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001561 return nullptr;
1562 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001563 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001564 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001565}
1566
Brian Salomonaff27a22017-02-06 15:47:44 -05001567sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001568 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001569 SkScalar lightRadius, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001570 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() ||
Jim Van Verth1989c492018-05-31 13:15:16 -04001571 !lightPos.isFinite() || !(lightPos.fZ >= SK_ScalarNearlyZero) ||
1572 !SkScalarIsFinite(lightRadius) || !(lightRadius >= SK_ScalarNearlyZero)) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001573 return nullptr;
1574 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001575 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001576 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001577}