blob: 3e237b04fccdf3ab5b6a49935058339bb1aa7230 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkPath.h"
9#include "include/core/SkPoint3.h"
10#include "include/core/SkVertices.h"
11#include "include/private/SkColorData.h"
12#include "src/core/SkDrawShadowInfo.h"
13#include "src/core/SkGeometry.h"
14#include "src/core/SkPointPriv.h"
15#include "src/utils/SkPolyUtils.h"
16#include "src/utils/SkShadowTessellator.h"
Jim Van Verth85dc96b2017-01-30 14:49:21 -050017
18#if SK_SUPPORT_GPU
Michael Ludwig663afe52019-06-03 16:46:19 -040019#include "src/gpu/geometry/GrPathUtils.h"
Jim Van Verth85dc96b2017-01-30 14:49:21 -050020#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 Vertha5ef3972019-05-01 13:28:07 -040028 SkBaseShadowTessellator(const SkPoint3& zPlaneParams, const SkRect& bounds, 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,
Jim Van Verth6bdfebe2018-09-17 11:46:50 -040058 const SkTDArray<SkPoint>& umbraPolygon, bool lastEdge, bool doClip);
59 bool addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor,
60 const SkTDArray<SkPoint>& umbraPolygon, int* currUmbraIndex);
61 int getClosestUmbraIndex(const SkPoint& point, const SkTDArray<SkPoint>& umbraPolygon);
Jim Van Verthd737ab92018-09-10 15:19:01 -040062
63 // concave shadow methods
64 bool computeConcaveShadow(SkScalar inset, SkScalar outset);
Jim Van Verth8760e2f2018-06-12 14:21:38 -040065 void stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
66 SkTDArray<int>* umbraIndices,
67 const SkTDArray<SkPoint>& penumbraPolygon,
68 SkTDArray<int>* penumbraIndices);
Jim Van Verthcdaf6612018-06-05 15:21:13 -040069
70 void handleLine(const SkPoint& p);
Jim Van Vertha84898d2017-02-06 13:38:23 -050071 void handleLine(const SkMatrix& m, SkPoint* p);
Brian Salomon958fbc42017-01-30 17:01:28 -050072
73 void handleQuad(const SkPoint pts[3]);
Jim Van Vertha84898d2017-02-06 13:38:23 -050074 void handleQuad(const SkMatrix& m, SkPoint pts[3]);
Brian Salomon958fbc42017-01-30 17:01:28 -050075
Jim Van Vertha84898d2017-02-06 13:38:23 -050076 void handleCubic(const SkMatrix& m, SkPoint pts[4]);
Brian Salomon958fbc42017-01-30 17:01:28 -050077
Jim Van Vertha84898d2017-02-06 13:38:23 -050078 void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
Brian Salomon958fbc42017-01-30 17:01:28 -050079
Jim Van Verthd737ab92018-09-10 15:19:01 -040080 bool addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc);
Jim Van Verthb4366552017-03-27 14:25:29 -040081
Jim Van Verth872da6b2018-04-10 11:24:11 -040082 void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
83 void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
84
Jim Van Verthe308a122017-05-08 14:19:30 -040085 SkScalar heightFunc(SkScalar x, SkScalar y) {
86 return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
87 }
88
Jim Van Verth0b7645f2018-08-31 12:36:52 -040089 SkPoint3 fZPlaneParams;
Jim Van Verthda965502017-04-11 15:29:14 -040090
Brian Salomon958fbc42017-01-30 17:01:28 -050091 // temporary buffer
92 SkTDArray<SkPoint> fPointBuffer;
Brian Salomon0dda9cb2017-02-03 10:33:25 -050093
Jim Van Vertha84898d2017-02-06 13:38:23 -050094 SkTDArray<SkPoint> fPositions;
95 SkTDArray<SkColor> fColors;
96 SkTDArray<uint16_t> fIndices;
97
Jim Van Verth6bdfebe2018-09-17 11:46:50 -040098 SkTDArray<SkPoint> fPathPolygon;
99 SkTDArray<SkPoint> fClipPolygon;
100 SkTDArray<SkVector> fClipVectors;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400101
Jim Van Vertha5ef3972019-05-01 13:28:07 -0400102 SkRect fPathBounds;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400103 SkPoint fCentroid;
104 SkScalar fArea;
105 SkScalar fLastArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400106 SkScalar fLastCross;
107
Jim Van Verth76387852017-05-16 16:55:16 -0400108 int fFirstVertexIndex;
109 SkVector fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500110 SkPoint fFirstPoint;
111
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500112 bool fSucceeded;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500113 bool fTransparent;
Jim Van Verthf507c282018-05-11 10:48:20 -0400114 bool fIsConvex;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400115 bool fValidUmbra;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500116
Jim Van Vertha84898d2017-02-06 13:38:23 -0500117 SkScalar fDirection;
118 int fPrevUmbraIndex;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400119 int fCurrUmbraIndex;
120 int fCurrClipIndex;
121 bool fPrevUmbraOutside;
122 bool fFirstUmbraOutside;
Jim Van Verth76387852017-05-16 16:55:16 -0400123 SkVector fPrevOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500124 SkPoint fPrevPoint;
Brian Salomon958fbc42017-01-30 17:01:28 -0500125};
126
Jim Van Verthfb186392018-09-11 11:37:46 -0400127// make external linkage happy
128constexpr SkColor SkBaseShadowTessellator::kUmbraColor;
129constexpr SkColor SkBaseShadowTessellator::kPenumbraColor;
130
Jim Van Verthda965502017-04-11 15:29:14 -0400131static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
Jim Van Verthbce74962017-01-25 09:39:46 -0500132 SkVector* newNormal) {
133 SkVector normal;
134 // compute perpendicular
135 normal.fX = p0.fY - p1.fY;
136 normal.fY = p1.fX - p0.fX;
Jim Van Verthda965502017-04-11 15:29:14 -0400137 normal *= dir;
Jim Van Verthbce74962017-01-25 09:39:46 -0500138 if (!normal.normalize()) {
139 return false;
140 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500141 *newNormal = normal;
142 return true;
143}
144
Jim Van Verthf507c282018-05-11 10:48:20 -0400145static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
146 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
147 static constexpr SkScalar kCloseSqd = kClose * kClose;
148
149 SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
150 return distSq < kCloseSqd;
151}
152
153static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
154 SkVector v0 = p1 - p0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400155 SkVector v1 = p2 - p1;
Jim Van Verthf507c282018-05-11 10:48:20 -0400156 return v0.cross(v1);
157}
158
Jim Van Vertha5ef3972019-05-01 13:28:07 -0400159SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, const SkRect& bounds,
160 bool transparent)
Jim Van Verthe308a122017-05-08 14:19:30 -0400161 : fZPlaneParams(zPlaneParams)
Jim Van Vertha5ef3972019-05-01 13:28:07 -0400162 , fPathBounds(bounds)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400163 , fCentroid({0, 0})
164 , fArea(0)
165 , fLastArea(0)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400166 , fLastCross(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400167 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500168 , fSucceeded(false)
169 , fTransparent(transparent)
Jim Van Verthf507c282018-05-11 10:48:20 -0400170 , fIsConvex(true)
Jim Van Verthd737ab92018-09-10 15:19:01 -0400171 , fValidUmbra(true)
Brian Salomonaff27a22017-02-06 15:47:44 -0500172 , fDirection(1)
Jim Van Verthd737ab92018-09-10 15:19:01 -0400173 , fPrevUmbraIndex(-1)
174 , fCurrUmbraIndex(0)
175 , fCurrClipIndex(0)
176 , fPrevUmbraOutside(false)
177 , fFirstUmbraOutside(false) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500178 // child classes will set reserve for positions, colors and indices
179}
180
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400181bool SkBaseShadowTessellator::accumulateCentroid(const SkPoint& curr, const SkPoint& next) {
182 if (duplicate_pt(curr, next)) {
183 return false;
184 }
185
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400186 SkASSERT(fPathPolygon.count() > 0);
187 SkVector v0 = curr - fPathPolygon[0];
188 SkVector v1 = next - fPathPolygon[0];
189 SkScalar quadArea = v0.cross(v1);
190 fCentroid.fX += (v0.fX + v1.fX) * quadArea;
191 fCentroid.fY += (v0.fY + v1.fY) * quadArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400192 fArea += quadArea;
193 // convexity check
Jim Van Verth3645bb02018-06-26 14:58:58 -0400194 if (quadArea*fLastArea < 0) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400195 fIsConvex = false;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400196 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400197 if (0 != quadArea) {
198 fLastArea = quadArea;
199 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400200
201 return true;
202}
203
204bool SkBaseShadowTessellator::checkConvexity(const SkPoint& p0,
205 const SkPoint& p1,
206 const SkPoint& p2) {
207 SkScalar cross = perp_dot(p0, p1, p2);
208 // skip collinear point
209 if (SkScalarNearlyZero(cross)) {
210 return false;
211 }
212
213 // check for convexity
214 if (fLastCross*cross < 0) {
215 fIsConvex = false;
216 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400217 if (0 != cross) {
218 fLastCross = cross;
219 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400220
221 return true;
222}
223
224void SkBaseShadowTessellator::finishPathPolygon() {
225 if (fPathPolygon.count() > 1) {
226 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], fPathPolygon[0])) {
227 // remove coincident point
228 fPathPolygon.pop();
229 }
230 }
231
232 if (fPathPolygon.count() > 2) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400233 // do this before the final convexity check, so we use the correct fPathPolygon[0]
234 fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
235 fCentroid += fPathPolygon[0];
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400236 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
237 fPathPolygon[fPathPolygon.count() - 1],
238 fPathPolygon[0])) {
239 // remove collinear point
240 fPathPolygon[0] = fPathPolygon[fPathPolygon.count() - 1];
241 fPathPolygon.pop();
242 }
243 }
244
Jim Van Verth7deacf42018-06-08 15:13:25 -0400245 // if area is positive, winding is ccw
246 fDirection = fArea > 0 ? -1 : 1;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400247}
248
Jim Van Verthd737ab92018-09-10 15:19:01 -0400249bool SkBaseShadowTessellator::computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip) {
250 if (doClip) {
251 this->computeClipVectorsAndTestCentroid();
252 }
253
Jim Van Verthfb186392018-09-11 11:37:46 -0400254 // adjust inset distance and umbra color if necessary
255 auto umbraColor = kUmbraColor;
256 SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
257 fPathPolygon[0],
258 fPathPolygon[1]);
259 SkRect bounds;
260 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
261 for (int i = 1; i < fPathPolygon.count(); ++i) {
262 int j = i + 1;
263 if (i == fPathPolygon.count() - 1) {
264 j = 0;
265 }
266 SkPoint currPoint = fPathPolygon[i];
267 SkPoint nextPoint = fPathPolygon[j];
268 SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
269 nextPoint);
270 if (distSq < minDistSq) {
271 minDistSq = distSq;
272 }
273 }
Jim Van Verthfb186392018-09-11 11:37:46 -0400274
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400275 SkTDArray<SkPoint> insetPolygon;
276 if (inset > SK_ScalarNearlyZero) {
277 static constexpr auto kTolerance = 1.0e-2f;
278 if (minDistSq < (inset + kTolerance)*(inset + kTolerance)) {
279 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
280 auto newInset = SkScalarSqrt(minDistSq) - kTolerance;
281 auto ratio = 128 * (newInset / inset + 1);
282 SkASSERT(SkScalarIsFinite(ratio));
283 // they aren't PMColors, but the interpolation algorithm is the same
284 umbraColor = SkPMLerp(kUmbraColor, kPenumbraColor, (unsigned)ratio);
285 inset = newInset;
286 }
287
288 // generate inner ring
289 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), inset,
290 &insetPolygon)) {
291 // not ideal, but in this case we'll inset using the centroid
292 fValidUmbra = false;
293 }
Jim Van Verthd737ab92018-09-10 15:19:01 -0400294 }
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400295 const SkTDArray<SkPoint>& umbraPolygon = (inset > SK_ScalarNearlyZero) ? insetPolygon
296 : fPathPolygon;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400297
298 // walk around the path polygon, generate outer ring and connect to inner ring
299 if (fTransparent) {
300 fPositions.push_back(fCentroid);
Jim Van Verthfb186392018-09-11 11:37:46 -0400301 fColors.push_back(umbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400302 }
303 fCurrUmbraIndex = 0;
304
305 // initial setup
306 // add first quad
307 int polyCount = fPathPolygon.count();
308 if (!compute_normal(fPathPolygon[polyCount - 1], fPathPolygon[0], fDirection, &fFirstOutset)) {
309 // polygon should be sanitized by this point, so this is unrecoverable
310 return false;
311 }
312
313 fFirstOutset *= outset;
314 fFirstPoint = fPathPolygon[polyCount - 1];
315 fFirstVertexIndex = fPositions.count();
316 fPrevOutset = fFirstOutset;
317 fPrevPoint = fFirstPoint;
318 fPrevUmbraIndex = -1;
319
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400320 this->addInnerPoint(fFirstPoint, umbraColor, umbraPolygon, &fPrevUmbraIndex);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400321
322 if (!fTransparent && doClip) {
323 SkPoint clipPoint;
324 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
325 fCentroid, &clipPoint);
326 if (isOutside) {
327 fPositions.push_back(clipPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400328 fColors.push_back(umbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400329 }
330 fPrevUmbraOutside = isOutside;
331 fFirstUmbraOutside = isOutside;
332 }
333
334 SkPoint newPoint = fFirstPoint + fFirstOutset;
335 fPositions.push_back(newPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400336 fColors.push_back(kPenumbraColor);
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400337 this->addEdge(fPathPolygon[0], fFirstOutset, umbraColor, umbraPolygon, false, doClip);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400338
339 for (int i = 1; i < polyCount; ++i) {
340 SkVector normal;
341 if (!compute_normal(fPrevPoint, fPathPolygon[i], fDirection, &normal)) {
342 return false;
343 }
344 normal *= outset;
345 this->addArc(normal, outset, true);
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400346 this->addEdge(fPathPolygon[i], normal, umbraColor, umbraPolygon,
347 i == polyCount - 1, doClip);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400348 }
349 SkASSERT(this->indexCount());
350
351 // final fan
352 SkASSERT(fPositions.count() >= 3);
353 if (this->addArc(fFirstOutset, outset, false)) {
354 if (fFirstUmbraOutside) {
355 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
356 fFirstVertexIndex + 2);
357 } else {
358 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
359 fFirstVertexIndex + 1);
360 }
361 } else {
362 // no arc added, fix up by setting first penumbra point position to last one
363 if (fFirstUmbraOutside) {
364 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
365 } else {
366 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
367 }
368 }
369
370 return true;
371}
372
373void SkBaseShadowTessellator::computeClipVectorsAndTestCentroid() {
374 SkASSERT(fClipPolygon.count() >= 3);
375 fCurrClipIndex = fClipPolygon.count() - 1;
376
377 // init clip vectors
378 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
379 SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
380 fClipVectors.push_back(v0);
381
382 // init centroid check
383 bool hiddenCentroid = true;
384 v1 = fCentroid - fClipPolygon[0];
385 SkScalar initCross = v0.cross(v1);
386
387 for (int p = 1; p < fClipPolygon.count(); ++p) {
388 // add to clip vectors
389 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
390 fClipVectors.push_back(v0);
391 // Determine if transformed centroid is inside clipPolygon.
392 v1 = fCentroid - fClipPolygon[p];
393 if (initCross*v0.cross(v1) <= 0) {
394 hiddenCentroid = false;
395 }
396 }
397 SkASSERT(fClipVectors.count() == fClipPolygon.count());
398
399 fTransparent = fTransparent || !hiddenCentroid;
400}
401
402void SkBaseShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400403 SkColor umbraColor, const SkTDArray<SkPoint>& umbraPolygon,
404 bool lastEdge, bool doClip) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400405 // add next umbra point
406 int currUmbraIndex;
407 bool duplicate;
408 if (lastEdge) {
409 duplicate = false;
410 currUmbraIndex = fFirstVertexIndex;
411 fPrevPoint = nextPoint;
412 } else {
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400413 duplicate = this->addInnerPoint(nextPoint, umbraColor, umbraPolygon, &currUmbraIndex);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400414 }
415 int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
416 ? fPositions.count() - 1
417 : fPositions.count() - 2;
418 if (!duplicate) {
419 // add to center fan if transparent or centroid showing
420 if (fTransparent) {
421 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
422 // otherwise add to clip ring
423 } else if (doClip) {
424 SkPoint clipPoint;
425 bool isOutside = lastEdge ? fFirstUmbraOutside
426 : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
427 &clipPoint);
428 if (isOutside) {
429 if (!lastEdge) {
430 fPositions.push_back(clipPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400431 fColors.push_back(umbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400432 }
433 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
434 if (fPrevUmbraOutside) {
435 // fill out quad
436 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
437 fPrevUmbraIndex + 1);
438 }
439 } else if (fPrevUmbraOutside) {
440 // add tri
441 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
442 }
443
444 fPrevUmbraOutside = isOutside;
445 }
446 }
447
448 // add next penumbra point and quad
449 SkPoint newPoint = nextPoint + nextNormal;
450 fPositions.push_back(newPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400451 fColors.push_back(kPenumbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400452
453 if (!duplicate) {
454 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
455 }
456 this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
457
458 fPrevUmbraIndex = currUmbraIndex;
459 fPrevOutset = nextNormal;
460}
461
462bool SkBaseShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
463 SkPoint* clipPoint) {
464 SkVector segmentVector = centroid - umbraPoint;
465
466 int startClipPoint = fCurrClipIndex;
467 do {
468 SkVector dp = umbraPoint - fClipPolygon[fCurrClipIndex];
469 SkScalar denom = fClipVectors[fCurrClipIndex].cross(segmentVector);
470 SkScalar t_num = dp.cross(segmentVector);
471 // if line segments are nearly parallel
472 if (SkScalarNearlyZero(denom)) {
473 // and collinear
474 if (SkScalarNearlyZero(t_num)) {
475 return false;
476 }
477 // otherwise are separate, will try the next poly segment
478 // else if crossing lies within poly segment
479 } else if (t_num >= 0 && t_num <= denom) {
480 SkScalar s_num = dp.cross(fClipVectors[fCurrClipIndex]);
481 // if umbra point is inside the clip polygon
482 if (s_num >= 0 && s_num <= denom) {
483 segmentVector *= s_num / denom;
484 *clipPoint = umbraPoint + segmentVector;
485 return true;
486 }
487 }
488 fCurrClipIndex = (fCurrClipIndex + 1) % fClipPolygon.count();
489 } while (fCurrClipIndex != startClipPoint);
490
491 return false;
492}
493
Jim Van Verthfb186392018-09-11 11:37:46 -0400494bool SkBaseShadowTessellator::addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor,
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400495 const SkTDArray<SkPoint>& umbraPolygon,
Jim Van Verthfb186392018-09-11 11:37:46 -0400496 int* currUmbraIndex) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400497 SkPoint umbraPoint;
498 if (!fValidUmbra) {
499 SkVector v = fCentroid - pathPoint;
500 v *= 0.95f;
501 umbraPoint = pathPoint + v;
502 } else {
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400503 umbraPoint = umbraPolygon[this->getClosestUmbraIndex(pathPoint, umbraPolygon)];
Jim Van Verthd737ab92018-09-10 15:19:01 -0400504 }
505
506 fPrevPoint = pathPoint;
507
508 // merge "close" points
509 if (fPrevUmbraIndex == -1 ||
510 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
511 // if we've wrapped around, don't add a new point
512 if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
513 *currUmbraIndex = fFirstVertexIndex;
514 } else {
515 *currUmbraIndex = fPositions.count();
516 fPositions.push_back(umbraPoint);
Jim Van Verthfb186392018-09-11 11:37:46 -0400517 fColors.push_back(umbraColor);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400518 }
519 return false;
520 } else {
521 *currUmbraIndex = fPrevUmbraIndex;
522 return true;
523 }
524}
525
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400526int SkBaseShadowTessellator::getClosestUmbraIndex(const SkPoint& p,
527 const SkTDArray<SkPoint>& umbraPolygon) {
528 SkScalar minDistance = SkPointPriv::DistanceToSqd(p, umbraPolygon[fCurrUmbraIndex]);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400529 int index = fCurrUmbraIndex;
530 int dir = 1;
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400531 int next = (index + dir) % umbraPolygon.count();
Jim Van Verthd737ab92018-09-10 15:19:01 -0400532
533 // init travel direction
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400534 SkScalar distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400535 if (distance < minDistance) {
536 index = next;
537 minDistance = distance;
538 } else {
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400539 dir = umbraPolygon.count() - 1;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400540 }
541
542 // iterate until we find a point that increases the distance
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400543 next = (index + dir) % umbraPolygon.count();
544 distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400545 while (distance < minDistance) {
546 index = next;
547 minDistance = distance;
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400548 next = (index + dir) % umbraPolygon.count();
549 distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400550 }
551
552 fCurrUmbraIndex = index;
553 return index;
554}
555
556bool SkBaseShadowTessellator::computeConcaveShadow(SkScalar inset, SkScalar outset) {
557 if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
558 return false;
559 }
560
561 // generate inner ring
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400562 SkTDArray<SkPoint> umbraPolygon;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400563 SkTDArray<int> umbraIndices;
564 umbraIndices.setReserve(fPathPolygon.count());
Jim Van Vertha5ef3972019-05-01 13:28:07 -0400565 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), fPathBounds, inset,
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400566 &umbraPolygon, &umbraIndices)) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400567 // TODO: figure out how to handle this case
568 return false;
569 }
570
571 // generate outer ring
572 SkTDArray<SkPoint> penumbraPolygon;
573 SkTDArray<int> penumbraIndices;
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400574 penumbraPolygon.setReserve(umbraPolygon.count());
575 penumbraIndices.setReserve(umbraPolygon.count());
Jim Van Vertha5ef3972019-05-01 13:28:07 -0400576 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), fPathBounds, -outset,
Jim Van Verthd737ab92018-09-10 15:19:01 -0400577 &penumbraPolygon, &penumbraIndices)) {
578 // TODO: figure out how to handle this case
579 return false;
580 }
581
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400582 if (!umbraPolygon.count() || !penumbraPolygon.count()) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400583 return false;
584 }
585
586 // attach the rings together
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400587 this->stitchConcaveRings(umbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400588
589 return true;
590}
591
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400592void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
593 SkTDArray<int>* umbraIndices,
594 const SkTDArray<SkPoint>& penumbraPolygon,
595 SkTDArray<int>* penumbraIndices) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400596 // TODO: only create and fill indexMap when fTransparent is true?
597 SkAutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.count());
598
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400599 // find minimum indices
600 int minIndex = 0;
601 int min = (*penumbraIndices)[0];
602 for (int i = 1; i < (*penumbraIndices).count(); ++i) {
603 if ((*penumbraIndices)[i] < min) {
604 min = (*penumbraIndices)[i];
605 minIndex = i;
606 }
607 }
608 int currPenumbra = minIndex;
609
610 minIndex = 0;
611 min = (*umbraIndices)[0];
612 for (int i = 1; i < (*umbraIndices).count(); ++i) {
613 if ((*umbraIndices)[i] < min) {
614 min = (*umbraIndices)[i];
615 minIndex = i;
616 }
617 }
618 int currUmbra = minIndex;
619
620 // now find a case where the indices are equal (there should be at least one)
621 int maxPenumbraIndex = fPathPolygon.count() - 1;
622 int maxUmbraIndex = fPathPolygon.count() - 1;
623 while ((*penumbraIndices)[currPenumbra] != (*umbraIndices)[currUmbra]) {
624 if ((*penumbraIndices)[currPenumbra] < (*umbraIndices)[currUmbra]) {
625 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
626 maxPenumbraIndex = (*penumbraIndices)[currPenumbra];
627 currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
628 } else {
629 (*umbraIndices)[currUmbra] += fPathPolygon.count();
630 maxUmbraIndex = (*umbraIndices)[currUmbra];
631 currUmbra = (currUmbra + 1) % umbraPolygon.count();
632 }
633 }
634
Jim Van Verthd737ab92018-09-10 15:19:01 -0400635 fPositions.push_back(penumbraPolygon[currPenumbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400636 fColors.push_back(kPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400637 int prevPenumbraIndex = 0;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400638 fPositions.push_back(umbraPolygon[currUmbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400639 fColors.push_back(kUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400640 fPrevUmbraIndex = 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400641 indexMap[currUmbra] = 1;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400642
643 int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
644 int nextUmbra = (currUmbra + 1) % umbraPolygon.count();
645 while ((*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex ||
646 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
647
648 if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) {
649 // advance both one step
Jim Van Verthd737ab92018-09-10 15:19:01 -0400650 fPositions.push_back(penumbraPolygon[nextPenumbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400651 fColors.push_back(kPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400652 int currPenumbraIndex = fPositions.count() - 1;
653
Jim Van Verthd737ab92018-09-10 15:19:01 -0400654 fPositions.push_back(umbraPolygon[nextUmbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400655 fColors.push_back(kUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400656 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400657 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400658
659 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
660 fPrevUmbraIndex, currUmbraIndex);
661
662 prevPenumbraIndex = currPenumbraIndex;
663 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
664 currPenumbra = nextPenumbra;
665 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
666
667 fPrevUmbraIndex = currUmbraIndex;
668 (*umbraIndices)[currUmbra] += fPathPolygon.count();
669 currUmbra = nextUmbra;
670 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
671 }
672
673 while ((*penumbraIndices)[nextPenumbra] < (*umbraIndices)[nextUmbra] &&
674 (*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex) {
675 // fill out penumbra arc
Jim Van Verthd737ab92018-09-10 15:19:01 -0400676 fPositions.push_back(penumbraPolygon[nextPenumbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400677 fColors.push_back(kPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400678 int currPenumbraIndex = fPositions.count() - 1;
679
680 this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
681
682 prevPenumbraIndex = currPenumbraIndex;
683 // this ensures the ordering when we wrap around
684 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
685 currPenumbra = nextPenumbra;
686 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
687 }
688
689 while ((*umbraIndices)[nextUmbra] < (*penumbraIndices)[nextPenumbra] &&
690 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
691 // fill out umbra arc
Jim Van Verthd737ab92018-09-10 15:19:01 -0400692 fPositions.push_back(umbraPolygon[nextUmbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400693 fColors.push_back(kUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400694 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400695 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400696
697 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
698
699 fPrevUmbraIndex = currUmbraIndex;
700 // this ensures the ordering when we wrap around
701 (*umbraIndices)[currUmbra] += fPathPolygon.count();
702 currUmbra = nextUmbra;
703 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
704 }
705 }
706 // finish up by advancing both one step
Jim Van Verthd737ab92018-09-10 15:19:01 -0400707 fPositions.push_back(penumbraPolygon[nextPenumbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400708 fColors.push_back(kPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400709 int currPenumbraIndex = fPositions.count() - 1;
710
Jim Van Verthd737ab92018-09-10 15:19:01 -0400711 fPositions.push_back(umbraPolygon[nextUmbra]);
Jim Van Verthfb186392018-09-11 11:37:46 -0400712 fColors.push_back(kUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400713 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400714 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400715
716 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
717 fPrevUmbraIndex, currUmbraIndex);
718
719 if (fTransparent) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400720 SkTriangulateSimplePolygon(umbraPolygon.begin(), indexMap, umbraPolygon.count(),
721 &fIndices);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400722 }
723}
724
725
Jim Van Vertha84898d2017-02-06 13:38:23 -0500726// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500727#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500728static const SkScalar kQuadTolerance = 0.2f;
729static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500730#endif
Brian Osmane3deee12018-11-20 11:10:15 -0500731static const SkScalar kConicTolerance = 0.25f;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500732
Jim Van Verth3645bb02018-06-26 14:58:58 -0400733// clamps the point to the nearest 16th of a pixel
734static void sanitize_point(const SkPoint& in, SkPoint* out) {
735 out->fX = SkScalarRoundToScalar(16.f*in.fX)*0.0625f;
736 out->fY = SkScalarRoundToScalar(16.f*in.fY)*0.0625f;
737}
738
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400739void SkBaseShadowTessellator::handleLine(const SkPoint& p) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400740 SkPoint pSanitized;
741 sanitize_point(p, &pSanitized);
742
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400743 if (fPathPolygon.count() > 0) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400744 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400745 // skip coincident point
746 return;
747 }
748 }
749
750 if (fPathPolygon.count() > 1) {
751 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
752 fPathPolygon[fPathPolygon.count() - 1],
Jim Van Verth3645bb02018-06-26 14:58:58 -0400753 pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400754 // remove collinear point
755 fPathPolygon.pop();
Jim Van Verth6bdfebe2018-09-17 11:46:50 -0400756 // it's possible that the previous point is coincident with the new one now
757 if (duplicate_pt(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) {
758 fPathPolygon.pop();
759 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400760 }
761 }
762
Jim Van Verthd737ab92018-09-10 15:19:01 -0400763 fPathPolygon.push_back(pSanitized);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400764}
765
Brian Salomonaff27a22017-02-06 15:47:44 -0500766void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500767 m.mapPoints(p, 1);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400768
Jim Van Vertha84898d2017-02-06 13:38:23 -0500769 this->handleLine(*p);
770}
771
Brian Salomonaff27a22017-02-06 15:47:44 -0500772void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500773#if SK_SUPPORT_GPU
Jim Van Vertha947e292018-02-26 13:54:34 -0500774 // check for degeneracy
775 SkVector v0 = pts[1] - pts[0];
776 SkVector v1 = pts[2] - pts[0];
777 if (SkScalarNearlyZero(v0.cross(v1))) {
778 return;
779 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500780 // TODO: Pull PathUtils out of Ganesh?
781 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400782 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500783 SkPoint* target = fPointBuffer.begin();
784 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
785 kQuadTolerance, &target, maxCount);
786 fPointBuffer.setCount(count);
787 for (int i = 0; i < count; i++) {
788 this->handleLine(fPointBuffer[i]);
789 }
790#else
791 // for now, just to draw something
792 this->handleLine(pts[1]);
793 this->handleLine(pts[2]);
794#endif
795}
796
Brian Salomonaff27a22017-02-06 15:47:44 -0500797void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500798 m.mapPoints(pts, 3);
799 this->handleQuad(pts);
800}
801
Brian Salomonaff27a22017-02-06 15:47:44 -0500802void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500803 m.mapPoints(pts, 4);
804#if SK_SUPPORT_GPU
805 // TODO: Pull PathUtils out of Ganesh?
806 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400807 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500808 SkPoint* target = fPointBuffer.begin();
809 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
810 kCubicTolerance, &target, maxCount);
811 fPointBuffer.setCount(count);
812 for (int i = 0; i < count; i++) {
813 this->handleLine(fPointBuffer[i]);
814 }
815#else
816 // for now, just to draw something
817 this->handleLine(pts[1]);
818 this->handleLine(pts[2]);
819 this->handleLine(pts[3]);
820#endif
821}
822
Brian Salomonaff27a22017-02-06 15:47:44 -0500823void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400824 if (m.hasPerspective()) {
825 w = SkConic::TransformW(pts, w, m);
826 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500827 m.mapPoints(pts, 3);
828 SkAutoConicToQuads quadder;
829 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
830 SkPoint lastPoint = *(quads++);
831 int count = quadder.countQuads();
832 for (int i = 0; i < count; ++i) {
833 SkPoint quadPts[3];
834 quadPts[0] = lastPoint;
835 quadPts[1] = quads[0];
836 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
837 this->handleQuad(quadPts);
838 lastPoint = quadPts[2];
839 quads += 2;
840 }
841}
842
Jim Van Verthd737ab92018-09-10 15:19:01 -0400843bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500844 // fill in fan from previous quad
845 SkScalar rotSin, rotCos;
846 int numSteps;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400847 if (!SkComputeRadialSteps(fPrevOutset, nextNormal, offset, &rotSin, &rotCos, &numSteps)) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400848 // recover as best we can
849 numSteps = 0;
850 }
Jim Van Verth76387852017-05-16 16:55:16 -0400851 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400852 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400853 SkVector currNormal;
854 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
855 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400856 fPositions.push_back(fPrevPoint + currNormal);
Jim Van Verthfb186392018-09-11 11:37:46 -0400857 fColors.push_back(kPenumbraColor);
Jim Van Verth872da6b2018-04-10 11:24:11 -0400858 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500859
Jim Van Verthda965502017-04-11 15:29:14 -0400860 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500861 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400862 if (finishArc && numSteps) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400863 fPositions.push_back(fPrevPoint + nextNormal);
Jim Van Verthfb186392018-09-11 11:37:46 -0400864 fColors.push_back(kPenumbraColor);
Jim Van Verth872da6b2018-04-10 11:24:11 -0400865 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Verthda965502017-04-11 15:29:14 -0400866 }
Jim Van Verth76387852017-05-16 16:55:16 -0400867 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400868
869 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500870}
871
Jim Van Verth872da6b2018-04-10 11:24:11 -0400872void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
873 auto indices = fIndices.append(3);
874
875 indices[0] = index0;
876 indices[1] = index1;
877 indices[2] = index2;
878}
879
880void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
881 uint16_t index2, uint16_t index3) {
882 auto indices = fIndices.append(6);
883
884 indices[0] = index0;
885 indices[1] = index1;
886 indices[2] = index2;
887
888 indices[3] = index2;
889 indices[4] = index1;
890 indices[5] = index3;
891}
892
Jim Van Vertha84898d2017-02-06 13:38:23 -0500893//////////////////////////////////////////////////////////////////////////////////////////////////
894
Brian Salomonaff27a22017-02-06 15:47:44 -0500895class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500896public:
897 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400898 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500899
900private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400901 bool computePathPolygon(const SkPath& path, const SkMatrix& ctm);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400902
Brian Salomonaff27a22017-02-06 15:47:44 -0500903 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500904};
905
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500906SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500907 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400908 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500909 bool transparent)
Jim Van Vertha5ef3972019-05-01 13:28:07 -0400910 : INHERITED(zPlaneParams, path.getBounds(), transparent) {
Jim Van Verthda965502017-04-11 15:29:14 -0400911 // Set base colors
Jim Van Vertha5ef3972019-05-01 13:28:07 -0400912 auto baseZ = heightFunc(fPathBounds.centerX(), fPathBounds.centerY());
Jim Van Verthb4366552017-03-27 14:25:29 -0400913 // umbraColor is the interior value, penumbraColor the exterior value.
Jim Van Verthfb186392018-09-11 11:37:46 -0400914 auto outset = SkDrawShadowMetrics::AmbientBlurRadius(baseZ);
915 auto inset = outset * SkDrawShadowMetrics::AmbientRecipAlpha(baseZ) - outset;
Jim Van Vertha5ef3972019-05-01 13:28:07 -0400916 inset = SkScalarPin(inset, 0, SkTMin(path.getBounds().width(),
917 path.getBounds().height()));
Jim Van Verthda965502017-04-11 15:29:14 -0400918
Jim Van Verth3645bb02018-06-26 14:58:58 -0400919 if (!this->computePathPolygon(path, ctm)) {
920 return;
921 }
922 if (fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
923 fSucceeded = true; // We don't want to try to blur these cases, so we will
924 // return an empty SkVertices instead.
925 return;
926 }
927
Jim Van Verthbce74962017-01-25 09:39:46 -0500928 // Outer ring: 3*numPts
929 // Middle ring: numPts
930 fPositions.setReserve(4 * path.countPoints());
931 fColors.setReserve(4 * path.countPoints());
932 // Outer ring: 12*numPts
933 // Middle ring: 0
934 fIndices.setReserve(12 * path.countPoints());
935
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400936 if (fIsConvex) {
Jim Van Verthfb186392018-09-11 11:37:46 -0400937 fSucceeded = this->computeConvexShadow(inset, outset, false);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400938 } else {
Jim Van Verthfb186392018-09-11 11:37:46 -0400939 fSucceeded = this->computeConcaveShadow(inset, outset);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400940 }
941}
942
Jim Van Verth3645bb02018-06-26 14:58:58 -0400943bool SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400944 fPathPolygon.setReserve(path.countPoints());
945
946 // walk around the path, tessellate and generate outer ring
947 // if original path is transparent, will accumulate sum of points for centroid
948 SkPath::Iter iter(path, true);
949 SkPoint pts[4];
950 SkPath::Verb verb;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400951 bool verbSeen = false;
952 bool closeSeen = false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400953 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400954 if (closeSeen) {
955 return false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400956 }
Jim Van Verth3645bb02018-06-26 14:58:58 -0400957 switch (verb) {
958 case SkPath::kLine_Verb:
959 this->handleLine(ctm, &pts[1]);
960 break;
961 case SkPath::kQuad_Verb:
962 this->handleQuad(ctm, pts);
963 break;
964 case SkPath::kCubic_Verb:
965 this->handleCubic(ctm, pts);
966 break;
967 case SkPath::kConic_Verb:
968 this->handleConic(ctm, pts, iter.conicWeight());
969 break;
970 case SkPath::kMove_Verb:
971 if (verbSeen) {
972 return false;
973 }
974 break;
975 case SkPath::kClose_Verb:
976 case SkPath::kDone_Verb:
977 closeSeen = true;
978 break;
979 }
980 verbSeen = true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400981 }
982
983 this->finishPathPolygon();
Jim Van Verth3645bb02018-06-26 14:58:58 -0400984 return true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400985}
986
Jim Van Verth91af7272017-01-27 14:15:54 -0500987///////////////////////////////////////////////////////////////////////////////////////////////////
988
Brian Salomonaff27a22017-02-06 15:47:44 -0500989class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500990public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500991 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400992 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400993 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500994
Brian Salomon958fbc42017-01-30 17:01:28 -0500995private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400996 bool computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400997 const SkMatrix& shadowTransform);
Jim Van Verthf507c282018-05-11 10:48:20 -0400998 void addToClip(const SkVector& nextPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400999
Brian Salomonaff27a22017-02-06 15:47:44 -05001000 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -05001001};
1002
Jim Van Vertha84898d2017-02-06 13:38:23 -05001003SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001004 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -04001005 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -04001006 bool transparent)
Jim Van Vertha5ef3972019-05-01 13:28:07 -04001007 : INHERITED(zPlaneParams, path.getBounds(), transparent) {
Jim Van Verthda965502017-04-11 15:29:14 -04001008
Jim Van Verth1af03d42017-07-31 09:34:58 -04001009 // Compute the blur radius, scale and translation for the spot shadow.
Jim Van Verthda965502017-04-11 15:29:14 -04001010 SkMatrix shadowTransform;
Jim Van Verth3a039d52018-09-14 17:14:47 -04001011 SkScalar outset;
1012 if (!SkDrawShadowMetrics::GetSpotShadowTransform(lightPos, lightRadius,
1013 ctm, zPlaneParams, path.getBounds(),
1014 &shadowTransform, &outset)) {
1015 return;
Jim Van Verthda965502017-04-11 15:29:14 -04001016 }
Jim Van Verthd737ab92018-09-10 15:19:01 -04001017 SkScalar inset = outset;
Jim Van Verthb4366552017-03-27 14:25:29 -04001018
Brian Salomonab664fa2017-03-24 16:07:20 +00001019 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verth3645bb02018-06-26 14:58:58 -04001020 if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) {
1021 return;
1022 }
1023 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
1024 fSucceeded = true; // We don't want to try to blur these cases, so we will
1025 // return an empty SkVertices instead.
Brian Salomon66085ed2017-02-02 15:39:34 -05001026 return;
1027 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001028
Jim Van Verthd737ab92018-09-10 15:19:01 -04001029 // TODO: calculate these reserves better
1030 // Penumbra ring: 3*numPts
1031 // Umbra ring: numPts
1032 // Inner ring: numPts
1033 fPositions.setReserve(5 * path.countPoints());
1034 fColors.setReserve(5 * path.countPoints());
1035 // Penumbra ring: 12*numPts
1036 // Umbra ring: 3*numPts
1037 fIndices.setReserve(15 * path.countPoints());
Jim Van Verthf507c282018-05-11 10:48:20 -04001038
Jim Van Verthf507c282018-05-11 10:48:20 -04001039 if (fIsConvex) {
Jim Van Verthd737ab92018-09-10 15:19:01 -04001040 fSucceeded = this->computeConvexShadow(inset, outset, true);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001041 } else {
Jim Van Verthd737ab92018-09-10 15:19:01 -04001042 fSucceeded = this->computeConcaveShadow(inset, outset);
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001043 }
1044
1045 if (!fSucceeded) {
Jim Van Verthf507c282018-05-11 10:48:20 -04001046 return;
Jim Van Verth91af7272017-01-27 14:15:54 -05001047 }
Jim Van Verthda965502017-04-11 15:29:14 -04001048
Brian Salomon0dda9cb2017-02-03 10:33:25 -05001049 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001050}
1051
Jim Van Verth3645bb02018-06-26 14:58:58 -04001052bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -04001053 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001054
1055 fPathPolygon.setReserve(path.countPoints());
Jim Van Verth3645bb02018-06-26 14:58:58 -04001056 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +00001057
1058 // Walk around the path and compute clip polygon and path polygon.
1059 // Will also accumulate sum of areas for centroid.
1060 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -05001061 SkPath::Iter iter(path, true);
1062 SkPoint pts[4];
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001063 SkPoint clipPts[4];
Jim Van Verth91af7272017-01-27 14:15:54 -05001064 SkPath::Verb verb;
1065
Brian Salomon66085ed2017-02-02 15:39:34 -05001066 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +00001067 static constexpr SkScalar kA = 0.32495117187f;
1068 static constexpr SkScalar kB = 0.44311523437f;
1069 static constexpr SkScalar kC = 0.20141601562f;
1070 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -05001071
1072 SkPoint curvePoint;
1073 SkScalar w;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001074 bool closeSeen = false;
1075 bool verbSeen = false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001076 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001077 if (closeSeen) {
1078 return false;
1079 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001080 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001081 case SkPath::kLine_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001082 ctm.mapPoints(clipPts, &pts[1], 1);
1083 this->addToClip(clipPts[0]);
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001084 this->handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -05001085 break;
1086 case SkPath::kQuad_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001087 ctm.mapPoints(clipPts, pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001088 // point at t = 1/2
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001089 curvePoint.fX = 0.25f*clipPts[0].fX + 0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1090 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 -04001091 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001092 this->addToClip(clipPts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001093 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001094 break;
1095 case SkPath::kConic_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001096 ctm.mapPoints(clipPts, pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001097 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -05001098 // point at t = 1/2
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001099 curvePoint.fX = 0.25f*clipPts[0].fX + w*0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1100 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 -05001101 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
Jim Van Verthf507c282018-05-11 10:48:20 -04001102 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001103 this->addToClip(clipPts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001104 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -05001105 break;
1106 case SkPath::kCubic_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001107 ctm.mapPoints(clipPts, pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -05001108 // point at t = 5/16
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001109 curvePoint.fX = kA*clipPts[0].fX + kB*clipPts[1].fX
1110 + kC*clipPts[2].fX + kD*clipPts[3].fX;
1111 curvePoint.fY = kA*clipPts[0].fY + kB*clipPts[1].fY
1112 + kC*clipPts[2].fY + kD*clipPts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001113 this->addToClip(curvePoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001114 // point at t = 11/16
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001115 curvePoint.fX = kD*clipPts[0].fX + kC*clipPts[1].fX
1116 + kB*clipPts[2].fX + kA*clipPts[3].fX;
1117 curvePoint.fY = kD*clipPts[0].fY + kC*clipPts[1].fY
1118 + kB*clipPts[2].fY + kA*clipPts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001119 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001120 this->addToClip(clipPts[3]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001121 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001122 break;
Brian Salomonab664fa2017-03-24 16:07:20 +00001123 case SkPath::kMove_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001124 if (verbSeen) {
1125 return false;
1126 }
1127 break;
Jim Van Verth91af7272017-01-27 14:15:54 -05001128 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +00001129 case SkPath::kDone_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001130 closeSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001131 break;
1132 default:
1133 SkDEBUGFAIL("unknown verb");
1134 }
Jim Van Verth3645bb02018-06-26 14:58:58 -04001135 verbSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001136 }
1137
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001138 this->finishPathPolygon();
Jim Van Verth3645bb02018-06-26 14:58:58 -04001139 return true;
Brian Salomon66085ed2017-02-02 15:39:34 -05001140}
1141
Jim Van Verthd737ab92018-09-10 15:19:01 -04001142void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
1143 if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count() - 1])) {
1144 fClipPolygon.push_back(point);
Brian Salomon66085ed2017-02-02 15:39:34 -05001145 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001146}
Brian Salomon958fbc42017-01-30 17:01:28 -05001147
1148///////////////////////////////////////////////////////////////////////////////////////////////////
1149
Brian Salomonaff27a22017-02-06 15:47:44 -05001150sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001151 const SkPoint3& zPlane, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001152 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001153 return nullptr;
1154 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001155 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001156 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001157}
1158
Brian Salomonaff27a22017-02-06 15:47:44 -05001159sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001160 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001161 SkScalar lightRadius, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001162 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() ||
Jim Van Verth1989c492018-05-31 13:15:16 -04001163 !lightPos.isFinite() || !(lightPos.fZ >= SK_ScalarNearlyZero) ||
1164 !SkScalarIsFinite(lightRadius) || !(lightRadius >= SK_ScalarNearlyZero)) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001165 return nullptr;
1166 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001167 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001168 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001169}