blob: 6c55ddd53c23d01123a583a952edc68b7f0dfb5d [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 Verth8664a1d2018-06-28 16:26:50 -040012#include "SkPolyUtils.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 Verthcdaf6612018-06-05 15:21:13 -040046 bool accumulateCentroid(const SkPoint& c, const SkPoint& n);
47 bool checkConvexity(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2);
48 void finishPathPolygon();
Jim Van Verth8760e2f2018-06-12 14:21:38 -040049 void stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
50 SkTDArray<int>* umbraIndices,
51 const SkTDArray<SkPoint>& penumbraPolygon,
52 SkTDArray<int>* penumbraIndices);
Jim Van Verthcdaf6612018-06-05 15:21:13 -040053
54 void handleLine(const SkPoint& p);
Jim Van Vertha84898d2017-02-06 13:38:23 -050055 void handleLine(const SkMatrix& m, SkPoint* p);
Brian Salomon958fbc42017-01-30 17:01:28 -050056
57 void handleQuad(const SkPoint pts[3]);
Jim Van Vertha84898d2017-02-06 13:38:23 -050058 void handleQuad(const SkMatrix& m, SkPoint pts[3]);
Brian Salomon958fbc42017-01-30 17:01:28 -050059
Jim Van Vertha84898d2017-02-06 13:38:23 -050060 void handleCubic(const SkMatrix& m, SkPoint pts[4]);
Brian Salomon958fbc42017-01-30 17:01:28 -050061
Jim Van Vertha84898d2017-02-06 13:38:23 -050062 void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
Brian Salomon958fbc42017-01-30 17:01:28 -050063
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -040064 bool addArc(const SkVector& nextNormal, bool finishArc);
Jim Van Verthb4366552017-03-27 14:25:29 -040065
Jim Van Verth872da6b2018-04-10 11:24:11 -040066 void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
67 void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
68
Jim Van Verthe308a122017-05-08 14:19:30 -040069 SkScalar heightFunc(SkScalar x, SkScalar y) {
70 return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
71 }
72
Jim Van Verth0b7645f2018-08-31 12:36:52 -040073 SkPoint3 fZPlaneParams;
Jim Van Verthda965502017-04-11 15:29:14 -040074
Brian Salomon958fbc42017-01-30 17:01:28 -050075 // 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 Verthcdaf6612018-06-05 15:21:13 -040082 SkTDArray<SkPoint> fPathPolygon;
83 SkPoint fCentroid;
84 SkScalar fArea;
85 SkScalar fLastArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -040086 SkScalar fLastCross;
87
Jim Van Verth76387852017-05-16 16:55:16 -040088 int fFirstVertexIndex;
89 SkVector fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -050090 SkPoint fFirstPoint;
91
Brian Salomon0dda9cb2017-02-03 10:33:25 -050092 bool fSucceeded;
Jim Van Vertha84898d2017-02-06 13:38:23 -050093 bool fTransparent;
Jim Van Verthf507c282018-05-11 10:48:20 -040094 bool fIsConvex;
Jim Van Vertha84898d2017-02-06 13:38:23 -050095
96 SkColor fUmbraColor;
97 SkColor fPenumbraColor;
98
99 SkScalar fRadius;
100 SkScalar fDirection;
101 int fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400102 SkVector fPrevOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500103 SkPoint fPrevPoint;
Brian Salomon958fbc42017-01-30 17:01:28 -0500104};
105
Jim Van Verthda965502017-04-11 15:29:14 -0400106static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
Jim Van Verthbce74962017-01-25 09:39:46 -0500107 SkVector* newNormal) {
108 SkVector normal;
109 // compute perpendicular
110 normal.fX = p0.fY - p1.fY;
111 normal.fY = p1.fX - p0.fX;
Jim Van Verthda965502017-04-11 15:29:14 -0400112 normal *= dir;
Jim Van Verthbce74962017-01-25 09:39:46 -0500113 if (!normal.normalize()) {
114 return false;
115 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500116 *newNormal = normal;
117 return true;
118}
119
Jim Van Verthf507c282018-05-11 10:48:20 -0400120static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
121 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
122 static constexpr SkScalar kCloseSqd = kClose * kClose;
123
124 SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
125 return distSq < kCloseSqd;
126}
127
128static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
129 SkVector v0 = p1 - p0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400130 SkVector v1 = p2 - p1;
Jim Van Verthf507c282018-05-11 10:48:20 -0400131 return v0.cross(v1);
132}
133
Jim Van Verthe308a122017-05-08 14:19:30 -0400134SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
135 : fZPlaneParams(zPlaneParams)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400136 , fCentroid({0, 0})
137 , fArea(0)
138 , fLastArea(0)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400139 , fLastCross(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400140 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500141 , fSucceeded(false)
142 , fTransparent(transparent)
Jim Van Verthf507c282018-05-11 10:48:20 -0400143 , fIsConvex(true)
Brian Salomonaff27a22017-02-06 15:47:44 -0500144 , fDirection(1)
145 , fPrevUmbraIndex(-1) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500146 // child classes will set reserve for positions, colors and indices
147}
148
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400149bool SkBaseShadowTessellator::accumulateCentroid(const SkPoint& curr, const SkPoint& next) {
150 if (duplicate_pt(curr, next)) {
151 return false;
152 }
153
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400154 SkASSERT(fPathPolygon.count() > 0);
155 SkVector v0 = curr - fPathPolygon[0];
156 SkVector v1 = next - fPathPolygon[0];
157 SkScalar quadArea = v0.cross(v1);
158 fCentroid.fX += (v0.fX + v1.fX) * quadArea;
159 fCentroid.fY += (v0.fY + v1.fY) * quadArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400160 fArea += quadArea;
161 // convexity check
Jim Van Verth3645bb02018-06-26 14:58:58 -0400162 if (quadArea*fLastArea < 0) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400163 fIsConvex = false;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400164 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400165 if (0 != quadArea) {
166 fLastArea = quadArea;
167 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400168
169 return true;
170}
171
172bool SkBaseShadowTessellator::checkConvexity(const SkPoint& p0,
173 const SkPoint& p1,
174 const SkPoint& p2) {
175 SkScalar cross = perp_dot(p0, p1, p2);
176 // skip collinear point
177 if (SkScalarNearlyZero(cross)) {
178 return false;
179 }
180
181 // check for convexity
182 if (fLastCross*cross < 0) {
183 fIsConvex = false;
184 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400185 if (0 != cross) {
186 fLastCross = cross;
187 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400188
189 return true;
190}
191
192void SkBaseShadowTessellator::finishPathPolygon() {
193 if (fPathPolygon.count() > 1) {
194 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], fPathPolygon[0])) {
195 // remove coincident point
196 fPathPolygon.pop();
197 }
198 }
199
200 if (fPathPolygon.count() > 2) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400201 // do this before the final convexity check, so we use the correct fPathPolygon[0]
202 fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
203 fCentroid += fPathPolygon[0];
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400204 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
205 fPathPolygon[fPathPolygon.count() - 1],
206 fPathPolygon[0])) {
207 // remove collinear point
208 fPathPolygon[0] = fPathPolygon[fPathPolygon.count() - 1];
209 fPathPolygon.pop();
210 }
211 }
212
Jim Van Verth7deacf42018-06-08 15:13:25 -0400213 // if area is positive, winding is ccw
214 fDirection = fArea > 0 ? -1 : 1;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400215}
216
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400217void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
218 SkTDArray<int>* umbraIndices,
219 const SkTDArray<SkPoint>& penumbraPolygon,
220 SkTDArray<int>* penumbraIndices) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400221 // TODO: only create and fill indexMap when fTransparent is true?
222 SkAutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.count());
223
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400224 // find minimum indices
225 int minIndex = 0;
226 int min = (*penumbraIndices)[0];
227 for (int i = 1; i < (*penumbraIndices).count(); ++i) {
228 if ((*penumbraIndices)[i] < min) {
229 min = (*penumbraIndices)[i];
230 minIndex = i;
231 }
232 }
233 int currPenumbra = minIndex;
234
235 minIndex = 0;
236 min = (*umbraIndices)[0];
237 for (int i = 1; i < (*umbraIndices).count(); ++i) {
238 if ((*umbraIndices)[i] < min) {
239 min = (*umbraIndices)[i];
240 minIndex = i;
241 }
242 }
243 int currUmbra = minIndex;
244
245 // now find a case where the indices are equal (there should be at least one)
246 int maxPenumbraIndex = fPathPolygon.count() - 1;
247 int maxUmbraIndex = fPathPolygon.count() - 1;
248 while ((*penumbraIndices)[currPenumbra] != (*umbraIndices)[currUmbra]) {
249 if ((*penumbraIndices)[currPenumbra] < (*umbraIndices)[currUmbra]) {
250 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
251 maxPenumbraIndex = (*penumbraIndices)[currPenumbra];
252 currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
253 } else {
254 (*umbraIndices)[currUmbra] += fPathPolygon.count();
255 maxUmbraIndex = (*umbraIndices)[currUmbra];
256 currUmbra = (currUmbra + 1) % umbraPolygon.count();
257 }
258 }
259
260 *fPositions.push() = penumbraPolygon[currPenumbra];
261 *fColors.push() = fPenumbraColor;
262 int prevPenumbraIndex = 0;
263 *fPositions.push() = umbraPolygon[currUmbra];
264 *fColors.push() = fUmbraColor;
265 fPrevUmbraIndex = 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400266 indexMap[currUmbra] = 1;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400267
268 int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
269 int nextUmbra = (currUmbra + 1) % umbraPolygon.count();
270 while ((*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex ||
271 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
272
273 if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) {
274 // advance both one step
275 *fPositions.push() = penumbraPolygon[nextPenumbra];
276 *fColors.push() = fPenumbraColor;
277 int currPenumbraIndex = fPositions.count() - 1;
278
279 *fPositions.push() = umbraPolygon[nextUmbra];
280 *fColors.push() = fUmbraColor;
281 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400282 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400283
284 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
285 fPrevUmbraIndex, currUmbraIndex);
286
287 prevPenumbraIndex = currPenumbraIndex;
288 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
289 currPenumbra = nextPenumbra;
290 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
291
292 fPrevUmbraIndex = currUmbraIndex;
293 (*umbraIndices)[currUmbra] += fPathPolygon.count();
294 currUmbra = nextUmbra;
295 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
296 }
297
298 while ((*penumbraIndices)[nextPenumbra] < (*umbraIndices)[nextUmbra] &&
299 (*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex) {
300 // fill out penumbra arc
301 *fPositions.push() = penumbraPolygon[nextPenumbra];
302 *fColors.push() = fPenumbraColor;
303 int currPenumbraIndex = fPositions.count() - 1;
304
305 this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
306
307 prevPenumbraIndex = currPenumbraIndex;
308 // this ensures the ordering when we wrap around
309 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
310 currPenumbra = nextPenumbra;
311 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
312 }
313
314 while ((*umbraIndices)[nextUmbra] < (*penumbraIndices)[nextPenumbra] &&
315 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
316 // fill out umbra arc
317 *fPositions.push() = umbraPolygon[nextUmbra];
318 *fColors.push() = fUmbraColor;
319 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400320 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400321
322 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
323
324 fPrevUmbraIndex = currUmbraIndex;
325 // this ensures the ordering when we wrap around
326 (*umbraIndices)[currUmbra] += fPathPolygon.count();
327 currUmbra = nextUmbra;
328 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
329 }
330 }
331 // finish up by advancing both one step
332 *fPositions.push() = penumbraPolygon[nextPenumbra];
333 *fColors.push() = fPenumbraColor;
334 int currPenumbraIndex = fPositions.count() - 1;
335
336 *fPositions.push() = umbraPolygon[nextUmbra];
337 *fColors.push() = fUmbraColor;
338 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400339 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400340
341 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
342 fPrevUmbraIndex, currUmbraIndex);
343
344 if (fTransparent) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400345 SkTriangulateSimplePolygon(umbraPolygon.begin(), indexMap, umbraPolygon.count(),
346 &fIndices);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400347 }
348}
349
350
Jim Van Vertha84898d2017-02-06 13:38:23 -0500351// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500352#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500353static const SkScalar kQuadTolerance = 0.2f;
354static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500355#endif
Jim Van Vertha84898d2017-02-06 13:38:23 -0500356static const SkScalar kConicTolerance = 0.5f;
357
Jim Van Verth3645bb02018-06-26 14:58:58 -0400358// clamps the point to the nearest 16th of a pixel
359static void sanitize_point(const SkPoint& in, SkPoint* out) {
360 out->fX = SkScalarRoundToScalar(16.f*in.fX)*0.0625f;
361 out->fY = SkScalarRoundToScalar(16.f*in.fY)*0.0625f;
362}
363
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400364void SkBaseShadowTessellator::handleLine(const SkPoint& p) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400365 SkPoint pSanitized;
366 sanitize_point(p, &pSanitized);
367
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400368 if (fPathPolygon.count() > 0) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400369 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400370 // skip coincident point
371 return;
372 }
373 }
374
375 if (fPathPolygon.count() > 1) {
376 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
377 fPathPolygon[fPathPolygon.count() - 1],
Jim Van Verth3645bb02018-06-26 14:58:58 -0400378 pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400379 // remove collinear point
380 fPathPolygon.pop();
381 }
382 }
383
Jim Van Verth3645bb02018-06-26 14:58:58 -0400384 *fPathPolygon.push() = pSanitized;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400385}
386
Brian Salomonaff27a22017-02-06 15:47:44 -0500387void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500388 m.mapPoints(p, 1);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400389
Jim Van Vertha84898d2017-02-06 13:38:23 -0500390 this->handleLine(*p);
391}
392
Brian Salomonaff27a22017-02-06 15:47:44 -0500393void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500394#if SK_SUPPORT_GPU
Jim Van Vertha947e292018-02-26 13:54:34 -0500395 // check for degeneracy
396 SkVector v0 = pts[1] - pts[0];
397 SkVector v1 = pts[2] - pts[0];
398 if (SkScalarNearlyZero(v0.cross(v1))) {
399 return;
400 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500401 // TODO: Pull PathUtils out of Ganesh?
402 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400403 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500404 SkPoint* target = fPointBuffer.begin();
405 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
406 kQuadTolerance, &target, maxCount);
407 fPointBuffer.setCount(count);
408 for (int i = 0; i < count; i++) {
409 this->handleLine(fPointBuffer[i]);
410 }
411#else
412 // for now, just to draw something
413 this->handleLine(pts[1]);
414 this->handleLine(pts[2]);
415#endif
416}
417
Brian Salomonaff27a22017-02-06 15:47:44 -0500418void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500419 m.mapPoints(pts, 3);
420 this->handleQuad(pts);
421}
422
Brian Salomonaff27a22017-02-06 15:47:44 -0500423void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500424 m.mapPoints(pts, 4);
425#if SK_SUPPORT_GPU
426 // TODO: Pull PathUtils out of Ganesh?
427 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400428 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500429 SkPoint* target = fPointBuffer.begin();
430 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
431 kCubicTolerance, &target, maxCount);
432 fPointBuffer.setCount(count);
433 for (int i = 0; i < count; i++) {
434 this->handleLine(fPointBuffer[i]);
435 }
436#else
437 // for now, just to draw something
438 this->handleLine(pts[1]);
439 this->handleLine(pts[2]);
440 this->handleLine(pts[3]);
441#endif
442}
443
Brian Salomonaff27a22017-02-06 15:47:44 -0500444void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400445 if (m.hasPerspective()) {
446 w = SkConic::TransformW(pts, w, m);
447 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500448 m.mapPoints(pts, 3);
449 SkAutoConicToQuads quadder;
450 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
451 SkPoint lastPoint = *(quads++);
452 int count = quadder.countQuads();
453 for (int i = 0; i < count; ++i) {
454 SkPoint quadPts[3];
455 quadPts[0] = lastPoint;
456 quadPts[1] = quads[0];
457 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
458 this->handleQuad(quadPts);
459 lastPoint = quadPts[2];
460 quads += 2;
461 }
462}
463
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400464bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500465 // fill in fan from previous quad
466 SkScalar rotSin, rotCos;
467 int numSteps;
Jim Van Verth061cc212018-07-11 14:09:09 -0400468 if (!SkComputeRadialSteps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps)) {
469 // recover as best we can
470 numSteps = 0;
471 }
Jim Van Verth76387852017-05-16 16:55:16 -0400472 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400473 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400474 SkVector currNormal;
475 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
476 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
477 *fPositions.push() = fPrevPoint + currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500478 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400479 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500480
Jim Van Verthda965502017-04-11 15:29:14 -0400481 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500482 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400483 if (finishArc && numSteps) {
Jim Van Verthda965502017-04-11 15:29:14 -0400484 *fPositions.push() = fPrevPoint + nextNormal;
485 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400486 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Verthda965502017-04-11 15:29:14 -0400487 }
Jim Van Verth76387852017-05-16 16:55:16 -0400488 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400489
490 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500491}
492
Jim Van Verth872da6b2018-04-10 11:24:11 -0400493void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
494 auto indices = fIndices.append(3);
495
496 indices[0] = index0;
497 indices[1] = index1;
498 indices[2] = index2;
499}
500
501void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
502 uint16_t index2, uint16_t index3) {
503 auto indices = fIndices.append(6);
504
505 indices[0] = index0;
506 indices[1] = index1;
507 indices[2] = index2;
508
509 indices[3] = index2;
510 indices[4] = index1;
511 indices[5] = index3;
512}
513
Jim Van Vertha84898d2017-02-06 13:38:23 -0500514//////////////////////////////////////////////////////////////////////////////////////////////////
515
Brian Salomonaff27a22017-02-06 15:47:44 -0500516class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500517public:
518 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400519 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500520
521private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400522 bool computePathPolygon(const SkPath& path, const SkMatrix& ctm);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400523 bool computeConvexShadow();
524 bool computeConcaveShadow();
525
Jim Van Verth7deacf42018-06-08 15:13:25 -0400526 void handlePolyPoint(const SkPoint& p, bool finalPoint);
527 void addEdge(const SkPoint& nextPoint, const SkVector& nextNormal, bool finalEdge);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500528
Jim Van Verth76387852017-05-16 16:55:16 -0400529 static constexpr auto kInsetFactor = -0.5f;
Jim Van Verthda965502017-04-11 15:29:14 -0400530
Brian Salomonaff27a22017-02-06 15:47:44 -0500531 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500532};
533
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500534SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500535 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400536 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500537 bool transparent)
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400538 : INHERITED(zPlaneParams, transparent) {
Jim Van Verthda965502017-04-11 15:29:14 -0400539 // Set base colors
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400540 SkScalar baseZ = heightFunc(path.getBounds().centerX(), path.getBounds().centerY());
541 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(baseZ));
Jim Van Verthb4366552017-03-27 14:25:29 -0400542 // umbraColor is the interior value, penumbraColor the exterior value.
543 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
544 // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
545 // the final alpha.
Jim Van Verth060d9822017-05-04 09:58:17 -0400546 fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
547 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400548 fRadius = SkDrawShadowMetrics::AmbientBlurRadius(baseZ);
Jim Van Verthda965502017-04-11 15:29:14 -0400549
Jim Van Verth3645bb02018-06-26 14:58:58 -0400550 if (!this->computePathPolygon(path, ctm)) {
551 return;
552 }
553 if (fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
554 fSucceeded = true; // We don't want to try to blur these cases, so we will
555 // return an empty SkVertices instead.
556 return;
557 }
558
Jim Van Verthbce74962017-01-25 09:39:46 -0500559 // Outer ring: 3*numPts
560 // Middle ring: numPts
561 fPositions.setReserve(4 * path.countPoints());
562 fColors.setReserve(4 * path.countPoints());
563 // Outer ring: 12*numPts
564 // Middle ring: 0
565 fIndices.setReserve(12 * path.countPoints());
566
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400567 if (fIsConvex) {
568 fSucceeded = this->computeConvexShadow();
569 } else {
570 fSucceeded = this->computeConcaveShadow();
571 }
572}
573
Jim Van Verth3645bb02018-06-26 14:58:58 -0400574bool SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400575 fPathPolygon.setReserve(path.countPoints());
576
577 // walk around the path, tessellate and generate outer ring
578 // if original path is transparent, will accumulate sum of points for centroid
579 SkPath::Iter iter(path, true);
580 SkPoint pts[4];
581 SkPath::Verb verb;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400582 bool verbSeen = false;
583 bool closeSeen = false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400584 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400585 if (closeSeen) {
586 return false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400587 }
Jim Van Verth3645bb02018-06-26 14:58:58 -0400588 switch (verb) {
589 case SkPath::kLine_Verb:
590 this->handleLine(ctm, &pts[1]);
591 break;
592 case SkPath::kQuad_Verb:
593 this->handleQuad(ctm, pts);
594 break;
595 case SkPath::kCubic_Verb:
596 this->handleCubic(ctm, pts);
597 break;
598 case SkPath::kConic_Verb:
599 this->handleConic(ctm, pts, iter.conicWeight());
600 break;
601 case SkPath::kMove_Verb:
602 if (verbSeen) {
603 return false;
604 }
605 break;
606 case SkPath::kClose_Verb:
607 case SkPath::kDone_Verb:
608 closeSeen = true;
609 break;
610 }
611 verbSeen = true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400612 }
613
614 this->finishPathPolygon();
Jim Van Verth3645bb02018-06-26 14:58:58 -0400615 return true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400616}
617
618bool SkAmbientShadowTessellator::computeConvexShadow() {
Jim Van Verth7deacf42018-06-08 15:13:25 -0400619 int polyCount = fPathPolygon.count();
620 if (polyCount < 3) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400621 return false;
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500622 }
623
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400624 // Add center point for fan if needed
625 if (fTransparent) {
626 *fPositions.push() = fCentroid;
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400627 *fColors.push() = fUmbraColor;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400628 }
629
Jim Van Verth7deacf42018-06-08 15:13:25 -0400630 // Initialize
631 SkVector normal;
632 if (!compute_normal(fPathPolygon[polyCount-1], fPathPolygon[0], fDirection, &normal)) {
633 // the polygon should be sanitized, so any issues at this point are unrecoverable
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400634 return false;
Jim Van Verthf507c282018-05-11 10:48:20 -0400635 }
Jim Van Verth7deacf42018-06-08 15:13:25 -0400636 fFirstPoint = fPathPolygon[polyCount - 1];
637 fFirstVertexIndex = fPositions.count();
Jim Van Verth7deacf42018-06-08 15:13:25 -0400638 fFirstOutset = normal;
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400639 fFirstOutset *= fRadius;
Jim Van Verthf507c282018-05-11 10:48:20 -0400640
Jim Van Verth7deacf42018-06-08 15:13:25 -0400641 fPrevOutset = fFirstOutset;
642 fPrevPoint = fFirstPoint;
643 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500644
Jim Van Verth7deacf42018-06-08 15:13:25 -0400645 // Add the first quad
646 *fPositions.push() = fFirstPoint;
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400647 *fColors.push() = fUmbraColor;
Jim Van Verth7deacf42018-06-08 15:13:25 -0400648 *fPositions.push() = fFirstPoint + fFirstOutset;
649 *fColors.push() = fPenumbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -0400650
Jim Van Verth7deacf42018-06-08 15:13:25 -0400651 this->addEdge(fPathPolygon[0], normal, false);
Jim Van Verthbce74962017-01-25 09:39:46 -0500652
Jim Van Verth7deacf42018-06-08 15:13:25 -0400653 // Process the remaining points
654 for (int i = 1; i < fPathPolygon.count(); ++i) {
655 this->handlePolyPoint(fPathPolygon[i], i == fPathPolygon.count()-1);
656 }
657 SkASSERT(this->indexCount());
Jim Van Verthda965502017-04-11 15:29:14 -0400658
Jim Van Verth7deacf42018-06-08 15:13:25 -0400659 // Final fan
660 SkASSERT(fPositions.count() >= 3);
661 if (this->addArc(fFirstOutset, false)) {
662 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1, fFirstVertexIndex + 1);
663 } else {
664 // arc is too small, set the first penumbra point to be the same position
665 // as the last one
666 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verthbce74962017-01-25 09:39:46 -0500667 }
668
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400669 return true;
Jim Van Verthbce74962017-01-25 09:39:46 -0500670}
671
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400672bool SkAmbientShadowTessellator::computeConcaveShadow() {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400673 if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400674 return false;
Jim Van Verthf507c282018-05-11 10:48:20 -0400675 }
676
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400677 // generate inner ring
678 SkTDArray<SkPoint> umbraPolygon;
679 SkTDArray<int> umbraIndices;
680 umbraIndices.setReserve(fPathPolygon.count());
681 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), 0.5f,
682 &umbraPolygon, &umbraIndices)) {
683 // TODO: figure out how to handle this case
684 return false;
685 }
686
687 // generate outer ring
688 SkTDArray<SkPoint> penumbraPolygon;
689 SkTDArray<int> penumbraIndices;
690 penumbraPolygon.setReserve(umbraPolygon.count());
691 penumbraIndices.setReserve(umbraPolygon.count());
Jim Van Verthe39bc9f2018-06-14 14:43:00 -0400692
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400693 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -fRadius,
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400694 &penumbraPolygon, &penumbraIndices)) {
695 // TODO: figure out how to handle this case
696 return false;
697 }
698
699 if (!umbraPolygon.count() || !penumbraPolygon.count()) {
700 return false;
701 }
702
703 // attach the rings together
704 this->stitchConcaveRings(umbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
705
706 return true;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400707}
708
Jim Van Verth7deacf42018-06-08 15:13:25 -0400709void SkAmbientShadowTessellator::handlePolyPoint(const SkPoint& p, bool finalPoint) {
Jim Van Verthbce74962017-01-25 09:39:46 -0500710 SkVector normal;
Jim Van Verth76387852017-05-16 16:55:16 -0400711 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400712 SkVector scaledNormal = normal;
713 scaledNormal *= fRadius;
714 this->addArc(scaledNormal, true);
Jim Van Verth7deacf42018-06-08 15:13:25 -0400715 this->addEdge(p, normal, finalPoint);
Jim Van Verthbce74962017-01-25 09:39:46 -0500716 }
717}
718
Jim Van Verth7deacf42018-06-08 15:13:25 -0400719void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
720 bool finalEdge) {
Jim Van Verth76387852017-05-16 16:55:16 -0400721 // We compute the inset in two stages: first we inset by half the current normal,
722 // then on the next addEdge() we add half of the next normal to get an average of the two
723 SkVector insetNormal = nextNormal;
724 insetNormal *= 0.5f*kInsetFactor;
725
726 // Adding the other half of the average for the previous edge
727 fPositions[fPrevUmbraIndex] += insetNormal;
728
Jim Van Verth7deacf42018-06-08 15:13:25 -0400729 SkPoint umbraPoint;
730 if (finalEdge) {
731 // Again, adding the other half of the average for the previous edge
732 fPositions[fFirstVertexIndex] += insetNormal;
Jim Van Verth7deacf42018-06-08 15:13:25 -0400733 umbraPoint = fPositions[fFirstVertexIndex];
734 } else {
735 umbraPoint = nextPoint + insetNormal;
736 }
Jim Van Verth76387852017-05-16 16:55:16 -0400737 SkVector outsetNormal = nextNormal;
738 outsetNormal *= fRadius;
739 SkPoint penumbraPoint = nextPoint + outsetNormal;
740
Jim Van Verth7deacf42018-06-08 15:13:25 -0400741 // add next quad
742 int prevPenumbraIndex;
743 int currUmbraIndex;
744 if (finalEdge) {
745 prevPenumbraIndex = fPositions.count() - 1;
746 currUmbraIndex = fFirstVertexIndex;
747 } else {
748 prevPenumbraIndex = fPositions.count() - 1;
749 *fPositions.push() = umbraPoint;
750 *fColors.push() = fUmbraColor;
751 currUmbraIndex = fPositions.count() - 1;
752 }
753
754 *fPositions.push() = penumbraPoint;
755 *fColors.push() = fPenumbraColor;
756
757 // set triangularization to get best interpolation of color
758 if (fColors[fPrevUmbraIndex] > fUmbraColor) {
759 this->appendQuad(fPrevUmbraIndex, prevPenumbraIndex,
760 currUmbraIndex, fPositions.count() - 1);
761 } else {
762 this->appendQuad(currUmbraIndex, fPositions.count() - 1,
763 fPrevUmbraIndex, prevPenumbraIndex);
764 }
765
766 // if transparent, add to center fan
767 if (fTransparent) {
768 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
769 }
770
771 fPrevUmbraIndex = currUmbraIndex;
772 fPrevPoint = nextPoint;
773 fPrevOutset = outsetNormal;
774}
775
Jim Van Verth91af7272017-01-27 14:15:54 -0500776///////////////////////////////////////////////////////////////////////////////////////////////////
777
Brian Salomonaff27a22017-02-06 15:47:44 -0500778class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500779public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500780 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400781 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400782 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500783
Brian Salomon958fbc42017-01-30 17:01:28 -0500784private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400785 bool computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400786 const SkMatrix& shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000787 void computeClipVectorsAndTestCentroid();
Brian Salomon66085ed2017-02-02 15:39:34 -0500788 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
Brian Salomonab664fa2017-03-24 16:07:20 +0000789 int getClosestUmbraPoint(const SkPoint& point);
Brian Salomon958fbc42017-01-30 17:01:28 -0500790
Jim Van Verth872da6b2018-04-10 11:24:11 -0400791 bool computeConvexShadow(SkScalar radius);
792 bool computeConcaveShadow(SkScalar radius);
793
Jim Van Verth7deacf42018-06-08 15:13:25 -0400794 bool handlePolyPoint(const SkPoint& p, bool lastPoint);
Brian Salomon958fbc42017-01-30 17:01:28 -0500795
796 void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
Jim Van Verth8c2de8f2018-05-14 11:20:58 -0400797 bool addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex);
Jim Van Verth7deacf42018-06-08 15:13:25 -0400798 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal, bool lastEdge);
Jim Van Verthf507c282018-05-11 10:48:20 -0400799 void addToClip(const SkVector& nextPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400800
801 SkScalar offset(SkScalar z) {
802 float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
803 return fLightRadius*zRatio;
804 }
805
806 SkScalar fLightZ;
807 SkScalar fLightRadius;
808 SkScalar fOffsetAdjust;
Brian Salomon958fbc42017-01-30 17:01:28 -0500809
Brian Salomon958fbc42017-01-30 17:01:28 -0500810 SkTDArray<SkPoint> fClipPolygon;
Brian Salomon66085ed2017-02-02 15:39:34 -0500811 SkTDArray<SkVector> fClipVectors;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500812
Brian Salomonab664fa2017-03-24 16:07:20 +0000813 SkTDArray<SkPoint> fUmbraPolygon;
814 int fCurrClipPoint;
815 int fCurrUmbraPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -0500816 bool fPrevUmbraOutside;
817 bool fFirstUmbraOutside;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500818 bool fValidUmbra;
Brian Salomon958fbc42017-01-30 17:01:28 -0500819
Brian Salomonaff27a22017-02-06 15:47:44 -0500820 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -0500821};
822
Jim Van Vertha84898d2017-02-06 13:38:23 -0500823SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400824 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -0400825 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -0400826 bool transparent)
Jim Van Verthe308a122017-05-08 14:19:30 -0400827 : INHERITED(zPlaneParams, transparent)
Jim Van Verthda965502017-04-11 15:29:14 -0400828 , fLightZ(lightPos.fZ)
829 , fLightRadius(lightRadius)
830 , fOffsetAdjust(0)
831 , fCurrClipPoint(0)
832 , fPrevUmbraOutside(false)
833 , fFirstUmbraOutside(false)
834 , fValidUmbra(true) {
835
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400836 const SkRect& pathBounds = path.getBounds();
Jim Van Verthb4366552017-03-27 14:25:29 -0400837
838 // Set radius and colors
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400839 SkScalar occluderHeight = this->heightFunc(pathBounds.centerX(), pathBounds.centerY());
Jim Van Verth060d9822017-05-04 09:58:17 -0400840 fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
841 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400842
Jim Van Verth1af03d42017-07-31 09:34:58 -0400843 // Compute the blur radius, scale and translation for the spot shadow.
844 SkScalar radius;
Jim Van Verthda965502017-04-11 15:29:14 -0400845 SkMatrix shadowTransform;
846 if (!ctm.hasPerspective()) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400847 SkScalar scale;
848 SkVector translate;
849 SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, fLightZ,
850 lightRadius, &radius, &scale, &translate);
Jim Van Verthda965502017-04-11 15:29:14 -0400851 shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400852 shadowTransform.preConcat(ctm);
Jim Van Verthda965502017-04-11 15:29:14 -0400853 } else {
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400854 // get rotated quad in 3D
855 SkPoint pts[4];
856 ctm.mapRectToQuad(pts, pathBounds);
857 SkPoint3 pts3D[4];
858 SkScalar z = this->heightFunc(pathBounds.fLeft, pathBounds.fTop);
859 pts3D[0].set(pts[0].fX, pts[0].fY, z);
860 z = this->heightFunc(pathBounds.fRight, pathBounds.fTop);
861 pts3D[1].set(pts[1].fX, pts[1].fY, z);
862 z = this->heightFunc(pathBounds.fRight, pathBounds.fBottom);
863 pts3D[2].set(pts[2].fX, pts[2].fY, z);
864 z = this->heightFunc(pathBounds.fLeft, pathBounds.fBottom);
865 pts3D[3].set(pts[3].fX, pts[3].fY, z);
866
867 // project from light through corners to z=0 plane
868 for (int i = 0; i < 4; ++i) {
869 SkScalar zRatio = pts3D[i].fZ / (fLightZ - pts3D[i].fZ);
870 pts3D[i].fX -= (lightPos.fX - pts3D[i].fX)*zRatio;
871 pts3D[i].fY -= (lightPos.fY - pts3D[i].fY)*zRatio;
872 pts3D[i].fZ = SK_Scalar1;
873 }
874
875 // Generate matrix that projects from [-1,1]x[-1,1] square to projected quad
876 SkPoint3 h0, h1, h2;
877 // Compute crossing point between top and bottom edges (gives new x-axis).
878 h0 = (pts3D[1].cross(pts3D[0])).cross(pts3D[2].cross(pts3D[3]));
879 // Compute crossing point between left and right edges (gives new y-axis).
880 h1 = (pts3D[0].cross(pts3D[3])).cross(pts3D[1].cross(pts3D[2]));
881 // Compute crossing point between diagonals (gives new origin).
882 h2 = (pts3D[0].cross(pts3D[2])).cross(pts3D[1].cross(pts3D[3]));
883 // In some cases the crossing points are in the wrong direction
884 // to map (-1,-1) to pts3D[0], so we need to correct for that.
885 // Want h0 to be to the right of the left edge.
886 SkVector3 v = pts3D[3] - pts3D[0];
887 SkVector3 w = h0 - pts3D[0];
888 SkScalar perpDot = v.fX*w.fY - v.fY*w.fX;
889 if (perpDot > 0) {
890 h0 = -h0;
891 }
892 // Want h1 to be above the bottom edge.
893 v = pts3D[1] - pts3D[0];
894 perpDot = v.fX*w.fY - v.fY*w.fX;
895 if (perpDot < 0) {
896 h1 = -h1;
897 }
898 shadowTransform.setAll(h0.fX / h2.fZ, h1.fX / h2.fZ, h2.fX / h2.fZ,
899 h0.fY / h2.fZ, h1.fY / h2.fZ, h2.fY / h2.fZ,
900 h0.fZ / h2.fZ, h1.fZ / h2.fZ, 1);
901 // generate matrix that transforms from bounds to [-1,1]x[-1,1] square
902 SkMatrix toHomogeneous;
903 SkScalar xScale = 2/(pathBounds.fRight - pathBounds.fLeft);
904 SkScalar yScale = 2/(pathBounds.fBottom - pathBounds.fTop);
905 toHomogeneous.setAll(xScale, 0, -xScale*pathBounds.fLeft - 1,
906 0, yScale, -yScale*pathBounds.fTop - 1,
907 0, 0, 1);
908 shadowTransform.preConcat(toHomogeneous);
909
Jim Van Verth1af03d42017-07-31 09:34:58 -0400910 radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
Jim Van Verthda965502017-04-11 15:29:14 -0400911 }
Jim Van Verth1af03d42017-07-31 09:34:58 -0400912 fRadius = radius;
Jim Van Verthb4366552017-03-27 14:25:29 -0400913
Brian Salomonab664fa2017-03-24 16:07:20 +0000914 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verth3645bb02018-06-26 14:58:58 -0400915 if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) {
916 return;
917 }
918 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
919 fSucceeded = true; // We don't want to try to blur these cases, so we will
920 // return an empty SkVertices instead.
Brian Salomon66085ed2017-02-02 15:39:34 -0500921 return;
922 }
Brian Salomon66085ed2017-02-02 15:39:34 -0500923
Jim Van Verthf507c282018-05-11 10:48:20 -0400924 // compute vectors for clip tests
925 this->computeClipVectorsAndTestCentroid();
926
Brian Salomonab664fa2017-03-24 16:07:20 +0000927 // check to see if umbra collapses
Jim Van Verthf507c282018-05-11 10:48:20 -0400928 if (fIsConvex) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400929 SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
930 fPathPolygon[0],
931 fPathPolygon[1]);
932 SkRect bounds;
933 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
934 for (int i = 1; i < fPathPolygon.count(); ++i) {
935 int j = i + 1;
936 if (i == fPathPolygon.count() - 1) {
937 j = 0;
938 }
939 SkPoint currPoint = fPathPolygon[i];
940 SkPoint nextPoint = fPathPolygon[j];
941 SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
942 nextPoint);
943 if (distSq < minDistSq) {
944 minDistSq = distSq;
945 }
Brian Salomonab664fa2017-03-24 16:07:20 +0000946 }
Jim Van Verth872da6b2018-04-10 11:24:11 -0400947 static constexpr auto kTolerance = 1.0e-2f;
948 if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
949 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
950 SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
951 fOffsetAdjust = newRadius - radius;
952 SkScalar ratio = 128 * (newRadius + radius) / radius;
953 // they aren't PMColors, but the interpolation algorithm is the same
954 fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
955 radius = newRadius;
Brian Salomonab664fa2017-03-24 16:07:20 +0000956 }
957 }
Jim Van Verth91af7272017-01-27 14:15:54 -0500958
Jim Van Verth3645bb02018-06-26 14:58:58 -0400959 // TODO: calculate these reserves better
960 // Penumbra ring: 3*numPts
961 // Umbra ring: numPts
962 // Inner ring: numPts
963 fPositions.setReserve(5 * path.countPoints());
964 fColors.setReserve(5 * path.countPoints());
965 // Penumbra ring: 12*numPts
966 // Umbra ring: 3*numPts
967 fIndices.setReserve(15 * path.countPoints());
968
Jim Van Verthf507c282018-05-11 10:48:20 -0400969 if (fIsConvex) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400970 fSucceeded = this->computeConvexShadow(radius);
Jim Van Verth872da6b2018-04-10 11:24:11 -0400971 } else {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400972 fSucceeded = this->computeConcaveShadow(radius);
973 }
974
975 if (!fSucceeded) {
Jim Van Verthf507c282018-05-11 10:48:20 -0400976 return;
Jim Van Verth91af7272017-01-27 14:15:54 -0500977 }
Jim Van Verthda965502017-04-11 15:29:14 -0400978
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500979 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -0500980}
981
Jim Van Verthf507c282018-05-11 10:48:20 -0400982void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
983 if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count()-1])) {
984 *fClipPolygon.push() = point;
985 }
986}
987
Jim Van Verth3645bb02018-06-26 14:58:58 -0400988bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400989 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +0000990
991 fPathPolygon.setReserve(path.countPoints());
Jim Van Verth3645bb02018-06-26 14:58:58 -0400992 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +0000993
994 // Walk around the path and compute clip polygon and path polygon.
995 // Will also accumulate sum of areas for centroid.
996 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -0500997 SkPath::Iter iter(path, true);
998 SkPoint pts[4];
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400999 SkPoint clipPts[4];
Jim Van Verth91af7272017-01-27 14:15:54 -05001000 SkPath::Verb verb;
1001
Brian Salomon66085ed2017-02-02 15:39:34 -05001002 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +00001003 static constexpr SkScalar kA = 0.32495117187f;
1004 static constexpr SkScalar kB = 0.44311523437f;
1005 static constexpr SkScalar kC = 0.20141601562f;
1006 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -05001007
1008 SkPoint curvePoint;
1009 SkScalar w;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001010 bool closeSeen = false;
1011 bool verbSeen = false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001012 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001013 if (closeSeen) {
1014 return false;
1015 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001016 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001017 case SkPath::kLine_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001018 ctm.mapPoints(clipPts, &pts[1], 1);
1019 this->addToClip(clipPts[0]);
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001020 this->handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -05001021 break;
1022 case SkPath::kQuad_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001023 ctm.mapPoints(clipPts, pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001024 // point at t = 1/2
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001025 curvePoint.fX = 0.25f*clipPts[0].fX + 0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1026 curvePoint.fY = 0.25f*clipPts[0].fY + 0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001027 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001028 this->addToClip(clipPts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001029 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001030 break;
1031 case SkPath::kConic_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001032 ctm.mapPoints(clipPts, pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001033 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -05001034 // point at t = 1/2
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001035 curvePoint.fX = 0.25f*clipPts[0].fX + w*0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1036 curvePoint.fY = 0.25f*clipPts[0].fY + w*0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY;
Brian Salomon66085ed2017-02-02 15:39:34 -05001037 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
Jim Van Verthf507c282018-05-11 10:48:20 -04001038 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001039 this->addToClip(clipPts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001040 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -05001041 break;
1042 case SkPath::kCubic_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001043 ctm.mapPoints(clipPts, pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -05001044 // point at t = 5/16
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001045 curvePoint.fX = kA*clipPts[0].fX + kB*clipPts[1].fX
1046 + kC*clipPts[2].fX + kD*clipPts[3].fX;
1047 curvePoint.fY = kA*clipPts[0].fY + kB*clipPts[1].fY
1048 + kC*clipPts[2].fY + kD*clipPts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001049 this->addToClip(curvePoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001050 // point at t = 11/16
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001051 curvePoint.fX = kD*clipPts[0].fX + kC*clipPts[1].fX
1052 + kB*clipPts[2].fX + kA*clipPts[3].fX;
1053 curvePoint.fY = kD*clipPts[0].fY + kC*clipPts[1].fY
1054 + kB*clipPts[2].fY + kA*clipPts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001055 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001056 this->addToClip(clipPts[3]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001057 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001058 break;
Brian Salomonab664fa2017-03-24 16:07:20 +00001059 case SkPath::kMove_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001060 if (verbSeen) {
1061 return false;
1062 }
1063 break;
Jim Van Verth91af7272017-01-27 14:15:54 -05001064 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +00001065 case SkPath::kDone_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001066 closeSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001067 break;
1068 default:
1069 SkDEBUGFAIL("unknown verb");
1070 }
Jim Van Verth3645bb02018-06-26 14:58:58 -04001071 verbSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001072 }
1073
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001074 this->finishPathPolygon();
Brian Salomonab664fa2017-03-24 16:07:20 +00001075 fCurrClipPoint = fClipPolygon.count() - 1;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001076 return true;
Brian Salomon66085ed2017-02-02 15:39:34 -05001077}
1078
Brian Salomonab664fa2017-03-24 16:07:20 +00001079void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
Brian Salomon66085ed2017-02-02 15:39:34 -05001080 SkASSERT(fClipPolygon.count() >= 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001081
Brian Salomonab664fa2017-03-24 16:07:20 +00001082 // init clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001083 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
Jim Van Verthf507c282018-05-11 10:48:20 -04001084 SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001085 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001086
1087 // init centroid check
1088 bool hiddenCentroid = true;
Jim Van Verthf507c282018-05-11 10:48:20 -04001089 v1 = fCentroid - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001090 SkScalar initCross = v0.cross(v1);
1091
1092 for (int p = 1; p < fClipPolygon.count(); ++p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001093 // add to clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001094 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
1095 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001096 // Determine if transformed centroid is inside clipPolygon.
Brian Salomonab664fa2017-03-24 16:07:20 +00001097 v1 = fCentroid - fClipPolygon[p];
Brian Salomon66085ed2017-02-02 15:39:34 -05001098 if (initCross*v0.cross(v1) <= 0) {
1099 hiddenCentroid = false;
1100 }
1101 }
1102 SkASSERT(fClipVectors.count() == fClipPolygon.count());
1103
Brian Salomonab664fa2017-03-24 16:07:20 +00001104 fTransparent = fTransparent || !hiddenCentroid;
Brian Salomon66085ed2017-02-02 15:39:34 -05001105}
1106
1107bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
1108 SkPoint* clipPoint) {
1109 SkVector segmentVector = centroid - umbraPoint;
1110
Brian Salomonab664fa2017-03-24 16:07:20 +00001111 int startClipPoint = fCurrClipPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001112 do {
Brian Salomonab664fa2017-03-24 16:07:20 +00001113 SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
1114 SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
Brian Salomon66085ed2017-02-02 15:39:34 -05001115 SkScalar t_num = dp.cross(segmentVector);
1116 // if line segments are nearly parallel
1117 if (SkScalarNearlyZero(denom)) {
1118 // and collinear
1119 if (SkScalarNearlyZero(t_num)) {
1120 return false;
1121 }
1122 // otherwise are separate, will try the next poly segment
1123 // else if crossing lies within poly segment
1124 } else if (t_num >= 0 && t_num <= denom) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001125 SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
Brian Salomon66085ed2017-02-02 15:39:34 -05001126 // if umbra point is inside the clip polygon
Jim Van Verthda965502017-04-11 15:29:14 -04001127 if (s_num >= 0 && s_num <= denom) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001128 segmentVector *= s_num/denom;
1129 *clipPoint = umbraPoint + segmentVector;
1130 return true;
1131 }
1132 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001133 fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
1134 } while (fCurrClipPoint != startClipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001135
1136 return false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001137}
1138
Brian Salomonab664fa2017-03-24 16:07:20 +00001139int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
Cary Clarkdf429f32017-11-08 11:44:31 -05001140 SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraPoint]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001141 int index = fCurrUmbraPoint;
1142 int dir = 1;
1143 int next = (index + dir) % fUmbraPolygon.count();
1144
1145 // init travel direction
Cary Clarkdf429f32017-11-08 11:44:31 -05001146 SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001147 if (distance < minDistance) {
1148 index = next;
1149 minDistance = distance;
1150 } else {
1151 dir = fUmbraPolygon.count()-1;
1152 }
1153
1154 // iterate until we find a point that increases the distance
1155 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001156 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001157 while (distance < minDistance) {
1158 index = next;
1159 minDistance = distance;
1160 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001161 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001162 }
1163
1164 fCurrUmbraPoint = index;
1165 return index;
1166}
1167
Jim Van Verth872da6b2018-04-10 11:24:11 -04001168bool SkSpotShadowTessellator::computeConvexShadow(SkScalar radius) {
1169 // generate inner ring
1170 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1171 &fUmbraPolygon)) {
1172 // this shouldn't happen, but just in case we'll inset using the centroid
1173 fValidUmbra = false;
1174 }
1175
1176 // walk around the path polygon, generate outer ring and connect to inner ring
1177 if (fTransparent) {
1178 *fPositions.push() = fCentroid;
1179 *fColors.push() = fUmbraColor;
1180 }
1181 fCurrUmbraPoint = 0;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001182
Jim Van Verth7deacf42018-06-08 15:13:25 -04001183 // initial setup
1184 // add first quad
1185 int polyCount = fPathPolygon.count();
1186 if (!compute_normal(fPathPolygon[polyCount-1], fPathPolygon[0], fDirection, &fFirstOutset)) {
1187 // polygon should be sanitized by this point, so this is unrecoverable
Jim Van Verth872da6b2018-04-10 11:24:11 -04001188 return false;
1189 }
1190
Jim Van Verth7deacf42018-06-08 15:13:25 -04001191 fFirstOutset *= fRadius;
1192 fFirstPoint = fPathPolygon[polyCount - 1];
1193 fFirstVertexIndex = fPositions.count();
1194 fPrevOutset = fFirstOutset;
1195 fPrevPoint = fFirstPoint;
1196 fPrevUmbraIndex = -1;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001197
Jim Van Verth7deacf42018-06-08 15:13:25 -04001198 this->addInnerPoint(fFirstPoint, &fPrevUmbraIndex);
1199
1200 if (!fTransparent) {
1201 SkPoint clipPoint;
1202 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
1203 fCentroid, &clipPoint);
1204 if (isOutside) {
1205 *fPositions.push() = clipPoint;
1206 *fColors.push() = fUmbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001207 }
Jim Van Verth7deacf42018-06-08 15:13:25 -04001208 fPrevUmbraOutside = isOutside;
1209 fFirstUmbraOutside = isOutside;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001210 }
1211
Jim Van Verth7deacf42018-06-08 15:13:25 -04001212 SkPoint newPoint = fFirstPoint + fFirstOutset;
1213 *fPositions.push() = newPoint;
1214 *fColors.push() = fPenumbraColor;
1215 this->addEdge(fPathPolygon[0], fFirstOutset, false);
1216
1217 for (int i = 1; i < polyCount; ++i) {
1218 if (!this->handlePolyPoint(fPathPolygon[i], i == polyCount-1)) {
1219 return false;
1220 }
1221 }
1222 SkASSERT(this->indexCount());
1223
Jim Van Verth872da6b2018-04-10 11:24:11 -04001224 // final fan
Jim Van Verth7deacf42018-06-08 15:13:25 -04001225 SkASSERT(fPositions.count() >= 3);
1226 if (this->addArc(fFirstOutset, false)) {
1227 if (fFirstUmbraOutside) {
1228 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1229 fFirstVertexIndex + 2);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001230 } else {
Jim Van Verth7deacf42018-06-08 15:13:25 -04001231 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1232 fFirstVertexIndex + 1);
1233 }
1234 } else {
1235 // no arc added, fix up by setting first penumbra point position to last one
1236 if (fFirstUmbraOutside) {
1237 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
1238 } else {
1239 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verth872da6b2018-04-10 11:24:11 -04001240 }
1241 }
1242
1243 return true;
1244}
1245
1246bool SkSpotShadowTessellator::computeConcaveShadow(SkScalar radius) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001247 if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001248 return false;
1249 }
1250
1251 // generate inner ring
1252 SkTDArray<int> umbraIndices;
1253 umbraIndices.setReserve(fPathPolygon.count());
1254 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1255 &fUmbraPolygon, &umbraIndices)) {
1256 // TODO: figure out how to handle this case
1257 return false;
1258 }
1259
1260 // generate outer ring
1261 SkTDArray<SkPoint> penumbraPolygon;
1262 SkTDArray<int> penumbraIndices;
1263 penumbraPolygon.setReserve(fUmbraPolygon.count());
1264 penumbraIndices.setReserve(fUmbraPolygon.count());
1265 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -radius,
1266 &penumbraPolygon, &penumbraIndices)) {
1267 // TODO: figure out how to handle this case
1268 return false;
1269 }
1270
1271 if (!fUmbraPolygon.count() || !penumbraPolygon.count()) {
1272 return false;
1273 }
1274
1275 // attach the rings together
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001276 this->stitchConcaveRings(fUmbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001277
1278 return true;
1279}
1280
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001281void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
Jim Van Verth91af7272017-01-27 14:15:54 -05001282 SkPoint* pts, int count) {
1283 // TODO: vectorize
1284 for (int i = 0; i < count; ++i) {
1285 pts[i] *= scale;
1286 pts[i] += xlate;
1287 }
1288}
1289
Jim Van Verth7deacf42018-06-08 15:13:25 -04001290bool SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p, bool lastPoint) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001291 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -04001292 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
1293 normal *= fRadius;
1294 this->addArc(normal, true);
Jim Van Verth7deacf42018-06-08 15:13:25 -04001295 this->addEdge(p, normal, lastPoint);
Jim Van Verth91af7272017-01-27 14:15:54 -05001296 }
Jim Van Verthb55eb282017-07-18 14:13:45 -04001297
1298 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001299}
1300
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001301bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001302 SkPoint umbraPoint;
1303 if (!fValidUmbra) {
1304 SkVector v = fCentroid - pathPoint;
1305 v *= 0.95f;
1306 umbraPoint = pathPoint + v;
Jim Van Verth91af7272017-01-27 14:15:54 -05001307 } else {
Brian Salomonab664fa2017-03-24 16:07:20 +00001308 umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
Jim Van Verth91af7272017-01-27 14:15:54 -05001309 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001310
Jim Van Verth91af7272017-01-27 14:15:54 -05001311 fPrevPoint = pathPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001312
1313 // merge "close" points
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001314 if (fPrevUmbraIndex == -1 ||
Brian Salomonab664fa2017-03-24 16:07:20 +00001315 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001316 // if we've wrapped around, don't add a new point
1317 if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
1318 *currUmbraIndex = fFirstVertexIndex;
1319 } else {
1320 *currUmbraIndex = fPositions.count();
1321 *fPositions.push() = umbraPoint;
1322 *fColors.push() = fUmbraColor;
1323 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001324 return false;
1325 } else {
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001326 *currUmbraIndex = fPrevUmbraIndex;
Brian Salomonab664fa2017-03-24 16:07:20 +00001327 return true;
1328 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001329}
1330
Jim Van Verth7deacf42018-06-08 15:13:25 -04001331void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
1332 bool lastEdge) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001333 // add next umbra point
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001334 int currUmbraIndex;
Jim Van Verth7deacf42018-06-08 15:13:25 -04001335 bool duplicate;
1336 if (lastEdge) {
1337 duplicate = false;
1338 currUmbraIndex = fFirstVertexIndex;
1339 fPrevPoint = nextPoint;
1340 } else {
1341 duplicate = this->addInnerPoint(nextPoint, &currUmbraIndex);
1342 }
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001343 int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
1344 ? fPositions.count()-1
1345 : fPositions.count()-2;
Brian Salomonab664fa2017-03-24 16:07:20 +00001346 if (!duplicate) {
1347 // add to center fan if transparent or centroid showing
1348 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001349 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001350 // otherwise add to clip ring
1351 } else {
Brian Salomon66085ed2017-02-02 15:39:34 -05001352 SkPoint clipPoint;
Jim Van Verth7deacf42018-06-08 15:13:25 -04001353 bool isOutside = lastEdge ? fFirstUmbraOutside
1354 : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
1355 &clipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001356 if (isOutside) {
Jim Van Verth7deacf42018-06-08 15:13:25 -04001357 if (!lastEdge) {
1358 *fPositions.push() = clipPoint;
1359 *fColors.push() = fUmbraColor;
1360 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001361 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001362 if (fPrevUmbraOutside) {
1363 // fill out quad
Jim Van Verth872da6b2018-04-10 11:24:11 -04001364 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
1365 fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001366 }
1367 } else if (fPrevUmbraOutside) {
1368 // add tri
Jim Van Verth872da6b2018-04-10 11:24:11 -04001369 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001370 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001371
Brian Salomon66085ed2017-02-02 15:39:34 -05001372 fPrevUmbraOutside = isOutside;
1373 }
1374 }
1375
1376 // add next penumbra point and quad
Jim Van Verth91af7272017-01-27 14:15:54 -05001377 SkPoint newPoint = nextPoint + nextNormal;
1378 *fPositions.push() = newPoint;
1379 *fColors.push() = fPenumbraColor;
1380
Brian Salomonab664fa2017-03-24 16:07:20 +00001381 if (!duplicate) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001382 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001383 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001384 this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
Jim Van Verth91af7272017-01-27 14:15:54 -05001385
Brian Salomon66085ed2017-02-02 15:39:34 -05001386 fPrevUmbraIndex = currUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -04001387 fPrevOutset = nextNormal;
Jim Van Verth91af7272017-01-27 14:15:54 -05001388}
Brian Salomon958fbc42017-01-30 17:01:28 -05001389
1390///////////////////////////////////////////////////////////////////////////////////////////////////
1391
Brian Salomonaff27a22017-02-06 15:47:44 -05001392sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001393 const SkPoint3& zPlane, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001394 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001395 return nullptr;
1396 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001397 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001398 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001399}
1400
Brian Salomonaff27a22017-02-06 15:47:44 -05001401sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001402 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001403 SkScalar lightRadius, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001404 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() ||
Jim Van Verth1989c492018-05-31 13:15:16 -04001405 !lightPos.isFinite() || !(lightPos.fZ >= SK_ScalarNearlyZero) ||
1406 !SkScalarIsFinite(lightRadius) || !(lightRadius >= SK_ScalarNearlyZero)) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001407 return nullptr;
1408 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001409 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001410 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001411}