blob: aff59bf36de419e0c3fc4ee724cae22d27a3757f [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"
Brian Salomonab664fa2017-03-24 16:07:20 +000012#include "SkInsetConvexPolygon.h"
Jim Van Verth85dc96b2017-01-30 14:49:21 -050013#include "SkPath.h"
Mike Reed54518ac2017-07-22 08:29:48 -040014#include "SkPoint3.h"
Brian Salomonaff27a22017-02-06 15:47:44 -050015#include "SkVertices.h"
Jim Van Verth85dc96b2017-01-30 14:49:21 -050016
17#if SK_SUPPORT_GPU
18#include "GrPathUtils.h"
19#endif
Jim Van Verth58abc9e2017-01-25 11:05:01 -050020
Brian Salomon958fbc42017-01-30 17:01:28 -050021
Jim Van Vertha84898d2017-02-06 13:38:23 -050022/**
23 * Base class
24 */
Brian Salomonaff27a22017-02-06 15:47:44 -050025class SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -050026public:
Jim Van Verthe308a122017-05-08 14:19:30 -040027 SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -050028 virtual ~SkBaseShadowTessellator() {}
Brian Salomon958fbc42017-01-30 17:01:28 -050029
Brian Salomonaff27a22017-02-06 15:47:44 -050030 sk_sp<SkVertices> releaseVertices() {
31 if (!fSucceeded) {
32 return nullptr;
33 }
Mike Reed887cdf12017-04-03 11:11:09 -040034 return SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, this->vertexCount(),
Mike Reed97eb4fe2017-03-14 12:04:16 -040035 fPositions.begin(), nullptr, fColors.begin(),
36 this->indexCount(), fIndices.begin());
Brian Salomon1a8b79a2017-01-31 11:26:26 -050037 }
Brian Salomon958fbc42017-01-30 17:01:28 -050038
Jim Van Vertha84898d2017-02-06 13:38:23 -050039protected:
Jim Van Verthda965502017-04-11 15:29:14 -040040 static constexpr auto kMinHeight = 0.1f;
41
Brian Salomonaff27a22017-02-06 15:47:44 -050042 int vertexCount() const { return fPositions.count(); }
43 int indexCount() const { return fIndices.count(); }
44
Jim Van Verthda965502017-04-11 15:29:14 -040045 bool setZOffset(const SkRect& bounds, bool perspective);
46
Jim Van Vertha84898d2017-02-06 13:38:23 -050047 virtual void handleLine(const SkPoint& p) = 0;
48 void handleLine(const SkMatrix& m, SkPoint* p);
Brian Salomon958fbc42017-01-30 17:01:28 -050049
50 void handleQuad(const SkPoint pts[3]);
Jim Van Vertha84898d2017-02-06 13:38:23 -050051 void handleQuad(const SkMatrix& m, SkPoint pts[3]);
Brian Salomon958fbc42017-01-30 17:01:28 -050052
Jim Van Vertha84898d2017-02-06 13:38:23 -050053 void handleCubic(const SkMatrix& m, SkPoint pts[4]);
Brian Salomon958fbc42017-01-30 17:01:28 -050054
Jim Van Vertha84898d2017-02-06 13:38:23 -050055 void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
Brian Salomon958fbc42017-01-30 17:01:28 -050056
Jim Van Verthda965502017-04-11 15:29:14 -040057 bool setTransformedHeightFunc(const SkMatrix& ctm);
Brian Salomon958fbc42017-01-30 17:01:28 -050058
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -040059 bool addArc(const SkVector& nextNormal, bool finishArc);
Jim Van Verthb4366552017-03-27 14:25:29 -040060
Jim Van Verthe308a122017-05-08 14:19:30 -040061 SkScalar heightFunc(SkScalar x, SkScalar y) {
62 return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
63 }
64
65 SkPoint3 fZPlaneParams;
Jim Van Verthda965502017-04-11 15:29:14 -040066 std::function<SkScalar(const SkPoint&)> fTransformedHeightFunc;
67 SkScalar fZOffset;
68 // members for perspective height function
Jim Van Verth4c9b8932017-05-15 13:49:21 -040069 SkPoint3 fTransformedZParams;
Jim Van Verthda965502017-04-11 15:29:14 -040070 SkScalar fPartialDeterminants[3];
71
72 // first two points
Brian Salomon958fbc42017-01-30 17:01:28 -050073 SkTDArray<SkPoint> fInitPoints;
74 // temporary buffer
75 SkTDArray<SkPoint> fPointBuffer;
Brian Salomon0dda9cb2017-02-03 10:33:25 -050076
Jim Van Vertha84898d2017-02-06 13:38:23 -050077 SkTDArray<SkPoint> fPositions;
78 SkTDArray<SkColor> fColors;
79 SkTDArray<uint16_t> fIndices;
80
Jim Van Verth76387852017-05-16 16:55:16 -040081 int fFirstVertexIndex;
82 SkVector fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -050083 SkPoint fFirstPoint;
84
Brian Salomon0dda9cb2017-02-03 10:33:25 -050085 bool fSucceeded;
Jim Van Vertha84898d2017-02-06 13:38:23 -050086 bool fTransparent;
87
88 SkColor fUmbraColor;
89 SkColor fPenumbraColor;
90
91 SkScalar fRadius;
92 SkScalar fDirection;
93 int fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -040094 SkVector fPrevOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -050095 SkPoint fPrevPoint;
Brian Salomon958fbc42017-01-30 17:01:28 -050096};
97
Jim Van Verthda965502017-04-11 15:29:14 -040098static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
Jim Van Verthbce74962017-01-25 09:39:46 -050099 SkVector* newNormal) {
100 SkVector normal;
101 // compute perpendicular
102 normal.fX = p0.fY - p1.fY;
103 normal.fY = p1.fX - p0.fX;
Jim Van Verthda965502017-04-11 15:29:14 -0400104 normal *= dir;
Jim Van Verthbce74962017-01-25 09:39:46 -0500105 if (!normal.normalize()) {
106 return false;
107 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500108 *newNormal = normal;
109 return true;
110}
111
112static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
113 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400114 const SkScalar kRecipPixelsPerArcSegment = 0.125f;
Jim Van Verthbce74962017-01-25 09:39:46 -0500115
116 SkScalar rCos = v1.dot(v2);
117 SkScalar rSin = v1.cross(v2);
118 SkScalar theta = SkScalarATan2(rSin, rCos);
119
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400120 int steps = SkScalarFloorToInt(r*theta*kRecipPixelsPerArcSegment);
Jim Van Verthbce74962017-01-25 09:39:46 -0500121
122 SkScalar dTheta = theta / steps;
123 *rotSin = SkScalarSinCos(dTheta, rotCos);
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400124 *n = steps;
Jim Van Verthbce74962017-01-25 09:39:46 -0500125}
126
Jim Van Verthe308a122017-05-08 14:19:30 -0400127SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
128 : fZPlaneParams(zPlaneParams)
Jim Van Verthda965502017-04-11 15:29:14 -0400129 , fZOffset(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400130 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500131 , fSucceeded(false)
132 , fTransparent(transparent)
Brian Salomonaff27a22017-02-06 15:47:44 -0500133 , fDirection(1)
134 , fPrevUmbraIndex(-1) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500135 fInitPoints.setReserve(3);
136
137 // child classes will set reserve for positions, colors and indices
138}
139
Jim Van Verthda965502017-04-11 15:29:14 -0400140bool SkBaseShadowTessellator::setZOffset(const SkRect& bounds, bool perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400141 SkScalar minZ = this->heightFunc(bounds.fLeft, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400142 if (perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400143 SkScalar z = this->heightFunc(bounds.fLeft, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400144 if (z < minZ) {
145 minZ = z;
146 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400147 z = this->heightFunc(bounds.fRight, bounds.fTop);
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.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400152 if (z < minZ) {
153 minZ = z;
154 }
155 }
156
157 if (minZ < kMinHeight) {
158 fZOffset = -minZ + kMinHeight;
159 return true;
160 }
161
162 return false;
163}
164
Jim Van Vertha84898d2017-02-06 13:38:23 -0500165// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500166#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500167static const SkScalar kQuadTolerance = 0.2f;
168static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500169#endif
Jim Van Vertha84898d2017-02-06 13:38:23 -0500170static const SkScalar kConicTolerance = 0.5f;
171
Brian Salomonaff27a22017-02-06 15:47:44 -0500172void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500173 m.mapPoints(p, 1);
174 this->handleLine(*p);
175}
176
Brian Salomonaff27a22017-02-06 15:47:44 -0500177void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500178#if SK_SUPPORT_GPU
179 // TODO: Pull PathUtils out of Ganesh?
180 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
181 fPointBuffer.setReserve(maxCount);
182 SkPoint* target = fPointBuffer.begin();
183 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
184 kQuadTolerance, &target, maxCount);
185 fPointBuffer.setCount(count);
186 for (int i = 0; i < count; i++) {
187 this->handleLine(fPointBuffer[i]);
188 }
189#else
190 // for now, just to draw something
191 this->handleLine(pts[1]);
192 this->handleLine(pts[2]);
193#endif
194}
195
Brian Salomonaff27a22017-02-06 15:47:44 -0500196void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500197 m.mapPoints(pts, 3);
198 this->handleQuad(pts);
199}
200
Brian Salomonaff27a22017-02-06 15:47:44 -0500201void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500202 m.mapPoints(pts, 4);
203#if SK_SUPPORT_GPU
204 // TODO: Pull PathUtils out of Ganesh?
205 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
206 fPointBuffer.setReserve(maxCount);
207 SkPoint* target = fPointBuffer.begin();
208 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
209 kCubicTolerance, &target, maxCount);
210 fPointBuffer.setCount(count);
211 for (int i = 0; i < count; i++) {
212 this->handleLine(fPointBuffer[i]);
213 }
214#else
215 // for now, just to draw something
216 this->handleLine(pts[1]);
217 this->handleLine(pts[2]);
218 this->handleLine(pts[3]);
219#endif
220}
221
Brian Salomonaff27a22017-02-06 15:47:44 -0500222void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400223 if (m.hasPerspective()) {
224 w = SkConic::TransformW(pts, w, m);
225 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500226 m.mapPoints(pts, 3);
227 SkAutoConicToQuads quadder;
228 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
229 SkPoint lastPoint = *(quads++);
230 int count = quadder.countQuads();
231 for (int i = 0; i < count; ++i) {
232 SkPoint quadPts[3];
233 quadPts[0] = lastPoint;
234 quadPts[1] = quads[0];
235 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
236 this->handleQuad(quadPts);
237 lastPoint = quadPts[2];
238 quads += 2;
239 }
240}
241
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400242bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500243 // fill in fan from previous quad
244 SkScalar rotSin, rotCos;
245 int numSteps;
Jim Van Verth76387852017-05-16 16:55:16 -0400246 compute_radial_steps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
247 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400248 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400249 SkVector currNormal;
250 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
251 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
252 *fPositions.push() = fPrevPoint + currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500253 *fColors.push() = fPenumbraColor;
254 *fIndices.push() = fPrevUmbraIndex;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500255 *fIndices.push() = fPositions.count() - 1;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400256 *fIndices.push() = fPositions.count() - 2;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500257
Jim Van Verthda965502017-04-11 15:29:14 -0400258 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500259 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400260 if (finishArc && numSteps) {
Jim Van Verthda965502017-04-11 15:29:14 -0400261 *fPositions.push() = fPrevPoint + nextNormal;
262 *fColors.push() = fPenumbraColor;
263 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verthda965502017-04-11 15:29:14 -0400264 *fIndices.push() = fPositions.count() - 1;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400265 *fIndices.push() = fPositions.count() - 2;
Jim Van Verthda965502017-04-11 15:29:14 -0400266 }
Jim Van Verth76387852017-05-16 16:55:16 -0400267 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400268
269 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500270}
271
Jim Van Verthda965502017-04-11 15:29:14 -0400272bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400273 if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400274 fTransformedHeightFunc = [this](const SkPoint& p) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400275 return fZPlaneParams.fZ;
Jim Van Verthda965502017-04-11 15:29:14 -0400276 };
277 } else {
278 SkMatrix ctmInverse;
279 if (!ctm.invert(&ctmInverse)) {
280 return false;
281 }
Jim Van Verthda965502017-04-11 15:29:14 -0400282 // multiply by transpose
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400283 fTransformedZParams = SkPoint3::Make(
Jim Van Verthe308a122017-05-08 14:19:30 -0400284 ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
285 ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
286 ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
287
288 ctmInverse[SkMatrix::kMSkewX] * fZPlaneParams.fX +
289 ctmInverse[SkMatrix::kMScaleY] * fZPlaneParams.fY +
290 ctmInverse[SkMatrix::kMPersp1] * fZPlaneParams.fZ,
291
292 ctmInverse[SkMatrix::kMTransX] * fZPlaneParams.fX +
293 ctmInverse[SkMatrix::kMTransY] * fZPlaneParams.fY +
294 ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
295 );
Jim Van Verthda965502017-04-11 15:29:14 -0400296
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400297 if (ctm.hasPerspective()) {
298 // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
299 // so pre-compute those values that are independent of X and Y.
300 // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
301 fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
302 ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
303 fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
304 ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
305 fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
306 ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
307 SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
308 ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
309 ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
Jim Van Verthda965502017-04-11 15:29:14 -0400310
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400311 // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
312 // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
313 fTransformedZParams.fX *= ctmDeterminant;
314 fTransformedZParams.fY *= ctmDeterminant;
315 fTransformedZParams.fZ *= ctmDeterminant;
Jim Van Verthda965502017-04-11 15:29:14 -0400316
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400317 fTransformedHeightFunc = [this](const SkPoint& p) {
318 SkScalar denom = p.fX * fPartialDeterminants[0] +
319 p.fY * fPartialDeterminants[1] +
320 fPartialDeterminants[2];
321 SkScalar w = SkScalarFastInvert(denom);
322 return fZOffset + w*(fTransformedZParams.fX * p.fX +
323 fTransformedZParams.fY * p.fY +
324 fTransformedZParams.fZ);
325 };
326 } else {
327 fTransformedHeightFunc = [this](const SkPoint& p) {
328 return fZOffset + fTransformedZParams.fX * p.fX +
329 fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
330 };
331 }
Jim Van Verthda965502017-04-11 15:29:14 -0400332 }
333
334 return true;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500335}
336
337
338//////////////////////////////////////////////////////////////////////////////////////////////////
339
Brian Salomonaff27a22017-02-06 15:47:44 -0500340class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500341public:
342 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400343 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500344
345private:
346 void handleLine(const SkPoint& p) override;
Jim Van Verthda965502017-04-11 15:29:14 -0400347 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500348
Jim Van Verthda965502017-04-11 15:29:14 -0400349 static constexpr auto kMaxEdgeLenSqr = 20 * 20;
Jim Van Verth76387852017-05-16 16:55:16 -0400350 static constexpr auto kInsetFactor = -0.5f;
Jim Van Verthda965502017-04-11 15:29:14 -0400351
352 SkScalar offset(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400353 return SkDrawShadowMetrics::AmbientBlurRadius(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400354 }
355 SkColor umbraColor(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400356 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(z));
Jim Van Verth060d9822017-05-04 09:58:17 -0400357 return SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
Jim Van Verthda965502017-04-11 15:29:14 -0400358 }
359
Jim Van Vertha84898d2017-02-06 13:38:23 -0500360 int fCentroidCount;
Jim Van Verth76387852017-05-16 16:55:16 -0400361 bool fSplitFirstEdge;
362 bool fSplitPreviousEdge;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500363
Brian Salomonaff27a22017-02-06 15:47:44 -0500364 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500365};
366
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500367SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500368 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400369 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500370 bool transparent)
Jim Van Verth76387852017-05-16 16:55:16 -0400371 : INHERITED(zPlaneParams, transparent)
372 , fSplitFirstEdge(false)
373 , fSplitPreviousEdge(false) {
Jim Van Verthda965502017-04-11 15:29:14 -0400374 // Set base colors
Jim Van Verth1af03d42017-07-31 09:34:58 -0400375 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(heightFunc(0, 0)));
Jim Van Verthb4366552017-03-27 14:25:29 -0400376 // umbraColor is the interior value, penumbraColor the exterior value.
377 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
378 // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
379 // the final alpha.
Jim Van Verth060d9822017-05-04 09:58:17 -0400380 fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
381 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400382
Jim Van Verthda965502017-04-11 15:29:14 -0400383 // make sure we're not below the canvas plane
384 this->setZOffset(path.getBounds(), ctm.hasPerspective());
385
386 this->setTransformedHeightFunc(ctm);
387
Jim Van Verthbce74962017-01-25 09:39:46 -0500388 // Outer ring: 3*numPts
389 // Middle ring: numPts
390 fPositions.setReserve(4 * path.countPoints());
391 fColors.setReserve(4 * path.countPoints());
392 // Outer ring: 12*numPts
393 // Middle ring: 0
394 fIndices.setReserve(12 * path.countPoints());
395
Jim Van Verthbce74962017-01-25 09:39:46 -0500396 // walk around the path, tessellate and generate outer ring
397 // if original path is transparent, will accumulate sum of points for centroid
398 SkPath::Iter iter(path, true);
399 SkPoint pts[4];
400 SkPath::Verb verb;
401 if (fTransparent) {
402 *fPositions.push() = SkPoint::Make(0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400403 *fColors.push() = fUmbraColor;
Jim Van Verthbce74962017-01-25 09:39:46 -0500404 fCentroidCount = 0;
405 }
406 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
407 switch (verb) {
408 case SkPath::kLine_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500409 this->INHERITED::handleLine(ctm, &pts[1]);
Jim Van Verthbce74962017-01-25 09:39:46 -0500410 break;
411 case SkPath::kQuad_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500412 this->handleQuad(ctm, pts);
Jim Van Verthbce74962017-01-25 09:39:46 -0500413 break;
414 case SkPath::kCubic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500415 this->handleCubic(ctm, pts);
Jim Van Verthbce74962017-01-25 09:39:46 -0500416 break;
417 case SkPath::kConic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500418 this->handleConic(ctm, pts, iter.conicWeight());
Jim Van Verthbce74962017-01-25 09:39:46 -0500419 break;
420 case SkPath::kMove_Verb:
421 case SkPath::kClose_Verb:
422 case SkPath::kDone_Verb:
423 break;
424 }
425 }
426
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500427 if (!this->indexCount()) {
428 return;
429 }
430
Jim Van Verth76387852017-05-16 16:55:16 -0400431 // Finish up
Jim Van Verthbce74962017-01-25 09:39:46 -0500432 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -0400433 if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
434 SkScalar z = fTransformedHeightFunc(fPrevPoint);
435 fRadius = this->offset(z);
436 SkVector scaledNormal(normal);
437 scaledNormal *= fRadius;
438 this->addArc(scaledNormal, true);
Jim Van Verthbce74962017-01-25 09:39:46 -0500439
Jim Van Verth76387852017-05-16 16:55:16 -0400440 // fix-up the last and first umbra points
441 SkVector inset = normal;
442 // adding to an average, so multiply by an additional half
443 inset *= 0.5f*kInsetFactor;
444 fPositions[fPrevUmbraIndex] += inset;
445 fPositions[fFirstVertexIndex] += inset;
446 // we multiply by another half because now we're adding to an average of an average
447 inset *= 0.5f;
448 if (fSplitPreviousEdge) {
449 fPositions[fPrevUmbraIndex - 2] += inset;
450 }
451 if (fSplitFirstEdge) {
452 fPositions[fFirstVertexIndex + 2] += inset;
453 }
454
Jim Van Verthda965502017-04-11 15:29:14 -0400455 // set up for final edge
456 z = fTransformedHeightFunc(fFirstPoint);
457 normal *= this->offset(z);
Jim Van Verthbce74962017-01-25 09:39:46 -0500458
Jim Van Verthda965502017-04-11 15:29:14 -0400459 // make sure we don't end up with a sharp alpha edge along the quad diagonal
Jim Van Verth76387852017-05-16 16:55:16 -0400460 if (fColors[fPrevUmbraIndex] != fColors[fFirstVertexIndex] &&
Jim Van Verthda965502017-04-11 15:29:14 -0400461 fFirstPoint.distanceToSqd(fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400462 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + fPositions[fFirstVertexIndex];
Jim Van Verthda965502017-04-11 15:29:14 -0400463 centerPoint *= 0.5f;
464 *fPositions.push() = centerPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400465 *fColors.push() = SkPMLerp(fColors[fFirstVertexIndex], fColors[fPrevUmbraIndex], 128);
466 centerPoint = fPositions[fPositions.count()-2] + fPositions[fFirstVertexIndex+1];
467 centerPoint *= 0.5f;
468 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400469 *fColors.push() = fPenumbraColor;
470
Jim Van Verth76387852017-05-16 16:55:16 -0400471 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
472 *fIndices.push() = fPrevUmbraIndex;
473 *fIndices.push() = fPositions.count() - 3;
474 *fIndices.push() = fPositions.count() - 2;
Jim Van Verthda965502017-04-11 15:29:14 -0400475
Jim Van Verth76387852017-05-16 16:55:16 -0400476 *fIndices.push() = fPositions.count() - 3;
477 *fIndices.push() = fPositions.count() - 1;
478 *fIndices.push() = fPositions.count() - 2;
479 } else {
480 *fIndices.push() = fPrevUmbraIndex;
481 *fIndices.push() = fPositions.count() - 2;
482 *fIndices.push() = fPositions.count() - 1;
483
484 *fIndices.push() = fPrevUmbraIndex;
485 *fIndices.push() = fPositions.count() - 1;
486 *fIndices.push() = fPositions.count() - 3;
487 }
Jim Van Verthda965502017-04-11 15:29:14 -0400488
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400489 // if transparent, add point to first one in array and add to center fan
490 if (fTransparent) {
491 fPositions[0] += centerPoint;
492 ++fCentroidCount;
493
494 *fIndices.push() = 0;
495 *fIndices.push() = fPrevUmbraIndex;
496 *fIndices.push() = fPositions.count() - 2;
497 }
498
Jim Van Verthda965502017-04-11 15:29:14 -0400499 fPrevUmbraIndex = fPositions.count() - 2;
500 }
501
502 // final edge
Jim Van Vertha84898d2017-02-06 13:38:23 -0500503 *fPositions.push() = fFirstPoint + normal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500504 *fColors.push() = fPenumbraColor;
505
Jim Van Verth76387852017-05-16 16:55:16 -0400506 if (fColors[fPrevUmbraIndex] > fColors[fFirstVertexIndex]) {
Jim Van Verthda965502017-04-11 15:29:14 -0400507 *fIndices.push() = fPrevUmbraIndex;
508 *fIndices.push() = fPositions.count() - 2;
Jim Van Verth76387852017-05-16 16:55:16 -0400509 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500510
Jim Van Verthda965502017-04-11 15:29:14 -0400511 *fIndices.push() = fPositions.count() - 2;
512 *fIndices.push() = fPositions.count() - 1;
Jim Van Verth76387852017-05-16 16:55:16 -0400513 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthda965502017-04-11 15:29:14 -0400514 } else {
515 *fIndices.push() = fPrevUmbraIndex;
516 *fIndices.push() = fPositions.count() - 2;
517 *fIndices.push() = fPositions.count() - 1;
518
519 *fIndices.push() = fPrevUmbraIndex;
520 *fIndices.push() = fPositions.count() - 1;
Jim Van Verth76387852017-05-16 16:55:16 -0400521 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthda965502017-04-11 15:29:14 -0400522 }
Jim Van Verth76387852017-05-16 16:55:16 -0400523 fPrevOutset = normal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500524 }
525
526 // finalize centroid
527 if (fTransparent) {
528 fPositions[0] *= SkScalarFastInvert(fCentroidCount);
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400529 fColors[0] = this->umbraColor(fTransformedHeightFunc(fPositions[0]));
Jim Van Verthbce74962017-01-25 09:39:46 -0500530
531 *fIndices.push() = 0;
Brian Salomon66085ed2017-02-02 15:39:34 -0500532 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400533 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500534 }
535
536 // final fan
537 if (fPositions.count() >= 3) {
Jim Van Verth76387852017-05-16 16:55:16 -0400538 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500539 fPrevPoint = fFirstPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400540 fRadius = this->offset(fTransformedHeightFunc(fPrevPoint));
Jim Van Verth76387852017-05-16 16:55:16 -0400541 if (this->addArc(fFirstOutset, false)) {
542 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400543 *fIndices.push() = fPositions.count() - 1;
Jim Van Verth76387852017-05-16 16:55:16 -0400544 *fIndices.push() = fFirstVertexIndex + 1;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400545 } else {
546 // arc is too small, set the first penumbra point to be the same position
547 // as the last one
Jim Van Verth76387852017-05-16 16:55:16 -0400548 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400549 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500550 }
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500551 fSucceeded = true;
Jim Van Verthbce74962017-01-25 09:39:46 -0500552}
553
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500554void SkAmbientShadowTessellator::handleLine(const SkPoint& p) {
Jim Van Verthbce74962017-01-25 09:39:46 -0500555 if (fInitPoints.count() < 2) {
556 *fInitPoints.push() = p;
557 return;
558 }
559
560 if (fInitPoints.count() == 2) {
561 // determine if cw or ccw
562 SkVector v0 = fInitPoints[1] - fInitPoints[0];
563 SkVector v1 = p - fInitPoints[0];
564 SkScalar perpDot = v0.fX*v1.fY - v0.fY*v1.fX;
565 if (SkScalarNearlyZero(perpDot)) {
566 // nearly parallel, just treat as straight line and continue
567 fInitPoints[1] = p;
568 return;
569 }
570
571 // if perpDot > 0, winding is ccw
572 fDirection = (perpDot > 0) ? -1 : 1;
573
574 // add first quad
Jim Van Verthda965502017-04-11 15:29:14 -0400575 SkVector normal;
576 if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &normal)) {
Jim Van Verthbce74962017-01-25 09:39:46 -0500577 // first two points are incident, make the third point the second and continue
578 fInitPoints[1] = p;
579 return;
580 }
581
Jim Van Vertha84898d2017-02-06 13:38:23 -0500582 fFirstPoint = fInitPoints[0];
Jim Van Verth76387852017-05-16 16:55:16 -0400583 fFirstVertexIndex = fPositions.count();
Jim Van Verthda965502017-04-11 15:29:14 -0400584 SkScalar z = fTransformedHeightFunc(fFirstPoint);
Jim Van Verth76387852017-05-16 16:55:16 -0400585 fFirstOutset = normal;
586 fFirstOutset *= this->offset(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400587
Jim Van Verth76387852017-05-16 16:55:16 -0400588 fPrevOutset = fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500589 fPrevPoint = fFirstPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400590 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500591
Jim Van Verthda965502017-04-11 15:29:14 -0400592 *fPositions.push() = fFirstPoint;
593 *fColors.push() = this->umbraColor(z);
Jim Van Verth76387852017-05-16 16:55:16 -0400594 *fPositions.push() = fFirstPoint + fFirstOutset;
Jim Van Verthbce74962017-01-25 09:39:46 -0500595 *fColors.push() = fPenumbraColor;
596 if (fTransparent) {
Jim Van Verthda965502017-04-11 15:29:14 -0400597 fPositions[0] += fFirstPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500598 fCentroidCount = 1;
599 }
Jim Van Verthda965502017-04-11 15:29:14 -0400600
601 // add the first quad
602 z = fTransformedHeightFunc(fInitPoints[1]);
603 fRadius = this->offset(z);
604 fUmbraColor = this->umbraColor(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400605 this->addEdge(fInitPoints[1], normal);
Jim Van Verthbce74962017-01-25 09:39:46 -0500606
607 // to ensure we skip this block next time
608 *fInitPoints.push() = p;
609 }
610
611 SkVector normal;
Jim Van Verth76387852017-05-16 16:55:16 -0400612 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400613 SkVector scaledNormal = normal;
614 scaledNormal *= fRadius;
615 this->addArc(scaledNormal, true);
616 SkScalar z = fTransformedHeightFunc(p);
617 fRadius = this->offset(z);
618 fUmbraColor = this->umbraColor(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400619 this->addEdge(p, normal);
Jim Van Verthbce74962017-01-25 09:39:46 -0500620 }
621}
622
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500623void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
Jim Van Verth76387852017-05-16 16:55:16 -0400624 // We compute the inset in two stages: first we inset by half the current normal,
625 // then on the next addEdge() we add half of the next normal to get an average of the two
626 SkVector insetNormal = nextNormal;
627 insetNormal *= 0.5f*kInsetFactor;
628
629 // Adding the other half of the average for the previous edge
630 fPositions[fPrevUmbraIndex] += insetNormal;
631
632 SkPoint umbraPoint = nextPoint + insetNormal;
633 SkVector outsetNormal = nextNormal;
634 outsetNormal *= fRadius;
635 SkPoint penumbraPoint = nextPoint + outsetNormal;
636
637 // For split edges, we're adding an average of two averages, so we multiply by another half
638 if (fSplitPreviousEdge) {
639 insetNormal *= 0.5f;
640 fPositions[fPrevUmbraIndex - 2] += insetNormal;
641 }
642
643 // 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 -0400644 if (fColors[fPrevUmbraIndex] != fUmbraColor &&
645 nextPoint.distanceToSqd(fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400646
647 // This is lacking 1/4 of the next inset -- we'll add it the next time we call addEdge()
648 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + umbraPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400649 centerPoint *= 0.5f;
650 *fPositions.push() = centerPoint;
651 *fColors.push() = SkPMLerp(fUmbraColor, fColors[fPrevUmbraIndex], 128);
Jim Van Verth76387852017-05-16 16:55:16 -0400652 centerPoint = fPositions[fPositions.count()-2] + penumbraPoint;
653 centerPoint *= 0.5f;
654 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400655 *fColors.push() = fPenumbraColor;
656
657 // set triangularization to get best interpolation of color
658 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
659 *fIndices.push() = fPrevUmbraIndex;
660 *fIndices.push() = fPositions.count() - 3;
661 *fIndices.push() = fPositions.count() - 2;
662
663 *fIndices.push() = fPositions.count() - 3;
664 *fIndices.push() = fPositions.count() - 1;
665 *fIndices.push() = fPositions.count() - 2;
666 } else {
667 *fIndices.push() = fPrevUmbraIndex;
668 *fIndices.push() = fPositions.count() - 2;
669 *fIndices.push() = fPositions.count() - 1;
670
671 *fIndices.push() = fPrevUmbraIndex;
672 *fIndices.push() = fPositions.count() - 1;
673 *fIndices.push() = fPositions.count() - 3;
674 }
675
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400676 // if transparent, add point to first one in array and add to center fan
677 if (fTransparent) {
678 fPositions[0] += centerPoint;
679 ++fCentroidCount;
680
681 *fIndices.push() = 0;
682 *fIndices.push() = fPrevUmbraIndex;
683 *fIndices.push() = fPositions.count() - 2;
684 }
685
Jim Van Verth76387852017-05-16 16:55:16 -0400686 fSplitPreviousEdge = true;
687 if (fPrevUmbraIndex == fFirstVertexIndex) {
688 fSplitFirstEdge = true;
689 }
Jim Van Verthda965502017-04-11 15:29:14 -0400690 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Verth76387852017-05-16 16:55:16 -0400691 } else {
692 fSplitPreviousEdge = false;
Jim Van Verthda965502017-04-11 15:29:14 -0400693 }
694
Jim Van Verthbce74962017-01-25 09:39:46 -0500695 // add next quad
Jim Van Verth76387852017-05-16 16:55:16 -0400696 *fPositions.push() = umbraPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500697 *fColors.push() = fUmbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -0400698 *fPositions.push() = penumbraPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500699 *fColors.push() = fPenumbraColor;
700
Jim Van Verthda965502017-04-11 15:29:14 -0400701 // set triangularization to get best interpolation of color
702 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
703 *fIndices.push() = fPrevUmbraIndex;
704 *fIndices.push() = fPositions.count() - 3;
705 *fIndices.push() = fPositions.count() - 2;
Jim Van Verthbce74962017-01-25 09:39:46 -0500706
Jim Van Verthda965502017-04-11 15:29:14 -0400707 *fIndices.push() = fPositions.count() - 3;
708 *fIndices.push() = fPositions.count() - 1;
709 *fIndices.push() = fPositions.count() - 2;
710 } else {
711 *fIndices.push() = fPrevUmbraIndex;
712 *fIndices.push() = fPositions.count() - 2;
713 *fIndices.push() = fPositions.count() - 1;
714
715 *fIndices.push() = fPrevUmbraIndex;
716 *fIndices.push() = fPositions.count() - 1;
717 *fIndices.push() = fPositions.count() - 3;
718 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500719
720 // if transparent, add point to first one in array and add to center fan
721 if (fTransparent) {
722 fPositions[0] += nextPoint;
723 ++fCentroidCount;
724
725 *fIndices.push() = 0;
Brian Salomon66085ed2017-02-02 15:39:34 -0500726 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500727 *fIndices.push() = fPositions.count() - 2;
728 }
729
Brian Salomon66085ed2017-02-02 15:39:34 -0500730 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500731 fPrevPoint = nextPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400732 fPrevOutset = outsetNormal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500733}
Jim Van Verth91af7272017-01-27 14:15:54 -0500734
735///////////////////////////////////////////////////////////////////////////////////////////////////
736
Brian Salomonaff27a22017-02-06 15:47:44 -0500737class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500738public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500739 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400740 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400741 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500742
Brian Salomon958fbc42017-01-30 17:01:28 -0500743private:
Brian Salomonab664fa2017-03-24 16:07:20 +0000744 void computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400745 const SkMatrix& shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000746 void computeClipVectorsAndTestCentroid();
Brian Salomon66085ed2017-02-02 15:39:34 -0500747 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
Brian Salomonab664fa2017-03-24 16:07:20 +0000748 int getClosestUmbraPoint(const SkPoint& point);
Brian Salomon958fbc42017-01-30 17:01:28 -0500749
Jim Van Vertha84898d2017-02-06 13:38:23 -0500750 void handleLine(const SkPoint& p) override;
Jim Van Verthb55eb282017-07-18 14:13:45 -0400751 bool handlePolyPoint(const SkPoint& p);
Brian Salomon958fbc42017-01-30 17:01:28 -0500752
753 void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
Brian Salomonab664fa2017-03-24 16:07:20 +0000754 bool addInnerPoint(const SkPoint& pathPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400755 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
756
757 SkScalar offset(SkScalar z) {
758 float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
759 return fLightRadius*zRatio;
760 }
761
762 SkScalar fLightZ;
763 SkScalar fLightRadius;
764 SkScalar fOffsetAdjust;
Brian Salomon958fbc42017-01-30 17:01:28 -0500765
Brian Salomon958fbc42017-01-30 17:01:28 -0500766 SkTDArray<SkPoint> fClipPolygon;
Brian Salomon66085ed2017-02-02 15:39:34 -0500767 SkTDArray<SkVector> fClipVectors;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500768 SkPoint fCentroid;
Brian Salomonab664fa2017-03-24 16:07:20 +0000769 SkScalar fArea;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500770
Brian Salomonab664fa2017-03-24 16:07:20 +0000771 SkTDArray<SkPoint> fPathPolygon;
772 SkTDArray<SkPoint> fUmbraPolygon;
773 int fCurrClipPoint;
774 int fCurrUmbraPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -0500775 bool fPrevUmbraOutside;
776 bool fFirstUmbraOutside;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500777 bool fValidUmbra;
Brian Salomon958fbc42017-01-30 17:01:28 -0500778
Brian Salomonaff27a22017-02-06 15:47:44 -0500779 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -0500780};
781
Jim Van Vertha84898d2017-02-06 13:38:23 -0500782SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400783 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -0400784 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -0400785 bool transparent)
Jim Van Verthe308a122017-05-08 14:19:30 -0400786 : INHERITED(zPlaneParams, transparent)
Jim Van Verthda965502017-04-11 15:29:14 -0400787 , fLightZ(lightPos.fZ)
788 , fLightRadius(lightRadius)
789 , fOffsetAdjust(0)
790 , fCurrClipPoint(0)
791 , fPrevUmbraOutside(false)
792 , fFirstUmbraOutside(false)
793 , fValidUmbra(true) {
794
795 // make sure we're not below the canvas plane
796 if (this->setZOffset(path.getBounds(), ctm.hasPerspective())) {
797 // Adjust light height and radius
798 fLightRadius *= (fLightZ + fZOffset) / fLightZ;
799 fLightZ += fZOffset;
800 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400801
802 // Set radius and colors
Jim Van Verthb4366552017-03-27 14:25:29 -0400803 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verthe308a122017-05-08 14:19:30 -0400804 SkScalar occluderHeight = this->heightFunc(center.fX, center.fY) + fZOffset;
Jim Van Verth060d9822017-05-04 09:58:17 -0400805 fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
806 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400807
Jim Van Verth1af03d42017-07-31 09:34:58 -0400808 // Compute the blur radius, scale and translation for the spot shadow.
809 SkScalar radius;
Jim Van Verthda965502017-04-11 15:29:14 -0400810 SkMatrix shadowTransform;
811 if (!ctm.hasPerspective()) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400812 SkScalar scale;
813 SkVector translate;
814 SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, fLightZ,
815 lightRadius, &radius, &scale, &translate);
Jim Van Verthda965502017-04-11 15:29:14 -0400816 shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
817 } else {
818 // For perspective, we have a scale, a z-shear, and another projective divide --
819 // this varies at each point so we can't use an affine transform.
820 // We'll just apply this to each generated point in turn.
821 shadowTransform.reset();
822 // Also can't cull the center (for now).
823 fTransparent = true;
Jim Van Verth1af03d42017-07-31 09:34:58 -0400824 radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
Jim Van Verthda965502017-04-11 15:29:14 -0400825 }
Jim Van Verth1af03d42017-07-31 09:34:58 -0400826 fRadius = radius;
Jim Van Verthda965502017-04-11 15:29:14 -0400827 SkMatrix fullTransform = SkMatrix::Concat(shadowTransform, ctm);
828
829 // Set up our reverse mapping
830 this->setTransformedHeightFunc(fullTransform);
Jim Van Verthb4366552017-03-27 14:25:29 -0400831
Brian Salomonab664fa2017-03-24 16:07:20 +0000832 // TODO: calculate these reserves better
Brian Salomon66085ed2017-02-02 15:39:34 -0500833 // Penumbra ring: 3*numPts
834 // Umbra ring: numPts
Jim Van Verth91af7272017-01-27 14:15:54 -0500835 // Inner ring: numPts
Brian Salomon66085ed2017-02-02 15:39:34 -0500836 fPositions.setReserve(5 * path.countPoints());
837 fColors.setReserve(5 * path.countPoints());
838 // Penumbra ring: 12*numPts
839 // Umbra ring: 3*numPts
840 fIndices.setReserve(15 * path.countPoints());
Brian Salomone7c85c42017-03-24 16:00:35 +0000841 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +0000842
843 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verthda965502017-04-11 15:29:14 -0400844 this->computeClipAndPathPolygons(path, ctm, shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000845 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3) {
Brian Salomon66085ed2017-02-02 15:39:34 -0500846 return;
847 }
Brian Salomon66085ed2017-02-02 15:39:34 -0500848
Brian Salomonab664fa2017-03-24 16:07:20 +0000849 // check to see if umbra collapses
850 SkScalar minDistSq = fCentroid.distanceToLineSegmentBetweenSqd(fPathPolygon[0],
851 fPathPolygon[1]);
Jim Van Verthda965502017-04-11 15:29:14 -0400852 SkRect bounds;
853 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
Brian Salomonab664fa2017-03-24 16:07:20 +0000854 for (int i = 1; i < fPathPolygon.count(); ++i) {
855 int j = i + 1;
856 if (i == fPathPolygon.count() - 1) {
857 j = 0;
858 }
859 SkPoint currPoint = fPathPolygon[i];
860 SkPoint nextPoint = fPathPolygon[j];
861 SkScalar distSq = fCentroid.distanceToLineSegmentBetweenSqd(currPoint, nextPoint);
862 if (distSq < minDistSq) {
863 minDistSq = distSq;
864 }
865 }
866 static constexpr auto kTolerance = 1.0e-2f;
867 if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
868 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
869 SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
Jim Van Verthda965502017-04-11 15:29:14 -0400870 fOffsetAdjust = newRadius - radius;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400871 SkScalar ratio = 128 * (newRadius + radius) / radius;
Brian Salomonab664fa2017-03-24 16:07:20 +0000872 // they aren't PMColors, but the interpolation algorithm is the same
873 fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
874 radius = newRadius;
875 }
Jim Van Verth91af7272017-01-27 14:15:54 -0500876
Brian Salomonab664fa2017-03-24 16:07:20 +0000877 // compute vectors for clip tests
878 this->computeClipVectorsAndTestCentroid();
879
880 // generate inner ring
Jim Van Verthda965502017-04-11 15:29:14 -0400881 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
882 &fUmbraPolygon)) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000883 // this shouldn't happen, but just in case we'll inset using the centroid
884 fValidUmbra = false;
885 }
886
887 // walk around the path polygon, generate outer ring and connect to inner ring
Brian Salomon66085ed2017-02-02 15:39:34 -0500888 if (fTransparent) {
889 *fPositions.push() = fCentroid;
890 *fColors.push() = fUmbraColor;
891 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000892 fCurrUmbraPoint = 0;
893 for (int i = 0; i < fPathPolygon.count(); ++i) {
Jim Van Verthb55eb282017-07-18 14:13:45 -0400894 if (!this->handlePolyPoint(fPathPolygon[i])) {
895 return;
896 }
Jim Van Verth91af7272017-01-27 14:15:54 -0500897 }
898
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500899 if (!this->indexCount()) {
900 return;
901 }
902
Brian Salomonab664fa2017-03-24 16:07:20 +0000903 // finish up the final verts
Jim Van Verth91af7272017-01-27 14:15:54 -0500904 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -0400905 if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
906 normal *= fRadius;
907 this->addArc(normal, true);
Jim Van Verth91af7272017-01-27 14:15:54 -0500908
Brian Salomon66085ed2017-02-02 15:39:34 -0500909 // add to center fan
910 if (fTransparent) {
911 *fIndices.push() = 0;
912 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400913 *fIndices.push() = fFirstVertexIndex;
Brian Salomon66085ed2017-02-02 15:39:34 -0500914 // or to clip ring
915 } else {
916 if (fFirstUmbraOutside) {
917 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400918 *fIndices.push() = fFirstVertexIndex;
919 *fIndices.push() = fFirstVertexIndex + 1;
Brian Salomon66085ed2017-02-02 15:39:34 -0500920 if (fPrevUmbraOutside) {
921 // fill out quad
922 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400923 *fIndices.push() = fFirstVertexIndex + 1;
Brian Salomon66085ed2017-02-02 15:39:34 -0500924 *fIndices.push() = fPrevUmbraIndex + 1;
925 }
926 } else if (fPrevUmbraOutside) {
927 // add tri
928 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400929 *fIndices.push() = fFirstVertexIndex;
Brian Salomon66085ed2017-02-02 15:39:34 -0500930 *fIndices.push() = fPrevUmbraIndex + 1;
931 }
932 }
933
Jim Van Verth91af7272017-01-27 14:15:54 -0500934 // add final edge
935 *fPositions.push() = fFirstPoint + normal;
936 *fColors.push() = fPenumbraColor;
937
Brian Salomon66085ed2017-02-02 15:39:34 -0500938 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verth91af7272017-01-27 14:15:54 -0500939 *fIndices.push() = fPositions.count() - 2;
Jim Van Verth76387852017-05-16 16:55:16 -0400940 *fIndices.push() = fFirstVertexIndex;
Jim Van Verth91af7272017-01-27 14:15:54 -0500941
942 *fIndices.push() = fPositions.count() - 2;
943 *fIndices.push() = fPositions.count() - 1;
Jim Van Verth76387852017-05-16 16:55:16 -0400944 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthda965502017-04-11 15:29:14 -0400945
Jim Van Verth76387852017-05-16 16:55:16 -0400946 fPrevOutset = normal;
Jim Van Verth91af7272017-01-27 14:15:54 -0500947 }
948
949 // final fan
950 if (fPositions.count() >= 3) {
Jim Van Verth76387852017-05-16 16:55:16 -0400951 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Verth91af7272017-01-27 14:15:54 -0500952 fPrevPoint = fFirstPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400953 if (this->addArc(fFirstOutset, false)) {
954 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400955 *fIndices.push() = fPositions.count() - 1;
956 if (fFirstUmbraOutside) {
Jim Van Verth76387852017-05-16 16:55:16 -0400957 *fIndices.push() = fFirstVertexIndex + 2;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400958 } else {
Jim Van Verth76387852017-05-16 16:55:16 -0400959 *fIndices.push() = fFirstVertexIndex + 1;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400960 }
Brian Salomon66085ed2017-02-02 15:39:34 -0500961 } else {
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400962 // no arc added, fix up by setting first penumbra point position to last one
963 if (fFirstUmbraOutside) {
Jim Van Verth76387852017-05-16 16:55:16 -0400964 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400965 } else {
Jim Van Verth76387852017-05-16 16:55:16 -0400966 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400967 }
Brian Salomon66085ed2017-02-02 15:39:34 -0500968 }
Jim Van Verth91af7272017-01-27 14:15:54 -0500969 }
Jim Van Verthda965502017-04-11 15:29:14 -0400970
971 if (ctm.hasPerspective()) {
972 for (int i = 0; i < fPositions.count(); ++i) {
973 SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
974 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
975 fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
976 fPositions[i].fY = (fPositions[i].fY*fLightZ - lightPos.fY*pathZ)*factor;
977 }
978#ifdef DRAW_CENTROID
979 SkScalar pathZ = fTransformedHeightFunc(fCentroid);
980 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
981 fCentroid.fX = (fCentroid.fX*fLightZ - lightPos.fX*pathZ)*factor;
982 fCentroid.fY = (fCentroid.fY*fLightZ - lightPos.fY*pathZ)*factor;
983#endif
984 }
985#ifdef DRAW_CENTROID
986 *fPositions.push() = fCentroid + SkVector::Make(-2, -2);
987 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
988 *fPositions.push() = fCentroid + SkVector::Make(2, -2);
989 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
990 *fPositions.push() = fCentroid + SkVector::Make(-2, 2);
991 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
992 *fPositions.push() = fCentroid + SkVector::Make(2, 2);
993 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
994
995 *fIndices.push() = fPositions.count() - 4;
996 *fIndices.push() = fPositions.count() - 2;
997 *fIndices.push() = fPositions.count() - 1;
998
999 *fIndices.push() = fPositions.count() - 4;
1000 *fIndices.push() = fPositions.count() - 1;
1001 *fIndices.push() = fPositions.count() - 3;
1002#endif
1003
Brian Salomon0dda9cb2017-02-03 10:33:25 -05001004 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001005}
1006
Brian Salomonab664fa2017-03-24 16:07:20 +00001007void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -04001008 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001009
1010 fPathPolygon.setReserve(path.countPoints());
1011
1012 // Walk around the path and compute clip polygon and path polygon.
1013 // Will also accumulate sum of areas for centroid.
1014 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -05001015 SkPath::Iter iter(path, true);
1016 SkPoint pts[4];
1017 SkPath::Verb verb;
1018
Jim Van Verth91af7272017-01-27 14:15:54 -05001019 fClipPolygon.reset();
1020
Brian Salomonab664fa2017-03-24 16:07:20 +00001021 // init centroid
1022 fCentroid = SkPoint::Make(0, 0);
1023 fArea = 0;
1024
Brian Salomon66085ed2017-02-02 15:39:34 -05001025 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +00001026 static constexpr SkScalar kA = 0.32495117187f;
1027 static constexpr SkScalar kB = 0.44311523437f;
1028 static constexpr SkScalar kC = 0.20141601562f;
1029 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -05001030
1031 SkPoint curvePoint;
1032 SkScalar w;
Jim Van Verth91af7272017-01-27 14:15:54 -05001033 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1034 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001035 case SkPath::kLine_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001036 ctm.mapPoints(&pts[1], 1);
Jim Van Verth91af7272017-01-27 14:15:54 -05001037 *fClipPolygon.push() = pts[1];
Brian Salomonab664fa2017-03-24 16:07:20 +00001038 this->INHERITED::handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -05001039 break;
1040 case SkPath::kQuad_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001041 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001042 // point at t = 1/2
1043 curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
1044 curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY;
1045 *fClipPolygon.push() = curvePoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001046 *fClipPolygon.push() = pts[2];
Brian Salomonab664fa2017-03-24 16:07:20 +00001047 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001048 break;
1049 case SkPath::kConic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001050 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001051 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -05001052 // point at t = 1/2
Brian Salomon66085ed2017-02-02 15:39:34 -05001053 curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
1054 curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
1055 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
1056 *fClipPolygon.push() = curvePoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001057 *fClipPolygon.push() = pts[2];
Brian Salomonab664fa2017-03-24 16:07:20 +00001058 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -05001059 break;
1060 case SkPath::kCubic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001061 ctm.mapPoints(pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -05001062 // point at t = 5/16
1063 curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
1064 curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY;
1065 *fClipPolygon.push() = curvePoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001066 // point at t = 11/16
1067 curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
1068 curvePoint.fY = kD*pts[0].fY + kC*pts[1].fY + kB*pts[2].fY + kA*pts[3].fY;
1069 *fClipPolygon.push() = curvePoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001070 *fClipPolygon.push() = pts[3];
Brian Salomonab664fa2017-03-24 16:07:20 +00001071 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001072 break;
Brian Salomonab664fa2017-03-24 16:07:20 +00001073 case SkPath::kMove_Verb:
Jim Van Verth91af7272017-01-27 14:15:54 -05001074 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +00001075 case SkPath::kDone_Verb:
Jim Van Verth91af7272017-01-27 14:15:54 -05001076 break;
1077 default:
1078 SkDEBUGFAIL("unknown verb");
1079 }
1080 }
1081
Brian Salomonab664fa2017-03-24 16:07:20 +00001082 // finish centroid
1083 if (fPathPolygon.count() > 0) {
1084 SkPoint currPoint = fPathPolygon[fPathPolygon.count() - 1];
1085 SkPoint nextPoint = fPathPolygon[0];
1086 SkScalar quadArea = currPoint.cross(nextPoint);
1087 fCentroid.fX += (currPoint.fX + nextPoint.fX) * quadArea;
1088 fCentroid.fY += (currPoint.fY + nextPoint.fY) * quadArea;
1089 fArea += quadArea;
1090 fCentroid *= SK_Scalar1 / (3 * fArea);
1091 }
1092
1093 fCurrClipPoint = fClipPolygon.count() - 1;
Brian Salomon66085ed2017-02-02 15:39:34 -05001094}
1095
Brian Salomonab664fa2017-03-24 16:07:20 +00001096void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
Brian Salomon66085ed2017-02-02 15:39:34 -05001097 SkASSERT(fClipPolygon.count() >= 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001098
Brian Salomonab664fa2017-03-24 16:07:20 +00001099 // init clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001100 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
1101 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001102
1103 // init centroid check
1104 bool hiddenCentroid = true;
Brian Salomonab664fa2017-03-24 16:07:20 +00001105 SkVector v1 = fCentroid - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001106 SkScalar initCross = v0.cross(v1);
1107
1108 for (int p = 1; p < fClipPolygon.count(); ++p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001109 // add to clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001110 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
1111 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001112 // Determine if transformed centroid is inside clipPolygon.
Brian Salomonab664fa2017-03-24 16:07:20 +00001113 v1 = fCentroid - fClipPolygon[p];
Brian Salomon66085ed2017-02-02 15:39:34 -05001114 if (initCross*v0.cross(v1) <= 0) {
1115 hiddenCentroid = false;
1116 }
1117 }
1118 SkASSERT(fClipVectors.count() == fClipPolygon.count());
1119
Brian Salomonab664fa2017-03-24 16:07:20 +00001120 fTransparent = fTransparent || !hiddenCentroid;
Brian Salomon66085ed2017-02-02 15:39:34 -05001121}
1122
1123bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
1124 SkPoint* clipPoint) {
1125 SkVector segmentVector = centroid - umbraPoint;
1126
Brian Salomonab664fa2017-03-24 16:07:20 +00001127 int startClipPoint = fCurrClipPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001128 do {
Brian Salomonab664fa2017-03-24 16:07:20 +00001129 SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
1130 SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
Brian Salomon66085ed2017-02-02 15:39:34 -05001131 SkScalar t_num = dp.cross(segmentVector);
1132 // if line segments are nearly parallel
1133 if (SkScalarNearlyZero(denom)) {
1134 // and collinear
1135 if (SkScalarNearlyZero(t_num)) {
1136 return false;
1137 }
1138 // otherwise are separate, will try the next poly segment
1139 // else if crossing lies within poly segment
1140 } else if (t_num >= 0 && t_num <= denom) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001141 SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
Brian Salomon66085ed2017-02-02 15:39:34 -05001142 // if umbra point is inside the clip polygon
Jim Van Verthda965502017-04-11 15:29:14 -04001143 if (s_num >= 0 && s_num <= denom) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001144 segmentVector *= s_num/denom;
1145 *clipPoint = umbraPoint + segmentVector;
1146 return true;
1147 }
1148 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001149 fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
1150 } while (fCurrClipPoint != startClipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001151
1152 return false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001153}
1154
Brian Salomonab664fa2017-03-24 16:07:20 +00001155int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
1156 SkScalar minDistance = p.distanceToSqd(fUmbraPolygon[fCurrUmbraPoint]);
1157 int index = fCurrUmbraPoint;
1158 int dir = 1;
1159 int next = (index + dir) % fUmbraPolygon.count();
1160
1161 // init travel direction
1162 SkScalar distance = p.distanceToSqd(fUmbraPolygon[next]);
1163 if (distance < minDistance) {
1164 index = next;
1165 minDistance = distance;
1166 } else {
1167 dir = fUmbraPolygon.count()-1;
1168 }
1169
1170 // iterate until we find a point that increases the distance
1171 next = (index + dir) % fUmbraPolygon.count();
1172 distance = p.distanceToSqd(fUmbraPolygon[next]);
1173 while (distance < minDistance) {
1174 index = next;
1175 minDistance = distance;
1176 next = (index + dir) % fUmbraPolygon.count();
1177 distance = p.distanceToSqd(fUmbraPolygon[next]);
1178 }
1179
1180 fCurrUmbraPoint = index;
1181 return index;
1182}
1183
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001184void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
Jim Van Verth91af7272017-01-27 14:15:54 -05001185 SkPoint* pts, int count) {
1186 // TODO: vectorize
1187 for (int i = 0; i < count; ++i) {
1188 pts[i] *= scale;
1189 pts[i] += xlate;
1190 }
1191}
1192
Brian Salomonab664fa2017-03-24 16:07:20 +00001193static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
1194 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
1195 static constexpr SkScalar kCloseSqd = kClose*kClose;
1196
1197 SkScalar distSq = p0.distanceToSqd(p1);
1198 return distSq < kCloseSqd;
1199}
1200
Jim Van Verthb55eb282017-07-18 14:13:45 -04001201static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001202 SkVector v0 = p1 - p0;
1203 SkVector v1 = p2 - p0;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001204 return v0.cross(v1);
1205}
1206
1207static bool is_collinear(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
1208 return (SkScalarNearlyZero(perp_dot(p0, p1, p2)));
Brian Salomonab664fa2017-03-24 16:07:20 +00001209}
1210
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001211void SkSpotShadowTessellator::handleLine(const SkPoint& p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001212 // remove coincident points and add to centroid
1213 if (fPathPolygon.count() > 0) {
1214 const SkPoint& lastPoint = fPathPolygon[fPathPolygon.count() - 1];
1215 if (duplicate_pt(p, lastPoint)) {
1216 return;
1217 }
1218 SkScalar quadArea = lastPoint.cross(p);
1219 fCentroid.fX += (p.fX + lastPoint.fX) * quadArea;
1220 fCentroid.fY += (p.fY + lastPoint.fY) * quadArea;
1221 fArea += quadArea;
1222 }
1223
1224 // try to remove collinear points
1225 if (fPathPolygon.count() > 1 && is_collinear(fPathPolygon[fPathPolygon.count()-2],
1226 fPathPolygon[fPathPolygon.count()-1],
1227 p)) {
1228 fPathPolygon[fPathPolygon.count() - 1] = p;
1229 } else {
1230 *fPathPolygon.push() = p;
1231 }
1232}
1233
Jim Van Verthb55eb282017-07-18 14:13:45 -04001234bool SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001235 if (fInitPoints.count() < 2) {
1236 *fInitPoints.push() = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001237 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001238 }
1239
1240 if (fInitPoints.count() == 2) {
1241 // determine if cw or ccw
Jim Van Verthb55eb282017-07-18 14:13:45 -04001242 SkScalar perpDot = perp_dot(fInitPoints[0], fInitPoints[1], p);
Jim Van Verth91af7272017-01-27 14:15:54 -05001243 if (SkScalarNearlyZero(perpDot)) {
1244 // nearly parallel, just treat as straight line and continue
1245 fInitPoints[1] = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001246 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001247 }
1248
1249 // if perpDot > 0, winding is ccw
1250 fDirection = (perpDot > 0) ? -1 : 1;
1251
1252 // add first quad
Jim Van Verth76387852017-05-16 16:55:16 -04001253 if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &fFirstOutset)) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001254 // first two points are incident, make the third point the second and continue
1255 fInitPoints[1] = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001256 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001257 }
1258
Jim Van Verth76387852017-05-16 16:55:16 -04001259 fFirstOutset *= fRadius;
Jim Van Verth91af7272017-01-27 14:15:54 -05001260 fFirstPoint = fInitPoints[0];
Jim Van Verth76387852017-05-16 16:55:16 -04001261 fFirstVertexIndex = fPositions.count();
1262 fPrevOutset = fFirstOutset;
Jim Van Verth91af7272017-01-27 14:15:54 -05001263 fPrevPoint = fFirstPoint;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001264 fPrevUmbraIndex = -1;
Jim Van Verth91af7272017-01-27 14:15:54 -05001265
Brian Salomon66085ed2017-02-02 15:39:34 -05001266 this->addInnerPoint(fFirstPoint);
Jim Van Verth76387852017-05-16 16:55:16 -04001267 fPrevUmbraIndex = fFirstVertexIndex;
Brian Salomon66085ed2017-02-02 15:39:34 -05001268
1269 if (!fTransparent) {
1270 SkPoint clipPoint;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001271 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
1272 fCentroid, &clipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001273 if (isOutside) {
1274 *fPositions.push() = clipPoint;
1275 *fColors.push() = fUmbraColor;
1276 }
1277 fPrevUmbraOutside = isOutside;
1278 fFirstUmbraOutside = isOutside;
1279 }
1280
Jim Van Verth76387852017-05-16 16:55:16 -04001281 SkPoint newPoint = fFirstPoint + fFirstOutset;
Jim Van Verth91af7272017-01-27 14:15:54 -05001282 *fPositions.push() = newPoint;
1283 *fColors.push() = fPenumbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -04001284 this->addEdge(fInitPoints[1], fFirstOutset);
Jim Van Verth91af7272017-01-27 14:15:54 -05001285
1286 // to ensure we skip this block next time
1287 *fInitPoints.push() = p;
1288 }
1289
Jim Van Verthb55eb282017-07-18 14:13:45 -04001290 // if concave, abort
1291 SkScalar perpDot = perp_dot(fInitPoints[1], fInitPoints[2], p);
1292 if (fDirection*perpDot > 0) {
1293 return false;
1294 }
1295
Jim Van Verth91af7272017-01-27 14:15:54 -05001296 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -04001297 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
1298 normal *= fRadius;
1299 this->addArc(normal, true);
1300 this->addEdge(p, normal);
Jim Van Verthb55eb282017-07-18 14:13:45 -04001301 fInitPoints[1] = fInitPoints[2];
1302 fInitPoints[2] = p;
Jim Van Verth91af7272017-01-27 14:15:54 -05001303 }
Jim Van Verthb55eb282017-07-18 14:13:45 -04001304
1305 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001306}
1307
Brian Salomonab664fa2017-03-24 16:07:20 +00001308bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint) {
1309 SkPoint umbraPoint;
1310 if (!fValidUmbra) {
1311 SkVector v = fCentroid - pathPoint;
1312 v *= 0.95f;
1313 umbraPoint = pathPoint + v;
Jim Van Verth91af7272017-01-27 14:15:54 -05001314 } else {
Brian Salomonab664fa2017-03-24 16:07:20 +00001315 umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
Jim Van Verth91af7272017-01-27 14:15:54 -05001316 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001317
Jim Van Verth91af7272017-01-27 14:15:54 -05001318 fPrevPoint = pathPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001319
1320 // merge "close" points
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001321 if (fPrevUmbraIndex == -1 ||
Brian Salomonab664fa2017-03-24 16:07:20 +00001322 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
1323 *fPositions.push() = umbraPoint;
1324 *fColors.push() = fUmbraColor;
1325
1326 return false;
1327 } else {
1328 return true;
1329 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001330}
1331
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001332void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001333 // add next umbra point
Brian Salomonab664fa2017-03-24 16:07:20 +00001334 bool duplicate = this->addInnerPoint(nextPoint);
1335 int prevPenumbraIndex = duplicate ? fPositions.count()-1 : fPositions.count()-2;
1336 int currUmbraIndex = duplicate ? fPrevUmbraIndex : fPositions.count()-1;
Brian Salomon66085ed2017-02-02 15:39:34 -05001337
Brian Salomonab664fa2017-03-24 16:07:20 +00001338 if (!duplicate) {
1339 // add to center fan if transparent or centroid showing
1340 if (fTransparent) {
1341 *fIndices.push() = 0;
1342 *fIndices.push() = fPrevUmbraIndex;
1343 *fIndices.push() = currUmbraIndex;
1344 // otherwise add to clip ring
1345 } else {
Brian Salomon66085ed2017-02-02 15:39:34 -05001346 SkPoint clipPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001347 bool isOutside = this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
1348 &clipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001349 if (isOutside) {
1350 *fPositions.push() = clipPoint;
1351 *fColors.push() = fUmbraColor;
1352
1353 *fIndices.push() = fPrevUmbraIndex;
1354 *fIndices.push() = currUmbraIndex;
1355 *fIndices.push() = currUmbraIndex + 1;
1356 if (fPrevUmbraOutside) {
1357 // fill out quad
1358 *fIndices.push() = fPrevUmbraIndex;
1359 *fIndices.push() = currUmbraIndex + 1;
1360 *fIndices.push() = fPrevUmbraIndex + 1;
1361 }
1362 } else if (fPrevUmbraOutside) {
1363 // add tri
1364 *fIndices.push() = fPrevUmbraIndex;
1365 *fIndices.push() = currUmbraIndex;
1366 *fIndices.push() = fPrevUmbraIndex + 1;
1367 }
1368 fPrevUmbraOutside = isOutside;
1369 }
1370 }
1371
1372 // add next penumbra point and quad
Jim Van Verth91af7272017-01-27 14:15:54 -05001373 SkPoint newPoint = nextPoint + nextNormal;
1374 *fPositions.push() = newPoint;
1375 *fColors.push() = fPenumbraColor;
1376
Brian Salomonab664fa2017-03-24 16:07:20 +00001377 if (!duplicate) {
1378 *fIndices.push() = fPrevUmbraIndex;
1379 *fIndices.push() = prevPenumbraIndex;
1380 *fIndices.push() = currUmbraIndex;
1381 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001382
Brian Salomon66085ed2017-02-02 15:39:34 -05001383 *fIndices.push() = prevPenumbraIndex;
Jim Van Verth91af7272017-01-27 14:15:54 -05001384 *fIndices.push() = fPositions.count() - 1;
Brian Salomon66085ed2017-02-02 15:39:34 -05001385 *fIndices.push() = currUmbraIndex;
Jim Van Verth91af7272017-01-27 14:15:54 -05001386
Brian Salomon66085ed2017-02-02 15:39:34 -05001387 fPrevUmbraIndex = currUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -04001388 fPrevOutset = nextNormal;
Jim Van Verth91af7272017-01-27 14:15:54 -05001389}
Brian Salomon958fbc42017-01-30 17:01:28 -05001390
1391///////////////////////////////////////////////////////////////////////////////////////////////////
1392
Brian Salomonaff27a22017-02-06 15:47:44 -05001393sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001394 const SkPoint3& zPlane, bool transparent) {
1395 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001396 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001397}
1398
Brian Salomonaff27a22017-02-06 15:47:44 -05001399sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001400 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001401 SkScalar lightRadius, bool transparent) {
Jim Van Verthe308a122017-05-08 14:19:30 -04001402 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001403 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001404}