blob: 61b1e970624c6dc2d5ca7a58e22dd0b3fc67cfe1 [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();
51
52 void handleLine(const SkPoint& p);
Jim Van Vertha84898d2017-02-06 13:38:23 -050053 void handleLine(const SkMatrix& m, SkPoint* p);
Brian Salomon958fbc42017-01-30 17:01:28 -050054
55 void handleQuad(const SkPoint pts[3]);
Jim Van Vertha84898d2017-02-06 13:38:23 -050056 void handleQuad(const SkMatrix& m, SkPoint pts[3]);
Brian Salomon958fbc42017-01-30 17:01:28 -050057
Jim Van Vertha84898d2017-02-06 13:38:23 -050058 void handleCubic(const SkMatrix& m, SkPoint pts[4]);
Brian Salomon958fbc42017-01-30 17:01:28 -050059
Jim Van Vertha84898d2017-02-06 13:38:23 -050060 void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
Brian Salomon958fbc42017-01-30 17:01:28 -050061
Jim Van Verthda965502017-04-11 15:29:14 -040062 bool setTransformedHeightFunc(const SkMatrix& ctm);
Brian Salomon958fbc42017-01-30 17:01:28 -050063
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -040064 bool addArc(const SkVector& nextNormal, bool finishArc);
Jim Van Verthb4366552017-03-27 14:25:29 -040065
Jim Van Verth872da6b2018-04-10 11:24:11 -040066 void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
67 void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
68
Jim Van Verthe308a122017-05-08 14:19:30 -040069 SkScalar heightFunc(SkScalar x, SkScalar y) {
70 return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
71 }
72
73 SkPoint3 fZPlaneParams;
Jim Van Verthda965502017-04-11 15:29:14 -040074 std::function<SkScalar(const SkPoint&)> fTransformedHeightFunc;
75 SkScalar fZOffset;
76 // members for perspective height function
Jim Van Verth4c9b8932017-05-15 13:49:21 -040077 SkPoint3 fTransformedZParams;
Jim Van Verthda965502017-04-11 15:29:14 -040078 SkScalar fPartialDeterminants[3];
79
80 // first two points
Brian Salomon958fbc42017-01-30 17:01:28 -050081 SkTDArray<SkPoint> fInitPoints;
82 // temporary buffer
83 SkTDArray<SkPoint> fPointBuffer;
Brian Salomon0dda9cb2017-02-03 10:33:25 -050084
Jim Van Vertha84898d2017-02-06 13:38:23 -050085 SkTDArray<SkPoint> fPositions;
86 SkTDArray<SkColor> fColors;
87 SkTDArray<uint16_t> fIndices;
88
Jim Van Verthcdaf6612018-06-05 15:21:13 -040089 SkTDArray<SkPoint> fPathPolygon;
90 SkPoint fCentroid;
91 SkScalar fArea;
92 SkScalar fLastArea;
93 int fAreaSignFlips;
94 SkScalar fLastCross;
95
Jim Van Verth76387852017-05-16 16:55:16 -040096 int fFirstVertexIndex;
97 SkVector fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -050098 SkPoint fFirstPoint;
99
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500100 bool fSucceeded;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500101 bool fTransparent;
Jim Van Verthf507c282018-05-11 10:48:20 -0400102 bool fIsConvex;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500103
104 SkColor fUmbraColor;
105 SkColor fPenumbraColor;
106
107 SkScalar fRadius;
108 SkScalar fDirection;
109 int fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400110 SkVector fPrevOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500111 SkPoint fPrevPoint;
Brian Salomon958fbc42017-01-30 17:01:28 -0500112};
113
Jim Van Verthda965502017-04-11 15:29:14 -0400114static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
Jim Van Verthbce74962017-01-25 09:39:46 -0500115 SkVector* newNormal) {
116 SkVector normal;
117 // compute perpendicular
118 normal.fX = p0.fY - p1.fY;
119 normal.fY = p1.fX - p0.fX;
Jim Van Verthda965502017-04-11 15:29:14 -0400120 normal *= dir;
Jim Van Verthbce74962017-01-25 09:39:46 -0500121 if (!normal.normalize()) {
122 return false;
123 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500124 *newNormal = normal;
125 return true;
126}
127
128static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
129 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400130 const SkScalar kRecipPixelsPerArcSegment = 0.125f;
Jim Van Verthbce74962017-01-25 09:39:46 -0500131
132 SkScalar rCos = v1.dot(v2);
133 SkScalar rSin = v1.cross(v2);
134 SkScalar theta = SkScalarATan2(rSin, rCos);
135
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400136 int steps = SkScalarRoundToInt(SkScalarAbs(r*theta*kRecipPixelsPerArcSegment));
Jim Van Verthbce74962017-01-25 09:39:46 -0500137
138 SkScalar dTheta = theta / steps;
139 *rotSin = SkScalarSinCos(dTheta, rotCos);
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400140 *n = steps;
Jim Van Verthbce74962017-01-25 09:39:46 -0500141}
142
Jim Van Verthf507c282018-05-11 10:48:20 -0400143static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
144 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
145 static constexpr SkScalar kCloseSqd = kClose * kClose;
146
147 SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
148 return distSq < kCloseSqd;
149}
150
151static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
152 SkVector v0 = p1 - p0;
153 SkVector v1 = p2 - p0;
154 return v0.cross(v1);
155}
156
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400157
Jim Van Verthe308a122017-05-08 14:19:30 -0400158SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
159 : fZPlaneParams(zPlaneParams)
Jim Van Verthda965502017-04-11 15:29:14 -0400160 , fZOffset(0)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400161 , fCentroid({0, 0})
162 , fArea(0)
163 , fLastArea(0)
164 , fAreaSignFlips(0)
165 , fLastCross(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400166 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500167 , fSucceeded(false)
168 , fTransparent(transparent)
Jim Van Verthf507c282018-05-11 10:48:20 -0400169 , fIsConvex(true)
Brian Salomonaff27a22017-02-06 15:47:44 -0500170 , fDirection(1)
171 , fPrevUmbraIndex(-1) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500172 fInitPoints.setReserve(3);
173
174 // 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
212 if (!SkScalarNearlyZero(quadArea)) {
213 if (quadArea*fLastArea < 0) {
214 ++fAreaSignFlips;
215 }
216 fLastArea = quadArea;
217 }
218
219 return true;
220}
221
222bool SkBaseShadowTessellator::checkConvexity(const SkPoint& p0,
223 const SkPoint& p1,
224 const SkPoint& p2) {
225 SkScalar cross = perp_dot(p0, p1, p2);
226 // skip collinear point
227 if (SkScalarNearlyZero(cross)) {
228 return false;
229 }
230
231 // check for convexity
232 if (fLastCross*cross < 0) {
233 fIsConvex = false;
234 }
235 fLastCross = cross;
236
237 return true;
238}
239
240void SkBaseShadowTessellator::finishPathPolygon() {
241 if (fPathPolygon.count() > 1) {
242 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], fPathPolygon[0])) {
243 // remove coincident point
244 fPathPolygon.pop();
245 }
246 }
247
248 if (fPathPolygon.count() > 2) {
249 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
250 fPathPolygon[fPathPolygon.count() - 1],
251 fPathPolygon[0])) {
252 // remove collinear point
253 fPathPolygon[0] = fPathPolygon[fPathPolygon.count() - 1];
254 fPathPolygon.pop();
255 }
256 }
257
258 fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
259 // It's possible to have a concave path that self-intersects but also passes the
260 // cross-product check (e.g., a star). In that case, the signed area will change signs more
261 // than twice, so we check for that here.
262 if (fAreaSignFlips > 2) {
263 fIsConvex = false;
264 }
265}
266
Jim Van Vertha84898d2017-02-06 13:38:23 -0500267// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500268#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500269static const SkScalar kQuadTolerance = 0.2f;
270static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500271#endif
Jim Van Vertha84898d2017-02-06 13:38:23 -0500272static const SkScalar kConicTolerance = 0.5f;
273
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400274void SkBaseShadowTessellator::handleLine(const SkPoint& p) {
275 if (fPathPolygon.count() > 0) {
276 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], p)) {
277 // skip coincident point
278 return;
279 }
280 }
281
282 if (fPathPolygon.count() > 1) {
283 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
284 fPathPolygon[fPathPolygon.count() - 1],
285 p)) {
286 // remove collinear point
287 fPathPolygon.pop();
288 }
289 }
290
291 *fPathPolygon.push() = p;
292}
293
Brian Salomonaff27a22017-02-06 15:47:44 -0500294void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500295 m.mapPoints(p, 1);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400296
Jim Van Vertha84898d2017-02-06 13:38:23 -0500297 this->handleLine(*p);
298}
299
Brian Salomonaff27a22017-02-06 15:47:44 -0500300void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500301#if SK_SUPPORT_GPU
Jim Van Vertha947e292018-02-26 13:54:34 -0500302 // check for degeneracy
303 SkVector v0 = pts[1] - pts[0];
304 SkVector v1 = pts[2] - pts[0];
305 if (SkScalarNearlyZero(v0.cross(v1))) {
306 return;
307 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500308 // TODO: Pull PathUtils out of Ganesh?
309 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400310 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500311 SkPoint* target = fPointBuffer.begin();
312 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
313 kQuadTolerance, &target, maxCount);
314 fPointBuffer.setCount(count);
315 for (int i = 0; i < count; i++) {
316 this->handleLine(fPointBuffer[i]);
317 }
318#else
319 // for now, just to draw something
320 this->handleLine(pts[1]);
321 this->handleLine(pts[2]);
322#endif
323}
324
Brian Salomonaff27a22017-02-06 15:47:44 -0500325void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500326 m.mapPoints(pts, 3);
327 this->handleQuad(pts);
328}
329
Brian Salomonaff27a22017-02-06 15:47:44 -0500330void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500331 m.mapPoints(pts, 4);
332#if SK_SUPPORT_GPU
333 // TODO: Pull PathUtils out of Ganesh?
334 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400335 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500336 SkPoint* target = fPointBuffer.begin();
337 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
338 kCubicTolerance, &target, maxCount);
339 fPointBuffer.setCount(count);
340 for (int i = 0; i < count; i++) {
341 this->handleLine(fPointBuffer[i]);
342 }
343#else
344 // for now, just to draw something
345 this->handleLine(pts[1]);
346 this->handleLine(pts[2]);
347 this->handleLine(pts[3]);
348#endif
349}
350
Brian Salomonaff27a22017-02-06 15:47:44 -0500351void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400352 if (m.hasPerspective()) {
353 w = SkConic::TransformW(pts, w, m);
354 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500355 m.mapPoints(pts, 3);
356 SkAutoConicToQuads quadder;
357 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
358 SkPoint lastPoint = *(quads++);
359 int count = quadder.countQuads();
360 for (int i = 0; i < count; ++i) {
361 SkPoint quadPts[3];
362 quadPts[0] = lastPoint;
363 quadPts[1] = quads[0];
364 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
365 this->handleQuad(quadPts);
366 lastPoint = quadPts[2];
367 quads += 2;
368 }
369}
370
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400371bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500372 // fill in fan from previous quad
373 SkScalar rotSin, rotCos;
374 int numSteps;
Jim Van Verth76387852017-05-16 16:55:16 -0400375 compute_radial_steps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
376 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400377 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400378 SkVector currNormal;
379 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
380 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
381 *fPositions.push() = fPrevPoint + currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500382 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400383 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500384
Jim Van Verthda965502017-04-11 15:29:14 -0400385 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500386 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400387 if (finishArc && numSteps) {
Jim Van Verthda965502017-04-11 15:29:14 -0400388 *fPositions.push() = fPrevPoint + nextNormal;
389 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400390 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Verthda965502017-04-11 15:29:14 -0400391 }
Jim Van Verth76387852017-05-16 16:55:16 -0400392 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400393
394 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500395}
396
Jim Van Verth872da6b2018-04-10 11:24:11 -0400397void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
398 auto indices = fIndices.append(3);
399
400 indices[0] = index0;
401 indices[1] = index1;
402 indices[2] = index2;
403}
404
405void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
406 uint16_t index2, uint16_t index3) {
407 auto indices = fIndices.append(6);
408
409 indices[0] = index0;
410 indices[1] = index1;
411 indices[2] = index2;
412
413 indices[3] = index2;
414 indices[4] = index1;
415 indices[5] = index3;
416}
417
Jim Van Verthda965502017-04-11 15:29:14 -0400418bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400419 if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400420 fTransformedHeightFunc = [this](const SkPoint& p) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400421 return fZPlaneParams.fZ;
Jim Van Verthda965502017-04-11 15:29:14 -0400422 };
423 } else {
424 SkMatrix ctmInverse;
Jim Van Vertha947e292018-02-26 13:54:34 -0500425 if (!ctm.invert(&ctmInverse) || !ctmInverse.isFinite()) {
Jim Van Verthda965502017-04-11 15:29:14 -0400426 return false;
427 }
Jim Van Verthda965502017-04-11 15:29:14 -0400428 // multiply by transpose
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400429 fTransformedZParams = SkPoint3::Make(
Jim Van Verthe308a122017-05-08 14:19:30 -0400430 ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
431 ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
432 ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
433
434 ctmInverse[SkMatrix::kMSkewX] * fZPlaneParams.fX +
435 ctmInverse[SkMatrix::kMScaleY] * fZPlaneParams.fY +
436 ctmInverse[SkMatrix::kMPersp1] * fZPlaneParams.fZ,
437
438 ctmInverse[SkMatrix::kMTransX] * fZPlaneParams.fX +
439 ctmInverse[SkMatrix::kMTransY] * fZPlaneParams.fY +
440 ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
441 );
Jim Van Verthda965502017-04-11 15:29:14 -0400442
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400443 if (ctm.hasPerspective()) {
444 // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
445 // so pre-compute those values that are independent of X and Y.
446 // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
447 fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
448 ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
449 fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
450 ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
451 fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
452 ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
453 SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
454 ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
455 ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
Jim Van Verthda965502017-04-11 15:29:14 -0400456
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400457 // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
458 // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
459 fTransformedZParams.fX *= ctmDeterminant;
460 fTransformedZParams.fY *= ctmDeterminant;
461 fTransformedZParams.fZ *= ctmDeterminant;
Jim Van Verthda965502017-04-11 15:29:14 -0400462
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400463 fTransformedHeightFunc = [this](const SkPoint& p) {
464 SkScalar denom = p.fX * fPartialDeterminants[0] +
465 p.fY * fPartialDeterminants[1] +
466 fPartialDeterminants[2];
467 SkScalar w = SkScalarFastInvert(denom);
468 return fZOffset + w*(fTransformedZParams.fX * p.fX +
469 fTransformedZParams.fY * p.fY +
470 fTransformedZParams.fZ);
471 };
472 } else {
473 fTransformedHeightFunc = [this](const SkPoint& p) {
474 return fZOffset + fTransformedZParams.fX * p.fX +
475 fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
476 };
477 }
Jim Van Verthda965502017-04-11 15:29:14 -0400478 }
479
480 return true;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500481}
482
483
484//////////////////////////////////////////////////////////////////////////////////////////////////
485
Brian Salomonaff27a22017-02-06 15:47:44 -0500486class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500487public:
488 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400489 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500490
491private:
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400492 void computePathPolygon(const SkPath& path, const SkMatrix& ctm);
493 void handlePolyPoint(const SkPoint& p);
Jim Van Verthda965502017-04-11 15:29:14 -0400494 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500495
Jim Van Verthda965502017-04-11 15:29:14 -0400496 static constexpr auto kMaxEdgeLenSqr = 20 * 20;
Jim Van Verth76387852017-05-16 16:55:16 -0400497 static constexpr auto kInsetFactor = -0.5f;
Jim Van Verthda965502017-04-11 15:29:14 -0400498
499 SkScalar offset(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400500 return SkDrawShadowMetrics::AmbientBlurRadius(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400501 }
502 SkColor umbraColor(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400503 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(z));
Jim Van Verth060d9822017-05-04 09:58:17 -0400504 return SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
Jim Van Verthda965502017-04-11 15:29:14 -0400505 }
506
Jim Van Verth76387852017-05-16 16:55:16 -0400507 bool fSplitFirstEdge;
508 bool fSplitPreviousEdge;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500509
Brian Salomonaff27a22017-02-06 15:47:44 -0500510 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500511};
512
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500513SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500514 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400515 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500516 bool transparent)
Jim Van Verth76387852017-05-16 16:55:16 -0400517 : INHERITED(zPlaneParams, transparent)
518 , fSplitFirstEdge(false)
519 , fSplitPreviousEdge(false) {
Jim Van Verthda965502017-04-11 15:29:14 -0400520 // Set base colors
Jim Van Verth1af03d42017-07-31 09:34:58 -0400521 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(heightFunc(0, 0)));
Jim Van Verthb4366552017-03-27 14:25:29 -0400522 // umbraColor is the interior value, penumbraColor the exterior value.
523 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
524 // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
525 // the final alpha.
Jim Van Verth060d9822017-05-04 09:58:17 -0400526 fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
527 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400528
Jim Van Verthda965502017-04-11 15:29:14 -0400529 // make sure we're not below the canvas plane
530 this->setZOffset(path.getBounds(), ctm.hasPerspective());
531
Jim Van Verth7c8008c2018-02-07 15:02:50 -0500532 if (!this->setTransformedHeightFunc(ctm)) {
533 return;
534 }
Jim Van Verthda965502017-04-11 15:29:14 -0400535
Jim Van Verthbce74962017-01-25 09:39:46 -0500536 // Outer ring: 3*numPts
537 // Middle ring: numPts
538 fPositions.setReserve(4 * path.countPoints());
539 fColors.setReserve(4 * path.countPoints());
540 // Outer ring: 12*numPts
541 // Middle ring: 0
542 fIndices.setReserve(12 * path.countPoints());
543
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400544 this->computePathPolygon(path, ctm);
545 if (fPathPolygon.count() < 3) {
546 return;
Jim Van Verthbce74962017-01-25 09:39:46 -0500547 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400548 // TODO: add support for concave paths
549 if (!fIsConvex) {
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500550 return;
551 }
552
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400553 // Add center point for fan if needed
554 if (fTransparent) {
555 *fPositions.push() = fCentroid;
556 *fColors.push() = this->umbraColor(fTransformedHeightFunc(fCentroid));
557 }
558
559 for (int i = 0; i < fPathPolygon.count(); ++i) {
560 this->handlePolyPoint(fPathPolygon[i]);
561 }
562
563 if (!this->indexCount()) {
Jim Van Verthf507c282018-05-11 10:48:20 -0400564 return;
565 }
566
Jim Van Verth76387852017-05-16 16:55:16 -0400567 // Finish up
Jim Van Verthbce74962017-01-25 09:39:46 -0500568 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -0400569 if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
570 SkScalar z = fTransformedHeightFunc(fPrevPoint);
571 fRadius = this->offset(z);
572 SkVector scaledNormal(normal);
573 scaledNormal *= fRadius;
574 this->addArc(scaledNormal, true);
Jim Van Verthbce74962017-01-25 09:39:46 -0500575
Jim Van Verth76387852017-05-16 16:55:16 -0400576 // fix-up the last and first umbra points
577 SkVector inset = normal;
578 // adding to an average, so multiply by an additional half
579 inset *= 0.5f*kInsetFactor;
580 fPositions[fPrevUmbraIndex] += inset;
581 fPositions[fFirstVertexIndex] += inset;
582 // we multiply by another half because now we're adding to an average of an average
583 inset *= 0.5f;
584 if (fSplitPreviousEdge) {
585 fPositions[fPrevUmbraIndex - 2] += inset;
586 }
587 if (fSplitFirstEdge) {
588 fPositions[fFirstVertexIndex + 2] += inset;
589 }
590
Jim Van Verthda965502017-04-11 15:29:14 -0400591 // set up for final edge
592 z = fTransformedHeightFunc(fFirstPoint);
593 normal *= this->offset(z);
Jim Van Verthbce74962017-01-25 09:39:46 -0500594
Jim Van Verthda965502017-04-11 15:29:14 -0400595 // make sure we don't end up with a sharp alpha edge along the quad diagonal
Jim Van Verth76387852017-05-16 16:55:16 -0400596 if (fColors[fPrevUmbraIndex] != fColors[fFirstVertexIndex] &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500597 SkPointPriv::DistanceToSqd(fFirstPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400598 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + fPositions[fFirstVertexIndex];
Jim Van Verthda965502017-04-11 15:29:14 -0400599 centerPoint *= 0.5f;
600 *fPositions.push() = centerPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400601 *fColors.push() = SkPMLerp(fColors[fFirstVertexIndex], fColors[fPrevUmbraIndex], 128);
602 centerPoint = fPositions[fPositions.count()-2] + fPositions[fFirstVertexIndex+1];
603 centerPoint *= 0.5f;
604 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400605 *fColors.push() = fPenumbraColor;
606
Jim Van Verth76387852017-05-16 16:55:16 -0400607 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400608 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
609 fPositions.count() - 2, fPositions.count() - 1);
Jim Van Verth76387852017-05-16 16:55:16 -0400610 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400611 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
612 fPrevUmbraIndex, fPositions.count() - 3);
Jim Van Verth76387852017-05-16 16:55:16 -0400613 }
Jim Van Verthda965502017-04-11 15:29:14 -0400614
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400615 // if transparent, add to center fan
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400616 if (fTransparent) {
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400617 *fIndices.push() = 0;
618 *fIndices.push() = fPrevUmbraIndex;
619 *fIndices.push() = fPositions.count() - 2;
620 }
621
Jim Van Verthda965502017-04-11 15:29:14 -0400622 fPrevUmbraIndex = fPositions.count() - 2;
623 }
624
625 // final edge
Jim Van Vertha84898d2017-02-06 13:38:23 -0500626 *fPositions.push() = fFirstPoint + normal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500627 *fColors.push() = fPenumbraColor;
628
Jim Van Verth76387852017-05-16 16:55:16 -0400629 if (fColors[fPrevUmbraIndex] > fColors[fFirstVertexIndex]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400630 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 2,
631 fFirstVertexIndex, fPositions.count() - 1);
Jim Van Verthda965502017-04-11 15:29:14 -0400632 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400633 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
634 fPrevUmbraIndex, fFirstVertexIndex);
Jim Van Verthda965502017-04-11 15:29:14 -0400635 }
Jim Van Verth76387852017-05-16 16:55:16 -0400636 fPrevOutset = normal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500637 }
638
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400639 // finalize center fan
Jim Van Verthbce74962017-01-25 09:39:46 -0500640 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400641 this->appendTriangle(0, fPrevUmbraIndex, fFirstVertexIndex);
Jim Van Verthbce74962017-01-25 09:39:46 -0500642 }
643
644 // final fan
645 if (fPositions.count() >= 3) {
Jim Van Verth76387852017-05-16 16:55:16 -0400646 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500647 fPrevPoint = fFirstPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400648 fRadius = this->offset(fTransformedHeightFunc(fPrevPoint));
Jim Van Verth76387852017-05-16 16:55:16 -0400649 if (this->addArc(fFirstOutset, false)) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400650 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1, fFirstVertexIndex + 1);
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400651 } else {
652 // arc is too small, set the first penumbra point to be the same position
653 // as the last one
Jim Van Verth76387852017-05-16 16:55:16 -0400654 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400655 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500656 }
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500657 fSucceeded = true;
Jim Van Verthbce74962017-01-25 09:39:46 -0500658}
659
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400660void SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) {
661 fPathPolygon.setReserve(path.countPoints());
662
663 // walk around the path, tessellate and generate outer ring
664 // if original path is transparent, will accumulate sum of points for centroid
665 SkPath::Iter iter(path, true);
666 SkPoint pts[4];
667 SkPath::Verb verb;
668 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
669 switch (verb) {
670 case SkPath::kLine_Verb:
671 this->handleLine(ctm, &pts[1]);
672 break;
673 case SkPath::kQuad_Verb:
674 this->handleQuad(ctm, pts);
675 break;
676 case SkPath::kCubic_Verb:
677 this->handleCubic(ctm, pts);
678 break;
679 case SkPath::kConic_Verb:
680 this->handleConic(ctm, pts, iter.conicWeight());
681 break;
682 case SkPath::kMove_Verb:
683 case SkPath::kClose_Verb:
684 case SkPath::kDone_Verb:
685 break;
686 }
Jim Van Verthf507c282018-05-11 10:48:20 -0400687 }
688
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400689 this->finishPathPolygon();
690}
691
692void SkAmbientShadowTessellator::handlePolyPoint(const SkPoint& p) {
693 // should have no coincident points
694 SkASSERT(fInitPoints.isEmpty() || !duplicate_pt(p, fInitPoints[fInitPoints.count() - 1]));
695
Jim Van Verthbce74962017-01-25 09:39:46 -0500696 if (fInitPoints.count() < 2) {
697 *fInitPoints.push() = p;
698 return;
699 }
700
701 if (fInitPoints.count() == 2) {
702 // determine if cw or ccw
Jim Van Verthf507c282018-05-11 10:48:20 -0400703 SkScalar perpDot = perp_dot(fInitPoints[0], fInitPoints[1], p);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400704 // should not be collinear
705 SkASSERT(!SkScalarNearlyZero(perpDot));
Jim Van Verthbce74962017-01-25 09:39:46 -0500706
707 // if perpDot > 0, winding is ccw
708 fDirection = (perpDot > 0) ? -1 : 1;
709
710 // add first quad
Jim Van Verthda965502017-04-11 15:29:14 -0400711 SkVector normal;
712 if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &normal)) {
Jim Van Verthbce74962017-01-25 09:39:46 -0500713 // first two points are incident, make the third point the second and continue
714 fInitPoints[1] = p;
715 return;
716 }
717
Jim Van Vertha84898d2017-02-06 13:38:23 -0500718 fFirstPoint = fInitPoints[0];
Jim Van Verth76387852017-05-16 16:55:16 -0400719 fFirstVertexIndex = fPositions.count();
Jim Van Verthda965502017-04-11 15:29:14 -0400720 SkScalar z = fTransformedHeightFunc(fFirstPoint);
Jim Van Verth76387852017-05-16 16:55:16 -0400721 fFirstOutset = normal;
722 fFirstOutset *= this->offset(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400723
Jim Van Verth76387852017-05-16 16:55:16 -0400724 fPrevOutset = fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500725 fPrevPoint = fFirstPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400726 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500727
Jim Van Verthda965502017-04-11 15:29:14 -0400728 *fPositions.push() = fFirstPoint;
729 *fColors.push() = this->umbraColor(z);
Jim Van Verth76387852017-05-16 16:55:16 -0400730 *fPositions.push() = fFirstPoint + fFirstOutset;
Jim Van Verthbce74962017-01-25 09:39:46 -0500731 *fColors.push() = fPenumbraColor;
Jim Van Verthda965502017-04-11 15:29:14 -0400732
733 // add the first quad
734 z = fTransformedHeightFunc(fInitPoints[1]);
735 fRadius = this->offset(z);
736 fUmbraColor = this->umbraColor(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400737 this->addEdge(fInitPoints[1], normal);
Jim Van Verthbce74962017-01-25 09:39:46 -0500738
739 // to ensure we skip this block next time
740 *fInitPoints.push() = p;
741 }
742
743 SkVector normal;
Jim Van Verth76387852017-05-16 16:55:16 -0400744 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400745 SkVector scaledNormal = normal;
746 scaledNormal *= fRadius;
747 this->addArc(scaledNormal, true);
748 SkScalar z = fTransformedHeightFunc(p);
749 fRadius = this->offset(z);
750 fUmbraColor = this->umbraColor(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400751 this->addEdge(p, normal);
Jim Van Verthbce74962017-01-25 09:39:46 -0500752 }
753}
754
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500755void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
Jim Van Verth76387852017-05-16 16:55:16 -0400756 // We compute the inset in two stages: first we inset by half the current normal,
757 // then on the next addEdge() we add half of the next normal to get an average of the two
758 SkVector insetNormal = nextNormal;
759 insetNormal *= 0.5f*kInsetFactor;
760
761 // Adding the other half of the average for the previous edge
762 fPositions[fPrevUmbraIndex] += insetNormal;
763
764 SkPoint umbraPoint = nextPoint + insetNormal;
765 SkVector outsetNormal = nextNormal;
766 outsetNormal *= fRadius;
767 SkPoint penumbraPoint = nextPoint + outsetNormal;
768
769 // For split edges, we're adding an average of two averages, so we multiply by another half
770 if (fSplitPreviousEdge) {
771 insetNormal *= 0.5f;
772 fPositions[fPrevUmbraIndex - 2] += insetNormal;
773 }
774
775 // Split the edge to make sure we don't end up with a sharp alpha edge along the quad diagonal
Jim Van Verthda965502017-04-11 15:29:14 -0400776 if (fColors[fPrevUmbraIndex] != fUmbraColor &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500777 SkPointPriv::DistanceToSqd(nextPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400778
779 // This is lacking 1/4 of the next inset -- we'll add it the next time we call addEdge()
780 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + umbraPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400781 centerPoint *= 0.5f;
782 *fPositions.push() = centerPoint;
783 *fColors.push() = SkPMLerp(fUmbraColor, fColors[fPrevUmbraIndex], 128);
Jim Van Verth76387852017-05-16 16:55:16 -0400784 centerPoint = fPositions[fPositions.count()-2] + penumbraPoint;
785 centerPoint *= 0.5f;
786 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400787 *fColors.push() = fPenumbraColor;
788
789 // set triangularization to get best interpolation of color
790 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400791 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
792 fPositions.count() - 2, fPositions.count() - 1);
Jim Van Verthda965502017-04-11 15:29:14 -0400793 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400794 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
795 fPrevUmbraIndex, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -0400796 }
797
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400798 // if transparent, add to center fan
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400799 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400800 this->appendTriangle(0, fPrevUmbraIndex, fPositions.count() - 2);
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400801 }
802
Jim Van Verth76387852017-05-16 16:55:16 -0400803 fSplitPreviousEdge = true;
804 if (fPrevUmbraIndex == fFirstVertexIndex) {
805 fSplitFirstEdge = true;
806 }
Jim Van Verthda965502017-04-11 15:29:14 -0400807 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Verth76387852017-05-16 16:55:16 -0400808 } else {
809 fSplitPreviousEdge = false;
Jim Van Verthda965502017-04-11 15:29:14 -0400810 }
811
Jim Van Verthbce74962017-01-25 09:39:46 -0500812 // add next quad
Jim Van Verth76387852017-05-16 16:55:16 -0400813 *fPositions.push() = umbraPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500814 *fColors.push() = fUmbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -0400815 *fPositions.push() = penumbraPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500816 *fColors.push() = fPenumbraColor;
817
Jim Van Verthda965502017-04-11 15:29:14 -0400818 // set triangularization to get best interpolation of color
819 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400820 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
821 fPositions.count() - 2, fPositions.count() - 1);
Jim Van Verthda965502017-04-11 15:29:14 -0400822 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400823 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
824 fPrevUmbraIndex, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -0400825 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500826
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400827 // if transparent, add to center fan
Jim Van Verthbce74962017-01-25 09:39:46 -0500828 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400829 this->appendTriangle(0, fPrevUmbraIndex, fPositions.count() - 2);
Jim Van Verthbce74962017-01-25 09:39:46 -0500830 }
831
Brian Salomon66085ed2017-02-02 15:39:34 -0500832 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500833 fPrevPoint = nextPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400834 fPrevOutset = outsetNormal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500835}
Jim Van Verth91af7272017-01-27 14:15:54 -0500836
837///////////////////////////////////////////////////////////////////////////////////////////////////
838
Brian Salomonaff27a22017-02-06 15:47:44 -0500839class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500840public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500841 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400842 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400843 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500844
Brian Salomon958fbc42017-01-30 17:01:28 -0500845private:
Brian Salomonab664fa2017-03-24 16:07:20 +0000846 void computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400847 const SkMatrix& shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000848 void computeClipVectorsAndTestCentroid();
Brian Salomon66085ed2017-02-02 15:39:34 -0500849 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
Brian Salomonab664fa2017-03-24 16:07:20 +0000850 int getClosestUmbraPoint(const SkPoint& point);
Brian Salomon958fbc42017-01-30 17:01:28 -0500851
Jim Van Verth872da6b2018-04-10 11:24:11 -0400852 bool computeConvexShadow(SkScalar radius);
853 bool computeConcaveShadow(SkScalar radius);
854
Jim Van Verthb55eb282017-07-18 14:13:45 -0400855 bool handlePolyPoint(const SkPoint& p);
Brian Salomon958fbc42017-01-30 17:01:28 -0500856
857 void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
Jim Van Verth8c2de8f2018-05-14 11:20:58 -0400858 bool addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex);
Jim Van Verthda965502017-04-11 15:29:14 -0400859 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
Jim Van Verthf507c282018-05-11 10:48:20 -0400860 void addToClip(const SkVector& nextPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400861
862 SkScalar offset(SkScalar z) {
863 float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
864 return fLightRadius*zRatio;
865 }
866
867 SkScalar fLightZ;
868 SkScalar fLightRadius;
869 SkScalar fOffsetAdjust;
Brian Salomon958fbc42017-01-30 17:01:28 -0500870
Brian Salomon958fbc42017-01-30 17:01:28 -0500871 SkTDArray<SkPoint> fClipPolygon;
Brian Salomon66085ed2017-02-02 15:39:34 -0500872 SkTDArray<SkVector> fClipVectors;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500873
Brian Salomonab664fa2017-03-24 16:07:20 +0000874 SkTDArray<SkPoint> fUmbraPolygon;
875 int fCurrClipPoint;
876 int fCurrUmbraPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -0500877 bool fPrevUmbraOutside;
878 bool fFirstUmbraOutside;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500879 bool fValidUmbra;
Brian Salomon958fbc42017-01-30 17:01:28 -0500880
Brian Salomonaff27a22017-02-06 15:47:44 -0500881 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -0500882};
883
Jim Van Vertha84898d2017-02-06 13:38:23 -0500884SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400885 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -0400886 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -0400887 bool transparent)
Jim Van Verthe308a122017-05-08 14:19:30 -0400888 : INHERITED(zPlaneParams, transparent)
Jim Van Verthda965502017-04-11 15:29:14 -0400889 , fLightZ(lightPos.fZ)
890 , fLightRadius(lightRadius)
891 , fOffsetAdjust(0)
892 , fCurrClipPoint(0)
893 , fPrevUmbraOutside(false)
894 , fFirstUmbraOutside(false)
895 , fValidUmbra(true) {
896
897 // make sure we're not below the canvas plane
898 if (this->setZOffset(path.getBounds(), ctm.hasPerspective())) {
899 // Adjust light height and radius
900 fLightRadius *= (fLightZ + fZOffset) / fLightZ;
901 fLightZ += fZOffset;
902 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400903
904 // Set radius and colors
Jim Van Verthb4366552017-03-27 14:25:29 -0400905 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verthe308a122017-05-08 14:19:30 -0400906 SkScalar occluderHeight = this->heightFunc(center.fX, center.fY) + fZOffset;
Jim Van Verth060d9822017-05-04 09:58:17 -0400907 fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
908 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400909
Jim Van Verth1af03d42017-07-31 09:34:58 -0400910 // Compute the blur radius, scale and translation for the spot shadow.
911 SkScalar radius;
Jim Van Verthda965502017-04-11 15:29:14 -0400912 SkMatrix shadowTransform;
913 if (!ctm.hasPerspective()) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400914 SkScalar scale;
915 SkVector translate;
916 SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, fLightZ,
917 lightRadius, &radius, &scale, &translate);
Jim Van Verthda965502017-04-11 15:29:14 -0400918 shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
919 } else {
920 // For perspective, we have a scale, a z-shear, and another projective divide --
921 // this varies at each point so we can't use an affine transform.
922 // We'll just apply this to each generated point in turn.
923 shadowTransform.reset();
924 // Also can't cull the center (for now).
925 fTransparent = true;
Jim Van Verth1af03d42017-07-31 09:34:58 -0400926 radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
Jim Van Verthda965502017-04-11 15:29:14 -0400927 }
Jim Van Verth1af03d42017-07-31 09:34:58 -0400928 fRadius = radius;
Jim Van Verthda965502017-04-11 15:29:14 -0400929 SkMatrix fullTransform = SkMatrix::Concat(shadowTransform, ctm);
930
931 // Set up our reverse mapping
Jim Van Verth7c8008c2018-02-07 15:02:50 -0500932 if (!this->setTransformedHeightFunc(fullTransform)) {
933 return;
934 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400935
Brian Salomonab664fa2017-03-24 16:07:20 +0000936 // TODO: calculate these reserves better
Brian Salomon66085ed2017-02-02 15:39:34 -0500937 // Penumbra ring: 3*numPts
938 // Umbra ring: numPts
Jim Van Verth91af7272017-01-27 14:15:54 -0500939 // Inner ring: numPts
Brian Salomon66085ed2017-02-02 15:39:34 -0500940 fPositions.setReserve(5 * path.countPoints());
941 fColors.setReserve(5 * path.countPoints());
942 // Penumbra ring: 12*numPts
943 // Umbra ring: 3*numPts
944 fIndices.setReserve(15 * path.countPoints());
Brian Salomone7c85c42017-03-24 16:00:35 +0000945 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +0000946
947 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verthda965502017-04-11 15:29:14 -0400948 this->computeClipAndPathPolygons(path, ctm, shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000949 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3) {
Brian Salomon66085ed2017-02-02 15:39:34 -0500950 return;
951 }
Brian Salomon66085ed2017-02-02 15:39:34 -0500952
Jim Van Verthf507c282018-05-11 10:48:20 -0400953 // compute vectors for clip tests
954 this->computeClipVectorsAndTestCentroid();
955
Brian Salomonab664fa2017-03-24 16:07:20 +0000956 // check to see if umbra collapses
Jim Van Verthf507c282018-05-11 10:48:20 -0400957 if (fIsConvex) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400958 SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
959 fPathPolygon[0],
960 fPathPolygon[1]);
961 SkRect bounds;
962 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
963 for (int i = 1; i < fPathPolygon.count(); ++i) {
964 int j = i + 1;
965 if (i == fPathPolygon.count() - 1) {
966 j = 0;
967 }
968 SkPoint currPoint = fPathPolygon[i];
969 SkPoint nextPoint = fPathPolygon[j];
970 SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
971 nextPoint);
972 if (distSq < minDistSq) {
973 minDistSq = distSq;
974 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000975 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400976 static constexpr auto kTolerance = 1.0e-2f;
977 if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
978 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
979 SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
980 fOffsetAdjust = newRadius - radius;
981 SkScalar ratio = 128 * (newRadius + radius) / radius;
982 // they aren't PMColors, but the interpolation algorithm is the same
983 fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
984 radius = newRadius;
Brian Salomonab664fa2017-03-24 16:07:20 +0000985 }
986 }
Jim Van Verth91af7272017-01-27 14:15:54 -0500987
Jim Van Verthf507c282018-05-11 10:48:20 -0400988 if (fIsConvex) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400989 if (!this->computeConvexShadow(radius)) {
Jim Van Verthb55eb282017-07-18 14:13:45 -0400990 return;
991 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400992 } else {
Jim Van Verthf507c282018-05-11 10:48:20 -0400993 // For now
994 return;
995 //if (!this->computeConcaveShadow(radius)) {
996 // return;
997 //}
Jim Van Verth91af7272017-01-27 14:15:54 -0500998 }
Jim Van Verthda965502017-04-11 15:29:14 -0400999
1000 if (ctm.hasPerspective()) {
1001 for (int i = 0; i < fPositions.count(); ++i) {
1002 SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
1003 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
1004 fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
1005 fPositions[i].fY = (fPositions[i].fY*fLightZ - lightPos.fY*pathZ)*factor;
1006 }
1007#ifdef DRAW_CENTROID
1008 SkScalar pathZ = fTransformedHeightFunc(fCentroid);
1009 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
1010 fCentroid.fX = (fCentroid.fX*fLightZ - lightPos.fX*pathZ)*factor;
1011 fCentroid.fY = (fCentroid.fY*fLightZ - lightPos.fY*pathZ)*factor;
1012#endif
1013 }
1014#ifdef DRAW_CENTROID
1015 *fPositions.push() = fCentroid + SkVector::Make(-2, -2);
1016 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1017 *fPositions.push() = fCentroid + SkVector::Make(2, -2);
1018 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1019 *fPositions.push() = fCentroid + SkVector::Make(-2, 2);
1020 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1021 *fPositions.push() = fCentroid + SkVector::Make(2, 2);
1022 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1023
Jim Van Verth872da6b2018-04-10 11:24:11 -04001024 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
1025 fPositions.count() - 4, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -04001026#endif
1027
Brian Salomon0dda9cb2017-02-03 10:33:25 -05001028 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001029}
1030
Jim Van Verthf507c282018-05-11 10:48:20 -04001031void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
1032 if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count()-1])) {
1033 *fClipPolygon.push() = point;
1034 }
1035}
1036
Brian Salomonab664fa2017-03-24 16:07:20 +00001037void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -04001038 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001039
1040 fPathPolygon.setReserve(path.countPoints());
1041
1042 // Walk around the path and compute clip polygon and path polygon.
1043 // Will also accumulate sum of areas for centroid.
1044 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -05001045 SkPath::Iter iter(path, true);
1046 SkPoint pts[4];
1047 SkPath::Verb verb;
1048
Jim Van Verth91af7272017-01-27 14:15:54 -05001049 fClipPolygon.reset();
1050
Brian Salomon66085ed2017-02-02 15:39:34 -05001051 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +00001052 static constexpr SkScalar kA = 0.32495117187f;
1053 static constexpr SkScalar kB = 0.44311523437f;
1054 static constexpr SkScalar kC = 0.20141601562f;
1055 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -05001056
1057 SkPoint curvePoint;
1058 SkScalar w;
Jim Van Verth91af7272017-01-27 14:15:54 -05001059 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1060 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001061 case SkPath::kLine_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001062 ctm.mapPoints(&pts[1], 1);
Jim Van Verthf507c282018-05-11 10:48:20 -04001063 this->addToClip(pts[1]);
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001064 this->handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -05001065 break;
1066 case SkPath::kQuad_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001067 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001068 // point at t = 1/2
1069 curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
1070 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 -04001071 this->addToClip(curvePoint);
1072 this->addToClip(pts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001073 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001074 break;
1075 case SkPath::kConic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001076 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001077 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -05001078 // point at t = 1/2
Brian Salomon66085ed2017-02-02 15:39:34 -05001079 curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
1080 curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
1081 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
Jim Van Verthf507c282018-05-11 10:48:20 -04001082 this->addToClip(curvePoint);
1083 this->addToClip(pts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001084 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -05001085 break;
1086 case SkPath::kCubic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001087 ctm.mapPoints(pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -05001088 // point at t = 5/16
1089 curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
1090 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 -04001091 this->addToClip(curvePoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001092 // point at t = 11/16
1093 curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
1094 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 -04001095 this->addToClip(curvePoint);
1096 this->addToClip(pts[3]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001097 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001098 break;
Brian Salomonab664fa2017-03-24 16:07:20 +00001099 case SkPath::kMove_Verb:
Jim Van Verth91af7272017-01-27 14:15:54 -05001100 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +00001101 case SkPath::kDone_Verb:
Jim Van Verth91af7272017-01-27 14:15:54 -05001102 break;
1103 default:
1104 SkDEBUGFAIL("unknown verb");
1105 }
1106 }
1107
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001108 this->finishPathPolygon();
Brian Salomonab664fa2017-03-24 16:07:20 +00001109 fCurrClipPoint = fClipPolygon.count() - 1;
Brian Salomon66085ed2017-02-02 15:39:34 -05001110}
1111
Brian Salomonab664fa2017-03-24 16:07:20 +00001112void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
Brian Salomon66085ed2017-02-02 15:39:34 -05001113 SkASSERT(fClipPolygon.count() >= 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001114
Brian Salomonab664fa2017-03-24 16:07:20 +00001115 // init clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001116 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
Jim Van Verthf507c282018-05-11 10:48:20 -04001117 SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001118 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001119
1120 // init centroid check
1121 bool hiddenCentroid = true;
Jim Van Verthf507c282018-05-11 10:48:20 -04001122 v1 = fCentroid - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001123 SkScalar initCross = v0.cross(v1);
1124
1125 for (int p = 1; p < fClipPolygon.count(); ++p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001126 // add to clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001127 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
1128 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001129 // Determine if transformed centroid is inside clipPolygon.
Brian Salomonab664fa2017-03-24 16:07:20 +00001130 v1 = fCentroid - fClipPolygon[p];
Brian Salomon66085ed2017-02-02 15:39:34 -05001131 if (initCross*v0.cross(v1) <= 0) {
1132 hiddenCentroid = false;
1133 }
1134 }
1135 SkASSERT(fClipVectors.count() == fClipPolygon.count());
1136
Brian Salomonab664fa2017-03-24 16:07:20 +00001137 fTransparent = fTransparent || !hiddenCentroid;
Brian Salomon66085ed2017-02-02 15:39:34 -05001138}
1139
1140bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
1141 SkPoint* clipPoint) {
1142 SkVector segmentVector = centroid - umbraPoint;
1143
Brian Salomonab664fa2017-03-24 16:07:20 +00001144 int startClipPoint = fCurrClipPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001145 do {
Brian Salomonab664fa2017-03-24 16:07:20 +00001146 SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
1147 SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
Brian Salomon66085ed2017-02-02 15:39:34 -05001148 SkScalar t_num = dp.cross(segmentVector);
1149 // if line segments are nearly parallel
1150 if (SkScalarNearlyZero(denom)) {
1151 // and collinear
1152 if (SkScalarNearlyZero(t_num)) {
1153 return false;
1154 }
1155 // otherwise are separate, will try the next poly segment
1156 // else if crossing lies within poly segment
1157 } else if (t_num >= 0 && t_num <= denom) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001158 SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
Brian Salomon66085ed2017-02-02 15:39:34 -05001159 // if umbra point is inside the clip polygon
Jim Van Verthda965502017-04-11 15:29:14 -04001160 if (s_num >= 0 && s_num <= denom) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001161 segmentVector *= s_num/denom;
1162 *clipPoint = umbraPoint + segmentVector;
1163 return true;
1164 }
1165 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001166 fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
1167 } while (fCurrClipPoint != startClipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001168
1169 return false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001170}
1171
Brian Salomonab664fa2017-03-24 16:07:20 +00001172int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
Cary Clarkdf429f32017-11-08 11:44:31 -05001173 SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraPoint]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001174 int index = fCurrUmbraPoint;
1175 int dir = 1;
1176 int next = (index + dir) % fUmbraPolygon.count();
1177
1178 // init travel direction
Cary Clarkdf429f32017-11-08 11:44:31 -05001179 SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001180 if (distance < minDistance) {
1181 index = next;
1182 minDistance = distance;
1183 } else {
1184 dir = fUmbraPolygon.count()-1;
1185 }
1186
1187 // iterate until we find a point that increases the distance
1188 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001189 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001190 while (distance < minDistance) {
1191 index = next;
1192 minDistance = distance;
1193 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001194 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001195 }
1196
1197 fCurrUmbraPoint = index;
1198 return index;
1199}
1200
Jim Van Verth872da6b2018-04-10 11:24:11 -04001201bool SkSpotShadowTessellator::computeConvexShadow(SkScalar radius) {
1202 // generate inner ring
1203 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1204 &fUmbraPolygon)) {
1205 // this shouldn't happen, but just in case we'll inset using the centroid
1206 fValidUmbra = false;
1207 }
1208
1209 // walk around the path polygon, generate outer ring and connect to inner ring
1210 if (fTransparent) {
1211 *fPositions.push() = fCentroid;
1212 *fColors.push() = fUmbraColor;
1213 }
1214 fCurrUmbraPoint = 0;
1215 for (int i = 0; i < fPathPolygon.count(); ++i) {
1216 if (!this->handlePolyPoint(fPathPolygon[i])) {
1217 return false;
1218 }
1219 }
1220
1221 if (!this->indexCount()) {
1222 return false;
1223 }
1224
1225 // finish up the final verts
1226 SkVector normal;
1227 if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
1228 normal *= fRadius;
1229 this->addArc(normal, true);
1230
1231 // add to center fan
1232 if (fTransparent) {
1233 this->appendTriangle(0, fPrevUmbraIndex, fFirstVertexIndex);
1234 // or to clip ring
1235 } else {
1236 if (fFirstUmbraOutside) {
1237 this->appendTriangle(fPrevUmbraIndex, fFirstVertexIndex, fFirstVertexIndex + 1);
1238 if (fPrevUmbraOutside) {
1239 // fill out quad
1240 this->appendTriangle(fPrevUmbraIndex, fFirstVertexIndex + 1,
1241 fPrevUmbraIndex + 1);
1242 }
1243 } else if (fPrevUmbraOutside) {
1244 // add tri
1245 this->appendTriangle(fPrevUmbraIndex, fFirstVertexIndex, fPrevUmbraIndex + 1);
1246 }
1247 }
1248
1249 // add final edge
1250 *fPositions.push() = fFirstPoint + normal;
1251 *fColors.push() = fPenumbraColor;
1252
1253 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 2,
1254 fFirstVertexIndex, fPositions.count() - 1);
1255
1256 fPrevOutset = normal;
1257 }
1258
1259 // final fan
1260 if (fPositions.count() >= 3) {
1261 fPrevUmbraIndex = fFirstVertexIndex;
1262 fPrevPoint = fFirstPoint;
1263 if (this->addArc(fFirstOutset, false)) {
1264 if (fFirstUmbraOutside) {
1265 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1266 fFirstVertexIndex + 2);
1267 } else {
1268 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1269 fFirstVertexIndex + 1);
1270 }
1271 } else {
1272 // no arc added, fix up by setting first penumbra point position to last one
1273 if (fFirstUmbraOutside) {
1274 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
1275 } else {
1276 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
1277 }
1278 }
1279 }
1280
1281 return true;
1282}
1283
1284bool SkSpotShadowTessellator::computeConcaveShadow(SkScalar radius) {
1285 // TODO: remove when we support filling the penumbra
1286 if (fTransparent) {
1287 return false;
1288 }
1289
1290 // generate inner ring
1291 SkTDArray<int> umbraIndices;
1292 umbraIndices.setReserve(fPathPolygon.count());
1293 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1294 &fUmbraPolygon, &umbraIndices)) {
1295 // TODO: figure out how to handle this case
1296 return false;
1297 }
1298
1299 // generate outer ring
1300 SkTDArray<SkPoint> penumbraPolygon;
1301 SkTDArray<int> penumbraIndices;
1302 penumbraPolygon.setReserve(fUmbraPolygon.count());
1303 penumbraIndices.setReserve(fUmbraPolygon.count());
1304 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -radius,
1305 &penumbraPolygon, &penumbraIndices)) {
1306 // TODO: figure out how to handle this case
1307 return false;
1308 }
1309
1310 if (!fUmbraPolygon.count() || !penumbraPolygon.count()) {
1311 return false;
1312 }
1313
1314 // attach the rings together
1315
1316 // find minimum indices
1317 int minIndex = 0;
1318 int min = penumbraIndices[0];
1319 for (int i = 1; i < penumbraIndices.count(); ++i) {
1320 if (penumbraIndices[i] < min) {
1321 min = penumbraIndices[i];
1322 minIndex = i;
1323 }
1324 }
1325 int currPenumbra = minIndex;
1326
1327 minIndex = 0;
1328 min = umbraIndices[0];
1329 for (int i = 1; i < umbraIndices.count(); ++i) {
1330 if (umbraIndices[i] < min) {
1331 min = umbraIndices[i];
1332 minIndex = i;
1333 }
1334 }
1335 int currUmbra = minIndex;
1336
1337 // now find a case where the indices are equal (there should be at least one)
1338 int maxPenumbraIndex = fPathPolygon.count()-1;
1339 int maxUmbraIndex = fPathPolygon.count()-1;
1340 while (penumbraIndices[currPenumbra] != umbraIndices[currUmbra]) {
1341 if (penumbraIndices[currPenumbra] < umbraIndices[currUmbra]) {
1342 penumbraIndices[currPenumbra] += fPathPolygon.count();
1343 maxPenumbraIndex = penumbraIndices[currPenumbra];
1344 currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
1345 } else {
1346 umbraIndices[currUmbra] += fPathPolygon.count();
1347 maxUmbraIndex = umbraIndices[currUmbra];
1348 currUmbra = (currUmbra + 1) % fUmbraPolygon.count();
1349 }
1350 }
1351
1352 *fPositions.push() = penumbraPolygon[currPenumbra];
1353 *fColors.push() = fPenumbraColor;
1354 int prevPenumbraIndex = 0;
1355 *fPositions.push() = fUmbraPolygon[currUmbra];
1356 *fColors.push() = fUmbraColor;
1357 fPrevUmbraIndex = 1;
1358
1359 int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
1360 int nextUmbra = (currUmbra + 1) % fUmbraPolygon.count();
1361 while (penumbraIndices[nextPenumbra] <= maxPenumbraIndex ||
1362 umbraIndices[nextUmbra] <= maxUmbraIndex) {
1363
1364 if (umbraIndices[nextUmbra] == penumbraIndices[nextPenumbra]) {
1365 // advance both one step
1366 *fPositions.push() = penumbraPolygon[nextPenumbra];
1367 *fColors.push() = fPenumbraColor;
1368 int currPenumbraIndex = fPositions.count() - 1;
1369
1370 *fPositions.push() = fUmbraPolygon[nextUmbra];
1371 *fColors.push() = fUmbraColor;
1372 int currUmbraIndex = fPositions.count() - 1;
1373
1374 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
1375 fPrevUmbraIndex, currUmbraIndex);
1376
1377 prevPenumbraIndex = currPenumbraIndex;
1378 penumbraIndices[currPenumbra] += fPathPolygon.count();
1379 currPenumbra = nextPenumbra;
1380 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
1381
1382 fPrevUmbraIndex = currUmbraIndex;
1383 umbraIndices[currUmbra] += fPathPolygon.count();
1384 currUmbra = nextUmbra;
1385 nextUmbra = (currUmbra + 1) % fUmbraPolygon.count();
1386 }
1387
1388 while (penumbraIndices[nextPenumbra] < umbraIndices[nextUmbra] &&
1389 penumbraIndices[nextPenumbra] <= maxPenumbraIndex) {
1390 // fill out penumbra arc
1391 *fPositions.push() = penumbraPolygon[nextPenumbra];
1392 *fColors.push() = fPenumbraColor;
1393 int currPenumbraIndex = fPositions.count() - 1;
1394
1395 this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
1396
1397 prevPenumbraIndex = currPenumbraIndex;
1398 // this ensures the ordering when we wrap around
1399 penumbraIndices[currPenumbra] += fPathPolygon.count();
1400 currPenumbra = nextPenumbra;
1401 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
1402 }
1403
1404 while (umbraIndices[nextUmbra] < penumbraIndices[nextPenumbra] &&
1405 umbraIndices[nextUmbra] <= maxUmbraIndex) {
1406 // fill out umbra arc
1407 *fPositions.push() = fUmbraPolygon[nextUmbra];
1408 *fColors.push() = fUmbraColor;
1409 int currUmbraIndex = fPositions.count() - 1;
1410
1411 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
1412
1413 fPrevUmbraIndex = currUmbraIndex;
1414 // this ensures the ordering when we wrap around
1415 umbraIndices[currUmbra] += fPathPolygon.count();
1416 currUmbra = nextUmbra;
1417 nextUmbra = (currUmbra + 1) % fUmbraPolygon.count();
1418 }
1419 }
1420 // finish up by advancing both one step
1421 *fPositions.push() = penumbraPolygon[nextPenumbra];
1422 *fColors.push() = fPenumbraColor;
1423 int currPenumbraIndex = fPositions.count() - 1;
1424
1425 *fPositions.push() = fUmbraPolygon[nextUmbra];
1426 *fColors.push() = fUmbraColor;
1427 int currUmbraIndex = fPositions.count() - 1;
1428
1429 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
1430 fPrevUmbraIndex, currUmbraIndex);
1431
1432 if (fTransparent) {
1433 // TODO: fill penumbra
1434 }
1435
1436 return true;
1437}
1438
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001439void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
Jim Van Verth91af7272017-01-27 14:15:54 -05001440 SkPoint* pts, int count) {
1441 // TODO: vectorize
1442 for (int i = 0; i < count; ++i) {
1443 pts[i] *= scale;
1444 pts[i] += xlate;
1445 }
1446}
1447
Jim Van Verthb55eb282017-07-18 14:13:45 -04001448bool SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001449 if (fInitPoints.count() < 2) {
1450 *fInitPoints.push() = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001451 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001452 }
1453
1454 if (fInitPoints.count() == 2) {
1455 // determine if cw or ccw
Jim Van Verthb55eb282017-07-18 14:13:45 -04001456 SkScalar perpDot = perp_dot(fInitPoints[0], fInitPoints[1], p);
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001457 // shouldn't be any collinear points
1458 SkASSERT(!SkScalarNearlyZero(perpDot));
Jim Van Verth91af7272017-01-27 14:15:54 -05001459
1460 // if perpDot > 0, winding is ccw
1461 fDirection = (perpDot > 0) ? -1 : 1;
1462
1463 // add first quad
Jim Van Verth76387852017-05-16 16:55:16 -04001464 if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &fFirstOutset)) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001465 // first two points are incident, make the third point the second and continue
1466 fInitPoints[1] = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001467 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001468 }
1469
Jim Van Verth76387852017-05-16 16:55:16 -04001470 fFirstOutset *= fRadius;
Jim Van Verth91af7272017-01-27 14:15:54 -05001471 fFirstPoint = fInitPoints[0];
Jim Van Verth76387852017-05-16 16:55:16 -04001472 fFirstVertexIndex = fPositions.count();
1473 fPrevOutset = fFirstOutset;
Jim Van Verth91af7272017-01-27 14:15:54 -05001474 fPrevPoint = fFirstPoint;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001475 fPrevUmbraIndex = -1;
Jim Van Verth91af7272017-01-27 14:15:54 -05001476
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001477 this->addInnerPoint(fFirstPoint, &fPrevUmbraIndex);
Brian Salomon66085ed2017-02-02 15:39:34 -05001478
1479 if (!fTransparent) {
1480 SkPoint clipPoint;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001481 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
1482 fCentroid, &clipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001483 if (isOutside) {
1484 *fPositions.push() = clipPoint;
1485 *fColors.push() = fUmbraColor;
1486 }
1487 fPrevUmbraOutside = isOutside;
1488 fFirstUmbraOutside = isOutside;
1489 }
1490
Jim Van Verth76387852017-05-16 16:55:16 -04001491 SkPoint newPoint = fFirstPoint + fFirstOutset;
Jim Van Verth91af7272017-01-27 14:15:54 -05001492 *fPositions.push() = newPoint;
1493 *fColors.push() = fPenumbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -04001494 this->addEdge(fInitPoints[1], fFirstOutset);
Jim Van Verth91af7272017-01-27 14:15:54 -05001495
1496 // to ensure we skip this block next time
1497 *fInitPoints.push() = p;
1498 }
1499
Jim Van Verthb55eb282017-07-18 14:13:45 -04001500 // if concave, abort
1501 SkScalar perpDot = perp_dot(fInitPoints[1], fInitPoints[2], p);
1502 if (fDirection*perpDot > 0) {
1503 return false;
1504 }
1505
Jim Van Verth91af7272017-01-27 14:15:54 -05001506 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -04001507 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
1508 normal *= fRadius;
1509 this->addArc(normal, true);
1510 this->addEdge(p, normal);
Jim Van Verthb55eb282017-07-18 14:13:45 -04001511 fInitPoints[1] = fInitPoints[2];
1512 fInitPoints[2] = p;
Jim Van Verth91af7272017-01-27 14:15:54 -05001513 }
Jim Van Verthb55eb282017-07-18 14:13:45 -04001514
1515 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001516}
1517
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001518bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001519 SkPoint umbraPoint;
1520 if (!fValidUmbra) {
1521 SkVector v = fCentroid - pathPoint;
1522 v *= 0.95f;
1523 umbraPoint = pathPoint + v;
Jim Van Verth91af7272017-01-27 14:15:54 -05001524 } else {
Brian Salomonab664fa2017-03-24 16:07:20 +00001525 umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
Jim Van Verth91af7272017-01-27 14:15:54 -05001526 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001527
Jim Van Verth91af7272017-01-27 14:15:54 -05001528 fPrevPoint = pathPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001529
1530 // merge "close" points
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001531 if (fPrevUmbraIndex == -1 ||
Brian Salomonab664fa2017-03-24 16:07:20 +00001532 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001533 // if we've wrapped around, don't add a new point
1534 if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
1535 *currUmbraIndex = fFirstVertexIndex;
1536 } else {
1537 *currUmbraIndex = fPositions.count();
1538 *fPositions.push() = umbraPoint;
1539 *fColors.push() = fUmbraColor;
1540 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001541 return false;
1542 } else {
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001543 *currUmbraIndex = fPrevUmbraIndex;
Brian Salomonab664fa2017-03-24 16:07:20 +00001544 return true;
1545 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001546}
1547
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001548void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001549 // add next umbra point
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001550 int currUmbraIndex;
1551 bool duplicate = this->addInnerPoint(nextPoint, &currUmbraIndex);
1552 int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
1553 ? fPositions.count()-1
1554 : fPositions.count()-2;
Brian Salomonab664fa2017-03-24 16:07:20 +00001555 if (!duplicate) {
1556 // add to center fan if transparent or centroid showing
1557 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001558 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001559 // otherwise add to clip ring
1560 } else {
Brian Salomon66085ed2017-02-02 15:39:34 -05001561 SkPoint clipPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001562 bool isOutside = this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
1563 &clipPoint);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001564
Brian Salomon66085ed2017-02-02 15:39:34 -05001565 if (isOutside) {
1566 *fPositions.push() = clipPoint;
1567 *fColors.push() = fUmbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001568 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001569 if (fPrevUmbraOutside) {
1570 // fill out quad
Jim Van Verth872da6b2018-04-10 11:24:11 -04001571 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
1572 fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001573 }
1574 } else if (fPrevUmbraOutside) {
1575 // add tri
Jim Van Verth872da6b2018-04-10 11:24:11 -04001576 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001577 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001578
Brian Salomon66085ed2017-02-02 15:39:34 -05001579 fPrevUmbraOutside = isOutside;
1580 }
1581 }
1582
1583 // add next penumbra point and quad
Jim Van Verth91af7272017-01-27 14:15:54 -05001584 SkPoint newPoint = nextPoint + nextNormal;
1585 *fPositions.push() = newPoint;
1586 *fColors.push() = fPenumbraColor;
1587
Brian Salomonab664fa2017-03-24 16:07:20 +00001588 if (!duplicate) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001589 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001590 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001591 this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
Jim Van Verth91af7272017-01-27 14:15:54 -05001592
Brian Salomon66085ed2017-02-02 15:39:34 -05001593 fPrevUmbraIndex = currUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -04001594 fPrevOutset = nextNormal;
Jim Van Verth91af7272017-01-27 14:15:54 -05001595}
Brian Salomon958fbc42017-01-30 17:01:28 -05001596
1597///////////////////////////////////////////////////////////////////////////////////////////////////
1598
Brian Salomonaff27a22017-02-06 15:47:44 -05001599sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001600 const SkPoint3& zPlane, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001601 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001602 return nullptr;
1603 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001604 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001605 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001606}
1607
Brian Salomonaff27a22017-02-06 15:47:44 -05001608sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001609 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001610 SkScalar lightRadius, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001611 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() ||
Jim Van Verth1989c492018-05-31 13:15:16 -04001612 !lightPos.isFinite() || !(lightPos.fZ >= SK_ScalarNearlyZero) ||
1613 !SkScalarIsFinite(lightRadius) || !(lightRadius >= SK_ScalarNearlyZero)) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001614 return nullptr;
1615 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001616 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001617 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001618}