blob: 0bf1adaaa4bd73fac48a469c7b1316c9e90b38ed [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 Vertha84898d2017-02-06 13:38:23 -050048 virtual void handleLine(const SkPoint& p) = 0;
49 void handleLine(const SkMatrix& m, SkPoint* p);
Brian Salomon958fbc42017-01-30 17:01:28 -050050
51 void handleQuad(const SkPoint pts[3]);
Jim Van Vertha84898d2017-02-06 13:38:23 -050052 void handleQuad(const SkMatrix& m, SkPoint pts[3]);
Brian Salomon958fbc42017-01-30 17:01:28 -050053
Jim Van Vertha84898d2017-02-06 13:38:23 -050054 void handleCubic(const SkMatrix& m, SkPoint pts[4]);
Brian Salomon958fbc42017-01-30 17:01:28 -050055
Jim Van Vertha84898d2017-02-06 13:38:23 -050056 void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
Brian Salomon958fbc42017-01-30 17:01:28 -050057
Jim Van Verthda965502017-04-11 15:29:14 -040058 bool setTransformedHeightFunc(const SkMatrix& ctm);
Brian Salomon958fbc42017-01-30 17:01:28 -050059
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -040060 bool addArc(const SkVector& nextNormal, bool finishArc);
Jim Van Verthb4366552017-03-27 14:25:29 -040061
Jim Van Verth872da6b2018-04-10 11:24:11 -040062 void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
63 void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
64
Jim Van Verthe308a122017-05-08 14:19:30 -040065 SkScalar heightFunc(SkScalar x, SkScalar y) {
66 return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
67 }
68
69 SkPoint3 fZPlaneParams;
Jim Van Verthda965502017-04-11 15:29:14 -040070 std::function<SkScalar(const SkPoint&)> fTransformedHeightFunc;
71 SkScalar fZOffset;
72 // members for perspective height function
Jim Van Verth4c9b8932017-05-15 13:49:21 -040073 SkPoint3 fTransformedZParams;
Jim Van Verthda965502017-04-11 15:29:14 -040074 SkScalar fPartialDeterminants[3];
75
76 // first two points
Brian Salomon958fbc42017-01-30 17:01:28 -050077 SkTDArray<SkPoint> fInitPoints;
78 // temporary buffer
79 SkTDArray<SkPoint> fPointBuffer;
Brian Salomon0dda9cb2017-02-03 10:33:25 -050080
Jim Van Vertha84898d2017-02-06 13:38:23 -050081 SkTDArray<SkPoint> fPositions;
82 SkTDArray<SkColor> fColors;
83 SkTDArray<uint16_t> fIndices;
84
Jim Van Verth76387852017-05-16 16:55:16 -040085 int fFirstVertexIndex;
86 SkVector fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -050087 SkPoint fFirstPoint;
88
Brian Salomon0dda9cb2017-02-03 10:33:25 -050089 bool fSucceeded;
Jim Van Vertha84898d2017-02-06 13:38:23 -050090 bool fTransparent;
91
92 SkColor fUmbraColor;
93 SkColor fPenumbraColor;
94
95 SkScalar fRadius;
96 SkScalar fDirection;
97 int fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -040098 SkVector fPrevOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -050099 SkPoint fPrevPoint;
Brian Salomon958fbc42017-01-30 17:01:28 -0500100};
101
Jim Van Verthda965502017-04-11 15:29:14 -0400102static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
Jim Van Verthbce74962017-01-25 09:39:46 -0500103 SkVector* newNormal) {
104 SkVector normal;
105 // compute perpendicular
106 normal.fX = p0.fY - p1.fY;
107 normal.fY = p1.fX - p0.fX;
Jim Van Verthda965502017-04-11 15:29:14 -0400108 normal *= dir;
Jim Van Verthbce74962017-01-25 09:39:46 -0500109 if (!normal.normalize()) {
110 return false;
111 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500112 *newNormal = normal;
113 return true;
114}
115
116static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
117 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400118 const SkScalar kRecipPixelsPerArcSegment = 0.125f;
Jim Van Verthbce74962017-01-25 09:39:46 -0500119
120 SkScalar rCos = v1.dot(v2);
121 SkScalar rSin = v1.cross(v2);
122 SkScalar theta = SkScalarATan2(rSin, rCos);
123
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400124 int steps = SkScalarRoundToInt(SkScalarAbs(r*theta*kRecipPixelsPerArcSegment));
Jim Van Verthbce74962017-01-25 09:39:46 -0500125
126 SkScalar dTheta = theta / steps;
127 *rotSin = SkScalarSinCos(dTheta, rotCos);
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400128 *n = steps;
Jim Van Verthbce74962017-01-25 09:39:46 -0500129}
130
Jim Van Verthe308a122017-05-08 14:19:30 -0400131SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
132 : fZPlaneParams(zPlaneParams)
Jim Van Verthda965502017-04-11 15:29:14 -0400133 , fZOffset(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400134 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500135 , fSucceeded(false)
136 , fTransparent(transparent)
Brian Salomonaff27a22017-02-06 15:47:44 -0500137 , fDirection(1)
138 , fPrevUmbraIndex(-1) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500139 fInitPoints.setReserve(3);
140
141 // child classes will set reserve for positions, colors and indices
142}
143
Jim Van Verthda965502017-04-11 15:29:14 -0400144bool SkBaseShadowTessellator::setZOffset(const SkRect& bounds, bool perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400145 SkScalar minZ = this->heightFunc(bounds.fLeft, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400146 if (perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400147 SkScalar z = this->heightFunc(bounds.fLeft, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400148 if (z < minZ) {
149 minZ = z;
150 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400151 z = this->heightFunc(bounds.fRight, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400152 if (z < minZ) {
153 minZ = z;
154 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400155 z = this->heightFunc(bounds.fRight, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400156 if (z < minZ) {
157 minZ = z;
158 }
159 }
160
161 if (minZ < kMinHeight) {
162 fZOffset = -minZ + kMinHeight;
163 return true;
164 }
165
166 return false;
167}
168
Jim Van Vertha84898d2017-02-06 13:38:23 -0500169// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500170#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500171static const SkScalar kQuadTolerance = 0.2f;
172static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500173#endif
Jim Van Vertha84898d2017-02-06 13:38:23 -0500174static const SkScalar kConicTolerance = 0.5f;
175
Brian Salomonaff27a22017-02-06 15:47:44 -0500176void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500177 m.mapPoints(p, 1);
178 this->handleLine(*p);
179}
180
Brian Salomonaff27a22017-02-06 15:47:44 -0500181void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500182#if SK_SUPPORT_GPU
Jim Van Vertha947e292018-02-26 13:54:34 -0500183 // check for degeneracy
184 SkVector v0 = pts[1] - pts[0];
185 SkVector v1 = pts[2] - pts[0];
186 if (SkScalarNearlyZero(v0.cross(v1))) {
187 return;
188 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500189 // TODO: Pull PathUtils out of Ganesh?
190 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400191 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500192 SkPoint* target = fPointBuffer.begin();
193 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
194 kQuadTolerance, &target, maxCount);
195 fPointBuffer.setCount(count);
196 for (int i = 0; i < count; i++) {
197 this->handleLine(fPointBuffer[i]);
198 }
199#else
200 // for now, just to draw something
201 this->handleLine(pts[1]);
202 this->handleLine(pts[2]);
203#endif
204}
205
Brian Salomonaff27a22017-02-06 15:47:44 -0500206void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500207 m.mapPoints(pts, 3);
208 this->handleQuad(pts);
209}
210
Brian Salomonaff27a22017-02-06 15:47:44 -0500211void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500212 m.mapPoints(pts, 4);
213#if SK_SUPPORT_GPU
214 // TODO: Pull PathUtils out of Ganesh?
215 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400216 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500217 SkPoint* target = fPointBuffer.begin();
218 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
219 kCubicTolerance, &target, maxCount);
220 fPointBuffer.setCount(count);
221 for (int i = 0; i < count; i++) {
222 this->handleLine(fPointBuffer[i]);
223 }
224#else
225 // for now, just to draw something
226 this->handleLine(pts[1]);
227 this->handleLine(pts[2]);
228 this->handleLine(pts[3]);
229#endif
230}
231
Brian Salomonaff27a22017-02-06 15:47:44 -0500232void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400233 if (m.hasPerspective()) {
234 w = SkConic::TransformW(pts, w, m);
235 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500236 m.mapPoints(pts, 3);
237 SkAutoConicToQuads quadder;
238 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
239 SkPoint lastPoint = *(quads++);
240 int count = quadder.countQuads();
241 for (int i = 0; i < count; ++i) {
242 SkPoint quadPts[3];
243 quadPts[0] = lastPoint;
244 quadPts[1] = quads[0];
245 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
246 this->handleQuad(quadPts);
247 lastPoint = quadPts[2];
248 quads += 2;
249 }
250}
251
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400252bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500253 // fill in fan from previous quad
254 SkScalar rotSin, rotCos;
255 int numSteps;
Jim Van Verth76387852017-05-16 16:55:16 -0400256 compute_radial_steps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
257 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400258 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400259 SkVector currNormal;
260 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
261 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
262 *fPositions.push() = fPrevPoint + currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500263 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400264 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500265
Jim Van Verthda965502017-04-11 15:29:14 -0400266 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500267 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400268 if (finishArc && numSteps) {
Jim Van Verthda965502017-04-11 15:29:14 -0400269 *fPositions.push() = fPrevPoint + nextNormal;
270 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400271 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Verthda965502017-04-11 15:29:14 -0400272 }
Jim Van Verth76387852017-05-16 16:55:16 -0400273 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400274
275 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500276}
277
Jim Van Verth872da6b2018-04-10 11:24:11 -0400278void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
279 auto indices = fIndices.append(3);
280
281 indices[0] = index0;
282 indices[1] = index1;
283 indices[2] = index2;
284}
285
286void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
287 uint16_t index2, uint16_t index3) {
288 auto indices = fIndices.append(6);
289
290 indices[0] = index0;
291 indices[1] = index1;
292 indices[2] = index2;
293
294 indices[3] = index2;
295 indices[4] = index1;
296 indices[5] = index3;
297}
298
Jim Van Verthda965502017-04-11 15:29:14 -0400299bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400300 if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400301 fTransformedHeightFunc = [this](const SkPoint& p) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400302 return fZPlaneParams.fZ;
Jim Van Verthda965502017-04-11 15:29:14 -0400303 };
304 } else {
305 SkMatrix ctmInverse;
Jim Van Vertha947e292018-02-26 13:54:34 -0500306 if (!ctm.invert(&ctmInverse) || !ctmInverse.isFinite()) {
Jim Van Verthda965502017-04-11 15:29:14 -0400307 return false;
308 }
Jim Van Verthda965502017-04-11 15:29:14 -0400309 // multiply by transpose
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400310 fTransformedZParams = SkPoint3::Make(
Jim Van Verthe308a122017-05-08 14:19:30 -0400311 ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
312 ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
313 ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
314
315 ctmInverse[SkMatrix::kMSkewX] * fZPlaneParams.fX +
316 ctmInverse[SkMatrix::kMScaleY] * fZPlaneParams.fY +
317 ctmInverse[SkMatrix::kMPersp1] * fZPlaneParams.fZ,
318
319 ctmInverse[SkMatrix::kMTransX] * fZPlaneParams.fX +
320 ctmInverse[SkMatrix::kMTransY] * fZPlaneParams.fY +
321 ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
322 );
Jim Van Verthda965502017-04-11 15:29:14 -0400323
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400324 if (ctm.hasPerspective()) {
325 // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
326 // so pre-compute those values that are independent of X and Y.
327 // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
328 fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
329 ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
330 fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
331 ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
332 fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
333 ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
334 SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
335 ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
336 ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
Jim Van Verthda965502017-04-11 15:29:14 -0400337
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400338 // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
339 // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
340 fTransformedZParams.fX *= ctmDeterminant;
341 fTransformedZParams.fY *= ctmDeterminant;
342 fTransformedZParams.fZ *= ctmDeterminant;
Jim Van Verthda965502017-04-11 15:29:14 -0400343
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400344 fTransformedHeightFunc = [this](const SkPoint& p) {
345 SkScalar denom = p.fX * fPartialDeterminants[0] +
346 p.fY * fPartialDeterminants[1] +
347 fPartialDeterminants[2];
348 SkScalar w = SkScalarFastInvert(denom);
349 return fZOffset + w*(fTransformedZParams.fX * p.fX +
350 fTransformedZParams.fY * p.fY +
351 fTransformedZParams.fZ);
352 };
353 } else {
354 fTransformedHeightFunc = [this](const SkPoint& p) {
355 return fZOffset + fTransformedZParams.fX * p.fX +
356 fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
357 };
358 }
Jim Van Verthda965502017-04-11 15:29:14 -0400359 }
360
361 return true;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500362}
363
364
365//////////////////////////////////////////////////////////////////////////////////////////////////
366
Brian Salomonaff27a22017-02-06 15:47:44 -0500367class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500368public:
369 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400370 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500371
372private:
373 void handleLine(const SkPoint& p) override;
Jim Van Verthda965502017-04-11 15:29:14 -0400374 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500375
Jim Van Verthda965502017-04-11 15:29:14 -0400376 static constexpr auto kMaxEdgeLenSqr = 20 * 20;
Jim Van Verth76387852017-05-16 16:55:16 -0400377 static constexpr auto kInsetFactor = -0.5f;
Jim Van Verthda965502017-04-11 15:29:14 -0400378
379 SkScalar offset(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400380 return SkDrawShadowMetrics::AmbientBlurRadius(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400381 }
382 SkColor umbraColor(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400383 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(z));
Jim Van Verth060d9822017-05-04 09:58:17 -0400384 return SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
Jim Van Verthda965502017-04-11 15:29:14 -0400385 }
386
Jim Van Vertha84898d2017-02-06 13:38:23 -0500387 int fCentroidCount;
Jim Van Verth76387852017-05-16 16:55:16 -0400388 bool fSplitFirstEdge;
389 bool fSplitPreviousEdge;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500390
Brian Salomonaff27a22017-02-06 15:47:44 -0500391 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500392};
393
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500394SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500395 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400396 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500397 bool transparent)
Jim Van Verth76387852017-05-16 16:55:16 -0400398 : INHERITED(zPlaneParams, transparent)
399 , fSplitFirstEdge(false)
400 , fSplitPreviousEdge(false) {
Jim Van Verth22526362018-02-28 14:51:19 -0500401 // TODO: support some concave paths
Mike Reed9d5c6742018-03-06 10:31:27 -0500402 SkASSERT(path.isConvex());
Jim Van Verth22526362018-02-28 14:51:19 -0500403
Jim Van Verthda965502017-04-11 15:29:14 -0400404 // Set base colors
Jim Van Verth1af03d42017-07-31 09:34:58 -0400405 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(heightFunc(0, 0)));
Jim Van Verthb4366552017-03-27 14:25:29 -0400406 // umbraColor is the interior value, penumbraColor the exterior value.
407 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
408 // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
409 // the final alpha.
Jim Van Verth060d9822017-05-04 09:58:17 -0400410 fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
411 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400412
Jim Van Verthda965502017-04-11 15:29:14 -0400413 // make sure we're not below the canvas plane
414 this->setZOffset(path.getBounds(), ctm.hasPerspective());
415
Jim Van Verth7c8008c2018-02-07 15:02:50 -0500416 if (!this->setTransformedHeightFunc(ctm)) {
417 return;
418 }
Jim Van Verthda965502017-04-11 15:29:14 -0400419
Jim Van Verthbce74962017-01-25 09:39:46 -0500420 // Outer ring: 3*numPts
421 // Middle ring: numPts
422 fPositions.setReserve(4 * path.countPoints());
423 fColors.setReserve(4 * path.countPoints());
424 // Outer ring: 12*numPts
425 // Middle ring: 0
426 fIndices.setReserve(12 * path.countPoints());
427
Jim Van Verthbce74962017-01-25 09:39:46 -0500428 // walk around the path, tessellate and generate outer ring
429 // if original path is transparent, will accumulate sum of points for centroid
430 SkPath::Iter iter(path, true);
431 SkPoint pts[4];
432 SkPath::Verb verb;
433 if (fTransparent) {
434 *fPositions.push() = SkPoint::Make(0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400435 *fColors.push() = fUmbraColor;
Jim Van Verthbce74962017-01-25 09:39:46 -0500436 fCentroidCount = 0;
437 }
438 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
439 switch (verb) {
440 case SkPath::kLine_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500441 this->INHERITED::handleLine(ctm, &pts[1]);
Jim Van Verthbce74962017-01-25 09:39:46 -0500442 break;
443 case SkPath::kQuad_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500444 this->handleQuad(ctm, pts);
Jim Van Verthbce74962017-01-25 09:39:46 -0500445 break;
446 case SkPath::kCubic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500447 this->handleCubic(ctm, pts);
Jim Van Verthbce74962017-01-25 09:39:46 -0500448 break;
449 case SkPath::kConic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500450 this->handleConic(ctm, pts, iter.conicWeight());
Jim Van Verthbce74962017-01-25 09:39:46 -0500451 break;
452 case SkPath::kMove_Verb:
453 case SkPath::kClose_Verb:
454 case SkPath::kDone_Verb:
455 break;
456 }
457 }
458
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500459 if (!this->indexCount()) {
460 return;
461 }
462
Jim Van Verth76387852017-05-16 16:55:16 -0400463 // Finish up
Jim Van Verthbce74962017-01-25 09:39:46 -0500464 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -0400465 if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
466 SkScalar z = fTransformedHeightFunc(fPrevPoint);
467 fRadius = this->offset(z);
468 SkVector scaledNormal(normal);
469 scaledNormal *= fRadius;
470 this->addArc(scaledNormal, true);
Jim Van Verthbce74962017-01-25 09:39:46 -0500471
Jim Van Verth76387852017-05-16 16:55:16 -0400472 // fix-up the last and first umbra points
473 SkVector inset = normal;
474 // adding to an average, so multiply by an additional half
475 inset *= 0.5f*kInsetFactor;
476 fPositions[fPrevUmbraIndex] += inset;
477 fPositions[fFirstVertexIndex] += inset;
478 // we multiply by another half because now we're adding to an average of an average
479 inset *= 0.5f;
480 if (fSplitPreviousEdge) {
481 fPositions[fPrevUmbraIndex - 2] += inset;
482 }
483 if (fSplitFirstEdge) {
484 fPositions[fFirstVertexIndex + 2] += inset;
485 }
486
Jim Van Verthda965502017-04-11 15:29:14 -0400487 // set up for final edge
488 z = fTransformedHeightFunc(fFirstPoint);
489 normal *= this->offset(z);
Jim Van Verthbce74962017-01-25 09:39:46 -0500490
Jim Van Verthda965502017-04-11 15:29:14 -0400491 // make sure we don't end up with a sharp alpha edge along the quad diagonal
Jim Van Verth76387852017-05-16 16:55:16 -0400492 if (fColors[fPrevUmbraIndex] != fColors[fFirstVertexIndex] &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500493 SkPointPriv::DistanceToSqd(fFirstPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400494 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + fPositions[fFirstVertexIndex];
Jim Van Verthda965502017-04-11 15:29:14 -0400495 centerPoint *= 0.5f;
496 *fPositions.push() = centerPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400497 *fColors.push() = SkPMLerp(fColors[fFirstVertexIndex], fColors[fPrevUmbraIndex], 128);
498 centerPoint = fPositions[fPositions.count()-2] + fPositions[fFirstVertexIndex+1];
499 centerPoint *= 0.5f;
500 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400501 *fColors.push() = fPenumbraColor;
502
Jim Van Verth76387852017-05-16 16:55:16 -0400503 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400504 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
505 fPositions.count() - 2, fPositions.count() - 1);
Jim Van Verth76387852017-05-16 16:55:16 -0400506 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400507 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
508 fPrevUmbraIndex, fPositions.count() - 3);
Jim Van Verth76387852017-05-16 16:55:16 -0400509 }
Jim Van Verthda965502017-04-11 15:29:14 -0400510
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400511 // if transparent, add point to first one in array and add to center fan
512 if (fTransparent) {
513 fPositions[0] += centerPoint;
514 ++fCentroidCount;
515
516 *fIndices.push() = 0;
517 *fIndices.push() = fPrevUmbraIndex;
518 *fIndices.push() = fPositions.count() - 2;
519 }
520
Jim Van Verthda965502017-04-11 15:29:14 -0400521 fPrevUmbraIndex = fPositions.count() - 2;
522 }
523
524 // final edge
Jim Van Vertha84898d2017-02-06 13:38:23 -0500525 *fPositions.push() = fFirstPoint + normal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500526 *fColors.push() = fPenumbraColor;
527
Jim Van Verth76387852017-05-16 16:55:16 -0400528 if (fColors[fPrevUmbraIndex] > fColors[fFirstVertexIndex]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400529 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 2,
530 fFirstVertexIndex, fPositions.count() - 1);
Jim Van Verthda965502017-04-11 15:29:14 -0400531 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400532 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
533 fPrevUmbraIndex, fFirstVertexIndex);
Jim Van Verthda965502017-04-11 15:29:14 -0400534 }
Jim Van Verth76387852017-05-16 16:55:16 -0400535 fPrevOutset = normal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500536 }
537
538 // finalize centroid
539 if (fTransparent) {
540 fPositions[0] *= SkScalarFastInvert(fCentroidCount);
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400541 fColors[0] = this->umbraColor(fTransformedHeightFunc(fPositions[0]));
Jim Van Verthbce74962017-01-25 09:39:46 -0500542
Jim Van Verth872da6b2018-04-10 11:24:11 -0400543 this->appendTriangle(0, fPrevUmbraIndex, fFirstVertexIndex);
Jim Van Verthbce74962017-01-25 09:39:46 -0500544 }
545
546 // final fan
547 if (fPositions.count() >= 3) {
Jim Van Verth76387852017-05-16 16:55:16 -0400548 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500549 fPrevPoint = fFirstPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400550 fRadius = this->offset(fTransformedHeightFunc(fPrevPoint));
Jim Van Verth76387852017-05-16 16:55:16 -0400551 if (this->addArc(fFirstOutset, false)) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400552 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1, fFirstVertexIndex + 1);
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400553 } else {
554 // arc is too small, set the first penumbra point to be the same position
555 // as the last one
Jim Van Verth76387852017-05-16 16:55:16 -0400556 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400557 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500558 }
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500559 fSucceeded = true;
Jim Van Verthbce74962017-01-25 09:39:46 -0500560}
561
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500562void SkAmbientShadowTessellator::handleLine(const SkPoint& p) {
Jim Van Verthbce74962017-01-25 09:39:46 -0500563 if (fInitPoints.count() < 2) {
564 *fInitPoints.push() = p;
565 return;
566 }
567
568 if (fInitPoints.count() == 2) {
569 // determine if cw or ccw
570 SkVector v0 = fInitPoints[1] - fInitPoints[0];
571 SkVector v1 = p - fInitPoints[0];
572 SkScalar perpDot = v0.fX*v1.fY - v0.fY*v1.fX;
573 if (SkScalarNearlyZero(perpDot)) {
574 // nearly parallel, just treat as straight line and continue
575 fInitPoints[1] = p;
576 return;
577 }
578
579 // if perpDot > 0, winding is ccw
580 fDirection = (perpDot > 0) ? -1 : 1;
581
582 // add first quad
Jim Van Verthda965502017-04-11 15:29:14 -0400583 SkVector normal;
584 if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &normal)) {
Jim Van Verthbce74962017-01-25 09:39:46 -0500585 // first two points are incident, make the third point the second and continue
586 fInitPoints[1] = p;
587 return;
588 }
589
Jim Van Vertha84898d2017-02-06 13:38:23 -0500590 fFirstPoint = fInitPoints[0];
Jim Van Verth76387852017-05-16 16:55:16 -0400591 fFirstVertexIndex = fPositions.count();
Jim Van Verthda965502017-04-11 15:29:14 -0400592 SkScalar z = fTransformedHeightFunc(fFirstPoint);
Jim Van Verth76387852017-05-16 16:55:16 -0400593 fFirstOutset = normal;
594 fFirstOutset *= this->offset(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400595
Jim Van Verth76387852017-05-16 16:55:16 -0400596 fPrevOutset = fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500597 fPrevPoint = fFirstPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400598 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500599
Jim Van Verthda965502017-04-11 15:29:14 -0400600 *fPositions.push() = fFirstPoint;
601 *fColors.push() = this->umbraColor(z);
Jim Van Verth76387852017-05-16 16:55:16 -0400602 *fPositions.push() = fFirstPoint + fFirstOutset;
Jim Van Verthbce74962017-01-25 09:39:46 -0500603 *fColors.push() = fPenumbraColor;
604 if (fTransparent) {
Jim Van Verthda965502017-04-11 15:29:14 -0400605 fPositions[0] += fFirstPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500606 fCentroidCount = 1;
607 }
Jim Van Verthda965502017-04-11 15:29:14 -0400608
609 // add the first quad
610 z = fTransformedHeightFunc(fInitPoints[1]);
611 fRadius = this->offset(z);
612 fUmbraColor = this->umbraColor(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400613 this->addEdge(fInitPoints[1], normal);
Jim Van Verthbce74962017-01-25 09:39:46 -0500614
615 // to ensure we skip this block next time
616 *fInitPoints.push() = p;
617 }
618
619 SkVector normal;
Jim Van Verth76387852017-05-16 16:55:16 -0400620 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400621 SkVector scaledNormal = normal;
622 scaledNormal *= fRadius;
623 this->addArc(scaledNormal, true);
624 SkScalar z = fTransformedHeightFunc(p);
625 fRadius = this->offset(z);
626 fUmbraColor = this->umbraColor(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400627 this->addEdge(p, normal);
Jim Van Verthbce74962017-01-25 09:39:46 -0500628 }
629}
630
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500631void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
Jim Van Verth76387852017-05-16 16:55:16 -0400632 // We compute the inset in two stages: first we inset by half the current normal,
633 // then on the next addEdge() we add half of the next normal to get an average of the two
634 SkVector insetNormal = nextNormal;
635 insetNormal *= 0.5f*kInsetFactor;
636
637 // Adding the other half of the average for the previous edge
638 fPositions[fPrevUmbraIndex] += insetNormal;
639
640 SkPoint umbraPoint = nextPoint + insetNormal;
641 SkVector outsetNormal = nextNormal;
642 outsetNormal *= fRadius;
643 SkPoint penumbraPoint = nextPoint + outsetNormal;
644
645 // For split edges, we're adding an average of two averages, so we multiply by another half
646 if (fSplitPreviousEdge) {
647 insetNormal *= 0.5f;
648 fPositions[fPrevUmbraIndex - 2] += insetNormal;
649 }
650
651 // 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 -0400652 if (fColors[fPrevUmbraIndex] != fUmbraColor &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500653 SkPointPriv::DistanceToSqd(nextPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400654
655 // This is lacking 1/4 of the next inset -- we'll add it the next time we call addEdge()
656 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + umbraPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400657 centerPoint *= 0.5f;
658 *fPositions.push() = centerPoint;
659 *fColors.push() = SkPMLerp(fUmbraColor, fColors[fPrevUmbraIndex], 128);
Jim Van Verth76387852017-05-16 16:55:16 -0400660 centerPoint = fPositions[fPositions.count()-2] + penumbraPoint;
661 centerPoint *= 0.5f;
662 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400663 *fColors.push() = fPenumbraColor;
664
665 // set triangularization to get best interpolation of color
666 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400667 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
668 fPositions.count() - 2, fPositions.count() - 1);
Jim Van Verthda965502017-04-11 15:29:14 -0400669 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400670 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
671 fPrevUmbraIndex, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -0400672 }
673
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400674 // if transparent, add point to first one in array and add to center fan
675 if (fTransparent) {
676 fPositions[0] += centerPoint;
677 ++fCentroidCount;
678
Jim Van Verth872da6b2018-04-10 11:24:11 -0400679 this->appendTriangle(0, fPrevUmbraIndex, fPositions.count() - 2);
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400680 }
681
Jim Van Verth76387852017-05-16 16:55:16 -0400682 fSplitPreviousEdge = true;
683 if (fPrevUmbraIndex == fFirstVertexIndex) {
684 fSplitFirstEdge = true;
685 }
Jim Van Verthda965502017-04-11 15:29:14 -0400686 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Verth76387852017-05-16 16:55:16 -0400687 } else {
688 fSplitPreviousEdge = false;
Jim Van Verthda965502017-04-11 15:29:14 -0400689 }
690
Jim Van Verthbce74962017-01-25 09:39:46 -0500691 // add next quad
Jim Van Verth76387852017-05-16 16:55:16 -0400692 *fPositions.push() = umbraPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500693 *fColors.push() = fUmbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -0400694 *fPositions.push() = penumbraPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500695 *fColors.push() = fPenumbraColor;
696
Jim Van Verthda965502017-04-11 15:29:14 -0400697 // set triangularization to get best interpolation of color
698 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400699 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
700 fPositions.count() - 2, fPositions.count() - 1);
Jim Van Verthda965502017-04-11 15:29:14 -0400701 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400702 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
703 fPrevUmbraIndex, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -0400704 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500705
706 // if transparent, add point to first one in array and add to center fan
707 if (fTransparent) {
708 fPositions[0] += nextPoint;
709 ++fCentroidCount;
710
Jim Van Verth872da6b2018-04-10 11:24:11 -0400711 this->appendTriangle(0, fPrevUmbraIndex, fPositions.count() - 2);
Jim Van Verthbce74962017-01-25 09:39:46 -0500712 }
713
Brian Salomon66085ed2017-02-02 15:39:34 -0500714 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500715 fPrevPoint = nextPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400716 fPrevOutset = outsetNormal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500717}
Jim Van Verth91af7272017-01-27 14:15:54 -0500718
719///////////////////////////////////////////////////////////////////////////////////////////////////
720
Brian Salomonaff27a22017-02-06 15:47:44 -0500721class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500722public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500723 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400724 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400725 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500726
Brian Salomon958fbc42017-01-30 17:01:28 -0500727private:
Brian Salomonab664fa2017-03-24 16:07:20 +0000728 void computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400729 const SkMatrix& shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000730 void computeClipVectorsAndTestCentroid();
Brian Salomon66085ed2017-02-02 15:39:34 -0500731 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
Brian Salomonab664fa2017-03-24 16:07:20 +0000732 int getClosestUmbraPoint(const SkPoint& point);
Brian Salomon958fbc42017-01-30 17:01:28 -0500733
Jim Van Verth872da6b2018-04-10 11:24:11 -0400734 bool computeConvexShadow(SkScalar radius);
735 bool computeConcaveShadow(SkScalar radius);
736
Jim Van Vertha84898d2017-02-06 13:38:23 -0500737 void handleLine(const SkPoint& p) override;
Jim Van Verthb55eb282017-07-18 14:13:45 -0400738 bool handlePolyPoint(const SkPoint& p);
Brian Salomon958fbc42017-01-30 17:01:28 -0500739
740 void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
Brian Salomonab664fa2017-03-24 16:07:20 +0000741 bool addInnerPoint(const SkPoint& pathPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400742 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
743
744 SkScalar offset(SkScalar z) {
745 float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
746 return fLightRadius*zRatio;
747 }
748
749 SkScalar fLightZ;
750 SkScalar fLightRadius;
751 SkScalar fOffsetAdjust;
Brian Salomon958fbc42017-01-30 17:01:28 -0500752
Brian Salomon958fbc42017-01-30 17:01:28 -0500753 SkTDArray<SkPoint> fClipPolygon;
Brian Salomon66085ed2017-02-02 15:39:34 -0500754 SkTDArray<SkVector> fClipVectors;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500755 SkPoint fCentroid;
Brian Salomonab664fa2017-03-24 16:07:20 +0000756 SkScalar fArea;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500757
Brian Salomonab664fa2017-03-24 16:07:20 +0000758 SkTDArray<SkPoint> fPathPolygon;
759 SkTDArray<SkPoint> fUmbraPolygon;
760 int fCurrClipPoint;
761 int fCurrUmbraPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -0500762 bool fPrevUmbraOutside;
763 bool fFirstUmbraOutside;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500764 bool fValidUmbra;
Brian Salomon958fbc42017-01-30 17:01:28 -0500765
Brian Salomonaff27a22017-02-06 15:47:44 -0500766 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -0500767};
768
Jim Van Vertha84898d2017-02-06 13:38:23 -0500769SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400770 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -0400771 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -0400772 bool transparent)
Jim Van Verthe308a122017-05-08 14:19:30 -0400773 : INHERITED(zPlaneParams, transparent)
Jim Van Verthda965502017-04-11 15:29:14 -0400774 , fLightZ(lightPos.fZ)
775 , fLightRadius(lightRadius)
776 , fOffsetAdjust(0)
777 , fCurrClipPoint(0)
778 , fPrevUmbraOutside(false)
779 , fFirstUmbraOutside(false)
780 , fValidUmbra(true) {
781
782 // make sure we're not below the canvas plane
783 if (this->setZOffset(path.getBounds(), ctm.hasPerspective())) {
784 // Adjust light height and radius
785 fLightRadius *= (fLightZ + fZOffset) / fLightZ;
786 fLightZ += fZOffset;
787 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400788
789 // Set radius and colors
Jim Van Verthb4366552017-03-27 14:25:29 -0400790 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verthe308a122017-05-08 14:19:30 -0400791 SkScalar occluderHeight = this->heightFunc(center.fX, center.fY) + fZOffset;
Jim Van Verth060d9822017-05-04 09:58:17 -0400792 fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
793 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400794
Jim Van Verth1af03d42017-07-31 09:34:58 -0400795 // Compute the blur radius, scale and translation for the spot shadow.
796 SkScalar radius;
Jim Van Verthda965502017-04-11 15:29:14 -0400797 SkMatrix shadowTransform;
798 if (!ctm.hasPerspective()) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400799 SkScalar scale;
800 SkVector translate;
801 SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, fLightZ,
802 lightRadius, &radius, &scale, &translate);
Jim Van Verthda965502017-04-11 15:29:14 -0400803 shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
804 } else {
805 // For perspective, we have a scale, a z-shear, and another projective divide --
806 // this varies at each point so we can't use an affine transform.
807 // We'll just apply this to each generated point in turn.
808 shadowTransform.reset();
809 // Also can't cull the center (for now).
810 fTransparent = true;
Jim Van Verth1af03d42017-07-31 09:34:58 -0400811 radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
Jim Van Verthda965502017-04-11 15:29:14 -0400812 }
Jim Van Verth1af03d42017-07-31 09:34:58 -0400813 fRadius = radius;
Jim Van Verthda965502017-04-11 15:29:14 -0400814 SkMatrix fullTransform = SkMatrix::Concat(shadowTransform, ctm);
815
816 // Set up our reverse mapping
Jim Van Verth7c8008c2018-02-07 15:02:50 -0500817 if (!this->setTransformedHeightFunc(fullTransform)) {
818 return;
819 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400820
Brian Salomonab664fa2017-03-24 16:07:20 +0000821 // TODO: calculate these reserves better
Brian Salomon66085ed2017-02-02 15:39:34 -0500822 // Penumbra ring: 3*numPts
823 // Umbra ring: numPts
Jim Van Verth91af7272017-01-27 14:15:54 -0500824 // Inner ring: numPts
Brian Salomon66085ed2017-02-02 15:39:34 -0500825 fPositions.setReserve(5 * path.countPoints());
826 fColors.setReserve(5 * path.countPoints());
827 // Penumbra ring: 12*numPts
828 // Umbra ring: 3*numPts
829 fIndices.setReserve(15 * path.countPoints());
Brian Salomone7c85c42017-03-24 16:00:35 +0000830 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +0000831
832 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verthda965502017-04-11 15:29:14 -0400833 this->computeClipAndPathPolygons(path, ctm, shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000834 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3) {
Brian Salomon66085ed2017-02-02 15:39:34 -0500835 return;
836 }
Brian Salomon66085ed2017-02-02 15:39:34 -0500837
Brian Salomonab664fa2017-03-24 16:07:20 +0000838 // check to see if umbra collapses
Jim Van Verth872da6b2018-04-10 11:24:11 -0400839 if (path.isConvex()) {
840 SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
841 fPathPolygon[0],
842 fPathPolygon[1]);
843 SkRect bounds;
844 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
845 for (int i = 1; i < fPathPolygon.count(); ++i) {
846 int j = i + 1;
847 if (i == fPathPolygon.count() - 1) {
848 j = 0;
849 }
850 SkPoint currPoint = fPathPolygon[i];
851 SkPoint nextPoint = fPathPolygon[j];
852 SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
853 nextPoint);
854 if (distSq < minDistSq) {
855 minDistSq = distSq;
856 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000857 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400858 static constexpr auto kTolerance = 1.0e-2f;
859 if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
860 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
861 SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
862 fOffsetAdjust = newRadius - radius;
863 SkScalar ratio = 128 * (newRadius + radius) / radius;
864 // they aren't PMColors, but the interpolation algorithm is the same
865 fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
866 radius = newRadius;
Brian Salomonab664fa2017-03-24 16:07:20 +0000867 }
868 }
Jim Van Verth91af7272017-01-27 14:15:54 -0500869
Brian Salomonab664fa2017-03-24 16:07:20 +0000870 // compute vectors for clip tests
871 this->computeClipVectorsAndTestCentroid();
872
Jim Van Verth872da6b2018-04-10 11:24:11 -0400873 if (ctm.hasPerspective()) {
874 for (int i = 0; i < fPositions.count(); ++i) {
875 SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
876 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
877 fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
878 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000879 }
880
Jim Van Verth872da6b2018-04-10 11:24:11 -0400881 if (path.isConvex()) {
882 if (!this->computeConvexShadow(radius)) {
Jim Van Verthb55eb282017-07-18 14:13:45 -0400883 return;
884 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400885 } else {
886 if (!this->computeConcaveShadow(radius)) {
887 return;
Brian Salomon66085ed2017-02-02 15:39:34 -0500888 }
Jim Van Verth91af7272017-01-27 14:15:54 -0500889 }
Jim Van Verthda965502017-04-11 15:29:14 -0400890
891 if (ctm.hasPerspective()) {
892 for (int i = 0; i < fPositions.count(); ++i) {
893 SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
894 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
895 fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
896 fPositions[i].fY = (fPositions[i].fY*fLightZ - lightPos.fY*pathZ)*factor;
897 }
898#ifdef DRAW_CENTROID
899 SkScalar pathZ = fTransformedHeightFunc(fCentroid);
900 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
901 fCentroid.fX = (fCentroid.fX*fLightZ - lightPos.fX*pathZ)*factor;
902 fCentroid.fY = (fCentroid.fY*fLightZ - lightPos.fY*pathZ)*factor;
903#endif
904 }
905#ifdef DRAW_CENTROID
906 *fPositions.push() = fCentroid + SkVector::Make(-2, -2);
907 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
908 *fPositions.push() = fCentroid + SkVector::Make(2, -2);
909 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
910 *fPositions.push() = fCentroid + SkVector::Make(-2, 2);
911 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
912 *fPositions.push() = fCentroid + SkVector::Make(2, 2);
913 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
914
Jim Van Verth872da6b2018-04-10 11:24:11 -0400915 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
916 fPositions.count() - 4, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -0400917#endif
918
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500919 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -0500920}
921
Brian Salomonab664fa2017-03-24 16:07:20 +0000922void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400923 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000924
925 fPathPolygon.setReserve(path.countPoints());
926
927 // Walk around the path and compute clip polygon and path polygon.
928 // Will also accumulate sum of areas for centroid.
929 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -0500930 SkPath::Iter iter(path, true);
931 SkPoint pts[4];
932 SkPath::Verb verb;
933
Jim Van Verth91af7272017-01-27 14:15:54 -0500934 fClipPolygon.reset();
935
Brian Salomonab664fa2017-03-24 16:07:20 +0000936 // init centroid
937 fCentroid = SkPoint::Make(0, 0);
938 fArea = 0;
939
Brian Salomon66085ed2017-02-02 15:39:34 -0500940 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +0000941 static constexpr SkScalar kA = 0.32495117187f;
942 static constexpr SkScalar kB = 0.44311523437f;
943 static constexpr SkScalar kC = 0.20141601562f;
944 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -0500945
946 SkPoint curvePoint;
947 SkScalar w;
Jim Van Verth91af7272017-01-27 14:15:54 -0500948 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
949 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -0500950 case SkPath::kLine_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500951 ctm.mapPoints(&pts[1], 1);
Jim Van Verth91af7272017-01-27 14:15:54 -0500952 *fClipPolygon.push() = pts[1];
Brian Salomonab664fa2017-03-24 16:07:20 +0000953 this->INHERITED::handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -0500954 break;
955 case SkPath::kQuad_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500956 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -0500957 // point at t = 1/2
958 curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
959 curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY;
960 *fClipPolygon.push() = curvePoint;
Brian Salomon66085ed2017-02-02 15:39:34 -0500961 *fClipPolygon.push() = pts[2];
Brian Salomonab664fa2017-03-24 16:07:20 +0000962 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -0500963 break;
964 case SkPath::kConic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500965 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -0500966 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -0500967 // point at t = 1/2
Brian Salomon66085ed2017-02-02 15:39:34 -0500968 curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
969 curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
970 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
971 *fClipPolygon.push() = curvePoint;
Brian Salomon66085ed2017-02-02 15:39:34 -0500972 *fClipPolygon.push() = pts[2];
Brian Salomonab664fa2017-03-24 16:07:20 +0000973 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -0500974 break;
975 case SkPath::kCubic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500976 ctm.mapPoints(pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -0500977 // point at t = 5/16
978 curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
979 curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY;
980 *fClipPolygon.push() = curvePoint;
Brian Salomon66085ed2017-02-02 15:39:34 -0500981 // point at t = 11/16
982 curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
983 curvePoint.fY = kD*pts[0].fY + kC*pts[1].fY + kB*pts[2].fY + kA*pts[3].fY;
984 *fClipPolygon.push() = curvePoint;
Brian Salomon66085ed2017-02-02 15:39:34 -0500985 *fClipPolygon.push() = pts[3];
Brian Salomonab664fa2017-03-24 16:07:20 +0000986 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -0500987 break;
Brian Salomonab664fa2017-03-24 16:07:20 +0000988 case SkPath::kMove_Verb:
Jim Van Verth91af7272017-01-27 14:15:54 -0500989 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +0000990 case SkPath::kDone_Verb:
Jim Van Verth91af7272017-01-27 14:15:54 -0500991 break;
992 default:
993 SkDEBUGFAIL("unknown verb");
994 }
995 }
996
Brian Salomonab664fa2017-03-24 16:07:20 +0000997 // finish centroid
998 if (fPathPolygon.count() > 0) {
999 SkPoint currPoint = fPathPolygon[fPathPolygon.count() - 1];
1000 SkPoint nextPoint = fPathPolygon[0];
1001 SkScalar quadArea = currPoint.cross(nextPoint);
1002 fCentroid.fX += (currPoint.fX + nextPoint.fX) * quadArea;
1003 fCentroid.fY += (currPoint.fY + nextPoint.fY) * quadArea;
1004 fArea += quadArea;
1005 fCentroid *= SK_Scalar1 / (3 * fArea);
1006 }
1007
1008 fCurrClipPoint = fClipPolygon.count() - 1;
Brian Salomon66085ed2017-02-02 15:39:34 -05001009}
1010
Brian Salomonab664fa2017-03-24 16:07:20 +00001011void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
Brian Salomon66085ed2017-02-02 15:39:34 -05001012 SkASSERT(fClipPolygon.count() >= 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001013
Brian Salomonab664fa2017-03-24 16:07:20 +00001014 // init clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001015 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
1016 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001017
1018 // init centroid check
1019 bool hiddenCentroid = true;
Brian Salomonab664fa2017-03-24 16:07:20 +00001020 SkVector v1 = fCentroid - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001021 SkScalar initCross = v0.cross(v1);
1022
1023 for (int p = 1; p < fClipPolygon.count(); ++p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001024 // add to clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001025 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
1026 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001027 // Determine if transformed centroid is inside clipPolygon.
Brian Salomonab664fa2017-03-24 16:07:20 +00001028 v1 = fCentroid - fClipPolygon[p];
Brian Salomon66085ed2017-02-02 15:39:34 -05001029 if (initCross*v0.cross(v1) <= 0) {
1030 hiddenCentroid = false;
1031 }
1032 }
1033 SkASSERT(fClipVectors.count() == fClipPolygon.count());
1034
Brian Salomonab664fa2017-03-24 16:07:20 +00001035 fTransparent = fTransparent || !hiddenCentroid;
Brian Salomon66085ed2017-02-02 15:39:34 -05001036}
1037
1038bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
1039 SkPoint* clipPoint) {
1040 SkVector segmentVector = centroid - umbraPoint;
1041
Brian Salomonab664fa2017-03-24 16:07:20 +00001042 int startClipPoint = fCurrClipPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001043 do {
Brian Salomonab664fa2017-03-24 16:07:20 +00001044 SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
1045 SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
Brian Salomon66085ed2017-02-02 15:39:34 -05001046 SkScalar t_num = dp.cross(segmentVector);
1047 // if line segments are nearly parallel
1048 if (SkScalarNearlyZero(denom)) {
1049 // and collinear
1050 if (SkScalarNearlyZero(t_num)) {
1051 return false;
1052 }
1053 // otherwise are separate, will try the next poly segment
1054 // else if crossing lies within poly segment
1055 } else if (t_num >= 0 && t_num <= denom) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001056 SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
Brian Salomon66085ed2017-02-02 15:39:34 -05001057 // if umbra point is inside the clip polygon
Jim Van Verthda965502017-04-11 15:29:14 -04001058 if (s_num >= 0 && s_num <= denom) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001059 segmentVector *= s_num/denom;
1060 *clipPoint = umbraPoint + segmentVector;
1061 return true;
1062 }
1063 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001064 fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
1065 } while (fCurrClipPoint != startClipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001066
1067 return false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001068}
1069
Brian Salomonab664fa2017-03-24 16:07:20 +00001070int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
Cary Clarkdf429f32017-11-08 11:44:31 -05001071 SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraPoint]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001072 int index = fCurrUmbraPoint;
1073 int dir = 1;
1074 int next = (index + dir) % fUmbraPolygon.count();
1075
1076 // init travel direction
Cary Clarkdf429f32017-11-08 11:44:31 -05001077 SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001078 if (distance < minDistance) {
1079 index = next;
1080 minDistance = distance;
1081 } else {
1082 dir = fUmbraPolygon.count()-1;
1083 }
1084
1085 // iterate until we find a point that increases the distance
1086 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001087 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001088 while (distance < minDistance) {
1089 index = next;
1090 minDistance = distance;
1091 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001092 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001093 }
1094
1095 fCurrUmbraPoint = index;
1096 return index;
1097}
1098
Jim Van Verth872da6b2018-04-10 11:24:11 -04001099bool SkSpotShadowTessellator::computeConvexShadow(SkScalar radius) {
1100 // generate inner ring
1101 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1102 &fUmbraPolygon)) {
1103 // this shouldn't happen, but just in case we'll inset using the centroid
1104 fValidUmbra = false;
1105 }
1106
1107 // walk around the path polygon, generate outer ring and connect to inner ring
1108 if (fTransparent) {
1109 *fPositions.push() = fCentroid;
1110 *fColors.push() = fUmbraColor;
1111 }
1112 fCurrUmbraPoint = 0;
1113 for (int i = 0; i < fPathPolygon.count(); ++i) {
1114 if (!this->handlePolyPoint(fPathPolygon[i])) {
1115 return false;
1116 }
1117 }
1118
1119 if (!this->indexCount()) {
1120 return false;
1121 }
1122
1123 // finish up the final verts
1124 SkVector normal;
1125 if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
1126 normal *= fRadius;
1127 this->addArc(normal, true);
1128
1129 // add to center fan
1130 if (fTransparent) {
1131 this->appendTriangle(0, fPrevUmbraIndex, fFirstVertexIndex);
1132 // or to clip ring
1133 } else {
1134 if (fFirstUmbraOutside) {
1135 this->appendTriangle(fPrevUmbraIndex, fFirstVertexIndex, fFirstVertexIndex + 1);
1136 if (fPrevUmbraOutside) {
1137 // fill out quad
1138 this->appendTriangle(fPrevUmbraIndex, fFirstVertexIndex + 1,
1139 fPrevUmbraIndex + 1);
1140 }
1141 } else if (fPrevUmbraOutside) {
1142 // add tri
1143 this->appendTriangle(fPrevUmbraIndex, fFirstVertexIndex, fPrevUmbraIndex + 1);
1144 }
1145 }
1146
1147 // add final edge
1148 *fPositions.push() = fFirstPoint + normal;
1149 *fColors.push() = fPenumbraColor;
1150
1151 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 2,
1152 fFirstVertexIndex, fPositions.count() - 1);
1153
1154 fPrevOutset = normal;
1155 }
1156
1157 // final fan
1158 if (fPositions.count() >= 3) {
1159 fPrevUmbraIndex = fFirstVertexIndex;
1160 fPrevPoint = fFirstPoint;
1161 if (this->addArc(fFirstOutset, false)) {
1162 if (fFirstUmbraOutside) {
1163 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1164 fFirstVertexIndex + 2);
1165 } else {
1166 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1167 fFirstVertexIndex + 1);
1168 }
1169 } else {
1170 // no arc added, fix up by setting first penumbra point position to last one
1171 if (fFirstUmbraOutside) {
1172 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
1173 } else {
1174 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
1175 }
1176 }
1177 }
1178
1179 return true;
1180}
1181
1182bool SkSpotShadowTessellator::computeConcaveShadow(SkScalar radius) {
1183 // TODO: remove when we support filling the penumbra
1184 if (fTransparent) {
1185 return false;
1186 }
1187
1188 // generate inner ring
1189 SkTDArray<int> umbraIndices;
1190 umbraIndices.setReserve(fPathPolygon.count());
1191 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1192 &fUmbraPolygon, &umbraIndices)) {
1193 // TODO: figure out how to handle this case
1194 return false;
1195 }
1196
1197 // generate outer ring
1198 SkTDArray<SkPoint> penumbraPolygon;
1199 SkTDArray<int> penumbraIndices;
1200 penumbraPolygon.setReserve(fUmbraPolygon.count());
1201 penumbraIndices.setReserve(fUmbraPolygon.count());
1202 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -radius,
1203 &penumbraPolygon, &penumbraIndices)) {
1204 // TODO: figure out how to handle this case
1205 return false;
1206 }
1207
1208 if (!fUmbraPolygon.count() || !penumbraPolygon.count()) {
1209 return false;
1210 }
1211
1212 // attach the rings together
1213
1214 // find minimum indices
1215 int minIndex = 0;
1216 int min = penumbraIndices[0];
1217 for (int i = 1; i < penumbraIndices.count(); ++i) {
1218 if (penumbraIndices[i] < min) {
1219 min = penumbraIndices[i];
1220 minIndex = i;
1221 }
1222 }
1223 int currPenumbra = minIndex;
1224
1225 minIndex = 0;
1226 min = umbraIndices[0];
1227 for (int i = 1; i < umbraIndices.count(); ++i) {
1228 if (umbraIndices[i] < min) {
1229 min = umbraIndices[i];
1230 minIndex = i;
1231 }
1232 }
1233 int currUmbra = minIndex;
1234
1235 // now find a case where the indices are equal (there should be at least one)
1236 int maxPenumbraIndex = fPathPolygon.count()-1;
1237 int maxUmbraIndex = fPathPolygon.count()-1;
1238 while (penumbraIndices[currPenumbra] != umbraIndices[currUmbra]) {
1239 if (penumbraIndices[currPenumbra] < umbraIndices[currUmbra]) {
1240 penumbraIndices[currPenumbra] += fPathPolygon.count();
1241 maxPenumbraIndex = penumbraIndices[currPenumbra];
1242 currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
1243 } else {
1244 umbraIndices[currUmbra] += fPathPolygon.count();
1245 maxUmbraIndex = umbraIndices[currUmbra];
1246 currUmbra = (currUmbra + 1) % fUmbraPolygon.count();
1247 }
1248 }
1249
1250 *fPositions.push() = penumbraPolygon[currPenumbra];
1251 *fColors.push() = fPenumbraColor;
1252 int prevPenumbraIndex = 0;
1253 *fPositions.push() = fUmbraPolygon[currUmbra];
1254 *fColors.push() = fUmbraColor;
1255 fPrevUmbraIndex = 1;
1256
1257 int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
1258 int nextUmbra = (currUmbra + 1) % fUmbraPolygon.count();
1259 while (penumbraIndices[nextPenumbra] <= maxPenumbraIndex ||
1260 umbraIndices[nextUmbra] <= maxUmbraIndex) {
1261
1262 if (umbraIndices[nextUmbra] == penumbraIndices[nextPenumbra]) {
1263 // advance both one step
1264 *fPositions.push() = penumbraPolygon[nextPenumbra];
1265 *fColors.push() = fPenumbraColor;
1266 int currPenumbraIndex = fPositions.count() - 1;
1267
1268 *fPositions.push() = fUmbraPolygon[nextUmbra];
1269 *fColors.push() = fUmbraColor;
1270 int currUmbraIndex = fPositions.count() - 1;
1271
1272 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
1273 fPrevUmbraIndex, currUmbraIndex);
1274
1275 prevPenumbraIndex = currPenumbraIndex;
1276 penumbraIndices[currPenumbra] += fPathPolygon.count();
1277 currPenumbra = nextPenumbra;
1278 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
1279
1280 fPrevUmbraIndex = currUmbraIndex;
1281 umbraIndices[currUmbra] += fPathPolygon.count();
1282 currUmbra = nextUmbra;
1283 nextUmbra = (currUmbra + 1) % fUmbraPolygon.count();
1284 }
1285
1286 while (penumbraIndices[nextPenumbra] < umbraIndices[nextUmbra] &&
1287 penumbraIndices[nextPenumbra] <= maxPenumbraIndex) {
1288 // fill out penumbra arc
1289 *fPositions.push() = penumbraPolygon[nextPenumbra];
1290 *fColors.push() = fPenumbraColor;
1291 int currPenumbraIndex = fPositions.count() - 1;
1292
1293 this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
1294
1295 prevPenumbraIndex = currPenumbraIndex;
1296 // this ensures the ordering when we wrap around
1297 penumbraIndices[currPenumbra] += fPathPolygon.count();
1298 currPenumbra = nextPenumbra;
1299 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
1300 }
1301
1302 while (umbraIndices[nextUmbra] < penumbraIndices[nextPenumbra] &&
1303 umbraIndices[nextUmbra] <= maxUmbraIndex) {
1304 // fill out umbra arc
1305 *fPositions.push() = fUmbraPolygon[nextUmbra];
1306 *fColors.push() = fUmbraColor;
1307 int currUmbraIndex = fPositions.count() - 1;
1308
1309 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
1310
1311 fPrevUmbraIndex = currUmbraIndex;
1312 // this ensures the ordering when we wrap around
1313 umbraIndices[currUmbra] += fPathPolygon.count();
1314 currUmbra = nextUmbra;
1315 nextUmbra = (currUmbra + 1) % fUmbraPolygon.count();
1316 }
1317 }
1318 // finish up by advancing both one step
1319 *fPositions.push() = penumbraPolygon[nextPenumbra];
1320 *fColors.push() = fPenumbraColor;
1321 int currPenumbraIndex = fPositions.count() - 1;
1322
1323 *fPositions.push() = fUmbraPolygon[nextUmbra];
1324 *fColors.push() = fUmbraColor;
1325 int currUmbraIndex = fPositions.count() - 1;
1326
1327 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
1328 fPrevUmbraIndex, currUmbraIndex);
1329
1330 if (fTransparent) {
1331 // TODO: fill penumbra
1332 }
1333
1334 return true;
1335}
1336
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001337void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
Jim Van Verth91af7272017-01-27 14:15:54 -05001338 SkPoint* pts, int count) {
1339 // TODO: vectorize
1340 for (int i = 0; i < count; ++i) {
1341 pts[i] *= scale;
1342 pts[i] += xlate;
1343 }
1344}
1345
Brian Salomonab664fa2017-03-24 16:07:20 +00001346static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
1347 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
1348 static constexpr SkScalar kCloseSqd = kClose*kClose;
1349
Cary Clarkdf429f32017-11-08 11:44:31 -05001350 SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
Brian Salomonab664fa2017-03-24 16:07:20 +00001351 return distSq < kCloseSqd;
1352}
1353
Jim Van Verthb55eb282017-07-18 14:13:45 -04001354static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001355 SkVector v0 = p1 - p0;
1356 SkVector v1 = p2 - p0;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001357 return v0.cross(v1);
1358}
1359
1360static bool is_collinear(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
1361 return (SkScalarNearlyZero(perp_dot(p0, p1, p2)));
Brian Salomonab664fa2017-03-24 16:07:20 +00001362}
1363
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001364void SkSpotShadowTessellator::handleLine(const SkPoint& p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001365 // remove coincident points and add to centroid
1366 if (fPathPolygon.count() > 0) {
1367 const SkPoint& lastPoint = fPathPolygon[fPathPolygon.count() - 1];
1368 if (duplicate_pt(p, lastPoint)) {
1369 return;
1370 }
1371 SkScalar quadArea = lastPoint.cross(p);
1372 fCentroid.fX += (p.fX + lastPoint.fX) * quadArea;
1373 fCentroid.fY += (p.fY + lastPoint.fY) * quadArea;
1374 fArea += quadArea;
1375 }
1376
1377 // try to remove collinear points
1378 if (fPathPolygon.count() > 1 && is_collinear(fPathPolygon[fPathPolygon.count()-2],
1379 fPathPolygon[fPathPolygon.count()-1],
1380 p)) {
1381 fPathPolygon[fPathPolygon.count() - 1] = p;
1382 } else {
1383 *fPathPolygon.push() = p;
1384 }
1385}
1386
Jim Van Verthb55eb282017-07-18 14:13:45 -04001387bool SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001388 if (fInitPoints.count() < 2) {
1389 *fInitPoints.push() = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001390 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001391 }
1392
1393 if (fInitPoints.count() == 2) {
1394 // determine if cw or ccw
Jim Van Verthb55eb282017-07-18 14:13:45 -04001395 SkScalar perpDot = perp_dot(fInitPoints[0], fInitPoints[1], p);
Jim Van Verth91af7272017-01-27 14:15:54 -05001396 if (SkScalarNearlyZero(perpDot)) {
1397 // nearly parallel, just treat as straight line and continue
1398 fInitPoints[1] = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001399 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001400 }
1401
1402 // if perpDot > 0, winding is ccw
1403 fDirection = (perpDot > 0) ? -1 : 1;
1404
1405 // add first quad
Jim Van Verth76387852017-05-16 16:55:16 -04001406 if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &fFirstOutset)) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001407 // first two points are incident, make the third point the second and continue
1408 fInitPoints[1] = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001409 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001410 }
1411
Jim Van Verth76387852017-05-16 16:55:16 -04001412 fFirstOutset *= fRadius;
Jim Van Verth91af7272017-01-27 14:15:54 -05001413 fFirstPoint = fInitPoints[0];
Jim Van Verth76387852017-05-16 16:55:16 -04001414 fFirstVertexIndex = fPositions.count();
1415 fPrevOutset = fFirstOutset;
Jim Van Verth91af7272017-01-27 14:15:54 -05001416 fPrevPoint = fFirstPoint;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001417 fPrevUmbraIndex = -1;
Jim Van Verth91af7272017-01-27 14:15:54 -05001418
Brian Salomon66085ed2017-02-02 15:39:34 -05001419 this->addInnerPoint(fFirstPoint);
Jim Van Verth76387852017-05-16 16:55:16 -04001420 fPrevUmbraIndex = fFirstVertexIndex;
Brian Salomon66085ed2017-02-02 15:39:34 -05001421
1422 if (!fTransparent) {
1423 SkPoint clipPoint;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001424 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
1425 fCentroid, &clipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001426 if (isOutside) {
1427 *fPositions.push() = clipPoint;
1428 *fColors.push() = fUmbraColor;
1429 }
1430 fPrevUmbraOutside = isOutside;
1431 fFirstUmbraOutside = isOutside;
1432 }
1433
Jim Van Verth76387852017-05-16 16:55:16 -04001434 SkPoint newPoint = fFirstPoint + fFirstOutset;
Jim Van Verth91af7272017-01-27 14:15:54 -05001435 *fPositions.push() = newPoint;
1436 *fColors.push() = fPenumbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -04001437 this->addEdge(fInitPoints[1], fFirstOutset);
Jim Van Verth91af7272017-01-27 14:15:54 -05001438
1439 // to ensure we skip this block next time
1440 *fInitPoints.push() = p;
1441 }
1442
Jim Van Verthb55eb282017-07-18 14:13:45 -04001443 // if concave, abort
1444 SkScalar perpDot = perp_dot(fInitPoints[1], fInitPoints[2], p);
1445 if (fDirection*perpDot > 0) {
1446 return false;
1447 }
1448
Jim Van Verth91af7272017-01-27 14:15:54 -05001449 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -04001450 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
1451 normal *= fRadius;
1452 this->addArc(normal, true);
1453 this->addEdge(p, normal);
Jim Van Verthb55eb282017-07-18 14:13:45 -04001454 fInitPoints[1] = fInitPoints[2];
1455 fInitPoints[2] = p;
Jim Van Verth91af7272017-01-27 14:15:54 -05001456 }
Jim Van Verthb55eb282017-07-18 14:13:45 -04001457
1458 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001459}
1460
Brian Salomonab664fa2017-03-24 16:07:20 +00001461bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint) {
1462 SkPoint umbraPoint;
1463 if (!fValidUmbra) {
1464 SkVector v = fCentroid - pathPoint;
1465 v *= 0.95f;
1466 umbraPoint = pathPoint + v;
Jim Van Verth91af7272017-01-27 14:15:54 -05001467 } else {
Brian Salomonab664fa2017-03-24 16:07:20 +00001468 umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
Jim Van Verth91af7272017-01-27 14:15:54 -05001469 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001470
Jim Van Verth91af7272017-01-27 14:15:54 -05001471 fPrevPoint = pathPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001472
1473 // merge "close" points
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001474 if (fPrevUmbraIndex == -1 ||
Brian Salomonab664fa2017-03-24 16:07:20 +00001475 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
1476 *fPositions.push() = umbraPoint;
1477 *fColors.push() = fUmbraColor;
1478
1479 return false;
1480 } else {
1481 return true;
1482 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001483}
1484
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001485void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001486 // add next umbra point
Brian Salomonab664fa2017-03-24 16:07:20 +00001487 bool duplicate = this->addInnerPoint(nextPoint);
1488 int prevPenumbraIndex = duplicate ? fPositions.count()-1 : fPositions.count()-2;
1489 int currUmbraIndex = duplicate ? fPrevUmbraIndex : fPositions.count()-1;
Brian Salomon66085ed2017-02-02 15:39:34 -05001490
Brian Salomonab664fa2017-03-24 16:07:20 +00001491 if (!duplicate) {
1492 // add to center fan if transparent or centroid showing
1493 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001494 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001495 // otherwise add to clip ring
1496 } else {
Brian Salomon66085ed2017-02-02 15:39:34 -05001497 SkPoint clipPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001498 bool isOutside = this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
1499 &clipPoint);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001500
Brian Salomon66085ed2017-02-02 15:39:34 -05001501 if (isOutside) {
1502 *fPositions.push() = clipPoint;
1503 *fColors.push() = fUmbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001504 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001505 if (fPrevUmbraOutside) {
1506 // fill out quad
Jim Van Verth872da6b2018-04-10 11:24:11 -04001507 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
1508 fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001509 }
1510 } else if (fPrevUmbraOutside) {
1511 // add tri
Jim Van Verth872da6b2018-04-10 11:24:11 -04001512 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001513 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001514
Brian Salomon66085ed2017-02-02 15:39:34 -05001515 fPrevUmbraOutside = isOutside;
1516 }
1517 }
1518
1519 // add next penumbra point and quad
Jim Van Verth91af7272017-01-27 14:15:54 -05001520 SkPoint newPoint = nextPoint + nextNormal;
1521 *fPositions.push() = newPoint;
1522 *fColors.push() = fPenumbraColor;
1523
Brian Salomonab664fa2017-03-24 16:07:20 +00001524 if (!duplicate) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001525 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001526 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001527 this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
Jim Van Verth91af7272017-01-27 14:15:54 -05001528
Brian Salomon66085ed2017-02-02 15:39:34 -05001529 fPrevUmbraIndex = currUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -04001530 fPrevOutset = nextNormal;
Jim Van Verth91af7272017-01-27 14:15:54 -05001531}
Brian Salomon958fbc42017-01-30 17:01:28 -05001532
1533///////////////////////////////////////////////////////////////////////////////////////////////////
1534
Brian Salomonaff27a22017-02-06 15:47:44 -05001535sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001536 const SkPoint3& zPlane, bool transparent) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001537 if (!path.isFinite() || !path.isConvex() || !ctm.isFinite() || !zPlane.isFinite()) {
1538 return nullptr;
1539 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001540 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001541 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001542}
1543
Brian Salomonaff27a22017-02-06 15:47:44 -05001544sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001545 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001546 SkScalar lightRadius, bool transparent) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001547 if (!path.isFinite() || !path.isConvex() || !ctm.isFinite() || !zPlane.isFinite() ||
1548 !lightPos.isFinite() || !SkScalarIsFinite(lightRadius) || !(lightRadius > 0)) {
1549 return nullptr;
1550 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001551 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001552 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001553}