blob: c377579de2a1d4533845174f50a7b4bd8f356bfa [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;
Jim Van Verthf507c282018-05-11 10:48:20 -040091 bool fIsConvex;
Jim Van Vertha84898d2017-02-06 13:38:23 -050092
93 SkColor fUmbraColor;
94 SkColor fPenumbraColor;
95
96 SkScalar fRadius;
97 SkScalar fDirection;
98 int fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -040099 SkVector fPrevOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500100 SkPoint fPrevPoint;
Brian Salomon958fbc42017-01-30 17:01:28 -0500101};
102
Jim Van Verthda965502017-04-11 15:29:14 -0400103static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
Jim Van Verthbce74962017-01-25 09:39:46 -0500104 SkVector* newNormal) {
105 SkVector normal;
106 // compute perpendicular
107 normal.fX = p0.fY - p1.fY;
108 normal.fY = p1.fX - p0.fX;
Jim Van Verthda965502017-04-11 15:29:14 -0400109 normal *= dir;
Jim Van Verthbce74962017-01-25 09:39:46 -0500110 if (!normal.normalize()) {
111 return false;
112 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500113 *newNormal = normal;
114 return true;
115}
116
117static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
118 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400119 const SkScalar kRecipPixelsPerArcSegment = 0.125f;
Jim Van Verthbce74962017-01-25 09:39:46 -0500120
121 SkScalar rCos = v1.dot(v2);
122 SkScalar rSin = v1.cross(v2);
123 SkScalar theta = SkScalarATan2(rSin, rCos);
124
Jim Van Verth4c8c1e82018-04-23 17:14:48 -0400125 int steps = SkScalarRoundToInt(SkScalarAbs(r*theta*kRecipPixelsPerArcSegment));
Jim Van Verthbce74962017-01-25 09:39:46 -0500126
127 SkScalar dTheta = theta / steps;
128 *rotSin = SkScalarSinCos(dTheta, rotCos);
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400129 *n = steps;
Jim Van Verthbce74962017-01-25 09:39:46 -0500130}
131
Jim Van Verthf507c282018-05-11 10:48:20 -0400132static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
133 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
134 static constexpr SkScalar kCloseSqd = kClose * kClose;
135
136 SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
137 return distSq < kCloseSqd;
138}
139
140static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
141 SkVector v0 = p1 - p0;
142 SkVector v1 = p2 - p0;
143 return v0.cross(v1);
144}
145
Jim Van Verthe308a122017-05-08 14:19:30 -0400146SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
147 : fZPlaneParams(zPlaneParams)
Jim Van Verthda965502017-04-11 15:29:14 -0400148 , fZOffset(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400149 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500150 , fSucceeded(false)
151 , fTransparent(transparent)
Jim Van Verthf507c282018-05-11 10:48:20 -0400152 , fIsConvex(true)
Brian Salomonaff27a22017-02-06 15:47:44 -0500153 , fDirection(1)
154 , fPrevUmbraIndex(-1) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500155 fInitPoints.setReserve(3);
156
157 // child classes will set reserve for positions, colors and indices
158}
159
Jim Van Verthda965502017-04-11 15:29:14 -0400160bool SkBaseShadowTessellator::setZOffset(const SkRect& bounds, bool perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400161 SkScalar minZ = this->heightFunc(bounds.fLeft, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400162 if (perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400163 SkScalar z = this->heightFunc(bounds.fLeft, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400164 if (z < minZ) {
165 minZ = z;
166 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400167 z = this->heightFunc(bounds.fRight, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400168 if (z < minZ) {
169 minZ = z;
170 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400171 z = this->heightFunc(bounds.fRight, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400172 if (z < minZ) {
173 minZ = z;
174 }
175 }
176
177 if (minZ < kMinHeight) {
178 fZOffset = -minZ + kMinHeight;
179 return true;
180 }
181
182 return false;
183}
184
Jim Van Vertha84898d2017-02-06 13:38:23 -0500185// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500186#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500187static const SkScalar kQuadTolerance = 0.2f;
188static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500189#endif
Jim Van Vertha84898d2017-02-06 13:38:23 -0500190static const SkScalar kConicTolerance = 0.5f;
191
Brian Salomonaff27a22017-02-06 15:47:44 -0500192void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500193 m.mapPoints(p, 1);
194 this->handleLine(*p);
195}
196
Brian Salomonaff27a22017-02-06 15:47:44 -0500197void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500198#if SK_SUPPORT_GPU
Jim Van Vertha947e292018-02-26 13:54:34 -0500199 // check for degeneracy
200 SkVector v0 = pts[1] - pts[0];
201 SkVector v1 = pts[2] - pts[0];
202 if (SkScalarNearlyZero(v0.cross(v1))) {
203 return;
204 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500205 // TODO: Pull PathUtils out of Ganesh?
206 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400207 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500208 SkPoint* target = fPointBuffer.begin();
209 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
210 kQuadTolerance, &target, maxCount);
211 fPointBuffer.setCount(count);
212 for (int i = 0; i < count; i++) {
213 this->handleLine(fPointBuffer[i]);
214 }
215#else
216 // for now, just to draw something
217 this->handleLine(pts[1]);
218 this->handleLine(pts[2]);
219#endif
220}
221
Brian Salomonaff27a22017-02-06 15:47:44 -0500222void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500223 m.mapPoints(pts, 3);
224 this->handleQuad(pts);
225}
226
Brian Salomonaff27a22017-02-06 15:47:44 -0500227void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500228 m.mapPoints(pts, 4);
229#if SK_SUPPORT_GPU
230 // TODO: Pull PathUtils out of Ganesh?
231 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400232 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500233 SkPoint* target = fPointBuffer.begin();
234 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
235 kCubicTolerance, &target, maxCount);
236 fPointBuffer.setCount(count);
237 for (int i = 0; i < count; i++) {
238 this->handleLine(fPointBuffer[i]);
239 }
240#else
241 // for now, just to draw something
242 this->handleLine(pts[1]);
243 this->handleLine(pts[2]);
244 this->handleLine(pts[3]);
245#endif
246}
247
Brian Salomonaff27a22017-02-06 15:47:44 -0500248void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400249 if (m.hasPerspective()) {
250 w = SkConic::TransformW(pts, w, m);
251 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500252 m.mapPoints(pts, 3);
253 SkAutoConicToQuads quadder;
254 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
255 SkPoint lastPoint = *(quads++);
256 int count = quadder.countQuads();
257 for (int i = 0; i < count; ++i) {
258 SkPoint quadPts[3];
259 quadPts[0] = lastPoint;
260 quadPts[1] = quads[0];
261 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
262 this->handleQuad(quadPts);
263 lastPoint = quadPts[2];
264 quads += 2;
265 }
266}
267
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400268bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500269 // fill in fan from previous quad
270 SkScalar rotSin, rotCos;
271 int numSteps;
Jim Van Verth76387852017-05-16 16:55:16 -0400272 compute_radial_steps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
273 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400274 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400275 SkVector currNormal;
276 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
277 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
278 *fPositions.push() = fPrevPoint + currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500279 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400280 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500281
Jim Van Verthda965502017-04-11 15:29:14 -0400282 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500283 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400284 if (finishArc && numSteps) {
Jim Van Verthda965502017-04-11 15:29:14 -0400285 *fPositions.push() = fPrevPoint + nextNormal;
286 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400287 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Verthda965502017-04-11 15:29:14 -0400288 }
Jim Van Verth76387852017-05-16 16:55:16 -0400289 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400290
291 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500292}
293
Jim Van Verth872da6b2018-04-10 11:24:11 -0400294void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
295 auto indices = fIndices.append(3);
296
297 indices[0] = index0;
298 indices[1] = index1;
299 indices[2] = index2;
300}
301
302void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
303 uint16_t index2, uint16_t index3) {
304 auto indices = fIndices.append(6);
305
306 indices[0] = index0;
307 indices[1] = index1;
308 indices[2] = index2;
309
310 indices[3] = index2;
311 indices[4] = index1;
312 indices[5] = index3;
313}
314
Jim Van Verthda965502017-04-11 15:29:14 -0400315bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400316 if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400317 fTransformedHeightFunc = [this](const SkPoint& p) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400318 return fZPlaneParams.fZ;
Jim Van Verthda965502017-04-11 15:29:14 -0400319 };
320 } else {
321 SkMatrix ctmInverse;
Jim Van Vertha947e292018-02-26 13:54:34 -0500322 if (!ctm.invert(&ctmInverse) || !ctmInverse.isFinite()) {
Jim Van Verthda965502017-04-11 15:29:14 -0400323 return false;
324 }
Jim Van Verthda965502017-04-11 15:29:14 -0400325 // multiply by transpose
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400326 fTransformedZParams = SkPoint3::Make(
Jim Van Verthe308a122017-05-08 14:19:30 -0400327 ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
328 ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
329 ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
330
331 ctmInverse[SkMatrix::kMSkewX] * fZPlaneParams.fX +
332 ctmInverse[SkMatrix::kMScaleY] * fZPlaneParams.fY +
333 ctmInverse[SkMatrix::kMPersp1] * fZPlaneParams.fZ,
334
335 ctmInverse[SkMatrix::kMTransX] * fZPlaneParams.fX +
336 ctmInverse[SkMatrix::kMTransY] * fZPlaneParams.fY +
337 ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
338 );
Jim Van Verthda965502017-04-11 15:29:14 -0400339
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400340 if (ctm.hasPerspective()) {
341 // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
342 // so pre-compute those values that are independent of X and Y.
343 // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
344 fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
345 ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
346 fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
347 ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
348 fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
349 ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
350 SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
351 ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
352 ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
Jim Van Verthda965502017-04-11 15:29:14 -0400353
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400354 // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
355 // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
356 fTransformedZParams.fX *= ctmDeterminant;
357 fTransformedZParams.fY *= ctmDeterminant;
358 fTransformedZParams.fZ *= ctmDeterminant;
Jim Van Verthda965502017-04-11 15:29:14 -0400359
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400360 fTransformedHeightFunc = [this](const SkPoint& p) {
361 SkScalar denom = p.fX * fPartialDeterminants[0] +
362 p.fY * fPartialDeterminants[1] +
363 fPartialDeterminants[2];
364 SkScalar w = SkScalarFastInvert(denom);
365 return fZOffset + w*(fTransformedZParams.fX * p.fX +
366 fTransformedZParams.fY * p.fY +
367 fTransformedZParams.fZ);
368 };
369 } else {
370 fTransformedHeightFunc = [this](const SkPoint& p) {
371 return fZOffset + fTransformedZParams.fX * p.fX +
372 fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
373 };
374 }
Jim Van Verthda965502017-04-11 15:29:14 -0400375 }
376
377 return true;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500378}
379
380
381//////////////////////////////////////////////////////////////////////////////////////////////////
382
Brian Salomonaff27a22017-02-06 15:47:44 -0500383class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500384public:
385 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400386 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500387
388private:
389 void handleLine(const SkPoint& p) override;
Jim Van Verthda965502017-04-11 15:29:14 -0400390 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500391
Jim Van Verthda965502017-04-11 15:29:14 -0400392 static constexpr auto kMaxEdgeLenSqr = 20 * 20;
Jim Van Verth76387852017-05-16 16:55:16 -0400393 static constexpr auto kInsetFactor = -0.5f;
Jim Van Verthda965502017-04-11 15:29:14 -0400394
395 SkScalar offset(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400396 return SkDrawShadowMetrics::AmbientBlurRadius(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400397 }
398 SkColor umbraColor(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400399 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(z));
Jim Van Verth060d9822017-05-04 09:58:17 -0400400 return SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
Jim Van Verthda965502017-04-11 15:29:14 -0400401 }
402
Jim Van Vertha84898d2017-02-06 13:38:23 -0500403 int fCentroidCount;
Jim Van Verth76387852017-05-16 16:55:16 -0400404 bool fSplitFirstEdge;
405 bool fSplitPreviousEdge;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500406
Brian Salomonaff27a22017-02-06 15:47:44 -0500407 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500408};
409
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500410SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500411 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400412 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500413 bool transparent)
Jim Van Verth76387852017-05-16 16:55:16 -0400414 : INHERITED(zPlaneParams, transparent)
415 , fSplitFirstEdge(false)
416 , fSplitPreviousEdge(false) {
Jim Van Verthda965502017-04-11 15:29:14 -0400417 // Set base colors
Jim Van Verth1af03d42017-07-31 09:34:58 -0400418 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(heightFunc(0, 0)));
Jim Van Verthb4366552017-03-27 14:25:29 -0400419 // umbraColor is the interior value, penumbraColor the exterior value.
420 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
421 // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
422 // the final alpha.
Jim Van Verth060d9822017-05-04 09:58:17 -0400423 fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
424 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400425
Jim Van Verthda965502017-04-11 15:29:14 -0400426 // make sure we're not below the canvas plane
427 this->setZOffset(path.getBounds(), ctm.hasPerspective());
428
Jim Van Verth7c8008c2018-02-07 15:02:50 -0500429 if (!this->setTransformedHeightFunc(ctm)) {
430 return;
431 }
Jim Van Verthda965502017-04-11 15:29:14 -0400432
Jim Van Verthbce74962017-01-25 09:39:46 -0500433 // Outer ring: 3*numPts
434 // Middle ring: numPts
435 fPositions.setReserve(4 * path.countPoints());
436 fColors.setReserve(4 * path.countPoints());
437 // Outer ring: 12*numPts
438 // Middle ring: 0
439 fIndices.setReserve(12 * path.countPoints());
440
Jim Van Verthbce74962017-01-25 09:39:46 -0500441 // walk around the path, tessellate and generate outer ring
442 // if original path is transparent, will accumulate sum of points for centroid
443 SkPath::Iter iter(path, true);
444 SkPoint pts[4];
445 SkPath::Verb verb;
446 if (fTransparent) {
447 *fPositions.push() = SkPoint::Make(0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400448 *fColors.push() = fUmbraColor;
Jim Van Verthbce74962017-01-25 09:39:46 -0500449 fCentroidCount = 0;
450 }
451 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
452 switch (verb) {
453 case SkPath::kLine_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500454 this->INHERITED::handleLine(ctm, &pts[1]);
Jim Van Verthbce74962017-01-25 09:39:46 -0500455 break;
456 case SkPath::kQuad_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500457 this->handleQuad(ctm, pts);
Jim Van Verthbce74962017-01-25 09:39:46 -0500458 break;
459 case SkPath::kCubic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500460 this->handleCubic(ctm, pts);
Jim Van Verthbce74962017-01-25 09:39:46 -0500461 break;
462 case SkPath::kConic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500463 this->handleConic(ctm, pts, iter.conicWeight());
Jim Van Verthbce74962017-01-25 09:39:46 -0500464 break;
465 case SkPath::kMove_Verb:
466 case SkPath::kClose_Verb:
467 case SkPath::kDone_Verb:
468 break;
469 }
Jim Van Verthf507c282018-05-11 10:48:20 -0400470 // TODO: add support for concave paths
471 if (!fIsConvex) {
472 return;
473 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500474 }
475
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500476 if (!this->indexCount()) {
477 return;
478 }
479
Jim Van Verthf507c282018-05-11 10:48:20 -0400480 // final convexity check
481 // TODO: add support for concave paths
482 if (fDirection*perp_dot(fInitPoints[1], fInitPoints[2], fFirstPoint) > 0) {
483 return;
484 }
485
Jim Van Verth76387852017-05-16 16:55:16 -0400486 // Finish up
Jim Van Verthbce74962017-01-25 09:39:46 -0500487 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -0400488 if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
489 SkScalar z = fTransformedHeightFunc(fPrevPoint);
490 fRadius = this->offset(z);
491 SkVector scaledNormal(normal);
492 scaledNormal *= fRadius;
493 this->addArc(scaledNormal, true);
Jim Van Verthbce74962017-01-25 09:39:46 -0500494
Jim Van Verth76387852017-05-16 16:55:16 -0400495 // fix-up the last and first umbra points
496 SkVector inset = normal;
497 // adding to an average, so multiply by an additional half
498 inset *= 0.5f*kInsetFactor;
499 fPositions[fPrevUmbraIndex] += inset;
500 fPositions[fFirstVertexIndex] += inset;
501 // we multiply by another half because now we're adding to an average of an average
502 inset *= 0.5f;
503 if (fSplitPreviousEdge) {
504 fPositions[fPrevUmbraIndex - 2] += inset;
505 }
506 if (fSplitFirstEdge) {
507 fPositions[fFirstVertexIndex + 2] += inset;
508 }
509
Jim Van Verthda965502017-04-11 15:29:14 -0400510 // set up for final edge
511 z = fTransformedHeightFunc(fFirstPoint);
512 normal *= this->offset(z);
Jim Van Verthbce74962017-01-25 09:39:46 -0500513
Jim Van Verthda965502017-04-11 15:29:14 -0400514 // make sure we don't end up with a sharp alpha edge along the quad diagonal
Jim Van Verth76387852017-05-16 16:55:16 -0400515 if (fColors[fPrevUmbraIndex] != fColors[fFirstVertexIndex] &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500516 SkPointPriv::DistanceToSqd(fFirstPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400517 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + fPositions[fFirstVertexIndex];
Jim Van Verthda965502017-04-11 15:29:14 -0400518 centerPoint *= 0.5f;
519 *fPositions.push() = centerPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400520 *fColors.push() = SkPMLerp(fColors[fFirstVertexIndex], fColors[fPrevUmbraIndex], 128);
521 centerPoint = fPositions[fPositions.count()-2] + fPositions[fFirstVertexIndex+1];
522 centerPoint *= 0.5f;
523 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400524 *fColors.push() = fPenumbraColor;
525
Jim Van Verth76387852017-05-16 16:55:16 -0400526 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400527 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
528 fPositions.count() - 2, fPositions.count() - 1);
Jim Van Verth76387852017-05-16 16:55:16 -0400529 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400530 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
531 fPrevUmbraIndex, fPositions.count() - 3);
Jim Van Verth76387852017-05-16 16:55:16 -0400532 }
Jim Van Verthda965502017-04-11 15:29:14 -0400533
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400534 // if transparent, add point to first one in array and add to center fan
535 if (fTransparent) {
536 fPositions[0] += centerPoint;
537 ++fCentroidCount;
538
539 *fIndices.push() = 0;
540 *fIndices.push() = fPrevUmbraIndex;
541 *fIndices.push() = fPositions.count() - 2;
542 }
543
Jim Van Verthda965502017-04-11 15:29:14 -0400544 fPrevUmbraIndex = fPositions.count() - 2;
545 }
546
547 // final edge
Jim Van Vertha84898d2017-02-06 13:38:23 -0500548 *fPositions.push() = fFirstPoint + normal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500549 *fColors.push() = fPenumbraColor;
550
Jim Van Verth76387852017-05-16 16:55:16 -0400551 if (fColors[fPrevUmbraIndex] > fColors[fFirstVertexIndex]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400552 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 2,
553 fFirstVertexIndex, fPositions.count() - 1);
Jim Van Verthda965502017-04-11 15:29:14 -0400554 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400555 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
556 fPrevUmbraIndex, fFirstVertexIndex);
Jim Van Verthda965502017-04-11 15:29:14 -0400557 }
Jim Van Verth76387852017-05-16 16:55:16 -0400558 fPrevOutset = normal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500559 }
560
561 // finalize centroid
562 if (fTransparent) {
563 fPositions[0] *= SkScalarFastInvert(fCentroidCount);
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400564 fColors[0] = this->umbraColor(fTransformedHeightFunc(fPositions[0]));
Jim Van Verthbce74962017-01-25 09:39:46 -0500565
Jim Van Verth872da6b2018-04-10 11:24:11 -0400566 this->appendTriangle(0, fPrevUmbraIndex, fFirstVertexIndex);
Jim Van Verthbce74962017-01-25 09:39:46 -0500567 }
568
569 // final fan
570 if (fPositions.count() >= 3) {
Jim Van Verth76387852017-05-16 16:55:16 -0400571 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500572 fPrevPoint = fFirstPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400573 fRadius = this->offset(fTransformedHeightFunc(fPrevPoint));
Jim Van Verth76387852017-05-16 16:55:16 -0400574 if (this->addArc(fFirstOutset, false)) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400575 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1, fFirstVertexIndex + 1);
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400576 } else {
577 // arc is too small, set the first penumbra point to be the same position
578 // as the last one
Jim Van Verth76387852017-05-16 16:55:16 -0400579 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400580 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500581 }
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500582 fSucceeded = true;
Jim Van Verthbce74962017-01-25 09:39:46 -0500583}
584
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500585void SkAmbientShadowTessellator::handleLine(const SkPoint& p) {
Jim Van Verthf507c282018-05-11 10:48:20 -0400586 // skip duplicate points
587 if (!fInitPoints.isEmpty() && duplicate_pt(p, fInitPoints[fInitPoints.count() - 1])) {
588 return;
589 }
590
Jim Van Verthbce74962017-01-25 09:39:46 -0500591 if (fInitPoints.count() < 2) {
592 *fInitPoints.push() = p;
593 return;
594 }
595
596 if (fInitPoints.count() == 2) {
597 // determine if cw or ccw
Jim Van Verthf507c282018-05-11 10:48:20 -0400598 SkScalar perpDot = perp_dot(fInitPoints[0], fInitPoints[1], p);
Jim Van Verthbce74962017-01-25 09:39:46 -0500599 if (SkScalarNearlyZero(perpDot)) {
600 // nearly parallel, just treat as straight line and continue
601 fInitPoints[1] = p;
602 return;
603 }
604
605 // if perpDot > 0, winding is ccw
606 fDirection = (perpDot > 0) ? -1 : 1;
607
608 // add first quad
Jim Van Verthda965502017-04-11 15:29:14 -0400609 SkVector normal;
610 if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &normal)) {
Jim Van Verthbce74962017-01-25 09:39:46 -0500611 // first two points are incident, make the third point the second and continue
612 fInitPoints[1] = p;
613 return;
614 }
615
Jim Van Vertha84898d2017-02-06 13:38:23 -0500616 fFirstPoint = fInitPoints[0];
Jim Van Verth76387852017-05-16 16:55:16 -0400617 fFirstVertexIndex = fPositions.count();
Jim Van Verthda965502017-04-11 15:29:14 -0400618 SkScalar z = fTransformedHeightFunc(fFirstPoint);
Jim Van Verth76387852017-05-16 16:55:16 -0400619 fFirstOutset = normal;
620 fFirstOutset *= this->offset(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400621
Jim Van Verth76387852017-05-16 16:55:16 -0400622 fPrevOutset = fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500623 fPrevPoint = fFirstPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400624 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500625
Jim Van Verthda965502017-04-11 15:29:14 -0400626 *fPositions.push() = fFirstPoint;
627 *fColors.push() = this->umbraColor(z);
Jim Van Verth76387852017-05-16 16:55:16 -0400628 *fPositions.push() = fFirstPoint + fFirstOutset;
Jim Van Verthbce74962017-01-25 09:39:46 -0500629 *fColors.push() = fPenumbraColor;
630 if (fTransparent) {
Jim Van Verthda965502017-04-11 15:29:14 -0400631 fPositions[0] += fFirstPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500632 fCentroidCount = 1;
633 }
Jim Van Verthda965502017-04-11 15:29:14 -0400634
635 // add the first quad
636 z = fTransformedHeightFunc(fInitPoints[1]);
637 fRadius = this->offset(z);
638 fUmbraColor = this->umbraColor(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400639 this->addEdge(fInitPoints[1], normal);
Jim Van Verthbce74962017-01-25 09:39:46 -0500640
641 // to ensure we skip this block next time
642 *fInitPoints.push() = p;
Jim Van Verthf507c282018-05-11 10:48:20 -0400643 } else {
644 // reuse fInitPoints to track last three points
645 fInitPoints[0] = fInitPoints[1];
646 fInitPoints[1] = fInitPoints[2];
647 fInitPoints[2] = p;
648 // convexity check
649 if (fDirection*perp_dot(fInitPoints[0], fInitPoints[1], p) > 0) {
650 fIsConvex = false;
651 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500652 }
653
654 SkVector normal;
Jim Van Verth76387852017-05-16 16:55:16 -0400655 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400656 SkVector scaledNormal = normal;
657 scaledNormal *= fRadius;
658 this->addArc(scaledNormal, true);
659 SkScalar z = fTransformedHeightFunc(p);
660 fRadius = this->offset(z);
661 fUmbraColor = this->umbraColor(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400662 this->addEdge(p, normal);
Jim Van Verthbce74962017-01-25 09:39:46 -0500663 }
664}
665
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500666void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
Jim Van Verth76387852017-05-16 16:55:16 -0400667 // We compute the inset in two stages: first we inset by half the current normal,
668 // then on the next addEdge() we add half of the next normal to get an average of the two
669 SkVector insetNormal = nextNormal;
670 insetNormal *= 0.5f*kInsetFactor;
671
672 // Adding the other half of the average for the previous edge
673 fPositions[fPrevUmbraIndex] += insetNormal;
674
675 SkPoint umbraPoint = nextPoint + insetNormal;
676 SkVector outsetNormal = nextNormal;
677 outsetNormal *= fRadius;
678 SkPoint penumbraPoint = nextPoint + outsetNormal;
679
680 // For split edges, we're adding an average of two averages, so we multiply by another half
681 if (fSplitPreviousEdge) {
682 insetNormal *= 0.5f;
683 fPositions[fPrevUmbraIndex - 2] += insetNormal;
684 }
685
686 // 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 -0400687 if (fColors[fPrevUmbraIndex] != fUmbraColor &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500688 SkPointPriv::DistanceToSqd(nextPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400689
690 // This is lacking 1/4 of the next inset -- we'll add it the next time we call addEdge()
691 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + umbraPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400692 centerPoint *= 0.5f;
693 *fPositions.push() = centerPoint;
694 *fColors.push() = SkPMLerp(fUmbraColor, fColors[fPrevUmbraIndex], 128);
Jim Van Verth76387852017-05-16 16:55:16 -0400695 centerPoint = fPositions[fPositions.count()-2] + penumbraPoint;
696 centerPoint *= 0.5f;
697 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400698 *fColors.push() = fPenumbraColor;
699
700 // set triangularization to get best interpolation of color
701 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400702 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
703 fPositions.count() - 2, fPositions.count() - 1);
Jim Van Verthda965502017-04-11 15:29:14 -0400704 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400705 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
706 fPrevUmbraIndex, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -0400707 }
708
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400709 // if transparent, add point to first one in array and add to center fan
710 if (fTransparent) {
711 fPositions[0] += centerPoint;
712 ++fCentroidCount;
713
Jim Van Verth872da6b2018-04-10 11:24:11 -0400714 this->appendTriangle(0, fPrevUmbraIndex, fPositions.count() - 2);
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400715 }
716
Jim Van Verth76387852017-05-16 16:55:16 -0400717 fSplitPreviousEdge = true;
718 if (fPrevUmbraIndex == fFirstVertexIndex) {
719 fSplitFirstEdge = true;
720 }
Jim Van Verthda965502017-04-11 15:29:14 -0400721 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Verth76387852017-05-16 16:55:16 -0400722 } else {
723 fSplitPreviousEdge = false;
Jim Van Verthda965502017-04-11 15:29:14 -0400724 }
725
Jim Van Verthbce74962017-01-25 09:39:46 -0500726 // add next quad
Jim Van Verth76387852017-05-16 16:55:16 -0400727 *fPositions.push() = umbraPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500728 *fColors.push() = fUmbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -0400729 *fPositions.push() = penumbraPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500730 *fColors.push() = fPenumbraColor;
731
Jim Van Verthda965502017-04-11 15:29:14 -0400732 // set triangularization to get best interpolation of color
733 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400734 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
735 fPositions.count() - 2, fPositions.count() - 1);
Jim Van Verthda965502017-04-11 15:29:14 -0400736 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400737 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
738 fPrevUmbraIndex, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -0400739 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500740
741 // if transparent, add point to first one in array and add to center fan
742 if (fTransparent) {
743 fPositions[0] += nextPoint;
744 ++fCentroidCount;
745
Jim Van Verth872da6b2018-04-10 11:24:11 -0400746 this->appendTriangle(0, fPrevUmbraIndex, fPositions.count() - 2);
Jim Van Verthbce74962017-01-25 09:39:46 -0500747 }
748
Brian Salomon66085ed2017-02-02 15:39:34 -0500749 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500750 fPrevPoint = nextPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400751 fPrevOutset = outsetNormal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500752}
Jim Van Verth91af7272017-01-27 14:15:54 -0500753
754///////////////////////////////////////////////////////////////////////////////////////////////////
755
Brian Salomonaff27a22017-02-06 15:47:44 -0500756class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500757public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500758 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400759 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400760 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500761
Brian Salomon958fbc42017-01-30 17:01:28 -0500762private:
Brian Salomonab664fa2017-03-24 16:07:20 +0000763 void computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400764 const SkMatrix& shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000765 void computeClipVectorsAndTestCentroid();
Brian Salomon66085ed2017-02-02 15:39:34 -0500766 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
Brian Salomonab664fa2017-03-24 16:07:20 +0000767 int getClosestUmbraPoint(const SkPoint& point);
Brian Salomon958fbc42017-01-30 17:01:28 -0500768
Jim Van Verth872da6b2018-04-10 11:24:11 -0400769 bool computeConvexShadow(SkScalar radius);
770 bool computeConcaveShadow(SkScalar radius);
771
Jim Van Vertha84898d2017-02-06 13:38:23 -0500772 void handleLine(const SkPoint& p) override;
Jim Van Verthb55eb282017-07-18 14:13:45 -0400773 bool handlePolyPoint(const SkPoint& p);
Brian Salomon958fbc42017-01-30 17:01:28 -0500774
775 void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
Jim Van Verth8c2de8f2018-05-14 11:20:58 -0400776 bool addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex);
Jim Van Verthda965502017-04-11 15:29:14 -0400777 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
Jim Van Verthf507c282018-05-11 10:48:20 -0400778 void addToClip(const SkVector& nextPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400779
780 SkScalar offset(SkScalar z) {
781 float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
782 return fLightRadius*zRatio;
783 }
784
785 SkScalar fLightZ;
786 SkScalar fLightRadius;
787 SkScalar fOffsetAdjust;
Brian Salomon958fbc42017-01-30 17:01:28 -0500788
Brian Salomon958fbc42017-01-30 17:01:28 -0500789 SkTDArray<SkPoint> fClipPolygon;
Brian Salomon66085ed2017-02-02 15:39:34 -0500790 SkTDArray<SkVector> fClipVectors;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500791 SkPoint fCentroid;
Brian Salomonab664fa2017-03-24 16:07:20 +0000792 SkScalar fArea;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500793
Brian Salomonab664fa2017-03-24 16:07:20 +0000794 SkTDArray<SkPoint> fPathPolygon;
795 SkTDArray<SkPoint> fUmbraPolygon;
796 int fCurrClipPoint;
797 int fCurrUmbraPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -0500798 bool fPrevUmbraOutside;
799 bool fFirstUmbraOutside;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500800 bool fValidUmbra;
Brian Salomon958fbc42017-01-30 17:01:28 -0500801
Brian Salomonaff27a22017-02-06 15:47:44 -0500802 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -0500803};
804
Jim Van Vertha84898d2017-02-06 13:38:23 -0500805SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400806 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -0400807 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -0400808 bool transparent)
Jim Van Verthe308a122017-05-08 14:19:30 -0400809 : INHERITED(zPlaneParams, transparent)
Jim Van Verthda965502017-04-11 15:29:14 -0400810 , fLightZ(lightPos.fZ)
811 , fLightRadius(lightRadius)
812 , fOffsetAdjust(0)
813 , fCurrClipPoint(0)
814 , fPrevUmbraOutside(false)
815 , fFirstUmbraOutside(false)
816 , fValidUmbra(true) {
817
818 // make sure we're not below the canvas plane
819 if (this->setZOffset(path.getBounds(), ctm.hasPerspective())) {
820 // Adjust light height and radius
821 fLightRadius *= (fLightZ + fZOffset) / fLightZ;
822 fLightZ += fZOffset;
823 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400824
825 // Set radius and colors
Jim Van Verthb4366552017-03-27 14:25:29 -0400826 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verthe308a122017-05-08 14:19:30 -0400827 SkScalar occluderHeight = this->heightFunc(center.fX, center.fY) + fZOffset;
Jim Van Verth060d9822017-05-04 09:58:17 -0400828 fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
829 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400830
Jim Van Verth1af03d42017-07-31 09:34:58 -0400831 // Compute the blur radius, scale and translation for the spot shadow.
832 SkScalar radius;
Jim Van Verthda965502017-04-11 15:29:14 -0400833 SkMatrix shadowTransform;
834 if (!ctm.hasPerspective()) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400835 SkScalar scale;
836 SkVector translate;
837 SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, fLightZ,
838 lightRadius, &radius, &scale, &translate);
Jim Van Verthda965502017-04-11 15:29:14 -0400839 shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
840 } else {
841 // For perspective, we have a scale, a z-shear, and another projective divide --
842 // this varies at each point so we can't use an affine transform.
843 // We'll just apply this to each generated point in turn.
844 shadowTransform.reset();
845 // Also can't cull the center (for now).
846 fTransparent = true;
Jim Van Verth1af03d42017-07-31 09:34:58 -0400847 radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
Jim Van Verthda965502017-04-11 15:29:14 -0400848 }
Jim Van Verth1af03d42017-07-31 09:34:58 -0400849 fRadius = radius;
Jim Van Verthda965502017-04-11 15:29:14 -0400850 SkMatrix fullTransform = SkMatrix::Concat(shadowTransform, ctm);
851
852 // Set up our reverse mapping
Jim Van Verth7c8008c2018-02-07 15:02:50 -0500853 if (!this->setTransformedHeightFunc(fullTransform)) {
854 return;
855 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400856
Brian Salomonab664fa2017-03-24 16:07:20 +0000857 // TODO: calculate these reserves better
Brian Salomon66085ed2017-02-02 15:39:34 -0500858 // Penumbra ring: 3*numPts
859 // Umbra ring: numPts
Jim Van Verth91af7272017-01-27 14:15:54 -0500860 // Inner ring: numPts
Brian Salomon66085ed2017-02-02 15:39:34 -0500861 fPositions.setReserve(5 * path.countPoints());
862 fColors.setReserve(5 * path.countPoints());
863 // Penumbra ring: 12*numPts
864 // Umbra ring: 3*numPts
865 fIndices.setReserve(15 * path.countPoints());
Brian Salomone7c85c42017-03-24 16:00:35 +0000866 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +0000867
868 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verthda965502017-04-11 15:29:14 -0400869 this->computeClipAndPathPolygons(path, ctm, shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000870 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3) {
Brian Salomon66085ed2017-02-02 15:39:34 -0500871 return;
872 }
Brian Salomon66085ed2017-02-02 15:39:34 -0500873
Jim Van Verthf507c282018-05-11 10:48:20 -0400874 // compute vectors for clip tests
875 this->computeClipVectorsAndTestCentroid();
876
Brian Salomonab664fa2017-03-24 16:07:20 +0000877 // check to see if umbra collapses
Jim Van Verthf507c282018-05-11 10:48:20 -0400878 if (fIsConvex) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400879 SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
880 fPathPolygon[0],
881 fPathPolygon[1]);
882 SkRect bounds;
883 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
884 for (int i = 1; i < fPathPolygon.count(); ++i) {
885 int j = i + 1;
886 if (i == fPathPolygon.count() - 1) {
887 j = 0;
888 }
889 SkPoint currPoint = fPathPolygon[i];
890 SkPoint nextPoint = fPathPolygon[j];
891 SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
892 nextPoint);
893 if (distSq < minDistSq) {
894 minDistSq = distSq;
895 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000896 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400897 static constexpr auto kTolerance = 1.0e-2f;
898 if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
899 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
900 SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
901 fOffsetAdjust = newRadius - radius;
902 SkScalar ratio = 128 * (newRadius + radius) / radius;
903 // they aren't PMColors, but the interpolation algorithm is the same
904 fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
905 radius = newRadius;
Brian Salomonab664fa2017-03-24 16:07:20 +0000906 }
907 }
Jim Van Verth91af7272017-01-27 14:15:54 -0500908
Jim Van Verth872da6b2018-04-10 11:24:11 -0400909 if (ctm.hasPerspective()) {
910 for (int i = 0; i < fPositions.count(); ++i) {
911 SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
912 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
913 fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
914 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000915 }
916
Jim Van Verthf507c282018-05-11 10:48:20 -0400917 if (fIsConvex) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400918 if (!this->computeConvexShadow(radius)) {
Jim Van Verthb55eb282017-07-18 14:13:45 -0400919 return;
920 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400921 } else {
Jim Van Verthf507c282018-05-11 10:48:20 -0400922 // For now
923 return;
924 //if (!this->computeConcaveShadow(radius)) {
925 // return;
926 //}
Jim Van Verth91af7272017-01-27 14:15:54 -0500927 }
Jim Van Verthda965502017-04-11 15:29:14 -0400928
929 if (ctm.hasPerspective()) {
930 for (int i = 0; i < fPositions.count(); ++i) {
931 SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
932 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
933 fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
934 fPositions[i].fY = (fPositions[i].fY*fLightZ - lightPos.fY*pathZ)*factor;
935 }
936#ifdef DRAW_CENTROID
937 SkScalar pathZ = fTransformedHeightFunc(fCentroid);
938 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
939 fCentroid.fX = (fCentroid.fX*fLightZ - lightPos.fX*pathZ)*factor;
940 fCentroid.fY = (fCentroid.fY*fLightZ - lightPos.fY*pathZ)*factor;
941#endif
942 }
943#ifdef DRAW_CENTROID
944 *fPositions.push() = fCentroid + SkVector::Make(-2, -2);
945 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
946 *fPositions.push() = fCentroid + SkVector::Make(2, -2);
947 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
948 *fPositions.push() = fCentroid + SkVector::Make(-2, 2);
949 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
950 *fPositions.push() = fCentroid + SkVector::Make(2, 2);
951 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
952
Jim Van Verth872da6b2018-04-10 11:24:11 -0400953 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
954 fPositions.count() - 4, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -0400955#endif
956
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500957 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -0500958}
959
Jim Van Verthf507c282018-05-11 10:48:20 -0400960void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
961 if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count()-1])) {
962 *fClipPolygon.push() = point;
963 }
964}
965
Brian Salomonab664fa2017-03-24 16:07:20 +0000966void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400967 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000968
969 fPathPolygon.setReserve(path.countPoints());
970
971 // Walk around the path and compute clip polygon and path polygon.
972 // Will also accumulate sum of areas for centroid.
973 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -0500974 SkPath::Iter iter(path, true);
975 SkPoint pts[4];
976 SkPath::Verb verb;
977
Jim Van Verth91af7272017-01-27 14:15:54 -0500978 fClipPolygon.reset();
979
Brian Salomonab664fa2017-03-24 16:07:20 +0000980 // init centroid
981 fCentroid = SkPoint::Make(0, 0);
982 fArea = 0;
983
Brian Salomon66085ed2017-02-02 15:39:34 -0500984 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +0000985 static constexpr SkScalar kA = 0.32495117187f;
986 static constexpr SkScalar kB = 0.44311523437f;
987 static constexpr SkScalar kC = 0.20141601562f;
988 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -0500989
990 SkPoint curvePoint;
991 SkScalar w;
Jim Van Verth91af7272017-01-27 14:15:54 -0500992 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
993 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -0500994 case SkPath::kLine_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500995 ctm.mapPoints(&pts[1], 1);
Jim Van Verthf507c282018-05-11 10:48:20 -0400996 this->addToClip(pts[1]);
Brian Salomonab664fa2017-03-24 16:07:20 +0000997 this->INHERITED::handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -0500998 break;
999 case SkPath::kQuad_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001000 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001001 // point at t = 1/2
1002 curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
1003 curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001004 this->addToClip(curvePoint);
1005 this->addToClip(pts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001006 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001007 break;
1008 case SkPath::kConic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001009 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001010 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -05001011 // point at t = 1/2
Brian Salomon66085ed2017-02-02 15:39:34 -05001012 curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
1013 curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
1014 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
Jim Van Verthf507c282018-05-11 10:48:20 -04001015 this->addToClip(curvePoint);
1016 this->addToClip(pts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001017 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -05001018 break;
1019 case SkPath::kCubic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001020 ctm.mapPoints(pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -05001021 // point at t = 5/16
1022 curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
1023 curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001024 this->addToClip(curvePoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001025 // point at t = 11/16
1026 curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
1027 curvePoint.fY = kD*pts[0].fY + kC*pts[1].fY + kB*pts[2].fY + kA*pts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001028 this->addToClip(curvePoint);
1029 this->addToClip(pts[3]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001030 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001031 break;
Brian Salomonab664fa2017-03-24 16:07:20 +00001032 case SkPath::kMove_Verb:
Jim Van Verth91af7272017-01-27 14:15:54 -05001033 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +00001034 case SkPath::kDone_Verb:
Jim Van Verth91af7272017-01-27 14:15:54 -05001035 break;
1036 default:
1037 SkDEBUGFAIL("unknown verb");
1038 }
1039 }
1040
Brian Salomonab664fa2017-03-24 16:07:20 +00001041 // finish centroid
1042 if (fPathPolygon.count() > 0) {
1043 SkPoint currPoint = fPathPolygon[fPathPolygon.count() - 1];
1044 SkPoint nextPoint = fPathPolygon[0];
1045 SkScalar quadArea = currPoint.cross(nextPoint);
1046 fCentroid.fX += (currPoint.fX + nextPoint.fX) * quadArea;
1047 fCentroid.fY += (currPoint.fY + nextPoint.fY) * quadArea;
1048 fArea += quadArea;
Mike Reed27575e82018-05-17 10:11:31 -04001049 fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
Brian Salomonab664fa2017-03-24 16:07:20 +00001050 }
1051
1052 fCurrClipPoint = fClipPolygon.count() - 1;
Brian Salomon66085ed2017-02-02 15:39:34 -05001053}
1054
Brian Salomonab664fa2017-03-24 16:07:20 +00001055void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
Brian Salomon66085ed2017-02-02 15:39:34 -05001056 SkASSERT(fClipPolygon.count() >= 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001057
Brian Salomonab664fa2017-03-24 16:07:20 +00001058 // init clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001059 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
Jim Van Verthf507c282018-05-11 10:48:20 -04001060 SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
1061 SkScalar winding = v0.cross(v1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001062 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001063
1064 // init centroid check
1065 bool hiddenCentroid = true;
Jim Van Verthf507c282018-05-11 10:48:20 -04001066 v1 = fCentroid - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001067 SkScalar initCross = v0.cross(v1);
1068
1069 for (int p = 1; p < fClipPolygon.count(); ++p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001070 // add to clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001071 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
1072 *fClipVectors.push() = v0;
Jim Van Verthf507c282018-05-11 10:48:20 -04001073 v1 = fClipPolygon[(p + 2) % fClipPolygon.count()] - fClipPolygon[p];
1074 if (winding*v0.cross(v1) < 0) {
1075 fIsConvex = false;
1076 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001077 // Determine if transformed centroid is inside clipPolygon.
Brian Salomonab664fa2017-03-24 16:07:20 +00001078 v1 = fCentroid - fClipPolygon[p];
Brian Salomon66085ed2017-02-02 15:39:34 -05001079 if (initCross*v0.cross(v1) <= 0) {
1080 hiddenCentroid = false;
1081 }
1082 }
1083 SkASSERT(fClipVectors.count() == fClipPolygon.count());
1084
Brian Salomonab664fa2017-03-24 16:07:20 +00001085 fTransparent = fTransparent || !hiddenCentroid;
Brian Salomon66085ed2017-02-02 15:39:34 -05001086}
1087
1088bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
1089 SkPoint* clipPoint) {
1090 SkVector segmentVector = centroid - umbraPoint;
1091
Brian Salomonab664fa2017-03-24 16:07:20 +00001092 int startClipPoint = fCurrClipPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001093 do {
Brian Salomonab664fa2017-03-24 16:07:20 +00001094 SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
1095 SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
Brian Salomon66085ed2017-02-02 15:39:34 -05001096 SkScalar t_num = dp.cross(segmentVector);
1097 // if line segments are nearly parallel
1098 if (SkScalarNearlyZero(denom)) {
1099 // and collinear
1100 if (SkScalarNearlyZero(t_num)) {
1101 return false;
1102 }
1103 // otherwise are separate, will try the next poly segment
1104 // else if crossing lies within poly segment
1105 } else if (t_num >= 0 && t_num <= denom) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001106 SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
Brian Salomon66085ed2017-02-02 15:39:34 -05001107 // if umbra point is inside the clip polygon
Jim Van Verthda965502017-04-11 15:29:14 -04001108 if (s_num >= 0 && s_num <= denom) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001109 segmentVector *= s_num/denom;
1110 *clipPoint = umbraPoint + segmentVector;
1111 return true;
1112 }
1113 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001114 fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
1115 } while (fCurrClipPoint != startClipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001116
1117 return false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001118}
1119
Brian Salomonab664fa2017-03-24 16:07:20 +00001120int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
Cary Clarkdf429f32017-11-08 11:44:31 -05001121 SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraPoint]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001122 int index = fCurrUmbraPoint;
1123 int dir = 1;
1124 int next = (index + dir) % fUmbraPolygon.count();
1125
1126 // init travel direction
Cary Clarkdf429f32017-11-08 11:44:31 -05001127 SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001128 if (distance < minDistance) {
1129 index = next;
1130 minDistance = distance;
1131 } else {
1132 dir = fUmbraPolygon.count()-1;
1133 }
1134
1135 // iterate until we find a point that increases the distance
1136 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001137 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001138 while (distance < minDistance) {
1139 index = next;
1140 minDistance = distance;
1141 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001142 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001143 }
1144
1145 fCurrUmbraPoint = index;
1146 return index;
1147}
1148
Jim Van Verth872da6b2018-04-10 11:24:11 -04001149bool SkSpotShadowTessellator::computeConvexShadow(SkScalar radius) {
1150 // generate inner ring
1151 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1152 &fUmbraPolygon)) {
1153 // this shouldn't happen, but just in case we'll inset using the centroid
1154 fValidUmbra = false;
1155 }
1156
1157 // walk around the path polygon, generate outer ring and connect to inner ring
1158 if (fTransparent) {
1159 *fPositions.push() = fCentroid;
1160 *fColors.push() = fUmbraColor;
1161 }
1162 fCurrUmbraPoint = 0;
1163 for (int i = 0; i < fPathPolygon.count(); ++i) {
1164 if (!this->handlePolyPoint(fPathPolygon[i])) {
1165 return false;
1166 }
1167 }
1168
1169 if (!this->indexCount()) {
1170 return false;
1171 }
1172
1173 // finish up the final verts
1174 SkVector normal;
1175 if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
1176 normal *= fRadius;
1177 this->addArc(normal, true);
1178
1179 // add to center fan
1180 if (fTransparent) {
1181 this->appendTriangle(0, fPrevUmbraIndex, fFirstVertexIndex);
1182 // or to clip ring
1183 } else {
1184 if (fFirstUmbraOutside) {
1185 this->appendTriangle(fPrevUmbraIndex, fFirstVertexIndex, fFirstVertexIndex + 1);
1186 if (fPrevUmbraOutside) {
1187 // fill out quad
1188 this->appendTriangle(fPrevUmbraIndex, fFirstVertexIndex + 1,
1189 fPrevUmbraIndex + 1);
1190 }
1191 } else if (fPrevUmbraOutside) {
1192 // add tri
1193 this->appendTriangle(fPrevUmbraIndex, fFirstVertexIndex, fPrevUmbraIndex + 1);
1194 }
1195 }
1196
1197 // add final edge
1198 *fPositions.push() = fFirstPoint + normal;
1199 *fColors.push() = fPenumbraColor;
1200
1201 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 2,
1202 fFirstVertexIndex, fPositions.count() - 1);
1203
1204 fPrevOutset = normal;
1205 }
1206
1207 // final fan
1208 if (fPositions.count() >= 3) {
1209 fPrevUmbraIndex = fFirstVertexIndex;
1210 fPrevPoint = fFirstPoint;
1211 if (this->addArc(fFirstOutset, false)) {
1212 if (fFirstUmbraOutside) {
1213 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1214 fFirstVertexIndex + 2);
1215 } else {
1216 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1217 fFirstVertexIndex + 1);
1218 }
1219 } else {
1220 // no arc added, fix up by setting first penumbra point position to last one
1221 if (fFirstUmbraOutside) {
1222 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
1223 } else {
1224 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
1225 }
1226 }
1227 }
1228
1229 return true;
1230}
1231
1232bool SkSpotShadowTessellator::computeConcaveShadow(SkScalar radius) {
1233 // TODO: remove when we support filling the penumbra
1234 if (fTransparent) {
1235 return false;
1236 }
1237
1238 // generate inner ring
1239 SkTDArray<int> umbraIndices;
1240 umbraIndices.setReserve(fPathPolygon.count());
1241 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1242 &fUmbraPolygon, &umbraIndices)) {
1243 // TODO: figure out how to handle this case
1244 return false;
1245 }
1246
1247 // generate outer ring
1248 SkTDArray<SkPoint> penumbraPolygon;
1249 SkTDArray<int> penumbraIndices;
1250 penumbraPolygon.setReserve(fUmbraPolygon.count());
1251 penumbraIndices.setReserve(fUmbraPolygon.count());
1252 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -radius,
1253 &penumbraPolygon, &penumbraIndices)) {
1254 // TODO: figure out how to handle this case
1255 return false;
1256 }
1257
1258 if (!fUmbraPolygon.count() || !penumbraPolygon.count()) {
1259 return false;
1260 }
1261
1262 // attach the rings together
1263
1264 // find minimum indices
1265 int minIndex = 0;
1266 int min = penumbraIndices[0];
1267 for (int i = 1; i < penumbraIndices.count(); ++i) {
1268 if (penumbraIndices[i] < min) {
1269 min = penumbraIndices[i];
1270 minIndex = i;
1271 }
1272 }
1273 int currPenumbra = minIndex;
1274
1275 minIndex = 0;
1276 min = umbraIndices[0];
1277 for (int i = 1; i < umbraIndices.count(); ++i) {
1278 if (umbraIndices[i] < min) {
1279 min = umbraIndices[i];
1280 minIndex = i;
1281 }
1282 }
1283 int currUmbra = minIndex;
1284
1285 // now find a case where the indices are equal (there should be at least one)
1286 int maxPenumbraIndex = fPathPolygon.count()-1;
1287 int maxUmbraIndex = fPathPolygon.count()-1;
1288 while (penumbraIndices[currPenumbra] != umbraIndices[currUmbra]) {
1289 if (penumbraIndices[currPenumbra] < umbraIndices[currUmbra]) {
1290 penumbraIndices[currPenumbra] += fPathPolygon.count();
1291 maxPenumbraIndex = penumbraIndices[currPenumbra];
1292 currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
1293 } else {
1294 umbraIndices[currUmbra] += fPathPolygon.count();
1295 maxUmbraIndex = umbraIndices[currUmbra];
1296 currUmbra = (currUmbra + 1) % fUmbraPolygon.count();
1297 }
1298 }
1299
1300 *fPositions.push() = penumbraPolygon[currPenumbra];
1301 *fColors.push() = fPenumbraColor;
1302 int prevPenumbraIndex = 0;
1303 *fPositions.push() = fUmbraPolygon[currUmbra];
1304 *fColors.push() = fUmbraColor;
1305 fPrevUmbraIndex = 1;
1306
1307 int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
1308 int nextUmbra = (currUmbra + 1) % fUmbraPolygon.count();
1309 while (penumbraIndices[nextPenumbra] <= maxPenumbraIndex ||
1310 umbraIndices[nextUmbra] <= maxUmbraIndex) {
1311
1312 if (umbraIndices[nextUmbra] == penumbraIndices[nextPenumbra]) {
1313 // advance both one step
1314 *fPositions.push() = penumbraPolygon[nextPenumbra];
1315 *fColors.push() = fPenumbraColor;
1316 int currPenumbraIndex = fPositions.count() - 1;
1317
1318 *fPositions.push() = fUmbraPolygon[nextUmbra];
1319 *fColors.push() = fUmbraColor;
1320 int currUmbraIndex = fPositions.count() - 1;
1321
1322 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
1323 fPrevUmbraIndex, currUmbraIndex);
1324
1325 prevPenumbraIndex = currPenumbraIndex;
1326 penumbraIndices[currPenumbra] += fPathPolygon.count();
1327 currPenumbra = nextPenumbra;
1328 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
1329
1330 fPrevUmbraIndex = currUmbraIndex;
1331 umbraIndices[currUmbra] += fPathPolygon.count();
1332 currUmbra = nextUmbra;
1333 nextUmbra = (currUmbra + 1) % fUmbraPolygon.count();
1334 }
1335
1336 while (penumbraIndices[nextPenumbra] < umbraIndices[nextUmbra] &&
1337 penumbraIndices[nextPenumbra] <= maxPenumbraIndex) {
1338 // fill out penumbra arc
1339 *fPositions.push() = penumbraPolygon[nextPenumbra];
1340 *fColors.push() = fPenumbraColor;
1341 int currPenumbraIndex = fPositions.count() - 1;
1342
1343 this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
1344
1345 prevPenumbraIndex = currPenumbraIndex;
1346 // this ensures the ordering when we wrap around
1347 penumbraIndices[currPenumbra] += fPathPolygon.count();
1348 currPenumbra = nextPenumbra;
1349 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
1350 }
1351
1352 while (umbraIndices[nextUmbra] < penumbraIndices[nextPenumbra] &&
1353 umbraIndices[nextUmbra] <= maxUmbraIndex) {
1354 // fill out umbra arc
1355 *fPositions.push() = fUmbraPolygon[nextUmbra];
1356 *fColors.push() = fUmbraColor;
1357 int currUmbraIndex = fPositions.count() - 1;
1358
1359 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
1360
1361 fPrevUmbraIndex = currUmbraIndex;
1362 // this ensures the ordering when we wrap around
1363 umbraIndices[currUmbra] += fPathPolygon.count();
1364 currUmbra = nextUmbra;
1365 nextUmbra = (currUmbra + 1) % fUmbraPolygon.count();
1366 }
1367 }
1368 // finish up by advancing both one step
1369 *fPositions.push() = penumbraPolygon[nextPenumbra];
1370 *fColors.push() = fPenumbraColor;
1371 int currPenumbraIndex = fPositions.count() - 1;
1372
1373 *fPositions.push() = fUmbraPolygon[nextUmbra];
1374 *fColors.push() = fUmbraColor;
1375 int currUmbraIndex = fPositions.count() - 1;
1376
1377 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
1378 fPrevUmbraIndex, currUmbraIndex);
1379
1380 if (fTransparent) {
1381 // TODO: fill penumbra
1382 }
1383
1384 return true;
1385}
1386
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001387void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
Jim Van Verth91af7272017-01-27 14:15:54 -05001388 SkPoint* pts, int count) {
1389 // TODO: vectorize
1390 for (int i = 0; i < count; ++i) {
1391 pts[i] *= scale;
1392 pts[i] += xlate;
1393 }
1394}
1395
Jim Van Verthb55eb282017-07-18 14:13:45 -04001396static bool is_collinear(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
1397 return (SkScalarNearlyZero(perp_dot(p0, p1, p2)));
Brian Salomonab664fa2017-03-24 16:07:20 +00001398}
1399
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001400void SkSpotShadowTessellator::handleLine(const SkPoint& p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001401 // remove coincident points and add to centroid
1402 if (fPathPolygon.count() > 0) {
1403 const SkPoint& lastPoint = fPathPolygon[fPathPolygon.count() - 1];
1404 if (duplicate_pt(p, lastPoint)) {
1405 return;
1406 }
1407 SkScalar quadArea = lastPoint.cross(p);
1408 fCentroid.fX += (p.fX + lastPoint.fX) * quadArea;
1409 fCentroid.fY += (p.fY + lastPoint.fY) * quadArea;
1410 fArea += quadArea;
1411 }
1412
1413 // try to remove collinear points
1414 if (fPathPolygon.count() > 1 && is_collinear(fPathPolygon[fPathPolygon.count()-2],
1415 fPathPolygon[fPathPolygon.count()-1],
1416 p)) {
1417 fPathPolygon[fPathPolygon.count() - 1] = p;
1418 } else {
1419 *fPathPolygon.push() = p;
1420 }
1421}
1422
Jim Van Verthb55eb282017-07-18 14:13:45 -04001423bool SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001424 if (fInitPoints.count() < 2) {
1425 *fInitPoints.push() = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001426 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001427 }
1428
1429 if (fInitPoints.count() == 2) {
1430 // determine if cw or ccw
Jim Van Verthb55eb282017-07-18 14:13:45 -04001431 SkScalar perpDot = perp_dot(fInitPoints[0], fInitPoints[1], p);
Jim Van Verth91af7272017-01-27 14:15:54 -05001432 if (SkScalarNearlyZero(perpDot)) {
1433 // nearly parallel, just treat as straight line and continue
1434 fInitPoints[1] = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001435 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001436 }
1437
1438 // if perpDot > 0, winding is ccw
1439 fDirection = (perpDot > 0) ? -1 : 1;
1440
1441 // add first quad
Jim Van Verth76387852017-05-16 16:55:16 -04001442 if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &fFirstOutset)) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001443 // first two points are incident, make the third point the second and continue
1444 fInitPoints[1] = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001445 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001446 }
1447
Jim Van Verth76387852017-05-16 16:55:16 -04001448 fFirstOutset *= fRadius;
Jim Van Verth91af7272017-01-27 14:15:54 -05001449 fFirstPoint = fInitPoints[0];
Jim Van Verth76387852017-05-16 16:55:16 -04001450 fFirstVertexIndex = fPositions.count();
1451 fPrevOutset = fFirstOutset;
Jim Van Verth91af7272017-01-27 14:15:54 -05001452 fPrevPoint = fFirstPoint;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001453 fPrevUmbraIndex = -1;
Jim Van Verth91af7272017-01-27 14:15:54 -05001454
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001455 this->addInnerPoint(fFirstPoint, &fPrevUmbraIndex);
Brian Salomon66085ed2017-02-02 15:39:34 -05001456
1457 if (!fTransparent) {
1458 SkPoint clipPoint;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001459 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
1460 fCentroid, &clipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001461 if (isOutside) {
1462 *fPositions.push() = clipPoint;
1463 *fColors.push() = fUmbraColor;
1464 }
1465 fPrevUmbraOutside = isOutside;
1466 fFirstUmbraOutside = isOutside;
1467 }
1468
Jim Van Verth76387852017-05-16 16:55:16 -04001469 SkPoint newPoint = fFirstPoint + fFirstOutset;
Jim Van Verth91af7272017-01-27 14:15:54 -05001470 *fPositions.push() = newPoint;
1471 *fColors.push() = fPenumbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -04001472 this->addEdge(fInitPoints[1], fFirstOutset);
Jim Van Verth91af7272017-01-27 14:15:54 -05001473
1474 // to ensure we skip this block next time
1475 *fInitPoints.push() = p;
1476 }
1477
Jim Van Verthb55eb282017-07-18 14:13:45 -04001478 // if concave, abort
1479 SkScalar perpDot = perp_dot(fInitPoints[1], fInitPoints[2], p);
1480 if (fDirection*perpDot > 0) {
1481 return false;
1482 }
1483
Jim Van Verth91af7272017-01-27 14:15:54 -05001484 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -04001485 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
1486 normal *= fRadius;
1487 this->addArc(normal, true);
1488 this->addEdge(p, normal);
Jim Van Verthb55eb282017-07-18 14:13:45 -04001489 fInitPoints[1] = fInitPoints[2];
1490 fInitPoints[2] = p;
Jim Van Verth91af7272017-01-27 14:15:54 -05001491 }
Jim Van Verthb55eb282017-07-18 14:13:45 -04001492
1493 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001494}
1495
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001496bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001497 SkPoint umbraPoint;
1498 if (!fValidUmbra) {
1499 SkVector v = fCentroid - pathPoint;
1500 v *= 0.95f;
1501 umbraPoint = pathPoint + v;
Jim Van Verth91af7272017-01-27 14:15:54 -05001502 } else {
Brian Salomonab664fa2017-03-24 16:07:20 +00001503 umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
Jim Van Verth91af7272017-01-27 14:15:54 -05001504 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001505
Jim Van Verth91af7272017-01-27 14:15:54 -05001506 fPrevPoint = pathPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001507
1508 // merge "close" points
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001509 if (fPrevUmbraIndex == -1 ||
Brian Salomonab664fa2017-03-24 16:07:20 +00001510 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001511 // if we've wrapped around, don't add a new point
1512 if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
1513 *currUmbraIndex = fFirstVertexIndex;
1514 } else {
1515 *currUmbraIndex = fPositions.count();
1516 *fPositions.push() = umbraPoint;
1517 *fColors.push() = fUmbraColor;
1518 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001519 return false;
1520 } else {
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001521 *currUmbraIndex = fPrevUmbraIndex;
Brian Salomonab664fa2017-03-24 16:07:20 +00001522 return true;
1523 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001524}
1525
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001526void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001527 // add next umbra point
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001528 int currUmbraIndex;
1529 bool duplicate = this->addInnerPoint(nextPoint, &currUmbraIndex);
1530 int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
1531 ? fPositions.count()-1
1532 : fPositions.count()-2;
Brian Salomonab664fa2017-03-24 16:07:20 +00001533 if (!duplicate) {
1534 // add to center fan if transparent or centroid showing
1535 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001536 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001537 // otherwise add to clip ring
1538 } else {
Brian Salomon66085ed2017-02-02 15:39:34 -05001539 SkPoint clipPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001540 bool isOutside = this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
1541 &clipPoint);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001542
Brian Salomon66085ed2017-02-02 15:39:34 -05001543 if (isOutside) {
1544 *fPositions.push() = clipPoint;
1545 *fColors.push() = fUmbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001546 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001547 if (fPrevUmbraOutside) {
1548 // fill out quad
Jim Van Verth872da6b2018-04-10 11:24:11 -04001549 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
1550 fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001551 }
1552 } else if (fPrevUmbraOutside) {
1553 // add tri
Jim Van Verth872da6b2018-04-10 11:24:11 -04001554 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001555 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001556
Brian Salomon66085ed2017-02-02 15:39:34 -05001557 fPrevUmbraOutside = isOutside;
1558 }
1559 }
1560
1561 // add next penumbra point and quad
Jim Van Verth91af7272017-01-27 14:15:54 -05001562 SkPoint newPoint = nextPoint + nextNormal;
1563 *fPositions.push() = newPoint;
1564 *fColors.push() = fPenumbraColor;
1565
Brian Salomonab664fa2017-03-24 16:07:20 +00001566 if (!duplicate) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001567 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001568 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001569 this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
Jim Van Verth91af7272017-01-27 14:15:54 -05001570
Brian Salomon66085ed2017-02-02 15:39:34 -05001571 fPrevUmbraIndex = currUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -04001572 fPrevOutset = nextNormal;
Jim Van Verth91af7272017-01-27 14:15:54 -05001573}
Brian Salomon958fbc42017-01-30 17:01:28 -05001574
1575///////////////////////////////////////////////////////////////////////////////////////////////////
1576
Brian Salomonaff27a22017-02-06 15:47:44 -05001577sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001578 const SkPoint3& zPlane, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001579 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001580 return nullptr;
1581 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001582 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001583 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001584}
1585
Brian Salomonaff27a22017-02-06 15:47:44 -05001586sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001587 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001588 SkScalar lightRadius, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001589 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() ||
Mike Reed9d5c6742018-03-06 10:31:27 -05001590 !lightPos.isFinite() || !SkScalarIsFinite(lightRadius) || !(lightRadius > 0)) {
1591 return nullptr;
1592 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001593 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001594 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001595}