blob: b485f3f68cf76d7a3d70ab26f702511fed191b26 [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 Verthda965502017-04-11 15:29:14 -040046 bool setZOffset(const SkRect& bounds, bool perspective);
47
Jim Van Verthcdaf6612018-06-05 15:21:13 -040048 bool accumulateCentroid(const SkPoint& c, const SkPoint& n);
49 bool checkConvexity(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2);
50 void finishPathPolygon();
Jim Van Verth8760e2f2018-06-12 14:21:38 -040051 void stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
52 SkTDArray<int>* umbraIndices,
53 const SkTDArray<SkPoint>& penumbraPolygon,
54 SkTDArray<int>* penumbraIndices);
Jim Van Verthcdaf6612018-06-05 15:21:13 -040055
56 void handleLine(const SkPoint& p);
Jim Van Vertha84898d2017-02-06 13:38:23 -050057 void handleLine(const SkMatrix& m, SkPoint* p);
Brian Salomon958fbc42017-01-30 17:01:28 -050058
59 void handleQuad(const SkPoint pts[3]);
Jim Van Vertha84898d2017-02-06 13:38:23 -050060 void handleQuad(const SkMatrix& m, SkPoint pts[3]);
Brian Salomon958fbc42017-01-30 17:01:28 -050061
Jim Van Vertha84898d2017-02-06 13:38:23 -050062 void handleCubic(const SkMatrix& m, SkPoint pts[4]);
Brian Salomon958fbc42017-01-30 17:01:28 -050063
Jim Van Vertha84898d2017-02-06 13:38:23 -050064 void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
Brian Salomon958fbc42017-01-30 17:01:28 -050065
Jim Van Verthda965502017-04-11 15:29:14 -040066 bool setTransformedHeightFunc(const SkMatrix& ctm);
Brian Salomon958fbc42017-01-30 17:01:28 -050067
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -040068 bool addArc(const SkVector& nextNormal, bool finishArc);
Jim Van Verthb4366552017-03-27 14:25:29 -040069
Jim Van Verth872da6b2018-04-10 11:24:11 -040070 void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
71 void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
72
Jim Van Verthe308a122017-05-08 14:19:30 -040073 SkScalar heightFunc(SkScalar x, SkScalar y) {
74 return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
75 }
76
77 SkPoint3 fZPlaneParams;
Jim Van Verthda965502017-04-11 15:29:14 -040078 std::function<SkScalar(const SkPoint&)> fTransformedHeightFunc;
79 SkScalar fZOffset;
80 // members for perspective height function
Jim Van Verth4c9b8932017-05-15 13:49:21 -040081 SkPoint3 fTransformedZParams;
Jim Van Verthda965502017-04-11 15:29:14 -040082 SkScalar fPartialDeterminants[3];
83
Brian Salomon958fbc42017-01-30 17:01:28 -050084 // temporary buffer
85 SkTDArray<SkPoint> fPointBuffer;
Brian Salomon0dda9cb2017-02-03 10:33:25 -050086
Jim Van Vertha84898d2017-02-06 13:38:23 -050087 SkTDArray<SkPoint> fPositions;
88 SkTDArray<SkColor> fColors;
89 SkTDArray<uint16_t> fIndices;
90
Jim Van Verthcdaf6612018-06-05 15:21:13 -040091 SkTDArray<SkPoint> fPathPolygon;
92 SkPoint fCentroid;
93 SkScalar fArea;
94 SkScalar fLastArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -040095 SkScalar fLastCross;
96
Jim Van Verth76387852017-05-16 16:55:16 -040097 int fFirstVertexIndex;
98 SkVector fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -050099 SkPoint fFirstPoint;
100
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500101 bool fSucceeded;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500102 bool fTransparent;
Jim Van Verthf507c282018-05-11 10:48:20 -0400103 bool fIsConvex;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500104
105 SkColor fUmbraColor;
106 SkColor fPenumbraColor;
107
108 SkScalar fRadius;
109 SkScalar fDirection;
110 int fPrevUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -0400111 SkVector fPrevOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500112 SkPoint fPrevPoint;
Brian Salomon958fbc42017-01-30 17:01:28 -0500113};
114
Jim Van Verthda965502017-04-11 15:29:14 -0400115static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
Jim Van Verthbce74962017-01-25 09:39:46 -0500116 SkVector* newNormal) {
117 SkVector normal;
118 // compute perpendicular
119 normal.fX = p0.fY - p1.fY;
120 normal.fY = p1.fX - p0.fX;
Jim Van Verthda965502017-04-11 15:29:14 -0400121 normal *= dir;
Jim Van Verthbce74962017-01-25 09:39:46 -0500122 if (!normal.normalize()) {
123 return false;
124 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500125 *newNormal = normal;
126 return true;
127}
128
Jim Van Verthf507c282018-05-11 10:48:20 -0400129static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
130 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
131 static constexpr SkScalar kCloseSqd = kClose * kClose;
132
133 SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
134 return distSq < kCloseSqd;
135}
136
137static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
138 SkVector v0 = p1 - p0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400139 SkVector v1 = p2 - p1;
Jim Van Verthf507c282018-05-11 10:48:20 -0400140 return v0.cross(v1);
141}
142
Jim Van Verthe308a122017-05-08 14:19:30 -0400143SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
144 : fZPlaneParams(zPlaneParams)
Jim Van Verthda965502017-04-11 15:29:14 -0400145 , fZOffset(0)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400146 , fCentroid({0, 0})
147 , fArea(0)
148 , fLastArea(0)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400149 , fLastCross(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400150 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500151 , fSucceeded(false)
152 , fTransparent(transparent)
Jim Van Verthf507c282018-05-11 10:48:20 -0400153 , fIsConvex(true)
Brian Salomonaff27a22017-02-06 15:47:44 -0500154 , fDirection(1)
155 , fPrevUmbraIndex(-1) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500156 // child classes will set reserve for positions, colors and indices
157}
158
Jim Van Verthda965502017-04-11 15:29:14 -0400159bool SkBaseShadowTessellator::setZOffset(const SkRect& bounds, bool perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400160 SkScalar minZ = this->heightFunc(bounds.fLeft, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400161 if (perspective) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400162 SkScalar z = this->heightFunc(bounds.fLeft, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400163 if (z < minZ) {
164 minZ = z;
165 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400166 z = this->heightFunc(bounds.fRight, bounds.fTop);
Jim Van Verthda965502017-04-11 15:29:14 -0400167 if (z < minZ) {
168 minZ = z;
169 }
Jim Van Verthe308a122017-05-08 14:19:30 -0400170 z = this->heightFunc(bounds.fRight, bounds.fBottom);
Jim Van Verthda965502017-04-11 15:29:14 -0400171 if (z < minZ) {
172 minZ = z;
173 }
174 }
175
176 if (minZ < kMinHeight) {
177 fZOffset = -minZ + kMinHeight;
178 return true;
179 }
180
181 return false;
182}
183
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400184bool SkBaseShadowTessellator::accumulateCentroid(const SkPoint& curr, const SkPoint& next) {
185 if (duplicate_pt(curr, next)) {
186 return false;
187 }
188
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400189 SkASSERT(fPathPolygon.count() > 0);
190 SkVector v0 = curr - fPathPolygon[0];
191 SkVector v1 = next - fPathPolygon[0];
192 SkScalar quadArea = v0.cross(v1);
193 fCentroid.fX += (v0.fX + v1.fX) * quadArea;
194 fCentroid.fY += (v0.fY + v1.fY) * quadArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400195 fArea += quadArea;
196 // convexity check
Jim Van Verth3645bb02018-06-26 14:58:58 -0400197 if (quadArea*fLastArea < 0) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400198 fIsConvex = false;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400199 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400200 if (0 != quadArea) {
201 fLastArea = quadArea;
202 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400203
204 return true;
205}
206
207bool SkBaseShadowTessellator::checkConvexity(const SkPoint& p0,
208 const SkPoint& p1,
209 const SkPoint& p2) {
210 SkScalar cross = perp_dot(p0, p1, p2);
211 // skip collinear point
212 if (SkScalarNearlyZero(cross)) {
213 return false;
214 }
215
216 // check for convexity
217 if (fLastCross*cross < 0) {
218 fIsConvex = false;
219 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400220 if (0 != cross) {
221 fLastCross = cross;
222 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400223
224 return true;
225}
226
227void SkBaseShadowTessellator::finishPathPolygon() {
228 if (fPathPolygon.count() > 1) {
229 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], fPathPolygon[0])) {
230 // remove coincident point
231 fPathPolygon.pop();
232 }
233 }
234
235 if (fPathPolygon.count() > 2) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400236 // do this before the final convexity check, so we use the correct fPathPolygon[0]
237 fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
238 fCentroid += fPathPolygon[0];
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400239 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
240 fPathPolygon[fPathPolygon.count() - 1],
241 fPathPolygon[0])) {
242 // remove collinear point
243 fPathPolygon[0] = fPathPolygon[fPathPolygon.count() - 1];
244 fPathPolygon.pop();
245 }
246 }
247
Jim Van Verth7deacf42018-06-08 15:13:25 -0400248 // if area is positive, winding is ccw
249 fDirection = fArea > 0 ? -1 : 1;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400250}
251
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400252void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
253 SkTDArray<int>* umbraIndices,
254 const SkTDArray<SkPoint>& penumbraPolygon,
255 SkTDArray<int>* penumbraIndices) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400256 // TODO: only create and fill indexMap when fTransparent is true?
257 SkAutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.count());
258
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400259 // find minimum indices
260 int minIndex = 0;
261 int min = (*penumbraIndices)[0];
262 for (int i = 1; i < (*penumbraIndices).count(); ++i) {
263 if ((*penumbraIndices)[i] < min) {
264 min = (*penumbraIndices)[i];
265 minIndex = i;
266 }
267 }
268 int currPenumbra = minIndex;
269
270 minIndex = 0;
271 min = (*umbraIndices)[0];
272 for (int i = 1; i < (*umbraIndices).count(); ++i) {
273 if ((*umbraIndices)[i] < min) {
274 min = (*umbraIndices)[i];
275 minIndex = i;
276 }
277 }
278 int currUmbra = minIndex;
279
280 // now find a case where the indices are equal (there should be at least one)
281 int maxPenumbraIndex = fPathPolygon.count() - 1;
282 int maxUmbraIndex = fPathPolygon.count() - 1;
283 while ((*penumbraIndices)[currPenumbra] != (*umbraIndices)[currUmbra]) {
284 if ((*penumbraIndices)[currPenumbra] < (*umbraIndices)[currUmbra]) {
285 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
286 maxPenumbraIndex = (*penumbraIndices)[currPenumbra];
287 currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
288 } else {
289 (*umbraIndices)[currUmbra] += fPathPolygon.count();
290 maxUmbraIndex = (*umbraIndices)[currUmbra];
291 currUmbra = (currUmbra + 1) % umbraPolygon.count();
292 }
293 }
294
295 *fPositions.push() = penumbraPolygon[currPenumbra];
296 *fColors.push() = fPenumbraColor;
297 int prevPenumbraIndex = 0;
298 *fPositions.push() = umbraPolygon[currUmbra];
299 *fColors.push() = fUmbraColor;
300 fPrevUmbraIndex = 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400301 indexMap[currUmbra] = 1;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400302
303 int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
304 int nextUmbra = (currUmbra + 1) % umbraPolygon.count();
305 while ((*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex ||
306 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
307
308 if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) {
309 // advance both one step
310 *fPositions.push() = penumbraPolygon[nextPenumbra];
311 *fColors.push() = fPenumbraColor;
312 int currPenumbraIndex = fPositions.count() - 1;
313
314 *fPositions.push() = umbraPolygon[nextUmbra];
315 *fColors.push() = fUmbraColor;
316 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400317 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400318
319 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
320 fPrevUmbraIndex, currUmbraIndex);
321
322 prevPenumbraIndex = currPenumbraIndex;
323 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
324 currPenumbra = nextPenumbra;
325 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
326
327 fPrevUmbraIndex = currUmbraIndex;
328 (*umbraIndices)[currUmbra] += fPathPolygon.count();
329 currUmbra = nextUmbra;
330 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
331 }
332
333 while ((*penumbraIndices)[nextPenumbra] < (*umbraIndices)[nextUmbra] &&
334 (*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex) {
335 // fill out penumbra arc
336 *fPositions.push() = penumbraPolygon[nextPenumbra];
337 *fColors.push() = fPenumbraColor;
338 int currPenumbraIndex = fPositions.count() - 1;
339
340 this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
341
342 prevPenumbraIndex = currPenumbraIndex;
343 // this ensures the ordering when we wrap around
344 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
345 currPenumbra = nextPenumbra;
346 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
347 }
348
349 while ((*umbraIndices)[nextUmbra] < (*penumbraIndices)[nextPenumbra] &&
350 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
351 // fill out umbra arc
352 *fPositions.push() = umbraPolygon[nextUmbra];
353 *fColors.push() = fUmbraColor;
354 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400355 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400356
357 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
358
359 fPrevUmbraIndex = currUmbraIndex;
360 // this ensures the ordering when we wrap around
361 (*umbraIndices)[currUmbra] += fPathPolygon.count();
362 currUmbra = nextUmbra;
363 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
364 }
365 }
366 // finish up by advancing both one step
367 *fPositions.push() = penumbraPolygon[nextPenumbra];
368 *fColors.push() = fPenumbraColor;
369 int currPenumbraIndex = fPositions.count() - 1;
370
371 *fPositions.push() = umbraPolygon[nextUmbra];
372 *fColors.push() = fUmbraColor;
373 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400374 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400375
376 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
377 fPrevUmbraIndex, currUmbraIndex);
378
379 if (fTransparent) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400380 SkTriangulateSimplePolygon(umbraPolygon.begin(), indexMap, umbraPolygon.count(),
381 &fIndices);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400382 }
383}
384
385
Jim Van Vertha84898d2017-02-06 13:38:23 -0500386// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500387#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500388static const SkScalar kQuadTolerance = 0.2f;
389static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500390#endif
Jim Van Vertha84898d2017-02-06 13:38:23 -0500391static const SkScalar kConicTolerance = 0.5f;
392
Jim Van Verth3645bb02018-06-26 14:58:58 -0400393// clamps the point to the nearest 16th of a pixel
394static void sanitize_point(const SkPoint& in, SkPoint* out) {
395 out->fX = SkScalarRoundToScalar(16.f*in.fX)*0.0625f;
396 out->fY = SkScalarRoundToScalar(16.f*in.fY)*0.0625f;
397}
398
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400399void SkBaseShadowTessellator::handleLine(const SkPoint& p) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400400 SkPoint pSanitized;
401 sanitize_point(p, &pSanitized);
402
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400403 if (fPathPolygon.count() > 0) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400404 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400405 // skip coincident point
406 return;
407 }
408 }
409
410 if (fPathPolygon.count() > 1) {
411 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
412 fPathPolygon[fPathPolygon.count() - 1],
Jim Van Verth3645bb02018-06-26 14:58:58 -0400413 pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400414 // remove collinear point
415 fPathPolygon.pop();
416 }
417 }
418
Jim Van Verth3645bb02018-06-26 14:58:58 -0400419 *fPathPolygon.push() = pSanitized;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400420}
421
Brian Salomonaff27a22017-02-06 15:47:44 -0500422void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500423 m.mapPoints(p, 1);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400424
Jim Van Vertha84898d2017-02-06 13:38:23 -0500425 this->handleLine(*p);
426}
427
Brian Salomonaff27a22017-02-06 15:47:44 -0500428void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500429#if SK_SUPPORT_GPU
Jim Van Vertha947e292018-02-26 13:54:34 -0500430 // check for degeneracy
431 SkVector v0 = pts[1] - pts[0];
432 SkVector v1 = pts[2] - pts[0];
433 if (SkScalarNearlyZero(v0.cross(v1))) {
434 return;
435 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500436 // TODO: Pull PathUtils out of Ganesh?
437 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400438 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500439 SkPoint* target = fPointBuffer.begin();
440 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
441 kQuadTolerance, &target, maxCount);
442 fPointBuffer.setCount(count);
443 for (int i = 0; i < count; i++) {
444 this->handleLine(fPointBuffer[i]);
445 }
446#else
447 // for now, just to draw something
448 this->handleLine(pts[1]);
449 this->handleLine(pts[2]);
450#endif
451}
452
Brian Salomonaff27a22017-02-06 15:47:44 -0500453void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500454 m.mapPoints(pts, 3);
455 this->handleQuad(pts);
456}
457
Brian Salomonaff27a22017-02-06 15:47:44 -0500458void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500459 m.mapPoints(pts, 4);
460#if SK_SUPPORT_GPU
461 // TODO: Pull PathUtils out of Ganesh?
462 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400463 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500464 SkPoint* target = fPointBuffer.begin();
465 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
466 kCubicTolerance, &target, maxCount);
467 fPointBuffer.setCount(count);
468 for (int i = 0; i < count; i++) {
469 this->handleLine(fPointBuffer[i]);
470 }
471#else
472 // for now, just to draw something
473 this->handleLine(pts[1]);
474 this->handleLine(pts[2]);
475 this->handleLine(pts[3]);
476#endif
477}
478
Brian Salomonaff27a22017-02-06 15:47:44 -0500479void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400480 if (m.hasPerspective()) {
481 w = SkConic::TransformW(pts, w, m);
482 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500483 m.mapPoints(pts, 3);
484 SkAutoConicToQuads quadder;
485 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
486 SkPoint lastPoint = *(quads++);
487 int count = quadder.countQuads();
488 for (int i = 0; i < count; ++i) {
489 SkPoint quadPts[3];
490 quadPts[0] = lastPoint;
491 quadPts[1] = quads[0];
492 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
493 this->handleQuad(quadPts);
494 lastPoint = quadPts[2];
495 quads += 2;
496 }
497}
498
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400499bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500500 // fill in fan from previous quad
501 SkScalar rotSin, rotCos;
502 int numSteps;
Jim Van Verth061cc212018-07-11 14:09:09 -0400503 if (!SkComputeRadialSteps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps)) {
504 // recover as best we can
505 numSteps = 0;
506 }
Jim Van Verth76387852017-05-16 16:55:16 -0400507 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400508 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400509 SkVector currNormal;
510 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
511 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
512 *fPositions.push() = fPrevPoint + currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500513 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400514 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500515
Jim Van Verthda965502017-04-11 15:29:14 -0400516 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500517 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400518 if (finishArc && numSteps) {
Jim Van Verthda965502017-04-11 15:29:14 -0400519 *fPositions.push() = fPrevPoint + nextNormal;
520 *fColors.push() = fPenumbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -0400521 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Verthda965502017-04-11 15:29:14 -0400522 }
Jim Van Verth76387852017-05-16 16:55:16 -0400523 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400524
525 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500526}
527
Jim Van Verth872da6b2018-04-10 11:24:11 -0400528void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
529 auto indices = fIndices.append(3);
530
531 indices[0] = index0;
532 indices[1] = index1;
533 indices[2] = index2;
534}
535
536void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
537 uint16_t index2, uint16_t index3) {
538 auto indices = fIndices.append(6);
539
540 indices[0] = index0;
541 indices[1] = index1;
542 indices[2] = index2;
543
544 indices[3] = index2;
545 indices[4] = index1;
546 indices[5] = index3;
547}
548
Jim Van Verthda965502017-04-11 15:29:14 -0400549bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400550 if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400551 fTransformedHeightFunc = [this](const SkPoint& p) {
Jim Van Verthe308a122017-05-08 14:19:30 -0400552 return fZPlaneParams.fZ;
Jim Van Verthda965502017-04-11 15:29:14 -0400553 };
554 } else {
555 SkMatrix ctmInverse;
Jim Van Vertha947e292018-02-26 13:54:34 -0500556 if (!ctm.invert(&ctmInverse) || !ctmInverse.isFinite()) {
Jim Van Verthda965502017-04-11 15:29:14 -0400557 return false;
558 }
Jim Van Verthda965502017-04-11 15:29:14 -0400559 // multiply by transpose
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400560 fTransformedZParams = SkPoint3::Make(
Jim Van Verthe308a122017-05-08 14:19:30 -0400561 ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
562 ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
563 ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
564
565 ctmInverse[SkMatrix::kMSkewX] * fZPlaneParams.fX +
566 ctmInverse[SkMatrix::kMScaleY] * fZPlaneParams.fY +
567 ctmInverse[SkMatrix::kMPersp1] * fZPlaneParams.fZ,
568
569 ctmInverse[SkMatrix::kMTransX] * fZPlaneParams.fX +
570 ctmInverse[SkMatrix::kMTransY] * fZPlaneParams.fY +
571 ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
572 );
Jim Van Verthda965502017-04-11 15:29:14 -0400573
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400574 if (ctm.hasPerspective()) {
575 // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
576 // so pre-compute those values that are independent of X and Y.
577 // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
578 fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
579 ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
580 fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
581 ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
582 fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
583 ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
584 SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
585 ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
586 ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
Jim Van Verthda965502017-04-11 15:29:14 -0400587
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400588 // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
589 // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
590 fTransformedZParams.fX *= ctmDeterminant;
591 fTransformedZParams.fY *= ctmDeterminant;
592 fTransformedZParams.fZ *= ctmDeterminant;
Jim Van Verthda965502017-04-11 15:29:14 -0400593
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400594 fTransformedHeightFunc = [this](const SkPoint& p) {
595 SkScalar denom = p.fX * fPartialDeterminants[0] +
596 p.fY * fPartialDeterminants[1] +
597 fPartialDeterminants[2];
598 SkScalar w = SkScalarFastInvert(denom);
599 return fZOffset + w*(fTransformedZParams.fX * p.fX +
600 fTransformedZParams.fY * p.fY +
601 fTransformedZParams.fZ);
602 };
603 } else {
604 fTransformedHeightFunc = [this](const SkPoint& p) {
605 return fZOffset + fTransformedZParams.fX * p.fX +
606 fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
607 };
608 }
Jim Van Verthda965502017-04-11 15:29:14 -0400609 }
610
611 return true;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500612}
613
614
615//////////////////////////////////////////////////////////////////////////////////////////////////
616
Brian Salomonaff27a22017-02-06 15:47:44 -0500617class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500618public:
619 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400620 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500621
622private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400623 bool computePathPolygon(const SkPath& path, const SkMatrix& ctm);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400624 bool computeConvexShadow();
625 bool computeConcaveShadow();
626
Jim Van Verth7deacf42018-06-08 15:13:25 -0400627 void handlePolyPoint(const SkPoint& p, bool finalPoint);
628 void addEdge(const SkPoint& nextPoint, const SkVector& nextNormal, bool finalEdge);
629 void splitEdge(const SkPoint& nextPoint, const SkVector& insetNormal,
630 const SkPoint& penumbraPoint, const SkPoint& umbraPoint, SkColor umbraColor);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500631
Jim Van Verthda965502017-04-11 15:29:14 -0400632 static constexpr auto kMaxEdgeLenSqr = 20 * 20;
Jim Van Verth76387852017-05-16 16:55:16 -0400633 static constexpr auto kInsetFactor = -0.5f;
Jim Van Verthda965502017-04-11 15:29:14 -0400634
635 SkScalar offset(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400636 return SkDrawShadowMetrics::AmbientBlurRadius(z);
Jim Van Verthda965502017-04-11 15:29:14 -0400637 }
638 SkColor umbraColor(SkScalar z) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400639 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(z));
Jim Van Verth060d9822017-05-04 09:58:17 -0400640 return SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
Jim Van Verthda965502017-04-11 15:29:14 -0400641 }
642
Jim Van Verth76387852017-05-16 16:55:16 -0400643 bool fSplitFirstEdge;
644 bool fSplitPreviousEdge;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500645
Brian Salomonaff27a22017-02-06 15:47:44 -0500646 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500647};
648
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500649SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500650 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400651 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500652 bool transparent)
Jim Van Verth76387852017-05-16 16:55:16 -0400653 : INHERITED(zPlaneParams, transparent)
654 , fSplitFirstEdge(false)
655 , fSplitPreviousEdge(false) {
Jim Van Verthda965502017-04-11 15:29:14 -0400656 // Set base colors
Jim Van Verth1af03d42017-07-31 09:34:58 -0400657 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(heightFunc(0, 0)));
Jim Van Verthb4366552017-03-27 14:25:29 -0400658 // umbraColor is the interior value, penumbraColor the exterior value.
659 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
660 // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
661 // the final alpha.
Jim Van Verth060d9822017-05-04 09:58:17 -0400662 fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
663 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400664
Jim Van Verthda965502017-04-11 15:29:14 -0400665 // make sure we're not below the canvas plane
666 this->setZOffset(path.getBounds(), ctm.hasPerspective());
667
Jim Van Verth7c8008c2018-02-07 15:02:50 -0500668 if (!this->setTransformedHeightFunc(ctm)) {
669 return;
670 }
Jim Van Verthda965502017-04-11 15:29:14 -0400671
Jim Van Verth3645bb02018-06-26 14:58:58 -0400672 if (!this->computePathPolygon(path, ctm)) {
673 return;
674 }
675 if (fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
676 fSucceeded = true; // We don't want to try to blur these cases, so we will
677 // return an empty SkVertices instead.
678 return;
679 }
680
Jim Van Verthbce74962017-01-25 09:39:46 -0500681 // Outer ring: 3*numPts
682 // Middle ring: numPts
683 fPositions.setReserve(4 * path.countPoints());
684 fColors.setReserve(4 * path.countPoints());
685 // Outer ring: 12*numPts
686 // Middle ring: 0
687 fIndices.setReserve(12 * path.countPoints());
688
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400689 if (fIsConvex) {
690 fSucceeded = this->computeConvexShadow();
691 } else {
692 fSucceeded = this->computeConcaveShadow();
693 }
694}
695
Jim Van Verth3645bb02018-06-26 14:58:58 -0400696bool SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400697 fPathPolygon.setReserve(path.countPoints());
698
699 // walk around the path, tessellate and generate outer ring
700 // if original path is transparent, will accumulate sum of points for centroid
701 SkPath::Iter iter(path, true);
702 SkPoint pts[4];
703 SkPath::Verb verb;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400704 bool verbSeen = false;
705 bool closeSeen = false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400706 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400707 if (closeSeen) {
708 return false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400709 }
Jim Van Verth3645bb02018-06-26 14:58:58 -0400710 switch (verb) {
711 case SkPath::kLine_Verb:
712 this->handleLine(ctm, &pts[1]);
713 break;
714 case SkPath::kQuad_Verb:
715 this->handleQuad(ctm, pts);
716 break;
717 case SkPath::kCubic_Verb:
718 this->handleCubic(ctm, pts);
719 break;
720 case SkPath::kConic_Verb:
721 this->handleConic(ctm, pts, iter.conicWeight());
722 break;
723 case SkPath::kMove_Verb:
724 if (verbSeen) {
725 return false;
726 }
727 break;
728 case SkPath::kClose_Verb:
729 case SkPath::kDone_Verb:
730 closeSeen = true;
731 break;
732 }
733 verbSeen = true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400734 }
735
736 this->finishPathPolygon();
Jim Van Verth3645bb02018-06-26 14:58:58 -0400737 return true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400738}
739
740bool SkAmbientShadowTessellator::computeConvexShadow() {
Jim Van Verth7deacf42018-06-08 15:13:25 -0400741 int polyCount = fPathPolygon.count();
742 if (polyCount < 3) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400743 return false;
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500744 }
745
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400746 // Add center point for fan if needed
747 if (fTransparent) {
748 *fPositions.push() = fCentroid;
749 *fColors.push() = this->umbraColor(fTransformedHeightFunc(fCentroid));
750 }
751
Jim Van Verth7deacf42018-06-08 15:13:25 -0400752 // Initialize
753 SkVector normal;
754 if (!compute_normal(fPathPolygon[polyCount-1], fPathPolygon[0], fDirection, &normal)) {
755 // the polygon should be sanitized, so any issues at this point are unrecoverable
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400756 return false;
Jim Van Verthf507c282018-05-11 10:48:20 -0400757 }
Jim Van Verth7deacf42018-06-08 15:13:25 -0400758 fFirstPoint = fPathPolygon[polyCount - 1];
759 fFirstVertexIndex = fPositions.count();
760 SkScalar z = fTransformedHeightFunc(fFirstPoint);
761 fFirstOutset = normal;
762 fFirstOutset *= this->offset(z);
Jim Van Verthf507c282018-05-11 10:48:20 -0400763
Jim Van Verth7deacf42018-06-08 15:13:25 -0400764 fPrevOutset = fFirstOutset;
765 fPrevPoint = fFirstPoint;
766 fPrevUmbraIndex = fFirstVertexIndex;
Jim Van Verthbce74962017-01-25 09:39:46 -0500767
Jim Van Verth7deacf42018-06-08 15:13:25 -0400768 // Add the first quad
769 *fPositions.push() = fFirstPoint;
770 *fColors.push() = this->umbraColor(z);
771 *fPositions.push() = fFirstPoint + fFirstOutset;
772 *fColors.push() = fPenumbraColor;
Jim Van Verth76387852017-05-16 16:55:16 -0400773
Jim Van Verth7deacf42018-06-08 15:13:25 -0400774 z = fTransformedHeightFunc(fPathPolygon[0]);
775 fRadius = this->offset(z);
776 fUmbraColor = this->umbraColor(z);
777 this->addEdge(fPathPolygon[0], normal, false);
Jim Van Verthbce74962017-01-25 09:39:46 -0500778
Jim Van Verth7deacf42018-06-08 15:13:25 -0400779 // Process the remaining points
780 for (int i = 1; i < fPathPolygon.count(); ++i) {
781 this->handlePolyPoint(fPathPolygon[i], i == fPathPolygon.count()-1);
782 }
783 SkASSERT(this->indexCount());
Jim Van Verthda965502017-04-11 15:29:14 -0400784
Jim Van Verth7deacf42018-06-08 15:13:25 -0400785 // Final fan
786 SkASSERT(fPositions.count() >= 3);
787 if (this->addArc(fFirstOutset, false)) {
788 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1, fFirstVertexIndex + 1);
789 } else {
790 // arc is too small, set the first penumbra point to be the same position
791 // as the last one
792 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verthbce74962017-01-25 09:39:46 -0500793 }
794
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400795 return true;
Jim Van Verthbce74962017-01-25 09:39:46 -0500796}
797
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400798bool SkAmbientShadowTessellator::computeConcaveShadow() {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400799 if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400800 return false;
Jim Van Verthf507c282018-05-11 10:48:20 -0400801 }
802
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400803 // generate inner ring
804 SkTDArray<SkPoint> umbraPolygon;
805 SkTDArray<int> umbraIndices;
806 umbraIndices.setReserve(fPathPolygon.count());
807 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), 0.5f,
808 &umbraPolygon, &umbraIndices)) {
809 // TODO: figure out how to handle this case
810 return false;
811 }
812
813 // generate outer ring
814 SkTDArray<SkPoint> penumbraPolygon;
815 SkTDArray<int> penumbraIndices;
816 penumbraPolygon.setReserve(umbraPolygon.count());
817 penumbraIndices.setReserve(umbraPolygon.count());
Jim Van Verthe39bc9f2018-06-14 14:43:00 -0400818
819 auto offsetFunc = [this](const SkPoint& p) { return -this->offset(fTransformedHeightFunc(p)); };
820 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), offsetFunc,
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400821 &penumbraPolygon, &penumbraIndices)) {
822 // TODO: figure out how to handle this case
823 return false;
824 }
825
826 if (!umbraPolygon.count() || !penumbraPolygon.count()) {
827 return false;
828 }
829
830 // attach the rings together
831 this->stitchConcaveRings(umbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
832
833 return true;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400834}
835
Jim Van Verth7deacf42018-06-08 15:13:25 -0400836void SkAmbientShadowTessellator::handlePolyPoint(const SkPoint& p, bool finalPoint) {
Jim Van Verthbce74962017-01-25 09:39:46 -0500837 SkVector normal;
Jim Van Verth76387852017-05-16 16:55:16 -0400838 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
Jim Van Verthda965502017-04-11 15:29:14 -0400839 SkVector scaledNormal = normal;
840 scaledNormal *= fRadius;
841 this->addArc(scaledNormal, true);
842 SkScalar z = fTransformedHeightFunc(p);
843 fRadius = this->offset(z);
844 fUmbraColor = this->umbraColor(z);
Jim Van Verth7deacf42018-06-08 15:13:25 -0400845 this->addEdge(p, normal, finalPoint);
Jim Van Verthbce74962017-01-25 09:39:46 -0500846 }
847}
848
Jim Van Verth7deacf42018-06-08 15:13:25 -0400849void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
850 bool finalEdge) {
Jim Van Verth76387852017-05-16 16:55:16 -0400851 // We compute the inset in two stages: first we inset by half the current normal,
852 // then on the next addEdge() we add half of the next normal to get an average of the two
853 SkVector insetNormal = nextNormal;
854 insetNormal *= 0.5f*kInsetFactor;
855
856 // Adding the other half of the average for the previous edge
857 fPositions[fPrevUmbraIndex] += insetNormal;
858
Jim Van Verth7deacf42018-06-08 15:13:25 -0400859 SkPoint umbraPoint;
860 if (finalEdge) {
861 // Again, adding the other half of the average for the previous edge
862 fPositions[fFirstVertexIndex] += insetNormal;
863 // we multiply by another half because now we're adding to an average of an average
864 if (fSplitFirstEdge) {
865 fPositions[fFirstVertexIndex + 2] += insetNormal * 0.5f;
866 }
867 umbraPoint = fPositions[fFirstVertexIndex];
868 } else {
869 umbraPoint = nextPoint + insetNormal;
870 }
Jim Van Verth76387852017-05-16 16:55:16 -0400871 SkVector outsetNormal = nextNormal;
872 outsetNormal *= fRadius;
873 SkPoint penumbraPoint = nextPoint + outsetNormal;
874
Jim Van Verth7deacf42018-06-08 15:13:25 -0400875 // make sure we don't end up with a sharp alpha edge along the quad diagonal
876 this->splitEdge(nextPoint, insetNormal, penumbraPoint, umbraPoint, fUmbraColor);
877
878 // add next quad
879 int prevPenumbraIndex;
880 int currUmbraIndex;
881 if (finalEdge) {
882 prevPenumbraIndex = fPositions.count() - 1;
883 currUmbraIndex = fFirstVertexIndex;
884 } else {
885 prevPenumbraIndex = fPositions.count() - 1;
886 *fPositions.push() = umbraPoint;
887 *fColors.push() = fUmbraColor;
888 currUmbraIndex = fPositions.count() - 1;
889 }
890
891 *fPositions.push() = penumbraPoint;
892 *fColors.push() = fPenumbraColor;
893
894 // set triangularization to get best interpolation of color
895 if (fColors[fPrevUmbraIndex] > fUmbraColor) {
896 this->appendQuad(fPrevUmbraIndex, prevPenumbraIndex,
897 currUmbraIndex, fPositions.count() - 1);
898 } else {
899 this->appendQuad(currUmbraIndex, fPositions.count() - 1,
900 fPrevUmbraIndex, prevPenumbraIndex);
901 }
902
903 // if transparent, add to center fan
904 if (fTransparent) {
905 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
906 }
907
908 fPrevUmbraIndex = currUmbraIndex;
909 fPrevPoint = nextPoint;
910 fPrevOutset = outsetNormal;
911}
912
913void SkAmbientShadowTessellator::splitEdge(const SkPoint& nextPoint, const SkVector& insetNormal,
914 const SkPoint& penumbraPoint, const SkPoint& umbraPoint,
915 SkColor umbraColor) {
Jim Van Verth76387852017-05-16 16:55:16 -0400916 // For split edges, we're adding an average of two averages, so we multiply by another half
917 if (fSplitPreviousEdge) {
Jim Van Verth7deacf42018-06-08 15:13:25 -0400918 fPositions[fPrevUmbraIndex - 2] += insetNormal*SK_ScalarHalf;
Jim Van Verth76387852017-05-16 16:55:16 -0400919 }
920
921 // Split the edge to make sure we don't end up with a sharp alpha edge along the quad diagonal
Jim Van Verth7deacf42018-06-08 15:13:25 -0400922 if (fColors[fPrevUmbraIndex] != umbraColor &&
Cary Clarkdf429f32017-11-08 11:44:31 -0500923 SkPointPriv::DistanceToSqd(nextPoint, fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
Jim Van Verth76387852017-05-16 16:55:16 -0400924
925 // This is lacking 1/4 of the next inset -- we'll add it the next time we call addEdge()
926 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + umbraPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400927 centerPoint *= 0.5f;
928 *fPositions.push() = centerPoint;
Jim Van Verth7deacf42018-06-08 15:13:25 -0400929 *fColors.push() = SkPMLerp(umbraColor, fColors[fPrevUmbraIndex], 128);
930 centerPoint = fPositions[fPositions.count() - 2] + penumbraPoint;
Jim Van Verth76387852017-05-16 16:55:16 -0400931 centerPoint *= 0.5f;
932 *fPositions.push() = centerPoint;
Jim Van Verthda965502017-04-11 15:29:14 -0400933 *fColors.push() = fPenumbraColor;
934
935 // set triangularization to get best interpolation of color
936 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400937 this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
938 fPositions.count() - 2, fPositions.count() - 1);
Jim Van Verthda965502017-04-11 15:29:14 -0400939 } else {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400940 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
941 fPrevUmbraIndex, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -0400942 }
943
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400944 // if transparent, add to center fan
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400945 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -0400946 this->appendTriangle(0, fPrevUmbraIndex, fPositions.count() - 2);
Jim Van Verth1c4c1142017-05-11 10:23:53 -0400947 }
948
Jim Van Verth76387852017-05-16 16:55:16 -0400949 fSplitPreviousEdge = true;
950 if (fPrevUmbraIndex == fFirstVertexIndex) {
951 fSplitFirstEdge = true;
952 }
Jim Van Verthda965502017-04-11 15:29:14 -0400953 fPrevUmbraIndex = fPositions.count() - 2;
Jim Van Verth76387852017-05-16 16:55:16 -0400954 } else {
955 fSplitPreviousEdge = false;
Jim Van Verthda965502017-04-11 15:29:14 -0400956 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500957}
Jim Van Verth91af7272017-01-27 14:15:54 -0500958
Jim Van Verth7deacf42018-06-08 15:13:25 -0400959
Jim Van Verth91af7272017-01-27 14:15:54 -0500960///////////////////////////////////////////////////////////////////////////////////////////////////
961
Brian Salomonaff27a22017-02-06 15:47:44 -0500962class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500963public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500964 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400965 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400966 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500967
Brian Salomon958fbc42017-01-30 17:01:28 -0500968private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400969 bool computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400970 const SkMatrix& shadowTransform);
Brian Salomonab664fa2017-03-24 16:07:20 +0000971 void computeClipVectorsAndTestCentroid();
Brian Salomon66085ed2017-02-02 15:39:34 -0500972 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
Brian Salomonab664fa2017-03-24 16:07:20 +0000973 int getClosestUmbraPoint(const SkPoint& point);
Brian Salomon958fbc42017-01-30 17:01:28 -0500974
Jim Van Verth872da6b2018-04-10 11:24:11 -0400975 bool computeConvexShadow(SkScalar radius);
976 bool computeConcaveShadow(SkScalar radius);
977
Jim Van Verth7deacf42018-06-08 15:13:25 -0400978 bool handlePolyPoint(const SkPoint& p, bool lastPoint);
Brian Salomon958fbc42017-01-30 17:01:28 -0500979
980 void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
Jim Van Verth8c2de8f2018-05-14 11:20:58 -0400981 bool addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex);
Jim Van Verth7deacf42018-06-08 15:13:25 -0400982 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal, bool lastEdge);
Jim Van Verthf507c282018-05-11 10:48:20 -0400983 void addToClip(const SkVector& nextPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400984
985 SkScalar offset(SkScalar z) {
986 float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
987 return fLightRadius*zRatio;
988 }
989
990 SkScalar fLightZ;
991 SkScalar fLightRadius;
992 SkScalar fOffsetAdjust;
Brian Salomon958fbc42017-01-30 17:01:28 -0500993
Brian Salomon958fbc42017-01-30 17:01:28 -0500994 SkTDArray<SkPoint> fClipPolygon;
Brian Salomon66085ed2017-02-02 15:39:34 -0500995 SkTDArray<SkVector> fClipVectors;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500996
Brian Salomonab664fa2017-03-24 16:07:20 +0000997 SkTDArray<SkPoint> fUmbraPolygon;
998 int fCurrClipPoint;
999 int fCurrUmbraPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001000 bool fPrevUmbraOutside;
1001 bool fFirstUmbraOutside;
Jim Van Vertha84898d2017-02-06 13:38:23 -05001002 bool fValidUmbra;
Brian Salomon958fbc42017-01-30 17:01:28 -05001003
Brian Salomonaff27a22017-02-06 15:47:44 -05001004 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -05001005};
1006
Jim Van Vertha84898d2017-02-06 13:38:23 -05001007SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001008 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -04001009 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -04001010 bool transparent)
Jim Van Verthe308a122017-05-08 14:19:30 -04001011 : INHERITED(zPlaneParams, transparent)
Jim Van Verthda965502017-04-11 15:29:14 -04001012 , fLightZ(lightPos.fZ)
1013 , fLightRadius(lightRadius)
1014 , fOffsetAdjust(0)
1015 , fCurrClipPoint(0)
1016 , fPrevUmbraOutside(false)
1017 , fFirstUmbraOutside(false)
1018 , fValidUmbra(true) {
1019
1020 // make sure we're not below the canvas plane
1021 if (this->setZOffset(path.getBounds(), ctm.hasPerspective())) {
1022 // Adjust light height and radius
1023 fLightRadius *= (fLightZ + fZOffset) / fLightZ;
1024 fLightZ += fZOffset;
1025 }
Jim Van Verthb4366552017-03-27 14:25:29 -04001026
1027 // Set radius and colors
Jim Van Verthb4366552017-03-27 14:25:29 -04001028 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verthe308a122017-05-08 14:19:30 -04001029 SkScalar occluderHeight = this->heightFunc(center.fX, center.fY) + fZOffset;
Jim Van Verth060d9822017-05-04 09:58:17 -04001030 fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
1031 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -04001032
Jim Van Verth1af03d42017-07-31 09:34:58 -04001033 // Compute the blur radius, scale and translation for the spot shadow.
1034 SkScalar radius;
Jim Van Verthda965502017-04-11 15:29:14 -04001035 SkMatrix shadowTransform;
1036 if (!ctm.hasPerspective()) {
Jim Van Verth1af03d42017-07-31 09:34:58 -04001037 SkScalar scale;
1038 SkVector translate;
1039 SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, fLightZ,
1040 lightRadius, &radius, &scale, &translate);
Jim Van Verthda965502017-04-11 15:29:14 -04001041 shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
1042 } else {
1043 // For perspective, we have a scale, a z-shear, and another projective divide --
1044 // this varies at each point so we can't use an affine transform.
1045 // We'll just apply this to each generated point in turn.
1046 shadowTransform.reset();
1047 // Also can't cull the center (for now).
1048 fTransparent = true;
Jim Van Verth1af03d42017-07-31 09:34:58 -04001049 radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
Jim Van Verthda965502017-04-11 15:29:14 -04001050 }
Jim Van Verth1af03d42017-07-31 09:34:58 -04001051 fRadius = radius;
Jim Van Verthda965502017-04-11 15:29:14 -04001052 SkMatrix fullTransform = SkMatrix::Concat(shadowTransform, ctm);
1053
1054 // Set up our reverse mapping
Jim Van Verth7c8008c2018-02-07 15:02:50 -05001055 if (!this->setTransformedHeightFunc(fullTransform)) {
1056 return;
1057 }
Jim Van Verthb4366552017-03-27 14:25:29 -04001058
Brian Salomonab664fa2017-03-24 16:07:20 +00001059 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verth3645bb02018-06-26 14:58:58 -04001060 if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) {
1061 return;
1062 }
1063 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
1064 fSucceeded = true; // We don't want to try to blur these cases, so we will
1065 // return an empty SkVertices instead.
Brian Salomon66085ed2017-02-02 15:39:34 -05001066 return;
1067 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001068
Jim Van Verthf507c282018-05-11 10:48:20 -04001069 // compute vectors for clip tests
1070 this->computeClipVectorsAndTestCentroid();
1071
Brian Salomonab664fa2017-03-24 16:07:20 +00001072 // check to see if umbra collapses
Jim Van Verthf507c282018-05-11 10:48:20 -04001073 if (fIsConvex) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001074 SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
1075 fPathPolygon[0],
1076 fPathPolygon[1]);
1077 SkRect bounds;
1078 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
1079 for (int i = 1; i < fPathPolygon.count(); ++i) {
1080 int j = i + 1;
1081 if (i == fPathPolygon.count() - 1) {
1082 j = 0;
1083 }
1084 SkPoint currPoint = fPathPolygon[i];
1085 SkPoint nextPoint = fPathPolygon[j];
1086 SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
1087 nextPoint);
1088 if (distSq < minDistSq) {
1089 minDistSq = distSq;
1090 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001091 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001092 static constexpr auto kTolerance = 1.0e-2f;
1093 if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
1094 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
1095 SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
1096 fOffsetAdjust = newRadius - radius;
1097 SkScalar ratio = 128 * (newRadius + radius) / radius;
1098 // they aren't PMColors, but the interpolation algorithm is the same
1099 fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
1100 radius = newRadius;
Brian Salomonab664fa2017-03-24 16:07:20 +00001101 }
1102 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001103
Jim Van Verth3645bb02018-06-26 14:58:58 -04001104 // TODO: calculate these reserves better
1105 // Penumbra ring: 3*numPts
1106 // Umbra ring: numPts
1107 // Inner ring: numPts
1108 fPositions.setReserve(5 * path.countPoints());
1109 fColors.setReserve(5 * path.countPoints());
1110 // Penumbra ring: 12*numPts
1111 // Umbra ring: 3*numPts
1112 fIndices.setReserve(15 * path.countPoints());
1113
Jim Van Verthf507c282018-05-11 10:48:20 -04001114 if (fIsConvex) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001115 fSucceeded = this->computeConvexShadow(radius);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001116 } else {
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001117 fSucceeded = this->computeConcaveShadow(radius);
1118 }
1119
1120 if (!fSucceeded) {
Jim Van Verthf507c282018-05-11 10:48:20 -04001121 return;
Jim Van Verth91af7272017-01-27 14:15:54 -05001122 }
Jim Van Verthda965502017-04-11 15:29:14 -04001123
1124 if (ctm.hasPerspective()) {
1125 for (int i = 0; i < fPositions.count(); ++i) {
1126 SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
1127 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
1128 fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
1129 fPositions[i].fY = (fPositions[i].fY*fLightZ - lightPos.fY*pathZ)*factor;
1130 }
1131#ifdef DRAW_CENTROID
1132 SkScalar pathZ = fTransformedHeightFunc(fCentroid);
1133 SkScalar factor = SkScalarInvert(fLightZ - pathZ);
1134 fCentroid.fX = (fCentroid.fX*fLightZ - lightPos.fX*pathZ)*factor;
1135 fCentroid.fY = (fCentroid.fY*fLightZ - lightPos.fY*pathZ)*factor;
1136#endif
1137 }
1138#ifdef DRAW_CENTROID
1139 *fPositions.push() = fCentroid + SkVector::Make(-2, -2);
1140 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1141 *fPositions.push() = fCentroid + SkVector::Make(2, -2);
1142 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1143 *fPositions.push() = fCentroid + SkVector::Make(-2, 2);
1144 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1145 *fPositions.push() = fCentroid + SkVector::Make(2, 2);
1146 *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
1147
Jim Van Verth872da6b2018-04-10 11:24:11 -04001148 this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
1149 fPositions.count() - 4, fPositions.count() - 3);
Jim Van Verthda965502017-04-11 15:29:14 -04001150#endif
1151
Brian Salomon0dda9cb2017-02-03 10:33:25 -05001152 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001153}
1154
Jim Van Verthf507c282018-05-11 10:48:20 -04001155void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
1156 if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count()-1])) {
1157 *fClipPolygon.push() = point;
1158 }
1159}
1160
Jim Van Verth3645bb02018-06-26 14:58:58 -04001161bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -04001162 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001163
1164 fPathPolygon.setReserve(path.countPoints());
Jim Van Verth3645bb02018-06-26 14:58:58 -04001165 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +00001166
1167 // Walk around the path and compute clip polygon and path polygon.
1168 // Will also accumulate sum of areas for centroid.
1169 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -05001170 SkPath::Iter iter(path, true);
1171 SkPoint pts[4];
1172 SkPath::Verb verb;
1173
Brian Salomon66085ed2017-02-02 15:39:34 -05001174 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +00001175 static constexpr SkScalar kA = 0.32495117187f;
1176 static constexpr SkScalar kB = 0.44311523437f;
1177 static constexpr SkScalar kC = 0.20141601562f;
1178 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -05001179
1180 SkPoint curvePoint;
1181 SkScalar w;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001182 bool closeSeen = false;
1183 bool verbSeen = false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001184 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001185 if (closeSeen) {
1186 return false;
1187 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001188 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001189 case SkPath::kLine_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001190 ctm.mapPoints(&pts[1], 1);
Jim Van Verthf507c282018-05-11 10:48:20 -04001191 this->addToClip(pts[1]);
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001192 this->handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -05001193 break;
1194 case SkPath::kQuad_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001195 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001196 // point at t = 1/2
1197 curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
1198 curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001199 this->addToClip(curvePoint);
1200 this->addToClip(pts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001201 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001202 break;
1203 case SkPath::kConic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001204 ctm.mapPoints(pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001205 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -05001206 // point at t = 1/2
Brian Salomon66085ed2017-02-02 15:39:34 -05001207 curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
1208 curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
1209 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
Jim Van Verthf507c282018-05-11 10:48:20 -04001210 this->addToClip(curvePoint);
1211 this->addToClip(pts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001212 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -05001213 break;
1214 case SkPath::kCubic_Verb:
Jim Van Vertha84898d2017-02-06 13:38:23 -05001215 ctm.mapPoints(pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -05001216 // point at t = 5/16
1217 curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
1218 curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001219 this->addToClip(curvePoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001220 // point at t = 11/16
1221 curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
1222 curvePoint.fY = kD*pts[0].fY + kC*pts[1].fY + kB*pts[2].fY + kA*pts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001223 this->addToClip(curvePoint);
1224 this->addToClip(pts[3]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001225 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001226 break;
Brian Salomonab664fa2017-03-24 16:07:20 +00001227 case SkPath::kMove_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001228 if (verbSeen) {
1229 return false;
1230 }
1231 break;
Jim Van Verth91af7272017-01-27 14:15:54 -05001232 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +00001233 case SkPath::kDone_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001234 closeSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001235 break;
1236 default:
1237 SkDEBUGFAIL("unknown verb");
1238 }
Jim Van Verth3645bb02018-06-26 14:58:58 -04001239 verbSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001240 }
1241
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001242 this->finishPathPolygon();
Brian Salomonab664fa2017-03-24 16:07:20 +00001243 fCurrClipPoint = fClipPolygon.count() - 1;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001244 return true;
Brian Salomon66085ed2017-02-02 15:39:34 -05001245}
1246
Brian Salomonab664fa2017-03-24 16:07:20 +00001247void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
Brian Salomon66085ed2017-02-02 15:39:34 -05001248 SkASSERT(fClipPolygon.count() >= 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001249
Brian Salomonab664fa2017-03-24 16:07:20 +00001250 // init clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001251 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
Jim Van Verthf507c282018-05-11 10:48:20 -04001252 SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001253 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001254
1255 // init centroid check
1256 bool hiddenCentroid = true;
Jim Van Verthf507c282018-05-11 10:48:20 -04001257 v1 = fCentroid - fClipPolygon[0];
Brian Salomon66085ed2017-02-02 15:39:34 -05001258 SkScalar initCross = v0.cross(v1);
1259
1260 for (int p = 1; p < fClipPolygon.count(); ++p) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001261 // add to clip vectors
Brian Salomon66085ed2017-02-02 15:39:34 -05001262 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
1263 *fClipVectors.push() = v0;
Brian Salomon66085ed2017-02-02 15:39:34 -05001264 // Determine if transformed centroid is inside clipPolygon.
Brian Salomonab664fa2017-03-24 16:07:20 +00001265 v1 = fCentroid - fClipPolygon[p];
Brian Salomon66085ed2017-02-02 15:39:34 -05001266 if (initCross*v0.cross(v1) <= 0) {
1267 hiddenCentroid = false;
1268 }
1269 }
1270 SkASSERT(fClipVectors.count() == fClipPolygon.count());
1271
Brian Salomonab664fa2017-03-24 16:07:20 +00001272 fTransparent = fTransparent || !hiddenCentroid;
Brian Salomon66085ed2017-02-02 15:39:34 -05001273}
1274
1275bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
1276 SkPoint* clipPoint) {
1277 SkVector segmentVector = centroid - umbraPoint;
1278
Brian Salomonab664fa2017-03-24 16:07:20 +00001279 int startClipPoint = fCurrClipPoint;
Brian Salomon66085ed2017-02-02 15:39:34 -05001280 do {
Brian Salomonab664fa2017-03-24 16:07:20 +00001281 SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
1282 SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
Brian Salomon66085ed2017-02-02 15:39:34 -05001283 SkScalar t_num = dp.cross(segmentVector);
1284 // if line segments are nearly parallel
1285 if (SkScalarNearlyZero(denom)) {
1286 // and collinear
1287 if (SkScalarNearlyZero(t_num)) {
1288 return false;
1289 }
1290 // otherwise are separate, will try the next poly segment
1291 // else if crossing lies within poly segment
1292 } else if (t_num >= 0 && t_num <= denom) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001293 SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
Brian Salomon66085ed2017-02-02 15:39:34 -05001294 // if umbra point is inside the clip polygon
Jim Van Verthda965502017-04-11 15:29:14 -04001295 if (s_num >= 0 && s_num <= denom) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001296 segmentVector *= s_num/denom;
1297 *clipPoint = umbraPoint + segmentVector;
1298 return true;
1299 }
1300 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001301 fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
1302 } while (fCurrClipPoint != startClipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001303
1304 return false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001305}
1306
Brian Salomonab664fa2017-03-24 16:07:20 +00001307int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
Cary Clarkdf429f32017-11-08 11:44:31 -05001308 SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraPoint]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001309 int index = fCurrUmbraPoint;
1310 int dir = 1;
1311 int next = (index + dir) % fUmbraPolygon.count();
1312
1313 // init travel direction
Cary Clarkdf429f32017-11-08 11:44:31 -05001314 SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001315 if (distance < minDistance) {
1316 index = next;
1317 minDistance = distance;
1318 } else {
1319 dir = fUmbraPolygon.count()-1;
1320 }
1321
1322 // iterate until we find a point that increases the distance
1323 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001324 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001325 while (distance < minDistance) {
1326 index = next;
1327 minDistance = distance;
1328 next = (index + dir) % fUmbraPolygon.count();
Cary Clarkdf429f32017-11-08 11:44:31 -05001329 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001330 }
1331
1332 fCurrUmbraPoint = index;
1333 return index;
1334}
1335
Jim Van Verth872da6b2018-04-10 11:24:11 -04001336bool SkSpotShadowTessellator::computeConvexShadow(SkScalar radius) {
1337 // generate inner ring
1338 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1339 &fUmbraPolygon)) {
1340 // this shouldn't happen, but just in case we'll inset using the centroid
1341 fValidUmbra = false;
1342 }
1343
1344 // walk around the path polygon, generate outer ring and connect to inner ring
1345 if (fTransparent) {
1346 *fPositions.push() = fCentroid;
1347 *fColors.push() = fUmbraColor;
1348 }
1349 fCurrUmbraPoint = 0;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001350
Jim Van Verth7deacf42018-06-08 15:13:25 -04001351 // initial setup
1352 // add first quad
1353 int polyCount = fPathPolygon.count();
1354 if (!compute_normal(fPathPolygon[polyCount-1], fPathPolygon[0], fDirection, &fFirstOutset)) {
1355 // polygon should be sanitized by this point, so this is unrecoverable
Jim Van Verth872da6b2018-04-10 11:24:11 -04001356 return false;
1357 }
1358
Jim Van Verth7deacf42018-06-08 15:13:25 -04001359 fFirstOutset *= fRadius;
1360 fFirstPoint = fPathPolygon[polyCount - 1];
1361 fFirstVertexIndex = fPositions.count();
1362 fPrevOutset = fFirstOutset;
1363 fPrevPoint = fFirstPoint;
1364 fPrevUmbraIndex = -1;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001365
Jim Van Verth7deacf42018-06-08 15:13:25 -04001366 this->addInnerPoint(fFirstPoint, &fPrevUmbraIndex);
1367
1368 if (!fTransparent) {
1369 SkPoint clipPoint;
1370 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
1371 fCentroid, &clipPoint);
1372 if (isOutside) {
1373 *fPositions.push() = clipPoint;
1374 *fColors.push() = fUmbraColor;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001375 }
Jim Van Verth7deacf42018-06-08 15:13:25 -04001376 fPrevUmbraOutside = isOutside;
1377 fFirstUmbraOutside = isOutside;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001378 }
1379
Jim Van Verth7deacf42018-06-08 15:13:25 -04001380 SkPoint newPoint = fFirstPoint + fFirstOutset;
1381 *fPositions.push() = newPoint;
1382 *fColors.push() = fPenumbraColor;
1383 this->addEdge(fPathPolygon[0], fFirstOutset, false);
1384
1385 for (int i = 1; i < polyCount; ++i) {
1386 if (!this->handlePolyPoint(fPathPolygon[i], i == polyCount-1)) {
1387 return false;
1388 }
1389 }
1390 SkASSERT(this->indexCount());
1391
Jim Van Verth872da6b2018-04-10 11:24:11 -04001392 // final fan
Jim Van Verth7deacf42018-06-08 15:13:25 -04001393 SkASSERT(fPositions.count() >= 3);
1394 if (this->addArc(fFirstOutset, false)) {
1395 if (fFirstUmbraOutside) {
1396 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1397 fFirstVertexIndex + 2);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001398 } else {
Jim Van Verth7deacf42018-06-08 15:13:25 -04001399 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
1400 fFirstVertexIndex + 1);
1401 }
1402 } else {
1403 // no arc added, fix up by setting first penumbra point position to last one
1404 if (fFirstUmbraOutside) {
1405 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
1406 } else {
1407 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
Jim Van Verth872da6b2018-04-10 11:24:11 -04001408 }
1409 }
1410
1411 return true;
1412}
1413
1414bool SkSpotShadowTessellator::computeConcaveShadow(SkScalar radius) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -04001415 if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001416 return false;
1417 }
1418
1419 // generate inner ring
1420 SkTDArray<int> umbraIndices;
1421 umbraIndices.setReserve(fPathPolygon.count());
1422 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
1423 &fUmbraPolygon, &umbraIndices)) {
1424 // TODO: figure out how to handle this case
1425 return false;
1426 }
1427
1428 // generate outer ring
1429 SkTDArray<SkPoint> penumbraPolygon;
1430 SkTDArray<int> penumbraIndices;
1431 penumbraPolygon.setReserve(fUmbraPolygon.count());
1432 penumbraIndices.setReserve(fUmbraPolygon.count());
1433 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -radius,
1434 &penumbraPolygon, &penumbraIndices)) {
1435 // TODO: figure out how to handle this case
1436 return false;
1437 }
1438
1439 if (!fUmbraPolygon.count() || !penumbraPolygon.count()) {
1440 return false;
1441 }
1442
1443 // attach the rings together
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001444 this->stitchConcaveRings(fUmbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001445
1446 return true;
1447}
1448
Jim Van Verthefe3ded2017-01-30 13:11:45 -05001449void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
Jim Van Verth91af7272017-01-27 14:15:54 -05001450 SkPoint* pts, int count) {
1451 // TODO: vectorize
1452 for (int i = 0; i < count; ++i) {
1453 pts[i] *= scale;
1454 pts[i] += xlate;
1455 }
1456}
1457
Jim Van Verth7deacf42018-06-08 15:13:25 -04001458bool SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p, bool lastPoint) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001459 SkVector normal;
Jim Van Verthda965502017-04-11 15:29:14 -04001460 if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
1461 normal *= fRadius;
1462 this->addArc(normal, true);
Jim Van Verth7deacf42018-06-08 15:13:25 -04001463 this->addEdge(p, normal, lastPoint);
Jim Van Verth91af7272017-01-27 14:15:54 -05001464 }
Jim Van Verthb55eb282017-07-18 14:13:45 -04001465
1466 return true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001467}
1468
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001469bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001470 SkPoint umbraPoint;
1471 if (!fValidUmbra) {
1472 SkVector v = fCentroid - pathPoint;
1473 v *= 0.95f;
1474 umbraPoint = pathPoint + v;
Jim Van Verth91af7272017-01-27 14:15:54 -05001475 } else {
Brian Salomonab664fa2017-03-24 16:07:20 +00001476 umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
Jim Van Verth91af7272017-01-27 14:15:54 -05001477 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001478
Jim Van Verth91af7272017-01-27 14:15:54 -05001479 fPrevPoint = pathPoint;
Brian Salomonab664fa2017-03-24 16:07:20 +00001480
1481 // merge "close" points
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -04001482 if (fPrevUmbraIndex == -1 ||
Brian Salomonab664fa2017-03-24 16:07:20 +00001483 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001484 // if we've wrapped around, don't add a new point
1485 if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
1486 *currUmbraIndex = fFirstVertexIndex;
1487 } else {
1488 *currUmbraIndex = fPositions.count();
1489 *fPositions.push() = umbraPoint;
1490 *fColors.push() = fUmbraColor;
1491 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001492 return false;
1493 } else {
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001494 *currUmbraIndex = fPrevUmbraIndex;
Brian Salomonab664fa2017-03-24 16:07:20 +00001495 return true;
1496 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001497}
1498
Jim Van Verth7deacf42018-06-08 15:13:25 -04001499void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
1500 bool lastEdge) {
Brian Salomon66085ed2017-02-02 15:39:34 -05001501 // add next umbra point
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001502 int currUmbraIndex;
Jim Van Verth7deacf42018-06-08 15:13:25 -04001503 bool duplicate;
1504 if (lastEdge) {
1505 duplicate = false;
1506 currUmbraIndex = fFirstVertexIndex;
1507 fPrevPoint = nextPoint;
1508 } else {
1509 duplicate = this->addInnerPoint(nextPoint, &currUmbraIndex);
1510 }
Jim Van Verth8c2de8f2018-05-14 11:20:58 -04001511 int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
1512 ? fPositions.count()-1
1513 : fPositions.count()-2;
Brian Salomonab664fa2017-03-24 16:07:20 +00001514 if (!duplicate) {
1515 // add to center fan if transparent or centroid showing
1516 if (fTransparent) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001517 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001518 // otherwise add to clip ring
1519 } else {
Brian Salomon66085ed2017-02-02 15:39:34 -05001520 SkPoint clipPoint;
Jim Van Verth7deacf42018-06-08 15:13:25 -04001521 bool isOutside = lastEdge ? fFirstUmbraOutside
1522 : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
1523 &clipPoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001524 if (isOutside) {
Jim Van Verth7deacf42018-06-08 15:13:25 -04001525 if (!lastEdge) {
1526 *fPositions.push() = clipPoint;
1527 *fColors.push() = fUmbraColor;
1528 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001529 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001530 if (fPrevUmbraOutside) {
1531 // fill out quad
Jim Van Verth872da6b2018-04-10 11:24:11 -04001532 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
1533 fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001534 }
1535 } else if (fPrevUmbraOutside) {
1536 // add tri
Jim Van Verth872da6b2018-04-10 11:24:11 -04001537 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
Brian Salomon66085ed2017-02-02 15:39:34 -05001538 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001539
Brian Salomon66085ed2017-02-02 15:39:34 -05001540 fPrevUmbraOutside = isOutside;
1541 }
1542 }
1543
1544 // add next penumbra point and quad
Jim Van Verth91af7272017-01-27 14:15:54 -05001545 SkPoint newPoint = nextPoint + nextNormal;
1546 *fPositions.push() = newPoint;
1547 *fColors.push() = fPenumbraColor;
1548
Brian Salomonab664fa2017-03-24 16:07:20 +00001549 if (!duplicate) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001550 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
Brian Salomonab664fa2017-03-24 16:07:20 +00001551 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001552 this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
Jim Van Verth91af7272017-01-27 14:15:54 -05001553
Brian Salomon66085ed2017-02-02 15:39:34 -05001554 fPrevUmbraIndex = currUmbraIndex;
Jim Van Verth76387852017-05-16 16:55:16 -04001555 fPrevOutset = nextNormal;
Jim Van Verth91af7272017-01-27 14:15:54 -05001556}
Brian Salomon958fbc42017-01-30 17:01:28 -05001557
1558///////////////////////////////////////////////////////////////////////////////////////////////////
1559
Brian Salomonaff27a22017-02-06 15:47:44 -05001560sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001561 const SkPoint3& zPlane, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001562 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001563 return nullptr;
1564 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001565 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001566 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001567}
1568
Brian Salomonaff27a22017-02-06 15:47:44 -05001569sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001570 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001571 SkScalar lightRadius, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001572 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() ||
Jim Van Verth1989c492018-05-31 13:15:16 -04001573 !lightPos.isFinite() || !(lightPos.fZ >= SK_ScalarNearlyZero) ||
1574 !SkScalarIsFinite(lightRadius) || !(lightRadius >= SK_ScalarNearlyZero)) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001575 return nullptr;
1576 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001577 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001578 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001579}