blob: 9deebea333c840e700fa7837fc5513b2d4e11860 [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;
Jim Van Verthfb186392018-09-11 11:37:46 -040042 static constexpr auto kPenumbraColor = SK_ColorTRANSPARENT;
43 static constexpr auto kUmbraColor = SK_ColorBLACK;
Jim Van Verthda965502017-04-11 15:29:14 -040044
Brian Salomonaff27a22017-02-06 15:47:44 -050045 int vertexCount() const { return fPositions.count(); }
46 int indexCount() const { return fIndices.count(); }
47
Jim Van Verthd737ab92018-09-10 15:19:01 -040048 // initialization methods
Jim Van Verthcdaf6612018-06-05 15:21:13 -040049 bool accumulateCentroid(const SkPoint& c, const SkPoint& n);
50 bool checkConvexity(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2);
51 void finishPathPolygon();
Jim Van Verthd737ab92018-09-10 15:19:01 -040052
53 // convex shadow methods
54 bool computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip);
55 void computeClipVectorsAndTestCentroid();
56 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -040057 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal, SkColor umbraColor,
58 bool lastEdge, bool doClip);
59 bool addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor, int* currUmbraIndex);
Jim Van Verthd737ab92018-09-10 15:19:01 -040060 int getClosestUmbraPoint(const SkPoint& point);
61
62 // concave shadow methods
63 bool computeConcaveShadow(SkScalar inset, SkScalar outset);
Jim Van Verth8760e2f2018-06-12 14:21:38 -040064 void stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
65 SkTDArray<int>* umbraIndices,
66 const SkTDArray<SkPoint>& penumbraPolygon,
67 SkTDArray<int>* penumbraIndices);
Jim Van Verthcdaf6612018-06-05 15:21:13 -040068
69 void handleLine(const SkPoint& p);
Jim Van Vertha84898d2017-02-06 13:38:23 -050070 void handleLine(const SkMatrix& m, SkPoint* p);
Brian Salomon958fbc42017-01-30 17:01:28 -050071
72 void handleQuad(const SkPoint pts[3]);
Jim Van Vertha84898d2017-02-06 13:38:23 -050073 void handleQuad(const SkMatrix& m, SkPoint pts[3]);
Brian Salomon958fbc42017-01-30 17:01:28 -050074
Jim Van Vertha84898d2017-02-06 13:38:23 -050075 void handleCubic(const SkMatrix& m, SkPoint pts[4]);
Brian Salomon958fbc42017-01-30 17:01:28 -050076
Jim Van Vertha84898d2017-02-06 13:38:23 -050077 void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
Brian Salomon958fbc42017-01-30 17:01:28 -050078
Jim Van Verthd737ab92018-09-10 15:19:01 -040079 bool addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc);
Jim Van Verthb4366552017-03-27 14:25:29 -040080
Jim Van Verth872da6b2018-04-10 11:24:11 -040081 void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
82 void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
83
Jim Van Verthe308a122017-05-08 14:19:30 -040084 SkScalar heightFunc(SkScalar x, SkScalar y) {
85 return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
86 }
87
Jim Van Verth0b7645f2018-08-31 12:36:52 -040088 SkPoint3 fZPlaneParams;
Jim Van Verthda965502017-04-11 15:29:14 -040089
Brian Salomon958fbc42017-01-30 17:01:28 -050090 // temporary buffer
91 SkTDArray<SkPoint> fPointBuffer;
Brian Salomon0dda9cb2017-02-03 10:33:25 -050092
Jim Van Vertha84898d2017-02-06 13:38:23 -050093 SkTDArray<SkPoint> fPositions;
94 SkTDArray<SkColor> fColors;
95 SkTDArray<uint16_t> fIndices;
96
Jim Van Verthcdaf6612018-06-05 15:21:13 -040097 SkTDArray<SkPoint> fPathPolygon;
Jim Van Verthd737ab92018-09-10 15:19:01 -040098 SkTDArray<SkPoint> fUmbraPolygon;
99 SkTDArray<SkPoint> fClipPolygon;
100 SkTDArray<SkVector> fClipVectors;
101
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400102 SkPoint fCentroid;
103 SkScalar fArea;
104 SkScalar fLastArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400105 SkScalar fLastCross;
106
Jim Van Verth76387852017-05-16 16:55:16 -0400107 int fFirstVertexIndex;
108 SkVector fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500109 SkPoint fFirstPoint;
110
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500111 bool fSucceeded;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500112 bool fTransparent;
Jim Van Verthf507c282018-05-11 10:48:20 -0400113 bool fIsConvex;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400114 bool fValidUmbra;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500115
Jim Van Vertha84898d2017-02-06 13:38:23 -0500116 SkScalar fDirection;
117 int fPrevUmbraIndex;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400118 int fCurrUmbraIndex;
119 int fCurrClipIndex;
120 bool fPrevUmbraOutside;
121 bool fFirstUmbraOutside;
Jim Van Verth76387852017-05-16 16:55:16 -0400122 SkVector fPrevOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500123 SkPoint fPrevPoint;
Brian Salomon958fbc42017-01-30 17:01:28 -0500124};
125
Jim Van Verthfb186392018-09-11 11:37:46 -0400126// make external linkage happy
127constexpr SkColor SkBaseShadowTessellator::kUmbraColor;
128constexpr SkColor SkBaseShadowTessellator::kPenumbraColor;
129
Jim Van Verthda965502017-04-11 15:29:14 -0400130static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
Jim Van Verthbce74962017-01-25 09:39:46 -0500131 SkVector* newNormal) {
132 SkVector normal;
133 // compute perpendicular
134 normal.fX = p0.fY - p1.fY;
135 normal.fY = p1.fX - p0.fX;
Jim Van Verthda965502017-04-11 15:29:14 -0400136 normal *= dir;
Jim Van Verthbce74962017-01-25 09:39:46 -0500137 if (!normal.normalize()) {
138 return false;
139 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500140 *newNormal = normal;
141 return true;
142}
143
Jim Van Verthf507c282018-05-11 10:48:20 -0400144static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
145 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
146 static constexpr SkScalar kCloseSqd = kClose * kClose;
147
148 SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
149 return distSq < kCloseSqd;
150}
151
152static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
153 SkVector v0 = p1 - p0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400154 SkVector v1 = p2 - p1;
Jim Van Verthf507c282018-05-11 10:48:20 -0400155 return v0.cross(v1);
156}
157
Jim Van Verthe308a122017-05-08 14:19:30 -0400158SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
159 : fZPlaneParams(zPlaneParams)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400160 , fCentroid({0, 0})
161 , fArea(0)
162 , fLastArea(0)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400163 , fLastCross(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400164 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500165 , fSucceeded(false)
166 , fTransparent(transparent)
Jim Van Verthf507c282018-05-11 10:48:20 -0400167 , fIsConvex(true)
Jim Van Verthd737ab92018-09-10 15:19:01 -0400168 , fValidUmbra(true)
Brian Salomonaff27a22017-02-06 15:47:44 -0500169 , fDirection(1)
Jim Van Verthd737ab92018-09-10 15:19:01 -0400170 , fPrevUmbraIndex(-1)
171 , fCurrUmbraIndex(0)
172 , fCurrClipIndex(0)
173 , fPrevUmbraOutside(false)
174 , fFirstUmbraOutside(false) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500175 // child classes will set reserve for positions, colors and indices
176}
177
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400178bool SkBaseShadowTessellator::accumulateCentroid(const SkPoint& curr, const SkPoint& next) {
179 if (duplicate_pt(curr, next)) {
180 return false;
181 }
182
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400183 SkASSERT(fPathPolygon.count() > 0);
184 SkVector v0 = curr - fPathPolygon[0];
185 SkVector v1 = next - fPathPolygon[0];
186 SkScalar quadArea = v0.cross(v1);
187 fCentroid.fX += (v0.fX + v1.fX) * quadArea;
188 fCentroid.fY += (v0.fY + v1.fY) * quadArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400189 fArea += quadArea;
190 // convexity check
Jim Van Verth3645bb02018-06-26 14:58:58 -0400191 if (quadArea*fLastArea < 0) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400192 fIsConvex = false;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400193 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400194 if (0 != quadArea) {
195 fLastArea = quadArea;
196 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400197
198 return true;
199}
200
201bool SkBaseShadowTessellator::checkConvexity(const SkPoint& p0,
202 const SkPoint& p1,
203 const SkPoint& p2) {
204 SkScalar cross = perp_dot(p0, p1, p2);
205 // skip collinear point
206 if (SkScalarNearlyZero(cross)) {
207 return false;
208 }
209
210 // check for convexity
211 if (fLastCross*cross < 0) {
212 fIsConvex = false;
213 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400214 if (0 != cross) {
215 fLastCross = cross;
216 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400217
218 return true;
219}
220
221void SkBaseShadowTessellator::finishPathPolygon() {
222 if (fPathPolygon.count() > 1) {
223 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], fPathPolygon[0])) {
224 // remove coincident point
225 fPathPolygon.pop();
226 }
227 }
228
229 if (fPathPolygon.count() > 2) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400230 // do this before the final convexity check, so we use the correct fPathPolygon[0]
231 fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
232 fCentroid += fPathPolygon[0];
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400233 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
234 fPathPolygon[fPathPolygon.count() - 1],
235 fPathPolygon[0])) {
236 // remove collinear point
237 fPathPolygon[0] = fPathPolygon[fPathPolygon.count() - 1];
238 fPathPolygon.pop();
239 }
240 }
241
Jim Van Verth7deacf42018-06-08 15:13:25 -0400242 // if area is positive, winding is ccw
243 fDirection = fArea > 0 ? -1 : 1;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400244}
245
Jim Van Verthd737ab92018-09-10 15:19:01 -0400246bool SkBaseShadowTessellator::computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip) {
247 if (doClip) {
248 this->computeClipVectorsAndTestCentroid();
249 }
250
Jim Van Verthfb186392018-09-11 11:37:46 -0400251 // adjust inset distance and umbra color if necessary
252 auto umbraColor = kUmbraColor;
253 SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
254 fPathPolygon[0],
255 fPathPolygon[1]);
256 SkRect bounds;
257 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
258 for (int i = 1; i < fPathPolygon.count(); ++i) {
259 int j = i + 1;
260 if (i == fPathPolygon.count() - 1) {
261 j = 0;
262 }
263 SkPoint currPoint = fPathPolygon[i];
264 SkPoint nextPoint = fPathPolygon[j];
265 SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
266 nextPoint);
267 if (distSq < minDistSq) {
268 minDistSq = distSq;
269 }
270 }
271 static constexpr auto kTolerance = 1.0e-2f;
272 if (minDistSq < (inset + kTolerance)*(inset + kTolerance)) {
273 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
274 auto newInset = SkScalarSqrt(minDistSq) - kTolerance;
Jim Van Verthe6146bd2018-09-11 14:16:42 -0400275 auto ratio = 128 * (newInset/inset + 1);
276 SkASSERT(SkScalarIsFinite(ratio));
Jim Van Verthfb186392018-09-11 11:37:46 -0400277 // they aren't PMColors, but the interpolation algorithm is the same
278 umbraColor = SkPMLerp(kUmbraColor, kPenumbraColor, (unsigned)ratio);
279 inset = newInset;
280 }
281
Jim Van Verthd737ab92018-09-10 15:19:01 -0400282 // generate inner ring
283 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), inset,
284 &fUmbraPolygon)) {
285 // not ideal, but in this case we'll inset using the centroid
286 fValidUmbra = false;
287 }
288
289 // walk around the path polygon, generate outer ring and connect to inner ring
290 if (fTransparent) {
291 fPositions.push_back(fCentroid);
Jim Van Verthfb186392018-09-11 11:37:46 -0400292 fColors.push_back(umbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400293 }
294 fCurrUmbraIndex = 0;
295
296 // initial setup
297 // add first quad
298 int polyCount = fPathPolygon.count();
299 if (!compute_normal(fPathPolygon[polyCount - 1], fPathPolygon[0], fDirection, &fFirstOutset)) {
300 // polygon should be sanitized by this point, so this is unrecoverable
301 return false;
302 }
303
304 fFirstOutset *= outset;
305 fFirstPoint = fPathPolygon[polyCount - 1];
306 fFirstVertexIndex = fPositions.count();
307 fPrevOutset = fFirstOutset;
308 fPrevPoint = fFirstPoint;
309 fPrevUmbraIndex = -1;
310
Jim Van Verthfb186392018-09-11 11:37:46 -0400311 this->addInnerPoint(fFirstPoint, umbraColor, &fPrevUmbraIndex);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400312
313 if (!fTransparent && doClip) {
314 SkPoint clipPoint;
315 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
316 fCentroid, &clipPoint);
317 if (isOutside) {
318 fPositions.push_back(clipPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400319 fColors.push_back(umbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400320 }
321 fPrevUmbraOutside = isOutside;
322 fFirstUmbraOutside = isOutside;
323 }
324
325 SkPoint newPoint = fFirstPoint + fFirstOutset;
326 fPositions.push_back(newPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400327 fColors.push_back(kPenumbraColor);
328 this->addEdge(fPathPolygon[0], fFirstOutset, umbraColor, false, doClip);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400329
330 for (int i = 1; i < polyCount; ++i) {
331 SkVector normal;
332 if (!compute_normal(fPrevPoint, fPathPolygon[i], fDirection, &normal)) {
333 return false;
334 }
335 normal *= outset;
336 this->addArc(normal, outset, true);
Jim Van Verthfb186392018-09-11 11:37:46 -0400337 this->addEdge(fPathPolygon[i], normal, umbraColor, i == polyCount - 1, doClip);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400338 }
339 SkASSERT(this->indexCount());
340
341 // final fan
342 SkASSERT(fPositions.count() >= 3);
343 if (this->addArc(fFirstOutset, outset, false)) {
344 if (fFirstUmbraOutside) {
345 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
346 fFirstVertexIndex + 2);
347 } else {
348 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
349 fFirstVertexIndex + 1);
350 }
351 } else {
352 // no arc added, fix up by setting first penumbra point position to last one
353 if (fFirstUmbraOutside) {
354 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
355 } else {
356 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
357 }
358 }
359
360 return true;
361}
362
363void SkBaseShadowTessellator::computeClipVectorsAndTestCentroid() {
364 SkASSERT(fClipPolygon.count() >= 3);
365 fCurrClipIndex = fClipPolygon.count() - 1;
366
367 // init clip vectors
368 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
369 SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
370 fClipVectors.push_back(v0);
371
372 // init centroid check
373 bool hiddenCentroid = true;
374 v1 = fCentroid - fClipPolygon[0];
375 SkScalar initCross = v0.cross(v1);
376
377 for (int p = 1; p < fClipPolygon.count(); ++p) {
378 // add to clip vectors
379 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
380 fClipVectors.push_back(v0);
381 // Determine if transformed centroid is inside clipPolygon.
382 v1 = fCentroid - fClipPolygon[p];
383 if (initCross*v0.cross(v1) <= 0) {
384 hiddenCentroid = false;
385 }
386 }
387 SkASSERT(fClipVectors.count() == fClipPolygon.count());
388
389 fTransparent = fTransparent || !hiddenCentroid;
390}
391
392void SkBaseShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
Jim Van Verthfb186392018-09-11 11:37:46 -0400393 SkColor umbraColor, bool lastEdge, bool doClip) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400394 // add next umbra point
395 int currUmbraIndex;
396 bool duplicate;
397 if (lastEdge) {
398 duplicate = false;
399 currUmbraIndex = fFirstVertexIndex;
400 fPrevPoint = nextPoint;
401 } else {
Jim Van Verthfb186392018-09-11 11:37:46 -0400402 duplicate = this->addInnerPoint(nextPoint, umbraColor, &currUmbraIndex);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400403 }
404 int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
405 ? fPositions.count() - 1
406 : fPositions.count() - 2;
407 if (!duplicate) {
408 // add to center fan if transparent or centroid showing
409 if (fTransparent) {
410 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
411 // otherwise add to clip ring
412 } else if (doClip) {
413 SkPoint clipPoint;
414 bool isOutside = lastEdge ? fFirstUmbraOutside
415 : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
416 &clipPoint);
417 if (isOutside) {
418 if (!lastEdge) {
419 fPositions.push_back(clipPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400420 fColors.push_back(umbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400421 }
422 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
423 if (fPrevUmbraOutside) {
424 // fill out quad
425 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
426 fPrevUmbraIndex + 1);
427 }
428 } else if (fPrevUmbraOutside) {
429 // add tri
430 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
431 }
432
433 fPrevUmbraOutside = isOutside;
434 }
435 }
436
437 // add next penumbra point and quad
438 SkPoint newPoint = nextPoint + nextNormal;
439 fPositions.push_back(newPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400440 fColors.push_back(kPenumbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400441
442 if (!duplicate) {
443 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
444 }
445 this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
446
447 fPrevUmbraIndex = currUmbraIndex;
448 fPrevOutset = nextNormal;
449}
450
451bool SkBaseShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
452 SkPoint* clipPoint) {
453 SkVector segmentVector = centroid - umbraPoint;
454
455 int startClipPoint = fCurrClipIndex;
456 do {
457 SkVector dp = umbraPoint - fClipPolygon[fCurrClipIndex];
458 SkScalar denom = fClipVectors[fCurrClipIndex].cross(segmentVector);
459 SkScalar t_num = dp.cross(segmentVector);
460 // if line segments are nearly parallel
461 if (SkScalarNearlyZero(denom)) {
462 // and collinear
463 if (SkScalarNearlyZero(t_num)) {
464 return false;
465 }
466 // otherwise are separate, will try the next poly segment
467 // else if crossing lies within poly segment
468 } else if (t_num >= 0 && t_num <= denom) {
469 SkScalar s_num = dp.cross(fClipVectors[fCurrClipIndex]);
470 // if umbra point is inside the clip polygon
471 if (s_num >= 0 && s_num <= denom) {
472 segmentVector *= s_num / denom;
473 *clipPoint = umbraPoint + segmentVector;
474 return true;
475 }
476 }
477 fCurrClipIndex = (fCurrClipIndex + 1) % fClipPolygon.count();
478 } while (fCurrClipIndex != startClipPoint);
479
480 return false;
481}
482
Jim Van Verthfb186392018-09-11 11:37:46 -0400483bool SkBaseShadowTessellator::addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor,
484 int* currUmbraIndex) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400485 SkPoint umbraPoint;
486 if (!fValidUmbra) {
487 SkVector v = fCentroid - pathPoint;
488 v *= 0.95f;
489 umbraPoint = pathPoint + v;
490 } else {
491 umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
492 }
493
494 fPrevPoint = pathPoint;
495
496 // merge "close" points
497 if (fPrevUmbraIndex == -1 ||
498 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
499 // if we've wrapped around, don't add a new point
500 if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
501 *currUmbraIndex = fFirstVertexIndex;
502 } else {
503 *currUmbraIndex = fPositions.count();
504 fPositions.push_back(umbraPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400505 fColors.push_back(umbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400506 }
507 return false;
508 } else {
509 *currUmbraIndex = fPrevUmbraIndex;
510 return true;
511 }
512}
513
514int SkBaseShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
515 SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraIndex]);
516 int index = fCurrUmbraIndex;
517 int dir = 1;
518 int next = (index + dir) % fUmbraPolygon.count();
519
520 // init travel direction
521 SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
522 if (distance < minDistance) {
523 index = next;
524 minDistance = distance;
525 } else {
526 dir = fUmbraPolygon.count() - 1;
527 }
528
529 // iterate until we find a point that increases the distance
530 next = (index + dir) % fUmbraPolygon.count();
531 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
532 while (distance < minDistance) {
533 index = next;
534 minDistance = distance;
535 next = (index + dir) % fUmbraPolygon.count();
536 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
537 }
538
539 fCurrUmbraIndex = index;
540 return index;
541}
542
543bool SkBaseShadowTessellator::computeConcaveShadow(SkScalar inset, SkScalar outset) {
544 if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
545 return false;
546 }
547
548 // generate inner ring
549 SkTDArray<int> umbraIndices;
550 umbraIndices.setReserve(fPathPolygon.count());
551 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), inset,
552 &fUmbraPolygon, &umbraIndices)) {
553 // TODO: figure out how to handle this case
554 return false;
555 }
556
557 // generate outer ring
558 SkTDArray<SkPoint> penumbraPolygon;
559 SkTDArray<int> penumbraIndices;
560 penumbraPolygon.setReserve(fUmbraPolygon.count());
561 penumbraIndices.setReserve(fUmbraPolygon.count());
562 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -outset,
563 &penumbraPolygon, &penumbraIndices)) {
564 // TODO: figure out how to handle this case
565 return false;
566 }
567
568 if (!fUmbraPolygon.count() || !penumbraPolygon.count()) {
569 return false;
570 }
571
572 // attach the rings together
573 this->stitchConcaveRings(fUmbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
574
575 return true;
576}
577
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400578void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
579 SkTDArray<int>* umbraIndices,
580 const SkTDArray<SkPoint>& penumbraPolygon,
581 SkTDArray<int>* penumbraIndices) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400582 // TODO: only create and fill indexMap when fTransparent is true?
583 SkAutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.count());
584
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400585 // find minimum indices
586 int minIndex = 0;
587 int min = (*penumbraIndices)[0];
588 for (int i = 1; i < (*penumbraIndices).count(); ++i) {
589 if ((*penumbraIndices)[i] < min) {
590 min = (*penumbraIndices)[i];
591 minIndex = i;
592 }
593 }
594 int currPenumbra = minIndex;
595
596 minIndex = 0;
597 min = (*umbraIndices)[0];
598 for (int i = 1; i < (*umbraIndices).count(); ++i) {
599 if ((*umbraIndices)[i] < min) {
600 min = (*umbraIndices)[i];
601 minIndex = i;
602 }
603 }
604 int currUmbra = minIndex;
605
606 // now find a case where the indices are equal (there should be at least one)
607 int maxPenumbraIndex = fPathPolygon.count() - 1;
608 int maxUmbraIndex = fPathPolygon.count() - 1;
609 while ((*penumbraIndices)[currPenumbra] != (*umbraIndices)[currUmbra]) {
610 if ((*penumbraIndices)[currPenumbra] < (*umbraIndices)[currUmbra]) {
611 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
612 maxPenumbraIndex = (*penumbraIndices)[currPenumbra];
613 currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
614 } else {
615 (*umbraIndices)[currUmbra] += fPathPolygon.count();
616 maxUmbraIndex = (*umbraIndices)[currUmbra];
617 currUmbra = (currUmbra + 1) % umbraPolygon.count();
618 }
619 }
620
Jim Van Verthd737ab92018-09-10 15:19:01 -0400621 fPositions.push_back(penumbraPolygon[currPenumbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400622 fColors.push_back(kPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400623 int prevPenumbraIndex = 0;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400624 fPositions.push_back(umbraPolygon[currUmbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400625 fColors.push_back(kUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400626 fPrevUmbraIndex = 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400627 indexMap[currUmbra] = 1;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400628
629 int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
630 int nextUmbra = (currUmbra + 1) % umbraPolygon.count();
631 while ((*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex ||
632 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
633
634 if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) {
635 // advance both one step
Jim Van Verthd737ab92018-09-10 15:19:01 -0400636 fPositions.push_back(penumbraPolygon[nextPenumbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400637 fColors.push_back(kPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400638 int currPenumbraIndex = fPositions.count() - 1;
639
Jim Van Verthd737ab92018-09-10 15:19:01 -0400640 fPositions.push_back(umbraPolygon[nextUmbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400641 fColors.push_back(kUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400642 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400643 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400644
645 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
646 fPrevUmbraIndex, currUmbraIndex);
647
648 prevPenumbraIndex = currPenumbraIndex;
649 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
650 currPenumbra = nextPenumbra;
651 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
652
653 fPrevUmbraIndex = currUmbraIndex;
654 (*umbraIndices)[currUmbra] += fPathPolygon.count();
655 currUmbra = nextUmbra;
656 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
657 }
658
659 while ((*penumbraIndices)[nextPenumbra] < (*umbraIndices)[nextUmbra] &&
660 (*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex) {
661 // fill out penumbra arc
Jim Van Verthd737ab92018-09-10 15:19:01 -0400662 fPositions.push_back(penumbraPolygon[nextPenumbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400663 fColors.push_back(kPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400664 int currPenumbraIndex = fPositions.count() - 1;
665
666 this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
667
668 prevPenumbraIndex = currPenumbraIndex;
669 // this ensures the ordering when we wrap around
670 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
671 currPenumbra = nextPenumbra;
672 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
673 }
674
675 while ((*umbraIndices)[nextUmbra] < (*penumbraIndices)[nextPenumbra] &&
676 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
677 // fill out umbra arc
Jim Van Verthd737ab92018-09-10 15:19:01 -0400678 fPositions.push_back(umbraPolygon[nextUmbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400679 fColors.push_back(kUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400680 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400681 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400682
683 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
684
685 fPrevUmbraIndex = currUmbraIndex;
686 // this ensures the ordering when we wrap around
687 (*umbraIndices)[currUmbra] += fPathPolygon.count();
688 currUmbra = nextUmbra;
689 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
690 }
691 }
692 // finish up by advancing both one step
Jim Van Verthd737ab92018-09-10 15:19:01 -0400693 fPositions.push_back(penumbraPolygon[nextPenumbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400694 fColors.push_back(kPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400695 int currPenumbraIndex = fPositions.count() - 1;
696
Jim Van Verthd737ab92018-09-10 15:19:01 -0400697 fPositions.push_back(umbraPolygon[nextUmbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400698 fColors.push_back(kUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400699 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400700 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400701
702 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
703 fPrevUmbraIndex, currUmbraIndex);
704
705 if (fTransparent) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400706 SkTriangulateSimplePolygon(umbraPolygon.begin(), indexMap, umbraPolygon.count(),
707 &fIndices);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400708 }
709}
710
711
Jim Van Vertha84898d2017-02-06 13:38:23 -0500712// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500713#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500714static const SkScalar kQuadTolerance = 0.2f;
715static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500716#endif
Jim Van Vertha84898d2017-02-06 13:38:23 -0500717static const SkScalar kConicTolerance = 0.5f;
718
Jim Van Verth3645bb02018-06-26 14:58:58 -0400719// clamps the point to the nearest 16th of a pixel
720static void sanitize_point(const SkPoint& in, SkPoint* out) {
721 out->fX = SkScalarRoundToScalar(16.f*in.fX)*0.0625f;
722 out->fY = SkScalarRoundToScalar(16.f*in.fY)*0.0625f;
723}
724
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400725void SkBaseShadowTessellator::handleLine(const SkPoint& p) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400726 SkPoint pSanitized;
727 sanitize_point(p, &pSanitized);
728
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400729 if (fPathPolygon.count() > 0) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400730 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400731 // skip coincident point
732 return;
733 }
734 }
735
736 if (fPathPolygon.count() > 1) {
737 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
738 fPathPolygon[fPathPolygon.count() - 1],
Jim Van Verth3645bb02018-06-26 14:58:58 -0400739 pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400740 // remove collinear point
741 fPathPolygon.pop();
742 }
743 }
744
Jim Van Verthd737ab92018-09-10 15:19:01 -0400745 fPathPolygon.push_back(pSanitized);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400746}
747
Brian Salomonaff27a22017-02-06 15:47:44 -0500748void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500749 m.mapPoints(p, 1);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400750
Jim Van Vertha84898d2017-02-06 13:38:23 -0500751 this->handleLine(*p);
752}
753
Brian Salomonaff27a22017-02-06 15:47:44 -0500754void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500755#if SK_SUPPORT_GPU
Jim Van Vertha947e292018-02-26 13:54:34 -0500756 // check for degeneracy
757 SkVector v0 = pts[1] - pts[0];
758 SkVector v1 = pts[2] - pts[0];
759 if (SkScalarNearlyZero(v0.cross(v1))) {
760 return;
761 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500762 // TODO: Pull PathUtils out of Ganesh?
763 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400764 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500765 SkPoint* target = fPointBuffer.begin();
766 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
767 kQuadTolerance, &target, maxCount);
768 fPointBuffer.setCount(count);
769 for (int i = 0; i < count; i++) {
770 this->handleLine(fPointBuffer[i]);
771 }
772#else
773 // for now, just to draw something
774 this->handleLine(pts[1]);
775 this->handleLine(pts[2]);
776#endif
777}
778
Brian Salomonaff27a22017-02-06 15:47:44 -0500779void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500780 m.mapPoints(pts, 3);
781 this->handleQuad(pts);
782}
783
Brian Salomonaff27a22017-02-06 15:47:44 -0500784void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500785 m.mapPoints(pts, 4);
786#if SK_SUPPORT_GPU
787 // TODO: Pull PathUtils out of Ganesh?
788 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400789 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500790 SkPoint* target = fPointBuffer.begin();
791 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
792 kCubicTolerance, &target, maxCount);
793 fPointBuffer.setCount(count);
794 for (int i = 0; i < count; i++) {
795 this->handleLine(fPointBuffer[i]);
796 }
797#else
798 // for now, just to draw something
799 this->handleLine(pts[1]);
800 this->handleLine(pts[2]);
801 this->handleLine(pts[3]);
802#endif
803}
804
Brian Salomonaff27a22017-02-06 15:47:44 -0500805void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400806 if (m.hasPerspective()) {
807 w = SkConic::TransformW(pts, w, m);
808 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500809 m.mapPoints(pts, 3);
810 SkAutoConicToQuads quadder;
811 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
812 SkPoint lastPoint = *(quads++);
813 int count = quadder.countQuads();
814 for (int i = 0; i < count; ++i) {
815 SkPoint quadPts[3];
816 quadPts[0] = lastPoint;
817 quadPts[1] = quads[0];
818 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
819 this->handleQuad(quadPts);
820 lastPoint = quadPts[2];
821 quads += 2;
822 }
823}
824
Jim Van Verthd737ab92018-09-10 15:19:01 -0400825bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500826 // fill in fan from previous quad
827 SkScalar rotSin, rotCos;
828 int numSteps;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400829 if (!SkComputeRadialSteps(fPrevOutset, nextNormal, offset, &rotSin, &rotCos, &numSteps)) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400830 // recover as best we can
831 numSteps = 0;
832 }
Jim Van Verth76387852017-05-16 16:55:16 -0400833 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400834 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400835 SkVector currNormal;
836 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
837 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400838 fPositions.push_back(fPrevPoint + currNormal);
Jim Van Verthfb186392018-09-11 11:37:46 -0400839 fColors.push_back(kPenumbraColor);
Jim Van Verth872da6b2018-04-10 11:24:11 -0400840 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500841
Jim Van Verthda965502017-04-11 15:29:14 -0400842 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500843 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400844 if (finishArc && numSteps) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400845 fPositions.push_back(fPrevPoint + nextNormal);
Jim Van Verthfb186392018-09-11 11:37:46 -0400846 fColors.push_back(kPenumbraColor);
Jim Van Verth872da6b2018-04-10 11:24:11 -0400847 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Verthda965502017-04-11 15:29:14 -0400848 }
Jim Van Verth76387852017-05-16 16:55:16 -0400849 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400850
851 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500852}
853
Jim Van Verth872da6b2018-04-10 11:24:11 -0400854void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
855 auto indices = fIndices.append(3);
856
857 indices[0] = index0;
858 indices[1] = index1;
859 indices[2] = index2;
860}
861
862void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
863 uint16_t index2, uint16_t index3) {
864 auto indices = fIndices.append(6);
865
866 indices[0] = index0;
867 indices[1] = index1;
868 indices[2] = index2;
869
870 indices[3] = index2;
871 indices[4] = index1;
872 indices[5] = index3;
873}
874
Jim Van Vertha84898d2017-02-06 13:38:23 -0500875//////////////////////////////////////////////////////////////////////////////////////////////////
876
Brian Salomonaff27a22017-02-06 15:47:44 -0500877class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500878public:
879 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400880 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500881
882private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400883 bool computePathPolygon(const SkPath& path, const SkMatrix& ctm);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400884
Brian Salomonaff27a22017-02-06 15:47:44 -0500885 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500886};
887
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500888SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500889 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400890 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500891 bool transparent)
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400892 : INHERITED(zPlaneParams, transparent) {
Jim Van Verthda965502017-04-11 15:29:14 -0400893 // Set base colors
Jim Van Verthfb186392018-09-11 11:37:46 -0400894 auto baseZ = heightFunc(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verthb4366552017-03-27 14:25:29 -0400895 // umbraColor is the interior value, penumbraColor the exterior value.
Jim Van Verthfb186392018-09-11 11:37:46 -0400896 auto outset = SkDrawShadowMetrics::AmbientBlurRadius(baseZ);
897 auto inset = outset * SkDrawShadowMetrics::AmbientRecipAlpha(baseZ) - outset;
Jim Van Verthda965502017-04-11 15:29:14 -0400898
Jim Van Verth3645bb02018-06-26 14:58:58 -0400899 if (!this->computePathPolygon(path, ctm)) {
900 return;
901 }
902 if (fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
903 fSucceeded = true; // We don't want to try to blur these cases, so we will
904 // return an empty SkVertices instead.
905 return;
906 }
907
Jim Van Verthbce74962017-01-25 09:39:46 -0500908 // Outer ring: 3*numPts
909 // Middle ring: numPts
910 fPositions.setReserve(4 * path.countPoints());
911 fColors.setReserve(4 * path.countPoints());
912 // Outer ring: 12*numPts
913 // Middle ring: 0
914 fIndices.setReserve(12 * path.countPoints());
915
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400916 if (fIsConvex) {
Jim Van Verthfb186392018-09-11 11:37:46 -0400917 fSucceeded = this->computeConvexShadow(inset, outset, false);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400918 } else {
Jim Van Verthfb186392018-09-11 11:37:46 -0400919 fSucceeded = this->computeConcaveShadow(inset, outset);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400920 }
921}
922
Jim Van Verth3645bb02018-06-26 14:58:58 -0400923bool SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400924 fPathPolygon.setReserve(path.countPoints());
925
926 // walk around the path, tessellate and generate outer ring
927 // if original path is transparent, will accumulate sum of points for centroid
928 SkPath::Iter iter(path, true);
929 SkPoint pts[4];
930 SkPath::Verb verb;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400931 bool verbSeen = false;
932 bool closeSeen = false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400933 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400934 if (closeSeen) {
935 return false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400936 }
Jim Van Verth3645bb02018-06-26 14:58:58 -0400937 switch (verb) {
938 case SkPath::kLine_Verb:
939 this->handleLine(ctm, &pts[1]);
940 break;
941 case SkPath::kQuad_Verb:
942 this->handleQuad(ctm, pts);
943 break;
944 case SkPath::kCubic_Verb:
945 this->handleCubic(ctm, pts);
946 break;
947 case SkPath::kConic_Verb:
948 this->handleConic(ctm, pts, iter.conicWeight());
949 break;
950 case SkPath::kMove_Verb:
951 if (verbSeen) {
952 return false;
953 }
954 break;
955 case SkPath::kClose_Verb:
956 case SkPath::kDone_Verb:
957 closeSeen = true;
958 break;
959 }
960 verbSeen = true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400961 }
962
963 this->finishPathPolygon();
Jim Van Verth3645bb02018-06-26 14:58:58 -0400964 return true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400965}
966
Jim Van Verth91af7272017-01-27 14:15:54 -0500967///////////////////////////////////////////////////////////////////////////////////////////////////
968
Brian Salomonaff27a22017-02-06 15:47:44 -0500969class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500970public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500971 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400972 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400973 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500974
Brian Salomon958fbc42017-01-30 17:01:28 -0500975private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400976 bool computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400977 const SkMatrix& shadowTransform);
Jim Van Verthf507c282018-05-11 10:48:20 -0400978 void addToClip(const SkVector& nextPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400979
Brian Salomonaff27a22017-02-06 15:47:44 -0500980 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -0500981};
982
Jim Van Vertha84898d2017-02-06 13:38:23 -0500983SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400984 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -0400985 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -0400986 bool transparent)
Jim Van Verthd737ab92018-09-10 15:19:01 -0400987 : INHERITED(zPlaneParams, transparent) {
Jim Van Verthda965502017-04-11 15:29:14 -0400988
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400989 const SkRect& pathBounds = path.getBounds();
Jim Van Verthb4366552017-03-27 14:25:29 -0400990
991 // Set radius and colors
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400992 SkScalar occluderHeight = this->heightFunc(pathBounds.centerX(), pathBounds.centerY());
Jim Van Verthb4366552017-03-27 14:25:29 -0400993
Jim Van Verth1af03d42017-07-31 09:34:58 -0400994 // Compute the blur radius, scale and translation for the spot shadow.
Jim Van Verthd737ab92018-09-10 15:19:01 -0400995 SkScalar outset;
Jim Van Verthda965502017-04-11 15:29:14 -0400996 SkMatrix shadowTransform;
997 if (!ctm.hasPerspective()) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400998 SkScalar scale;
999 SkVector translate;
Jim Van Verthd737ab92018-09-10 15:19:01 -04001000 SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, lightPos.fZ,
1001 lightRadius, &outset, &scale, &translate);
Jim Van Verthda965502017-04-11 15:29:14 -04001002 shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001003 shadowTransform.preConcat(ctm);
Jim Van Verthda965502017-04-11 15:29:14 -04001004 } else {
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001005 // get rotated quad in 3D
1006 SkPoint pts[4];
1007 ctm.mapRectToQuad(pts, pathBounds);
Jim Van Verth7b617a72018-09-14 14:03:52 -04001008 // No shadows for bowties or other degenerate cases
1009 if (!SkIsConvexPolygon(pts, 4)) {
1010 return;
1011 }
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001012 SkPoint3 pts3D[4];
1013 SkScalar z = this->heightFunc(pathBounds.fLeft, pathBounds.fTop);
1014 pts3D[0].set(pts[0].fX, pts[0].fY, z);
1015 z = this->heightFunc(pathBounds.fRight, pathBounds.fTop);
1016 pts3D[1].set(pts[1].fX, pts[1].fY, z);
1017 z = this->heightFunc(pathBounds.fRight, pathBounds.fBottom);
1018 pts3D[2].set(pts[2].fX, pts[2].fY, z);
1019 z = this->heightFunc(pathBounds.fLeft, pathBounds.fBottom);
1020 pts3D[3].set(pts[3].fX, pts[3].fY, z);
1021
1022 // project from light through corners to z=0 plane
1023 for (int i = 0; i < 4; ++i) {
Jim Van Verth7b617a72018-09-14 14:03:52 -04001024 SkScalar dz = lightPos.fZ - pts3D[i].fZ;
1025 // light shouldn't be below or at a corner's z-location
1026 if (dz <= SK_ScalarNearlyZero) {
1027 return;
1028 }
1029 SkScalar zRatio = pts3D[i].fZ / dz;
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001030 pts3D[i].fX -= (lightPos.fX - pts3D[i].fX)*zRatio;
1031 pts3D[i].fY -= (lightPos.fY - pts3D[i].fY)*zRatio;
1032 pts3D[i].fZ = SK_Scalar1;
1033 }
1034
1035 // Generate matrix that projects from [-1,1]x[-1,1] square to projected quad
1036 SkPoint3 h0, h1, h2;
Jim Van Verth7b617a72018-09-14 14:03:52 -04001037 // Compute homogenous crossing point between top and bottom edges (gives new x-axis).
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001038 h0 = (pts3D[1].cross(pts3D[0])).cross(pts3D[2].cross(pts3D[3]));
Jim Van Verth7b617a72018-09-14 14:03:52 -04001039 // Compute homogenous crossing point between left and right edges (gives new y-axis).
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001040 h1 = (pts3D[0].cross(pts3D[3])).cross(pts3D[1].cross(pts3D[2]));
Jim Van Verth7b617a72018-09-14 14:03:52 -04001041 // Compute homogenous crossing point between diagonals (gives new origin).
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001042 h2 = (pts3D[0].cross(pts3D[2])).cross(pts3D[1].cross(pts3D[3]));
Jim Van Verth7b617a72018-09-14 14:03:52 -04001043 // If h2 is a vector (z=0 in 2D homogeneous space), that means that at least
1044 // two of the quad corners are coincident and we don't have a realistic projection
1045 if (SkScalarNearlyZero(h2.fZ)) {
1046 return;
1047 }
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001048 // In some cases the crossing points are in the wrong direction
1049 // to map (-1,-1) to pts3D[0], so we need to correct for that.
1050 // Want h0 to be to the right of the left edge.
1051 SkVector3 v = pts3D[3] - pts3D[0];
1052 SkVector3 w = h0 - pts3D[0];
1053 SkScalar perpDot = v.fX*w.fY - v.fY*w.fX;
1054 if (perpDot > 0) {
1055 h0 = -h0;
1056 }
1057 // Want h1 to be above the bottom edge.
1058 v = pts3D[1] - pts3D[0];
1059 perpDot = v.fX*w.fY - v.fY*w.fX;
1060 if (perpDot < 0) {
1061 h1 = -h1;
1062 }
1063 shadowTransform.setAll(h0.fX / h2.fZ, h1.fX / h2.fZ, h2.fX / h2.fZ,
1064 h0.fY / h2.fZ, h1.fY / h2.fZ, h2.fY / h2.fZ,
1065 h0.fZ / h2.fZ, h1.fZ / h2.fZ, 1);
1066 // generate matrix that transforms from bounds to [-1,1]x[-1,1] square
1067 SkMatrix toHomogeneous;
1068 SkScalar xScale = 2/(pathBounds.fRight - pathBounds.fLeft);
1069 SkScalar yScale = 2/(pathBounds.fBottom - pathBounds.fTop);
1070 toHomogeneous.setAll(xScale, 0, -xScale*pathBounds.fLeft - 1,
1071 0, yScale, -yScale*pathBounds.fTop - 1,
1072 0, 0, 1);
1073 shadowTransform.preConcat(toHomogeneous);
1074
Jim Van Verthd737ab92018-09-10 15:19:01 -04001075 outset = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
Jim Van Verthda965502017-04-11 15:29:14 -04001076 }
Jim Van Verthd737ab92018-09-10 15:19:01 -04001077 SkScalar inset = outset;
Jim Van Verthb4366552017-03-27 14:25:29 -04001078
Brian Salomonab664fa2017-03-24 16:07:20 +00001079 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verth3645bb02018-06-26 14:58:58 -04001080 if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) {
1081 return;
1082 }
1083 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
1084 fSucceeded = true; // We don't want to try to blur these cases, so we will
1085 // return an empty SkVertices instead.
Brian Salomon66085ed2017-02-02 15:39:34 -05001086 return;
1087 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001088
Jim Van Verthd737ab92018-09-10 15:19:01 -04001089 // TODO: calculate these reserves better
1090 // Penumbra ring: 3*numPts
1091 // Umbra ring: numPts
1092 // Inner ring: numPts
1093 fPositions.setReserve(5 * path.countPoints());
1094 fColors.setReserve(5 * path.countPoints());
1095 // Penumbra ring: 12*numPts
1096 // Umbra ring: 3*numPts
1097 fIndices.setReserve(15 * path.countPoints());
Jim Van Verthf507c282018-05-11 10:48:20 -04001098
Jim Van Verthf507c282018-05-11 10:48:20 -04001099 if (fIsConvex) {
Jim Van Verthd737ab92018-09-10 15:19:01 -04001100 fSucceeded = this->computeConvexShadow(inset, outset, true);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001101 } else {
Jim Van Verthd737ab92018-09-10 15:19:01 -04001102 fSucceeded = this->computeConcaveShadow(inset, outset);
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001103 }
1104
1105 if (!fSucceeded) {
Jim Van Verthf507c282018-05-11 10:48:20 -04001106 return;
Jim Van Verth91af7272017-01-27 14:15:54 -05001107 }
Jim Van Verthda965502017-04-11 15:29:14 -04001108
Brian Salomon0dda9cb2017-02-03 10:33:25 -05001109 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001110}
1111
Jim Van Verth3645bb02018-06-26 14:58:58 -04001112bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -04001113 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001114
1115 fPathPolygon.setReserve(path.countPoints());
Jim Van Verth3645bb02018-06-26 14:58:58 -04001116 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +00001117
1118 // Walk around the path and compute clip polygon and path polygon.
1119 // Will also accumulate sum of areas for centroid.
1120 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -05001121 SkPath::Iter iter(path, true);
1122 SkPoint pts[4];
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001123 SkPoint clipPts[4];
Jim Van Verth91af7272017-01-27 14:15:54 -05001124 SkPath::Verb verb;
1125
Brian Salomon66085ed2017-02-02 15:39:34 -05001126 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +00001127 static constexpr SkScalar kA = 0.32495117187f;
1128 static constexpr SkScalar kB = 0.44311523437f;
1129 static constexpr SkScalar kC = 0.20141601562f;
1130 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -05001131
1132 SkPoint curvePoint;
1133 SkScalar w;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001134 bool closeSeen = false;
1135 bool verbSeen = false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001136 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001137 if (closeSeen) {
1138 return false;
1139 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001140 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001141 case SkPath::kLine_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001142 ctm.mapPoints(clipPts, &pts[1], 1);
1143 this->addToClip(clipPts[0]);
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001144 this->handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -05001145 break;
1146 case SkPath::kQuad_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001147 ctm.mapPoints(clipPts, pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001148 // point at t = 1/2
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001149 curvePoint.fX = 0.25f*clipPts[0].fX + 0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1150 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 -04001151 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001152 this->addToClip(clipPts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001153 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001154 break;
1155 case SkPath::kConic_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001156 ctm.mapPoints(clipPts, pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001157 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -05001158 // point at t = 1/2
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001159 curvePoint.fX = 0.25f*clipPts[0].fX + w*0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1160 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 -05001161 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
Jim Van Verthf507c282018-05-11 10:48:20 -04001162 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001163 this->addToClip(clipPts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001164 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -05001165 break;
1166 case SkPath::kCubic_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001167 ctm.mapPoints(clipPts, pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -05001168 // point at t = 5/16
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001169 curvePoint.fX = kA*clipPts[0].fX + kB*clipPts[1].fX
1170 + kC*clipPts[2].fX + kD*clipPts[3].fX;
1171 curvePoint.fY = kA*clipPts[0].fY + kB*clipPts[1].fY
1172 + kC*clipPts[2].fY + kD*clipPts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001173 this->addToClip(curvePoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001174 // point at t = 11/16
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001175 curvePoint.fX = kD*clipPts[0].fX + kC*clipPts[1].fX
1176 + kB*clipPts[2].fX + kA*clipPts[3].fX;
1177 curvePoint.fY = kD*clipPts[0].fY + kC*clipPts[1].fY
1178 + kB*clipPts[2].fY + kA*clipPts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001179 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001180 this->addToClip(clipPts[3]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001181 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001182 break;
Brian Salomonab664fa2017-03-24 16:07:20 +00001183 case SkPath::kMove_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001184 if (verbSeen) {
1185 return false;
1186 }
1187 break;
Jim Van Verth91af7272017-01-27 14:15:54 -05001188 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +00001189 case SkPath::kDone_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001190 closeSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001191 break;
1192 default:
1193 SkDEBUGFAIL("unknown verb");
1194 }
Jim Van Verth3645bb02018-06-26 14:58:58 -04001195 verbSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001196 }
1197
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001198 this->finishPathPolygon();
Jim Van Verth3645bb02018-06-26 14:58:58 -04001199 return true;
Brian Salomon66085ed2017-02-02 15:39:34 -05001200}
1201
Jim Van Verthd737ab92018-09-10 15:19:01 -04001202void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
1203 if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count() - 1])) {
1204 fClipPolygon.push_back(point);
Brian Salomon66085ed2017-02-02 15:39:34 -05001205 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001206}
Brian Salomon958fbc42017-01-30 17:01:28 -05001207
1208///////////////////////////////////////////////////////////////////////////////////////////////////
1209
Brian Salomonaff27a22017-02-06 15:47:44 -05001210sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001211 const SkPoint3& zPlane, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001212 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001213 return nullptr;
1214 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001215 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001216 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001217}
1218
Brian Salomonaff27a22017-02-06 15:47:44 -05001219sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001220 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001221 SkScalar lightRadius, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001222 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() ||
Jim Van Verth1989c492018-05-31 13:15:16 -04001223 !lightPos.isFinite() || !(lightPos.fZ >= SK_ScalarNearlyZero) ||
1224 !SkScalarIsFinite(lightRadius) || !(lightRadius >= SK_ScalarNearlyZero)) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001225 return nullptr;
1226 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001227 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001228 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001229}