blob: 11e8f2d5c67ac5cbbee14c643d2d74ed4c92c0ab [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"
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 Verthe308a122017-05-08 14:19:30 -040062 SkScalar heightFunc(SkScalar x, SkScalar y) {
63 return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
64 }
65
66 SkPoint3 fZPlaneParams;
Jim Van Verthda965502017-04-11 15:29:14 -040067 std::function<SkScalar(const SkPoint&)> fTransformedHeightFunc;
68 SkScalar fZOffset;
69 // members for perspective height function
Jim Van Verth4c9b8932017-05-15 13:49:21 -040070 SkPoint3 fTransformedZParams;
Jim Van Verthda965502017-04-11 15:29:14 -040071 SkScalar fPartialDeterminants[3];
72
73 // first two points
Brian Salomon958fbc42017-01-30 17:01:28 -050074 SkTDArray<SkPoint> fInitPoints;
75 // temporary buffer
76 SkTDArray<SkPoint> fPointBuffer;
Brian Salomon0dda9cb2017-02-03 10:33:25 -050077
Jim Van Vertha84898d2017-02-06 13:38:23 -050078 SkTDArray<SkPoint> fPositions;
79 SkTDArray<SkColor> fColors;
80 SkTDArray<uint16_t> fIndices;
81
Jim Van Verth76387852017-05-16 16:55:16 -040082 int fFirstVertexIndex;
83 SkVector fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -050084 SkPoint fFirstPoint;
85
Brian Salomon0dda9cb2017-02-03 10:33:25 -050086 bool fSucceeded;
Jim Van Vertha84898d2017-02-06 13:38:23 -050087 bool fTransparent;
88
89 SkColor fUmbraColor;
90 SkColor fPenumbraColor;
91
92 SkScalar fRadius;
93 SkScalar fDirection;
94 int fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -040095 SkVector fPrevOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -050096 SkPoint fPrevPoint;
Brian Salomon958fbc42017-01-30 17:01:28 -050097};
98
Jim Van Verthda965502017-04-11 15:29:14 -040099static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
Jim Van Verthbce74962017-01-25 09:39:46 -0500100 SkVector* newNormal) {
101 SkVector normal;
102 // compute perpendicular
103 normal.fX = p0.fY - p1.fY;
104 normal.fY = p1.fX - p0.fX;
Jim Van Verthda965502017-04-11 15:29:14 -0400105 normal *= dir;
Jim Van Verthbce74962017-01-25 09:39:46 -0500106 if (!normal.normalize()) {
107 return false;
108 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500109 *newNormal = normal;
110 return true;
111}
112
113static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
114 SkScalar* rotSin, SkScalar* rotCos, int* n) {
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400115 const SkScalar kRecipPixelsPerArcSegment = 0.125f;
Jim Van Verthbce74962017-01-25 09:39:46 -0500116
117 SkScalar rCos = v1.dot(v2);
118 SkScalar rSin = v1.cross(v2);
119 SkScalar theta = SkScalarATan2(rSin, rCos);
120
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400121 int steps = SkScalarFloorToInt(r*theta*kRecipPixelsPerArcSegment);
Jim Van Verthbce74962017-01-25 09:39:46 -0500122
123 SkScalar dTheta = theta / steps;
124 *rotSin = SkScalarSinCos(dTheta, rotCos);
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400125 *n = steps;
Jim Van Verthbce74962017-01-25 09:39:46 -0500126}
127
Jim Van Verthe308a122017-05-08 14:19:30 -0400128SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
129 : fZPlaneParams(zPlaneParams)
Jim Van Verthda965502017-04-11 15:29:14 -0400130 , fZOffset(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400131 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500132 , fSucceeded(false)
133 , fTransparent(transparent)
Brian Salomonaff27a22017-02-06 15:47:44 -0500134 , fDirection(1)
135 , fPrevUmbraIndex(-1) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500136 fInitPoints.setReserve(3);
137
138 // child classes will set reserve for positions, colors and indices
139}
140
Jim Van Verthda965502017-04-11 15:29:14 -0400141bool SkBaseShadowTessellator::setZOffset(const SkRect& bounds, bool perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400142 SkScalar minZ = this->heightFunc(bounds.fLeft, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400143 if (perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400144 SkScalar z = this->heightFunc(bounds.fLeft, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400145 if (z < minZ) {
146 minZ = z;
147 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400148 z = this->heightFunc(bounds.fRight, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400149 if (z < minZ) {
150 minZ = z;
151 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400152 z = this->heightFunc(bounds.fRight, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400153 if (z < minZ) {
154 minZ = z;
155 }
156 }
157
158 if (minZ < kMinHeight) {
159 fZOffset = -minZ + kMinHeight;
160 return true;
161 }
162
163 return false;
164}
165
Jim Van Vertha84898d2017-02-06 13:38:23 -0500166// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500167#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500168static const SkScalar kQuadTolerance = 0.2f;
169static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500170#endif
Jim Van Vertha84898d2017-02-06 13:38:23 -0500171static const SkScalar kConicTolerance = 0.5f;
172
Brian Salomonaff27a22017-02-06 15:47:44 -0500173void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500174 m.mapPoints(p, 1);
175 this->handleLine(*p);
176}
177
Brian Salomonaff27a22017-02-06 15:47:44 -0500178void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500179#if SK_SUPPORT_GPU
180 // TODO: Pull PathUtils out of Ganesh?
181 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
182 fPointBuffer.setReserve(maxCount);
183 SkPoint* target = fPointBuffer.begin();
184 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
185 kQuadTolerance, &target, maxCount);
186 fPointBuffer.setCount(count);
187 for (int i = 0; i < count; i++) {
188 this->handleLine(fPointBuffer[i]);
189 }
190#else
191 // for now, just to draw something
192 this->handleLine(pts[1]);
193 this->handleLine(pts[2]);
194#endif
195}
196
Brian Salomonaff27a22017-02-06 15:47:44 -0500197void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500198 m.mapPoints(pts, 3);
199 this->handleQuad(pts);
200}
201
Brian Salomonaff27a22017-02-06 15:47:44 -0500202void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500203 m.mapPoints(pts, 4);
204#if SK_SUPPORT_GPU
205 // TODO: Pull PathUtils out of Ganesh?
206 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
207 fPointBuffer.setReserve(maxCount);
208 SkPoint* target = fPointBuffer.begin();
209 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
210 kCubicTolerance, &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 this->handleLine(pts[3]);
220#endif
221}
222
Brian Salomonaff27a22017-02-06 15:47:44 -0500223void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400224 if (m.hasPerspective()) {
225 w = SkConic::TransformW(pts, w, m);
226 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500227 m.mapPoints(pts, 3);
228 SkAutoConicToQuads quadder;
229 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
230 SkPoint lastPoint = *(quads++);
231 int count = quadder.countQuads();
232 for (int i = 0; i < count; ++i) {
233 SkPoint quadPts[3];
234 quadPts[0] = lastPoint;
235 quadPts[1] = quads[0];
236 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
237 this->handleQuad(quadPts);
238 lastPoint = quadPts[2];
239 quads += 2;
240 }
241}
242
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400243bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500244 // fill in fan from previous quad
245 SkScalar rotSin, rotCos;
246 int numSteps;
Jim Van Verth76387852017-05-16 16:55:16 -0400247 compute_radial_steps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
248 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400249 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400250 SkVector currNormal;
251 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
252 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
253 *fPositions.push() = fPrevPoint + currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500254 *fColors.push() = fPenumbraColor;
255 *fIndices.push() = fPrevUmbraIndex;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500256 *fIndices.push() = fPositions.count() - 1;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400257 *fIndices.push() = fPositions.count() - 2;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500258
Jim Van Verthda965502017-04-11 15:29:14 -0400259 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500260 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400261 if (finishArc && numSteps) {
Jim Van Verthda965502017-04-11 15:29:14 -0400262 *fPositions.push() = fPrevPoint + nextNormal;
263 *fColors.push() = fPenumbraColor;
264 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verthda965502017-04-11 15:29:14 -0400265 *fIndices.push() = fPositions.count() - 1;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400266 *fIndices.push() = fPositions.count() - 2;
Jim Van Verthda965502017-04-11 15:29:14 -0400267 }
Jim Van Verth76387852017-05-16 16:55:16 -0400268 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400269
270 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500271}
272
Jim Van Verthda965502017-04-11 15:29:14 -0400273bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400274 if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400275 fTransformedHeightFunc = [this](const SkPoint& p) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400276 return fZPlaneParams.fZ;
Jim Van Verthda965502017-04-11 15:29:14 -0400277 };
278 } else {
279 SkMatrix ctmInverse;
280 if (!ctm.invert(&ctmInverse)) {
281 return false;
282 }
Jim Van Verthda965502017-04-11 15:29:14 -0400283 // multiply by transpose
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400284 fTransformedZParams = SkPoint3::Make(
Jim Van Verthe308a122017-05-08 14:19:30 -0400285 ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
286 ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
287 ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
288
289 ctmInverse[SkMatrix::kMSkewX] * fZPlaneParams.fX +
290 ctmInverse[SkMatrix::kMScaleY] * fZPlaneParams.fY +
291 ctmInverse[SkMatrix::kMPersp1] * fZPlaneParams.fZ,
292
293 ctmInverse[SkMatrix::kMTransX] * fZPlaneParams.fX +
294 ctmInverse[SkMatrix::kMTransY] * fZPlaneParams.fY +
295 ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
296 );
Jim Van Verthda965502017-04-11 15:29:14 -0400297
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400298 if (ctm.hasPerspective()) {
299 // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
300 // so pre-compute those values that are independent of X and Y.
301 // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
302 fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
303 ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
304 fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
305 ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
306 fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
307 ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
308 SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
309 ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
310 ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
Jim Van Verthda965502017-04-11 15:29:14 -0400311
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400312 // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
313 // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
314 fTransformedZParams.fX *= ctmDeterminant;
315 fTransformedZParams.fY *= ctmDeterminant;
316 fTransformedZParams.fZ *= ctmDeterminant;
Jim Van Verthda965502017-04-11 15:29:14 -0400317
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400318 fTransformedHeightFunc = [this](const SkPoint& p) {
319 SkScalar denom = p.fX * fPartialDeterminants[0] +
320 p.fY * fPartialDeterminants[1] +
321 fPartialDeterminants[2];
322 SkScalar w = SkScalarFastInvert(denom);
323 return fZOffset + w*(fTransformedZParams.fX * p.fX +
324 fTransformedZParams.fY * p.fY +
325 fTransformedZParams.fZ);
326 };
327 } else {
328 fTransformedHeightFunc = [this](const SkPoint& p) {
329 return fZOffset + fTransformedZParams.fX * p.fX +
330 fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
331 };
332 }
Jim Van Verthda965502017-04-11 15:29:14 -0400333 }
334
335 return true;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500336}
337
338
339//////////////////////////////////////////////////////////////////////////////////////////////////
340
Brian Salomonaff27a22017-02-06 15:47:44 -0500341class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500342public:
343 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400344 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500345
346private:
347 void handleLine(const SkPoint& p) override;
Jim Van Verthda965502017-04-11 15:29:14 -0400348 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500349
Jim Van Verthda965502017-04-11 15:29:14 -0400350 static constexpr auto kMaxEdgeLenSqr = 20 * 20;
Jim Van Verth76387852017-05-16 16:55:16 -0400351 static constexpr auto kInsetFactor = -0.5f;
Jim Van Verthda965502017-04-11 15:29:14 -0400352
353 SkScalar offset(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400354 return SkDrawShadowMetrics::AmbientBlurRadius(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400355 }
356 SkColor umbraColor(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400357 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(z));
Jim Van Verth060d9822017-05-04 09:58:17 -0400358 return SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
Jim Van Verthda965502017-04-11 15:29:14 -0400359 }
360
Jim Van Vertha84898d2017-02-06 13:38:23 -0500361 int fCentroidCount;
Jim Van Verth76387852017-05-16 16:55:16 -0400362 bool fSplitFirstEdge;
363 bool fSplitPreviousEdge;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500364
Brian Salomonaff27a22017-02-06 15:47:44 -0500365 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500366};
367
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500368SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500369 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400370 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500371 bool transparent)
Jim Van Verth76387852017-05-16 16:55:16 -0400372 : INHERITED(zPlaneParams, transparent)
373 , fSplitFirstEdge(false)
374 , fSplitPreviousEdge(false) {
Jim Van Verthda965502017-04-11 15:29:14 -0400375 // Set base colors
Jim Van Verth1af03d42017-07-31 09:34:58 -0400376 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(heightFunc(0, 0)));
Jim Van Verthb4366552017-03-27 14:25:29 -0400377 // umbraColor is the interior value, penumbraColor the exterior value.
378 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
379 // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
380 // the final alpha.
Jim Van Verth060d9822017-05-04 09:58:17 -0400381 fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
382 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400383
Jim Van Verthda965502017-04-11 15:29:14 -0400384 // make sure we're not below the canvas plane
385 this->setZOffset(path.getBounds(), ctm.hasPerspective());
386
Jim Van Verth7c8008c2018-02-07 15:02:50 -0500387 if (!this->setTransformedHeightFunc(ctm)) {
388 return;
389 }
Jim Van Verthda965502017-04-11 15:29:14 -0400390
Jim Van Verthbce74962017-01-25 09:39:46 -0500391 // Outer ring: 3*numPts
392 // Middle ring: numPts
393 fPositions.setReserve(4 * path.countPoints());
394 fColors.setReserve(4 * path.countPoints());
395 // Outer ring: 12*numPts
396 // Middle ring: 0
397 fIndices.setReserve(12 * path.countPoints());
398
Jim Van Verthbce74962017-01-25 09:39:46 -0500399 // walk around the path, tessellate and generate outer ring
400 // if original path is transparent, will accumulate sum of points for centroid
401 SkPath::Iter iter(path, true);
402 SkPoint pts[4];
403 SkPath::Verb verb;
404 if (fTransparent) {
405 *fPositions.push() = SkPoint::Make(0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400406 *fColors.push() = fUmbraColor;
Jim Van Verthbce74962017-01-25 09:39:46 -0500407 fCentroidCount = 0;
408 }
409 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
410 switch (verb) {
411 case SkPath::kLine_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500412 this->INHERITED::handleLine(ctm, &pts[1]);
Jim Van Verthbce74962017-01-25 09:39:46 -0500413 break;
414 case SkPath::kQuad_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500415 this->handleQuad(ctm, pts);
Jim Van Verthbce74962017-01-25 09:39:46 -0500416 break;
417 case SkPath::kCubic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500418 this->handleCubic(ctm, pts);
Jim Van Verthbce74962017-01-25 09:39:46 -0500419 break;
420 case SkPath::kConic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500421 this->handleConic(ctm, pts, iter.conicWeight());
Jim Van Verthbce74962017-01-25 09:39:46 -0500422 break;
423 case SkPath::kMove_Verb:
424 case SkPath::kClose_Verb:
425 case SkPath::kDone_Verb:
426 break;
427 }
428 }
429
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500430 if (!this->indexCount()) {
431 return;
432 }
433
Jim Van Verth76387852017-05-16 16:55:16 -0400434 // Finish up
Jim Van Verthbce74962017-01-25 09:39:46 -0500435 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -0400436 if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
437 SkScalar z = fTransformedHeightFunc(fPrevPoint);
438 fRadius = this->offset(z);
439 SkVector scaledNormal(normal);
440 scaledNormal *= fRadius;
441 this->addArc(scaledNormal, true);
Jim Van Verthbce74962017-01-25 09:39:46 -0500442
Jim Van Verth76387852017-05-16 16:55:16 -0400443 // fix-up the last and first umbra points
444 SkVector inset = normal;
445 // adding to an average, so multiply by an additional half
446 inset *= 0.5f*kInsetFactor;
447 fPositions[fPrevUmbraIndex] += inset;
448 fPositions[fFirstVertexIndex] += inset;
449 // we multiply by another half because now we're adding to an average of an average
450 inset *= 0.5f;
451 if (fSplitPreviousEdge) {
452 fPositions[fPrevUmbraIndex - 2] += inset;
453 }
454 if (fSplitFirstEdge) {
455 fPositions[fFirstVertexIndex + 2] += inset;
456 }
457
Jim Van Verthda965502017-04-11 15:29:14 -0400458 // set up for final edge
459 z = fTransformedHeightFunc(fFirstPoint);
460 normal *= this->offset(z);
Jim Van Verthbce74962017-01-25 09:39:46 -0500461
Jim Van Verthda965502017-04-11 15:29:14 -0400462 // make sure we don't end up with a sharp alpha edge along the quad diagonal
Jim Van Verth76387852017-05-16 16:55:16 -0400463 if (fColors[fPrevUmbraIndex] != fColors[fFirstVertexIndex] &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500464 SkPointPriv::DistanceToSqd(fFirstPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400465 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + fPositions[fFirstVertexIndex];
Jim Van Verthda965502017-04-11 15:29:14 -0400466 centerPoint *= 0.5f;
467 *fPositions.push() = centerPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400468 *fColors.push() = SkPMLerp(fColors[fFirstVertexIndex], fColors[fPrevUmbraIndex], 128);
469 centerPoint = fPositions[fPositions.count()-2] + fPositions[fFirstVertexIndex+1];
470 centerPoint *= 0.5f;
471 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400472 *fColors.push() = fPenumbraColor;
473
Jim Van Verth76387852017-05-16 16:55:16 -0400474 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
475 *fIndices.push() = fPrevUmbraIndex;
476 *fIndices.push() = fPositions.count() - 3;
477 *fIndices.push() = fPositions.count() - 2;
Jim Van Verthda965502017-04-11 15:29:14 -0400478
Jim Van Verth76387852017-05-16 16:55:16 -0400479 *fIndices.push() = fPositions.count() - 3;
480 *fIndices.push() = fPositions.count() - 1;
481 *fIndices.push() = fPositions.count() - 2;
482 } else {
483 *fIndices.push() = fPrevUmbraIndex;
484 *fIndices.push() = fPositions.count() - 2;
485 *fIndices.push() = fPositions.count() - 1;
486
487 *fIndices.push() = fPrevUmbraIndex;
488 *fIndices.push() = fPositions.count() - 1;
489 *fIndices.push() = fPositions.count() - 3;
490 }
Jim Van Verthda965502017-04-11 15:29:14 -0400491
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400492 // if transparent, add point to first one in array and add to center fan
493 if (fTransparent) {
494 fPositions[0] += centerPoint;
495 ++fCentroidCount;
496
497 *fIndices.push() = 0;
498 *fIndices.push() = fPrevUmbraIndex;
499 *fIndices.push() = fPositions.count() - 2;
500 }
501
Jim Van Verthda965502017-04-11 15:29:14 -0400502 fPrevUmbraIndex = fPositions.count() - 2;
503 }
504
505 // final edge
Jim Van Vertha84898d2017-02-06 13:38:23 -0500506 *fPositions.push() = fFirstPoint + normal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500507 *fColors.push() = fPenumbraColor;
508
Jim Van Verth76387852017-05-16 16:55:16 -0400509 if (fColors[fPrevUmbraIndex] > fColors[fFirstVertexIndex]) {
Jim Van Verthda965502017-04-11 15:29:14 -0400510 *fIndices.push() = fPrevUmbraIndex;
511 *fIndices.push() = fPositions.count() - 2;
Jim Van Verth76387852017-05-16 16:55:16 -0400512 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500513
Jim Van Verthda965502017-04-11 15:29:14 -0400514 *fIndices.push() = fPositions.count() - 2;
515 *fIndices.push() = fPositions.count() - 1;
Jim Van Verth76387852017-05-16 16:55:16 -0400516 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthda965502017-04-11 15:29:14 -0400517 } else {
518 *fIndices.push() = fPrevUmbraIndex;
519 *fIndices.push() = fPositions.count() - 2;
520 *fIndices.push() = fPositions.count() - 1;
521
522 *fIndices.push() = fPrevUmbraIndex;
523 *fIndices.push() = fPositions.count() - 1;
Jim Van Verth76387852017-05-16 16:55:16 -0400524 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthda965502017-04-11 15:29:14 -0400525 }
Jim Van Verth76387852017-05-16 16:55:16 -0400526 fPrevOutset = normal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500527 }
528
529 // finalize centroid
530 if (fTransparent) {
531 fPositions[0] *= SkScalarFastInvert(fCentroidCount);
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400532 fColors[0] = this->umbraColor(fTransformedHeightFunc(fPositions[0]));
Jim Van Verthbce74962017-01-25 09:39:46 -0500533
534 *fIndices.push() = 0;
Brian Salomon66085ed2017-02-02 15:39:34 -0500535 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400536 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500537 }
538
539 // final fan
540 if (fPositions.count() >= 3) {
Jim Van Verth76387852017-05-16 16:55:16 -0400541 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500542 fPrevPoint = fFirstPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400543 fRadius = this->offset(fTransformedHeightFunc(fPrevPoint));
Jim Van Verth76387852017-05-16 16:55:16 -0400544 if (this->addArc(fFirstOutset, false)) {
545 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400546 *fIndices.push() = fPositions.count() - 1;
Jim Van Verth76387852017-05-16 16:55:16 -0400547 *fIndices.push() = fFirstVertexIndex + 1;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400548 } else {
549 // arc is too small, set the first penumbra point to be the same position
550 // as the last one
Jim Van Verth76387852017-05-16 16:55:16 -0400551 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400552 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500553 }
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500554 fSucceeded = true;
Jim Van Verthbce74962017-01-25 09:39:46 -0500555}
556
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500557void SkAmbientShadowTessellator::handleLine(const SkPoint& p) {
Jim Van Verthbce74962017-01-25 09:39:46 -0500558 if (fInitPoints.count() < 2) {
559 *fInitPoints.push() = p;
560 return;
561 }
562
563 if (fInitPoints.count() == 2) {
564 // determine if cw or ccw
565 SkVector v0 = fInitPoints[1] - fInitPoints[0];
566 SkVector v1 = p - fInitPoints[0];
567 SkScalar perpDot = v0.fX*v1.fY - v0.fY*v1.fX;
568 if (SkScalarNearlyZero(perpDot)) {
569 // nearly parallel, just treat as straight line and continue
570 fInitPoints[1] = p;
571 return;
572 }
573
574 // if perpDot > 0, winding is ccw
575 fDirection = (perpDot > 0) ? -1 : 1;
576
577 // add first quad
Jim Van Verthda965502017-04-11 15:29:14 -0400578 SkVector normal;
579 if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &normal)) {
Jim Van Verthbce74962017-01-25 09:39:46 -0500580 // first two points are incident, make the third point the second and continue
581 fInitPoints[1] = p;
582 return;
583 }
584
Jim Van Vertha84898d2017-02-06 13:38:23 -0500585 fFirstPoint = fInitPoints[0];
Jim Van Verth76387852017-05-16 16:55:16 -0400586 fFirstVertexIndex = fPositions.count();
Jim Van Verthda965502017-04-11 15:29:14 -0400587 SkScalar z = fTransformedHeightFunc(fFirstPoint);
Jim Van Verth76387852017-05-16 16:55:16 -0400588 fFirstOutset = normal;
589 fFirstOutset *= this->offset(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400590
Jim Van Verth76387852017-05-16 16:55:16 -0400591 fPrevOutset = fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500592 fPrevPoint = fFirstPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400593 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500594
Jim Van Verthda965502017-04-11 15:29:14 -0400595 *fPositions.push() = fFirstPoint;
596 *fColors.push() = this->umbraColor(z);
Jim Van Verth76387852017-05-16 16:55:16 -0400597 *fPositions.push() = fFirstPoint + fFirstOutset;
Jim Van Verthbce74962017-01-25 09:39:46 -0500598 *fColors.push() = fPenumbraColor;
599 if (fTransparent) {
Jim Van Verthda965502017-04-11 15:29:14 -0400600 fPositions[0] += fFirstPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500601 fCentroidCount = 1;
602 }
Jim Van Verthda965502017-04-11 15:29:14 -0400603
604 // add the first quad
605 z = fTransformedHeightFunc(fInitPoints[1]);
606 fRadius = this->offset(z);
607 fUmbraColor = this->umbraColor(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400608 this->addEdge(fInitPoints[1], normal);
Jim Van Verthbce74962017-01-25 09:39:46 -0500609
610 // to ensure we skip this block next time
611 *fInitPoints.push() = p;
612 }
613
614 SkVector normal;
Jim Van Verth76387852017-05-16 16:55:16 -0400615 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400616 SkVector scaledNormal = normal;
617 scaledNormal *= fRadius;
618 this->addArc(scaledNormal, true);
619 SkScalar z = fTransformedHeightFunc(p);
620 fRadius = this->offset(z);
621 fUmbraColor = this->umbraColor(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400622 this->addEdge(p, normal);
Jim Van Verthbce74962017-01-25 09:39:46 -0500623 }
624}
625
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500626void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
Jim Van Verth76387852017-05-16 16:55:16 -0400627 // We compute the inset in two stages: first we inset by half the current normal,
628 // then on the next addEdge() we add half of the next normal to get an average of the two
629 SkVector insetNormal = nextNormal;
630 insetNormal *= 0.5f*kInsetFactor;
631
632 // Adding the other half of the average for the previous edge
633 fPositions[fPrevUmbraIndex] += insetNormal;
634
635 SkPoint umbraPoint = nextPoint + insetNormal;
636 SkVector outsetNormal = nextNormal;
637 outsetNormal *= fRadius;
638 SkPoint penumbraPoint = nextPoint + outsetNormal;
639
640 // For split edges, we're adding an average of two averages, so we multiply by another half
641 if (fSplitPreviousEdge) {
642 insetNormal *= 0.5f;
643 fPositions[fPrevUmbraIndex - 2] += insetNormal;
644 }
645
646 // 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 -0400647 if (fColors[fPrevUmbraIndex] != fUmbraColor &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500648 SkPointPriv::DistanceToSqd(nextPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400649
650 // This is lacking 1/4 of the next inset -- we'll add it the next time we call addEdge()
651 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + umbraPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400652 centerPoint *= 0.5f;
653 *fPositions.push() = centerPoint;
654 *fColors.push() = SkPMLerp(fUmbraColor, fColors[fPrevUmbraIndex], 128);
Jim Van Verth76387852017-05-16 16:55:16 -0400655 centerPoint = fPositions[fPositions.count()-2] + penumbraPoint;
656 centerPoint *= 0.5f;
657 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400658 *fColors.push() = fPenumbraColor;
659
660 // set triangularization to get best interpolation of color
661 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
662 *fIndices.push() = fPrevUmbraIndex;
663 *fIndices.push() = fPositions.count() - 3;
664 *fIndices.push() = fPositions.count() - 2;
665
666 *fIndices.push() = fPositions.count() - 3;
667 *fIndices.push() = fPositions.count() - 1;
668 *fIndices.push() = fPositions.count() - 2;
669 } else {
670 *fIndices.push() = fPrevUmbraIndex;
671 *fIndices.push() = fPositions.count() - 2;
672 *fIndices.push() = fPositions.count() - 1;
673
674 *fIndices.push() = fPrevUmbraIndex;
675 *fIndices.push() = fPositions.count() - 1;
676 *fIndices.push() = fPositions.count() - 3;
677 }
678
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400679 // if transparent, add point to first one in array and add to center fan
680 if (fTransparent) {
681 fPositions[0] += centerPoint;
682 ++fCentroidCount;
683
684 *fIndices.push() = 0;
685 *fIndices.push() = fPrevUmbraIndex;
686 *fIndices.push() = fPositions.count() - 2;
687 }
688
Jim Van Verth76387852017-05-16 16:55:16 -0400689 fSplitPreviousEdge = true;
690 if (fPrevUmbraIndex == fFirstVertexIndex) {
691 fSplitFirstEdge = true;
692 }
Jim Van Verthda965502017-04-11 15:29:14 -0400693 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Verth76387852017-05-16 16:55:16 -0400694 } else {
695 fSplitPreviousEdge = false;
Jim Van Verthda965502017-04-11 15:29:14 -0400696 }
697
Jim Van Verthbce74962017-01-25 09:39:46 -0500698 // add next quad
Jim Van Verth76387852017-05-16 16:55:16 -0400699 *fPositions.push() = umbraPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500700 *fColors.push() = fUmbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -0400701 *fPositions.push() = penumbraPoint;
Jim Van Verthbce74962017-01-25 09:39:46 -0500702 *fColors.push() = fPenumbraColor;
703
Jim Van Verthda965502017-04-11 15:29:14 -0400704 // set triangularization to get best interpolation of color
705 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
706 *fIndices.push() = fPrevUmbraIndex;
707 *fIndices.push() = fPositions.count() - 3;
708 *fIndices.push() = fPositions.count() - 2;
Jim Van Verthbce74962017-01-25 09:39:46 -0500709
Jim Van Verthda965502017-04-11 15:29:14 -0400710 *fIndices.push() = fPositions.count() - 3;
711 *fIndices.push() = fPositions.count() - 1;
712 *fIndices.push() = fPositions.count() - 2;
713 } else {
714 *fIndices.push() = fPrevUmbraIndex;
715 *fIndices.push() = fPositions.count() - 2;
716 *fIndices.push() = fPositions.count() - 1;
717
718 *fIndices.push() = fPrevUmbraIndex;
719 *fIndices.push() = fPositions.count() - 1;
720 *fIndices.push() = fPositions.count() - 3;
721 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500722
723 // if transparent, add point to first one in array and add to center fan
724 if (fTransparent) {
725 fPositions[0] += nextPoint;
726 ++fCentroidCount;
727
728 *fIndices.push() = 0;
Brian Salomon66085ed2017-02-02 15:39:34 -0500729 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500730 *fIndices.push() = fPositions.count() - 2;
731 }
732
Brian Salomon66085ed2017-02-02 15:39:34 -0500733 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500734 fPrevPoint = nextPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400735 fPrevOutset = outsetNormal;
Jim Van Verthbce74962017-01-25 09:39:46 -0500736}
Jim Van Verth91af7272017-01-27 14:15:54 -0500737
738///////////////////////////////////////////////////////////////////////////////////////////////////
739
Brian Salomonaff27a22017-02-06 15:47:44 -0500740class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500741public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500742 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400743 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400744 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500745
Brian Salomon958fbc42017-01-30 17:01:28 -0500746private:
Brian Salomonab664fa2017-03-24 16:07:20 +0000747 void computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400748 const SkMatrix& shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000749 void computeClipVectorsAndTestCentroid();
Brian Salomon66085ed2017-02-02 15:39:34 -0500750 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
Brian Salomonab664fa2017-03-24 16:07:20 +0000751 int getClosestUmbraPoint(const SkPoint& point);
Brian Salomon958fbc42017-01-30 17:01:28 -0500752
Jim Van Vertha84898d2017-02-06 13:38:23 -0500753 void handleLine(const SkPoint& p) override;
Jim Van Verthb55eb282017-07-18 14:13:45 -0400754 bool handlePolyPoint(const SkPoint& p);
Brian Salomon958fbc42017-01-30 17:01:28 -0500755
756 void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
Brian Salomonab664fa2017-03-24 16:07:20 +0000757 bool addInnerPoint(const SkPoint& pathPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400758 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
759
760 SkScalar offset(SkScalar z) {
761 float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
762 return fLightRadius*zRatio;
763 }
764
765 SkScalar fLightZ;
766 SkScalar fLightRadius;
767 SkScalar fOffsetAdjust;
Brian Salomon958fbc42017-01-30 17:01:28 -0500768
Brian Salomon958fbc42017-01-30 17:01:28 -0500769 SkTDArray<SkPoint> fClipPolygon;
Brian Salomon66085ed2017-02-02 15:39:34 -0500770 SkTDArray<SkVector> fClipVectors;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500771 SkPoint fCentroid;
Brian Salomonab664fa2017-03-24 16:07:20 +0000772 SkScalar fArea;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500773
Brian Salomonab664fa2017-03-24 16:07:20 +0000774 SkTDArray<SkPoint> fPathPolygon;
775 SkTDArray<SkPoint> fUmbraPolygon;
776 int fCurrClipPoint;
777 int fCurrUmbraPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -0500778 bool fPrevUmbraOutside;
779 bool fFirstUmbraOutside;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500780 bool fValidUmbra;
Brian Salomon958fbc42017-01-30 17:01:28 -0500781
Brian Salomonaff27a22017-02-06 15:47:44 -0500782 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -0500783};
784
Jim Van Vertha84898d2017-02-06 13:38:23 -0500785SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400786 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -0400787 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -0400788 bool transparent)
Jim Van Verthe308a122017-05-08 14:19:30 -0400789 : INHERITED(zPlaneParams, transparent)
Jim Van Verthda965502017-04-11 15:29:14 -0400790 , fLightZ(lightPos.fZ)
791 , fLightRadius(lightRadius)
792 , fOffsetAdjust(0)
793 , fCurrClipPoint(0)
794 , fPrevUmbraOutside(false)
795 , fFirstUmbraOutside(false)
796 , fValidUmbra(true) {
797
798 // make sure we're not below the canvas plane
799 if (this->setZOffset(path.getBounds(), ctm.hasPerspective())) {
800 // Adjust light height and radius
801 fLightRadius *= (fLightZ + fZOffset) / fLightZ;
802 fLightZ += fZOffset;
803 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400804
805 // Set radius and colors
Jim Van Verthb4366552017-03-27 14:25:29 -0400806 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verthe308a122017-05-08 14:19:30 -0400807 SkScalar occluderHeight = this->heightFunc(center.fX, center.fY) + fZOffset;
Jim Van Verth060d9822017-05-04 09:58:17 -0400808 fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
809 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400810
Jim Van Verth1af03d42017-07-31 09:34:58 -0400811 // Compute the blur radius, scale and translation for the spot shadow.
812 SkScalar radius;
Jim Van Verthda965502017-04-11 15:29:14 -0400813 SkMatrix shadowTransform;
814 if (!ctm.hasPerspective()) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400815 SkScalar scale;
816 SkVector translate;
817 SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, fLightZ,
818 lightRadius, &radius, &scale, &translate);
Jim Van Verthda965502017-04-11 15:29:14 -0400819 shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
820 } else {
821 // For perspective, we have a scale, a z-shear, and another projective divide --
822 // this varies at each point so we can't use an affine transform.
823 // We'll just apply this to each generated point in turn.
824 shadowTransform.reset();
825 // Also can't cull the center (for now).
826 fTransparent = true;
Jim Van Verth1af03d42017-07-31 09:34:58 -0400827 radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
Jim Van Verthda965502017-04-11 15:29:14 -0400828 }
Jim Van Verth1af03d42017-07-31 09:34:58 -0400829 fRadius = radius;
Jim Van Verthda965502017-04-11 15:29:14 -0400830 SkMatrix fullTransform = SkMatrix::Concat(shadowTransform, ctm);
831
832 // Set up our reverse mapping
Jim Van Verth7c8008c2018-02-07 15:02:50 -0500833 if (!this->setTransformedHeightFunc(fullTransform)) {
834 return;
835 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400836
Brian Salomonab664fa2017-03-24 16:07:20 +0000837 // TODO: calculate these reserves better
Brian Salomon66085ed2017-02-02 15:39:34 -0500838 // Penumbra ring: 3*numPts
839 // Umbra ring: numPts
Jim Van Verth91af7272017-01-27 14:15:54 -0500840 // Inner ring: numPts
Brian Salomon66085ed2017-02-02 15:39:34 -0500841 fPositions.setReserve(5 * path.countPoints());
842 fColors.setReserve(5 * path.countPoints());
843 // Penumbra ring: 12*numPts
844 // Umbra ring: 3*numPts
845 fIndices.setReserve(15 * path.countPoints());
Brian Salomone7c85c42017-03-24 16:00:35 +0000846 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +0000847
848 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verthda965502017-04-11 15:29:14 -0400849 this->computeClipAndPathPolygons(path, ctm, shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000850 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3) {
Brian Salomon66085ed2017-02-02 15:39:34 -0500851 return;
852 }
Brian Salomon66085ed2017-02-02 15:39:34 -0500853
Brian Salomonab664fa2017-03-24 16:07:20 +0000854 // check to see if umbra collapses
Cary Clarkdf429f32017-11-08 11:44:31 -0500855 SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, fPathPolygon[0],
856 fPathPolygon[1]);
Jim Van Verthda965502017-04-11 15:29:14 -0400857 SkRect bounds;
858 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
Brian Salomonab664fa2017-03-24 16:07:20 +0000859 for (int i = 1; i < fPathPolygon.count(); ++i) {
860 int j = i + 1;
861 if (i == fPathPolygon.count() - 1) {
862 j = 0;
863 }
864 SkPoint currPoint = fPathPolygon[i];
865 SkPoint nextPoint = fPathPolygon[j];
Cary Clarkdf429f32017-11-08 11:44:31 -0500866 SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
867 nextPoint);
Brian Salomonab664fa2017-03-24 16:07:20 +0000868 if (distSq < minDistSq) {
869 minDistSq = distSq;
870 }
871 }
872 static constexpr auto kTolerance = 1.0e-2f;
873 if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
874 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
875 SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
Jim Van Verthda965502017-04-11 15:29:14 -0400876 fOffsetAdjust = newRadius - radius;
Jim Van Verthb6069df2017-04-28 11:00:35 -0400877 SkScalar ratio = 128 * (newRadius + radius) / radius;
Brian Salomonab664fa2017-03-24 16:07:20 +0000878 // they aren't PMColors, but the interpolation algorithm is the same
879 fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
880 radius = newRadius;
881 }
Jim Van Verth91af7272017-01-27 14:15:54 -0500882
Brian Salomonab664fa2017-03-24 16:07:20 +0000883 // compute vectors for clip tests
884 this->computeClipVectorsAndTestCentroid();
885
886 // generate inner ring
Jim Van Verthda965502017-04-11 15:29:14 -0400887 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
888 &fUmbraPolygon)) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000889 // this shouldn't happen, but just in case we'll inset using the centroid
890 fValidUmbra = false;
891 }
892
893 // walk around the path polygon, generate outer ring and connect to inner ring
Brian Salomon66085ed2017-02-02 15:39:34 -0500894 if (fTransparent) {
895 *fPositions.push() = fCentroid;
896 *fColors.push() = fUmbraColor;
897 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000898 fCurrUmbraPoint = 0;
899 for (int i = 0; i < fPathPolygon.count(); ++i) {
Jim Van Verthb55eb282017-07-18 14:13:45 -0400900 if (!this->handlePolyPoint(fPathPolygon[i])) {
901 return;
902 }
Jim Van Verth91af7272017-01-27 14:15:54 -0500903 }
904
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500905 if (!this->indexCount()) {
906 return;
907 }
908
Brian Salomonab664fa2017-03-24 16:07:20 +0000909 // finish up the final verts
Jim Van Verth91af7272017-01-27 14:15:54 -0500910 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -0400911 if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
912 normal *= fRadius;
913 this->addArc(normal, true);
Jim Van Verth91af7272017-01-27 14:15:54 -0500914
Brian Salomon66085ed2017-02-02 15:39:34 -0500915 // add to center fan
916 if (fTransparent) {
917 *fIndices.push() = 0;
918 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400919 *fIndices.push() = fFirstVertexIndex;
Brian Salomon66085ed2017-02-02 15:39:34 -0500920 // or to clip ring
921 } else {
922 if (fFirstUmbraOutside) {
923 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400924 *fIndices.push() = fFirstVertexIndex;
925 *fIndices.push() = fFirstVertexIndex + 1;
Brian Salomon66085ed2017-02-02 15:39:34 -0500926 if (fPrevUmbraOutside) {
927 // fill out quad
928 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400929 *fIndices.push() = fFirstVertexIndex + 1;
Brian Salomon66085ed2017-02-02 15:39:34 -0500930 *fIndices.push() = fPrevUmbraIndex + 1;
931 }
932 } else if (fPrevUmbraOutside) {
933 // add tri
934 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400935 *fIndices.push() = fFirstVertexIndex;
Brian Salomon66085ed2017-02-02 15:39:34 -0500936 *fIndices.push() = fPrevUmbraIndex + 1;
937 }
938 }
939
Jim Van Verth91af7272017-01-27 14:15:54 -0500940 // add final edge
941 *fPositions.push() = fFirstPoint + normal;
942 *fColors.push() = fPenumbraColor;
943
Brian Salomon66085ed2017-02-02 15:39:34 -0500944 *fIndices.push() = fPrevUmbraIndex;
Jim Van Verth91af7272017-01-27 14:15:54 -0500945 *fIndices.push() = fPositions.count() - 2;
Jim Van Verth76387852017-05-16 16:55:16 -0400946 *fIndices.push() = fFirstVertexIndex;
Jim Van Verth91af7272017-01-27 14:15:54 -0500947
948 *fIndices.push() = fPositions.count() - 2;
949 *fIndices.push() = fPositions.count() - 1;
Jim Van Verth76387852017-05-16 16:55:16 -0400950 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthda965502017-04-11 15:29:14 -0400951
Jim Van Verth76387852017-05-16 16:55:16 -0400952 fPrevOutset = normal;
Jim Van Verth91af7272017-01-27 14:15:54 -0500953 }
954
955 // final fan
956 if (fPositions.count() >= 3) {
Jim Van Verth76387852017-05-16 16:55:16 -0400957 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Verth91af7272017-01-27 14:15:54 -0500958 fPrevPoint = fFirstPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400959 if (this->addArc(fFirstOutset, false)) {
960 *fIndices.push() = fFirstVertexIndex;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400961 *fIndices.push() = fPositions.count() - 1;
962 if (fFirstUmbraOutside) {
Jim Van Verth76387852017-05-16 16:55:16 -0400963 *fIndices.push() = fFirstVertexIndex + 2;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400964 } else {
Jim Van Verth76387852017-05-16 16:55:16 -0400965 *fIndices.push() = fFirstVertexIndex + 1;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400966 }
Brian Salomon66085ed2017-02-02 15:39:34 -0500967 } else {
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400968 // no arc added, fix up by setting first penumbra point position to last one
969 if (fFirstUmbraOutside) {
Jim Van Verth76387852017-05-16 16:55:16 -0400970 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400971 } else {
Jim Van Verth76387852017-05-16 16:55:16 -0400972 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400973 }
Brian Salomon66085ed2017-02-02 15:39:34 -0500974 }
Jim Van Verth91af7272017-01-27 14:15:54 -0500975 }
Jim Van Verthda965502017-04-11 15:29:14 -0400976
977 if (ctm.hasPerspective()) {
978 for (int i = 0; i < fPositions.count(); ++i) {
979 SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
980 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
981 fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
982 fPositions[i].fY = (fPositions[i].fY*fLightZ - lightPos.fY*pathZ)*factor;
983 }
984#ifdef DRAW_CENTROID
985 SkScalar pathZ = fTransformedHeightFunc(fCentroid);
986 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
987 fCentroid.fX = (fCentroid.fX*fLightZ - lightPos.fX*pathZ)*factor;
988 fCentroid.fY = (fCentroid.fY*fLightZ - lightPos.fY*pathZ)*factor;
989#endif
990 }
991#ifdef DRAW_CENTROID
992 *fPositions.push() = fCentroid + SkVector::Make(-2, -2);
993 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
994 *fPositions.push() = fCentroid + SkVector::Make(2, -2);
995 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
996 *fPositions.push() = fCentroid + SkVector::Make(-2, 2);
997 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
998 *fPositions.push() = fCentroid + SkVector::Make(2, 2);
999 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1000
1001 *fIndices.push() = fPositions.count() - 4;
1002 *fIndices.push() = fPositions.count() - 2;
1003 *fIndices.push() = fPositions.count() - 1;
1004
1005 *fIndices.push() = fPositions.count() - 4;
1006 *fIndices.push() = fPositions.count() - 1;
1007 *fIndices.push() = fPositions.count() - 3;
1008#endif
1009
Brian Salomon0dda9cb2017-02-03 10:33:25 -05001010 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001011}
1012
Brian Salomonab664fa2017-03-24 16:07:20 +00001013void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -04001014 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001015
1016 fPathPolygon.setReserve(path.countPoints());
1017
1018 // Walk around the path and compute clip polygon and path polygon.
1019 // Will also accumulate sum of areas for centroid.
1020 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -05001021 SkPath::Iter iter(path, true);
1022 SkPoint pts[4];
1023 SkPath::Verb verb;
1024
Jim Van Verth91af7272017-01-27 14:15:54 -05001025 fClipPolygon.reset();
1026
Brian Salomonab664fa2017-03-24 16:07:20 +00001027 // init centroid
1028 fCentroid = SkPoint::Make(0, 0);
1029 fArea = 0;
1030
Brian Salomon66085ed2017-02-02 15:39:34 -05001031 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +00001032 static constexpr SkScalar kA = 0.32495117187f;
1033 static constexpr SkScalar kB = 0.44311523437f;
1034 static constexpr SkScalar kC = 0.20141601562f;
1035 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -05001036
1037 SkPoint curvePoint;
1038 SkScalar w;
Jim Van Verth91af7272017-01-27 14:15:54 -05001039 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1040 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001041 case SkPath::kLine_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001042 ctm.mapPoints(&pts[1], 1);
Jim Van Verth91af7272017-01-27 14:15:54 -05001043 *fClipPolygon.push() = pts[1];
Brian Salomonab664fa2017-03-24 16:07:20 +00001044 this->INHERITED::handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -05001045 break;
1046 case SkPath::kQuad_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001047 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001048 // point at t = 1/2
1049 curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
1050 curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY;
1051 *fClipPolygon.push() = curvePoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001052 *fClipPolygon.push() = pts[2];
Brian Salomonab664fa2017-03-24 16:07:20 +00001053 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001054 break;
1055 case SkPath::kConic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001056 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001057 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -05001058 // point at t = 1/2
Brian Salomon66085ed2017-02-02 15:39:34 -05001059 curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
1060 curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
1061 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
1062 *fClipPolygon.push() = curvePoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001063 *fClipPolygon.push() = pts[2];
Brian Salomonab664fa2017-03-24 16:07:20 +00001064 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -05001065 break;
1066 case SkPath::kCubic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001067 ctm.mapPoints(pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -05001068 // point at t = 5/16
1069 curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
1070 curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY;
1071 *fClipPolygon.push() = curvePoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001072 // point at t = 11/16
1073 curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
1074 curvePoint.fY = kD*pts[0].fY + kC*pts[1].fY + kB*pts[2].fY + kA*pts[3].fY;
1075 *fClipPolygon.push() = curvePoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001076 *fClipPolygon.push() = pts[3];
Brian Salomonab664fa2017-03-24 16:07:20 +00001077 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001078 break;
Brian Salomonab664fa2017-03-24 16:07:20 +00001079 case SkPath::kMove_Verb:
Jim Van Verth91af7272017-01-27 14:15:54 -05001080 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +00001081 case SkPath::kDone_Verb:
Jim Van Verth91af7272017-01-27 14:15:54 -05001082 break;
1083 default:
1084 SkDEBUGFAIL("unknown verb");
1085 }
1086 }
1087
Brian Salomonab664fa2017-03-24 16:07:20 +00001088 // finish centroid
1089 if (fPathPolygon.count() > 0) {
1090 SkPoint currPoint = fPathPolygon[fPathPolygon.count() - 1];
1091 SkPoint nextPoint = fPathPolygon[0];
1092 SkScalar quadArea = currPoint.cross(nextPoint);
1093 fCentroid.fX += (currPoint.fX + nextPoint.fX) * quadArea;
1094 fCentroid.fY += (currPoint.fY + nextPoint.fY) * quadArea;
1095 fArea += quadArea;
1096 fCentroid *= SK_Scalar1 / (3 * fArea);
1097 }
1098
1099 fCurrClipPoint = fClipPolygon.count() - 1;
Brian Salomon66085ed2017-02-02 15:39:34 -05001100}
1101
Brian Salomonab664fa2017-03-24 16:07:20 +00001102void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
Brian Salomon66085ed2017-02-02 15:39:34 -05001103 SkASSERT(fClipPolygon.count() >= 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001104
Brian Salomonab664fa2017-03-24 16:07:20 +00001105 // init clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001106 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
1107 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001108
1109 // init centroid check
1110 bool hiddenCentroid = true;
Brian Salomonab664fa2017-03-24 16:07:20 +00001111 SkVector v1 = fCentroid - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001112 SkScalar initCross = v0.cross(v1);
1113
1114 for (int p = 1; p < fClipPolygon.count(); ++p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001115 // add to clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001116 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
1117 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001118 // Determine if transformed centroid is inside clipPolygon.
Brian Salomonab664fa2017-03-24 16:07:20 +00001119 v1 = fCentroid - fClipPolygon[p];
Brian Salomon66085ed2017-02-02 15:39:34 -05001120 if (initCross*v0.cross(v1) <= 0) {
1121 hiddenCentroid = false;
1122 }
1123 }
1124 SkASSERT(fClipVectors.count() == fClipPolygon.count());
1125
Brian Salomonab664fa2017-03-24 16:07:20 +00001126 fTransparent = fTransparent || !hiddenCentroid;
Brian Salomon66085ed2017-02-02 15:39:34 -05001127}
1128
1129bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
1130 SkPoint* clipPoint) {
1131 SkVector segmentVector = centroid - umbraPoint;
1132
Brian Salomonab664fa2017-03-24 16:07:20 +00001133 int startClipPoint = fCurrClipPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001134 do {
Brian Salomonab664fa2017-03-24 16:07:20 +00001135 SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
1136 SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
Brian Salomon66085ed2017-02-02 15:39:34 -05001137 SkScalar t_num = dp.cross(segmentVector);
1138 // if line segments are nearly parallel
1139 if (SkScalarNearlyZero(denom)) {
1140 // and collinear
1141 if (SkScalarNearlyZero(t_num)) {
1142 return false;
1143 }
1144 // otherwise are separate, will try the next poly segment
1145 // else if crossing lies within poly segment
1146 } else if (t_num >= 0 && t_num <= denom) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001147 SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
Brian Salomon66085ed2017-02-02 15:39:34 -05001148 // if umbra point is inside the clip polygon
Jim Van Verthda965502017-04-11 15:29:14 -04001149 if (s_num >= 0 && s_num <= denom) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001150 segmentVector *= s_num/denom;
1151 *clipPoint = umbraPoint + segmentVector;
1152 return true;
1153 }
1154 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001155 fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
1156 } while (fCurrClipPoint != startClipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001157
1158 return false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001159}
1160
Brian Salomonab664fa2017-03-24 16:07:20 +00001161int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
Cary Clarkdf429f32017-11-08 11:44:31 -05001162 SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraPoint]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001163 int index = fCurrUmbraPoint;
1164 int dir = 1;
1165 int next = (index + dir) % fUmbraPolygon.count();
1166
1167 // init travel direction
Cary Clarkdf429f32017-11-08 11:44:31 -05001168 SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001169 if (distance < minDistance) {
1170 index = next;
1171 minDistance = distance;
1172 } else {
1173 dir = fUmbraPolygon.count()-1;
1174 }
1175
1176 // iterate until we find a point that increases the distance
1177 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001178 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001179 while (distance < minDistance) {
1180 index = next;
1181 minDistance = distance;
1182 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001183 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001184 }
1185
1186 fCurrUmbraPoint = index;
1187 return index;
1188}
1189
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001190void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
Jim Van Verth91af7272017-01-27 14:15:54 -05001191 SkPoint* pts, int count) {
1192 // TODO: vectorize
1193 for (int i = 0; i < count; ++i) {
1194 pts[i] *= scale;
1195 pts[i] += xlate;
1196 }
1197}
1198
Brian Salomonab664fa2017-03-24 16:07:20 +00001199static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
1200 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
1201 static constexpr SkScalar kCloseSqd = kClose*kClose;
1202
Cary Clarkdf429f32017-11-08 11:44:31 -05001203 SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
Brian Salomonab664fa2017-03-24 16:07:20 +00001204 return distSq < kCloseSqd;
1205}
1206
Jim Van Verthb55eb282017-07-18 14:13:45 -04001207static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001208 SkVector v0 = p1 - p0;
1209 SkVector v1 = p2 - p0;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001210 return v0.cross(v1);
1211}
1212
1213static bool is_collinear(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
1214 return (SkScalarNearlyZero(perp_dot(p0, p1, p2)));
Brian Salomonab664fa2017-03-24 16:07:20 +00001215}
1216
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001217void SkSpotShadowTessellator::handleLine(const SkPoint& p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001218 // remove coincident points and add to centroid
1219 if (fPathPolygon.count() > 0) {
1220 const SkPoint& lastPoint = fPathPolygon[fPathPolygon.count() - 1];
1221 if (duplicate_pt(p, lastPoint)) {
1222 return;
1223 }
1224 SkScalar quadArea = lastPoint.cross(p);
1225 fCentroid.fX += (p.fX + lastPoint.fX) * quadArea;
1226 fCentroid.fY += (p.fY + lastPoint.fY) * quadArea;
1227 fArea += quadArea;
1228 }
1229
1230 // try to remove collinear points
1231 if (fPathPolygon.count() > 1 && is_collinear(fPathPolygon[fPathPolygon.count()-2],
1232 fPathPolygon[fPathPolygon.count()-1],
1233 p)) {
1234 fPathPolygon[fPathPolygon.count() - 1] = p;
1235 } else {
1236 *fPathPolygon.push() = p;
1237 }
1238}
1239
Jim Van Verthb55eb282017-07-18 14:13:45 -04001240bool SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001241 if (fInitPoints.count() < 2) {
1242 *fInitPoints.push() = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001243 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001244 }
1245
1246 if (fInitPoints.count() == 2) {
1247 // determine if cw or ccw
Jim Van Verthb55eb282017-07-18 14:13:45 -04001248 SkScalar perpDot = perp_dot(fInitPoints[0], fInitPoints[1], p);
Jim Van Verth91af7272017-01-27 14:15:54 -05001249 if (SkScalarNearlyZero(perpDot)) {
1250 // nearly parallel, just treat as straight line and continue
1251 fInitPoints[1] = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001252 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001253 }
1254
1255 // if perpDot > 0, winding is ccw
1256 fDirection = (perpDot > 0) ? -1 : 1;
1257
1258 // add first quad
Jim Van Verth76387852017-05-16 16:55:16 -04001259 if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &fFirstOutset)) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001260 // first two points are incident, make the third point the second and continue
1261 fInitPoints[1] = p;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001262 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001263 }
1264
Jim Van Verth76387852017-05-16 16:55:16 -04001265 fFirstOutset *= fRadius;
Jim Van Verth91af7272017-01-27 14:15:54 -05001266 fFirstPoint = fInitPoints[0];
Jim Van Verth76387852017-05-16 16:55:16 -04001267 fFirstVertexIndex = fPositions.count();
1268 fPrevOutset = fFirstOutset;
Jim Van Verth91af7272017-01-27 14:15:54 -05001269 fPrevPoint = fFirstPoint;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001270 fPrevUmbraIndex = -1;
Jim Van Verth91af7272017-01-27 14:15:54 -05001271
Brian Salomon66085ed2017-02-02 15:39:34 -05001272 this->addInnerPoint(fFirstPoint);
Jim Van Verth76387852017-05-16 16:55:16 -04001273 fPrevUmbraIndex = fFirstVertexIndex;
Brian Salomon66085ed2017-02-02 15:39:34 -05001274
1275 if (!fTransparent) {
1276 SkPoint clipPoint;
Jim Van Verthb55eb282017-07-18 14:13:45 -04001277 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
1278 fCentroid, &clipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001279 if (isOutside) {
1280 *fPositions.push() = clipPoint;
1281 *fColors.push() = fUmbraColor;
1282 }
1283 fPrevUmbraOutside = isOutside;
1284 fFirstUmbraOutside = isOutside;
1285 }
1286
Jim Van Verth76387852017-05-16 16:55:16 -04001287 SkPoint newPoint = fFirstPoint + fFirstOutset;
Jim Van Verth91af7272017-01-27 14:15:54 -05001288 *fPositions.push() = newPoint;
1289 *fColors.push() = fPenumbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -04001290 this->addEdge(fInitPoints[1], fFirstOutset);
Jim Van Verth91af7272017-01-27 14:15:54 -05001291
1292 // to ensure we skip this block next time
1293 *fInitPoints.push() = p;
1294 }
1295
Jim Van Verthb55eb282017-07-18 14:13:45 -04001296 // if concave, abort
1297 SkScalar perpDot = perp_dot(fInitPoints[1], fInitPoints[2], p);
1298 if (fDirection*perpDot > 0) {
1299 return false;
1300 }
1301
Jim Van Verth91af7272017-01-27 14:15:54 -05001302 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -04001303 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
1304 normal *= fRadius;
1305 this->addArc(normal, true);
1306 this->addEdge(p, normal);
Jim Van Verthb55eb282017-07-18 14:13:45 -04001307 fInitPoints[1] = fInitPoints[2];
1308 fInitPoints[2] = p;
Jim Van Verth91af7272017-01-27 14:15:54 -05001309 }
Jim Van Verthb55eb282017-07-18 14:13:45 -04001310
1311 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001312}
1313
Brian Salomonab664fa2017-03-24 16:07:20 +00001314bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint) {
1315 SkPoint umbraPoint;
1316 if (!fValidUmbra) {
1317 SkVector v = fCentroid - pathPoint;
1318 v *= 0.95f;
1319 umbraPoint = pathPoint + v;
Jim Van Verth91af7272017-01-27 14:15:54 -05001320 } else {
Brian Salomonab664fa2017-03-24 16:07:20 +00001321 umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
Jim Van Verth91af7272017-01-27 14:15:54 -05001322 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001323
Jim Van Verth91af7272017-01-27 14:15:54 -05001324 fPrevPoint = pathPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001325
1326 // merge "close" points
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001327 if (fPrevUmbraIndex == -1 ||
Brian Salomonab664fa2017-03-24 16:07:20 +00001328 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
1329 *fPositions.push() = umbraPoint;
1330 *fColors.push() = fUmbraColor;
1331
1332 return false;
1333 } else {
1334 return true;
1335 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001336}
1337
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001338void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001339 // add next umbra point
Brian Salomonab664fa2017-03-24 16:07:20 +00001340 bool duplicate = this->addInnerPoint(nextPoint);
1341 int prevPenumbraIndex = duplicate ? fPositions.count()-1 : fPositions.count()-2;
1342 int currUmbraIndex = duplicate ? fPrevUmbraIndex : fPositions.count()-1;
Brian Salomon66085ed2017-02-02 15:39:34 -05001343
Brian Salomonab664fa2017-03-24 16:07:20 +00001344 if (!duplicate) {
1345 // add to center fan if transparent or centroid showing
1346 if (fTransparent) {
1347 *fIndices.push() = 0;
1348 *fIndices.push() = fPrevUmbraIndex;
1349 *fIndices.push() = currUmbraIndex;
1350 // otherwise add to clip ring
1351 } else {
Brian Salomon66085ed2017-02-02 15:39:34 -05001352 SkPoint clipPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001353 bool isOutside = this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
1354 &clipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001355 if (isOutside) {
1356 *fPositions.push() = clipPoint;
1357 *fColors.push() = fUmbraColor;
1358
1359 *fIndices.push() = fPrevUmbraIndex;
1360 *fIndices.push() = currUmbraIndex;
1361 *fIndices.push() = currUmbraIndex + 1;
1362 if (fPrevUmbraOutside) {
1363 // fill out quad
1364 *fIndices.push() = fPrevUmbraIndex;
1365 *fIndices.push() = currUmbraIndex + 1;
1366 *fIndices.push() = fPrevUmbraIndex + 1;
1367 }
1368 } else if (fPrevUmbraOutside) {
1369 // add tri
1370 *fIndices.push() = fPrevUmbraIndex;
1371 *fIndices.push() = currUmbraIndex;
1372 *fIndices.push() = fPrevUmbraIndex + 1;
1373 }
1374 fPrevUmbraOutside = isOutside;
1375 }
1376 }
1377
1378 // add next penumbra point and quad
Jim Van Verth91af7272017-01-27 14:15:54 -05001379 SkPoint newPoint = nextPoint + nextNormal;
1380 *fPositions.push() = newPoint;
1381 *fColors.push() = fPenumbraColor;
1382
Brian Salomonab664fa2017-03-24 16:07:20 +00001383 if (!duplicate) {
1384 *fIndices.push() = fPrevUmbraIndex;
1385 *fIndices.push() = prevPenumbraIndex;
1386 *fIndices.push() = currUmbraIndex;
1387 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001388
Brian Salomon66085ed2017-02-02 15:39:34 -05001389 *fIndices.push() = prevPenumbraIndex;
Jim Van Verth91af7272017-01-27 14:15:54 -05001390 *fIndices.push() = fPositions.count() - 1;
Brian Salomon66085ed2017-02-02 15:39:34 -05001391 *fIndices.push() = currUmbraIndex;
Jim Van Verth91af7272017-01-27 14:15:54 -05001392
Brian Salomon66085ed2017-02-02 15:39:34 -05001393 fPrevUmbraIndex = currUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -04001394 fPrevOutset = nextNormal;
Jim Van Verth91af7272017-01-27 14:15:54 -05001395}
Brian Salomon958fbc42017-01-30 17:01:28 -05001396
1397///////////////////////////////////////////////////////////////////////////////////////////////////
1398
Brian Salomonaff27a22017-02-06 15:47:44 -05001399sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001400 const SkPoint3& zPlane, bool transparent) {
1401 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001402 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001403}
1404
Brian Salomonaff27a22017-02-06 15:47:44 -05001405sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001406 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001407 SkScalar lightRadius, bool transparent) {
Jim Van Verthe308a122017-05-08 14:19:30 -04001408 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001409 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001410}