blob: 7956f0e86faff85a6f3faafa26de9dc5caf692fc [file] [log] [blame]
Jim Van Verthbce74962017-01-25 09:39:46 -05001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Jim Van Verthefe3ded2017-01-30 13:11:45 -05008#include "SkShadowTessellator.h"
Cary Clarka4083c92017-09-15 11:59:23 -04009#include "SkColorData.h"
Jim Van Verth1af03d42017-07-31 09:34:58 -040010#include "SkDrawShadowInfo.h"
Jim Van Verth58abc9e2017-01-25 11:05:01 -050011#include "SkGeometry.h"
Jim Van Verth8664a1d2018-06-28 16:26:50 -040012#include "SkPolyUtils.h"
Jim Van Verth85dc96b2017-01-30 14:49:21 -050013#include "SkPath.h"
Mike Reed54518ac2017-07-22 08:29:48 -040014#include "SkPoint3.h"
Cary Clarkdf429f32017-11-08 11:44:31 -050015#include "SkPointPriv.h"
Brian Salomonaff27a22017-02-06 15:47:44 -050016#include "SkVertices.h"
Jim Van Verth85dc96b2017-01-30 14:49:21 -050017
18#if SK_SUPPORT_GPU
19#include "GrPathUtils.h"
20#endif
Jim Van Verth58abc9e2017-01-25 11:05:01 -050021
Brian Salomon958fbc42017-01-30 17:01:28 -050022
Jim Van Vertha84898d2017-02-06 13:38:23 -050023/**
24 * Base class
25 */
Brian Salomonaff27a22017-02-06 15:47:44 -050026class SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -050027public:
Jim Van Verthe308a122017-05-08 14:19:30 -040028 SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -050029 virtual ~SkBaseShadowTessellator() {}
Brian Salomon958fbc42017-01-30 17:01:28 -050030
Brian Salomonaff27a22017-02-06 15:47:44 -050031 sk_sp<SkVertices> releaseVertices() {
32 if (!fSucceeded) {
33 return nullptr;
34 }
Mike Reed887cdf12017-04-03 11:11:09 -040035 return SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, this->vertexCount(),
Mike Reed97eb4fe2017-03-14 12:04:16 -040036 fPositions.begin(), nullptr, fColors.begin(),
37 this->indexCount(), fIndices.begin());
Brian Salomon1a8b79a2017-01-31 11:26:26 -050038 }
Brian Salomon958fbc42017-01-30 17:01:28 -050039
Jim Van Vertha84898d2017-02-06 13:38:23 -050040protected:
Jim Van Verthda965502017-04-11 15:29:14 -040041 static constexpr auto kMinHeight = 0.1f;
42
Brian Salomonaff27a22017-02-06 15:47:44 -050043 int vertexCount() const { return fPositions.count(); }
44 int indexCount() const { return fIndices.count(); }
45
Jim Van Verthd737ab92018-09-10 15:19:01 -040046 // initialization methods
Jim Van Verthcdaf6612018-06-05 15:21:13 -040047 bool accumulateCentroid(const SkPoint& c, const SkPoint& n);
48 bool checkConvexity(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2);
49 void finishPathPolygon();
Jim Van Verthd737ab92018-09-10 15:19:01 -040050
51 // convex shadow methods
52 bool computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip);
53 void computeClipVectorsAndTestCentroid();
54 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
55 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal, bool lastEdge, bool doClip);
56 bool addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex);
57 int getClosestUmbraPoint(const SkPoint& point);
58
59 // concave shadow methods
60 bool computeConcaveShadow(SkScalar inset, SkScalar outset);
Jim Van Verth8760e2f2018-06-12 14:21:38 -040061 void stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
62 SkTDArray<int>* umbraIndices,
63 const SkTDArray<SkPoint>& penumbraPolygon,
64 SkTDArray<int>* penumbraIndices);
Jim Van Verthcdaf6612018-06-05 15:21:13 -040065
66 void handleLine(const SkPoint& p);
Jim Van Vertha84898d2017-02-06 13:38:23 -050067 void handleLine(const SkMatrix& m, SkPoint* p);
Brian Salomon958fbc42017-01-30 17:01:28 -050068
69 void handleQuad(const SkPoint pts[3]);
Jim Van Vertha84898d2017-02-06 13:38:23 -050070 void handleQuad(const SkMatrix& m, SkPoint pts[3]);
Brian Salomon958fbc42017-01-30 17:01:28 -050071
Jim Van Vertha84898d2017-02-06 13:38:23 -050072 void handleCubic(const SkMatrix& m, SkPoint pts[4]);
Brian Salomon958fbc42017-01-30 17:01:28 -050073
Jim Van Vertha84898d2017-02-06 13:38:23 -050074 void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
Brian Salomon958fbc42017-01-30 17:01:28 -050075
Jim Van Verthd737ab92018-09-10 15:19:01 -040076 bool addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc);
Jim Van Verthb4366552017-03-27 14:25:29 -040077
Jim Van Verth872da6b2018-04-10 11:24:11 -040078 void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
79 void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
80
Jim Van Verthe308a122017-05-08 14:19:30 -040081 SkScalar heightFunc(SkScalar x, SkScalar y) {
82 return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
83 }
84
Jim Van Verth0b7645f2018-08-31 12:36:52 -040085 SkPoint3 fZPlaneParams;
Jim Van Verthda965502017-04-11 15:29:14 -040086
Brian Salomon958fbc42017-01-30 17:01:28 -050087 // temporary buffer
88 SkTDArray<SkPoint> fPointBuffer;
Brian Salomon0dda9cb2017-02-03 10:33:25 -050089
Jim Van Vertha84898d2017-02-06 13:38:23 -050090 SkTDArray<SkPoint> fPositions;
91 SkTDArray<SkColor> fColors;
92 SkTDArray<uint16_t> fIndices;
93
Jim Van Verthcdaf6612018-06-05 15:21:13 -040094 SkTDArray<SkPoint> fPathPolygon;
Jim Van Verthd737ab92018-09-10 15:19:01 -040095 SkTDArray<SkPoint> fUmbraPolygon;
96 SkTDArray<SkPoint> fClipPolygon;
97 SkTDArray<SkVector> fClipVectors;
98
Jim Van Verthcdaf6612018-06-05 15:21:13 -040099 SkPoint fCentroid;
100 SkScalar fArea;
101 SkScalar fLastArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400102 SkScalar fLastCross;
103
Jim Van Verth76387852017-05-16 16:55:16 -0400104 int fFirstVertexIndex;
105 SkVector fFirstOutset;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500106 SkPoint fFirstPoint;
107
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500108 bool fSucceeded;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500109 bool fTransparent;
Jim Van Verthf507c282018-05-11 10:48:20 -0400110 bool fIsConvex;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400111 bool fValidUmbra;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500112
113 SkColor fUmbraColor;
114 SkColor fPenumbraColor;
115
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 Verthda965502017-04-11 15:29:14 -0400126static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
Jim Van Verthbce74962017-01-25 09:39:46 -0500127 SkVector* newNormal) {
128 SkVector normal;
129 // compute perpendicular
130 normal.fX = p0.fY - p1.fY;
131 normal.fY = p1.fX - p0.fX;
Jim Van Verthda965502017-04-11 15:29:14 -0400132 normal *= dir;
Jim Van Verthbce74962017-01-25 09:39:46 -0500133 if (!normal.normalize()) {
134 return false;
135 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500136 *newNormal = normal;
137 return true;
138}
139
Jim Van Verthf507c282018-05-11 10:48:20 -0400140static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
141 static constexpr SkScalar kClose = (SK_Scalar1 / 16);
142 static constexpr SkScalar kCloseSqd = kClose * kClose;
143
144 SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
145 return distSq < kCloseSqd;
146}
147
148static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
149 SkVector v0 = p1 - p0;
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400150 SkVector v1 = p2 - p1;
Jim Van Verthf507c282018-05-11 10:48:20 -0400151 return v0.cross(v1);
152}
153
Jim Van Verthe308a122017-05-08 14:19:30 -0400154SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
155 : fZPlaneParams(zPlaneParams)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400156 , fCentroid({0, 0})
157 , fArea(0)
158 , fLastArea(0)
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400159 , fLastCross(0)
Jim Van Verth76387852017-05-16 16:55:16 -0400160 , fFirstVertexIndex(-1)
Brian Salomonaff27a22017-02-06 15:47:44 -0500161 , fSucceeded(false)
162 , fTransparent(transparent)
Jim Van Verthf507c282018-05-11 10:48:20 -0400163 , fIsConvex(true)
Jim Van Verthd737ab92018-09-10 15:19:01 -0400164 , fValidUmbra(true)
Brian Salomonaff27a22017-02-06 15:47:44 -0500165 , fDirection(1)
Jim Van Verthd737ab92018-09-10 15:19:01 -0400166 , fPrevUmbraIndex(-1)
167 , fCurrUmbraIndex(0)
168 , fCurrClipIndex(0)
169 , fPrevUmbraOutside(false)
170 , fFirstUmbraOutside(false) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500171 // child classes will set reserve for positions, colors and indices
172}
173
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400174bool SkBaseShadowTessellator::accumulateCentroid(const SkPoint& curr, const SkPoint& next) {
175 if (duplicate_pt(curr, next)) {
176 return false;
177 }
178
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400179 SkASSERT(fPathPolygon.count() > 0);
180 SkVector v0 = curr - fPathPolygon[0];
181 SkVector v1 = next - fPathPolygon[0];
182 SkScalar quadArea = v0.cross(v1);
183 fCentroid.fX += (v0.fX + v1.fX) * quadArea;
184 fCentroid.fY += (v0.fY + v1.fY) * quadArea;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400185 fArea += quadArea;
186 // convexity check
Jim Van Verth3645bb02018-06-26 14:58:58 -0400187 if (quadArea*fLastArea < 0) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400188 fIsConvex = false;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400189 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400190 if (0 != quadArea) {
191 fLastArea = quadArea;
192 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400193
194 return true;
195}
196
197bool SkBaseShadowTessellator::checkConvexity(const SkPoint& p0,
198 const SkPoint& p1,
199 const SkPoint& p2) {
200 SkScalar cross = perp_dot(p0, p1, p2);
201 // skip collinear point
202 if (SkScalarNearlyZero(cross)) {
203 return false;
204 }
205
206 // check for convexity
207 if (fLastCross*cross < 0) {
208 fIsConvex = false;
209 }
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400210 if (0 != cross) {
211 fLastCross = cross;
212 }
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400213
214 return true;
215}
216
217void SkBaseShadowTessellator::finishPathPolygon() {
218 if (fPathPolygon.count() > 1) {
219 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], fPathPolygon[0])) {
220 // remove coincident point
221 fPathPolygon.pop();
222 }
223 }
224
225 if (fPathPolygon.count() > 2) {
Jim Van Verth6784ffa2018-07-03 16:12:39 -0400226 // do this before the final convexity check, so we use the correct fPathPolygon[0]
227 fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
228 fCentroid += fPathPolygon[0];
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400229 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
230 fPathPolygon[fPathPolygon.count() - 1],
231 fPathPolygon[0])) {
232 // remove collinear point
233 fPathPolygon[0] = fPathPolygon[fPathPolygon.count() - 1];
234 fPathPolygon.pop();
235 }
236 }
237
Jim Van Verth7deacf42018-06-08 15:13:25 -0400238 // if area is positive, winding is ccw
239 fDirection = fArea > 0 ? -1 : 1;
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400240}
241
Jim Van Verthd737ab92018-09-10 15:19:01 -0400242bool SkBaseShadowTessellator::computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip) {
243 if (doClip) {
244 this->computeClipVectorsAndTestCentroid();
245 }
246
247 // generate inner ring
248 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), inset,
249 &fUmbraPolygon)) {
250 // not ideal, but in this case we'll inset using the centroid
251 fValidUmbra = false;
252 }
253
254 // walk around the path polygon, generate outer ring and connect to inner ring
255 if (fTransparent) {
256 fPositions.push_back(fCentroid);
257 fColors.push_back(fUmbraColor);
258 }
259 fCurrUmbraIndex = 0;
260
261 // initial setup
262 // add first quad
263 int polyCount = fPathPolygon.count();
264 if (!compute_normal(fPathPolygon[polyCount - 1], fPathPolygon[0], fDirection, &fFirstOutset)) {
265 // polygon should be sanitized by this point, so this is unrecoverable
266 return false;
267 }
268
269 fFirstOutset *= outset;
270 fFirstPoint = fPathPolygon[polyCount - 1];
271 fFirstVertexIndex = fPositions.count();
272 fPrevOutset = fFirstOutset;
273 fPrevPoint = fFirstPoint;
274 fPrevUmbraIndex = -1;
275
276 this->addInnerPoint(fFirstPoint, &fPrevUmbraIndex);
277
278 if (!fTransparent && doClip) {
279 SkPoint clipPoint;
280 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
281 fCentroid, &clipPoint);
282 if (isOutside) {
283 fPositions.push_back(clipPoint);
284 fColors.push_back(fUmbraColor);
285 }
286 fPrevUmbraOutside = isOutside;
287 fFirstUmbraOutside = isOutside;
288 }
289
290 SkPoint newPoint = fFirstPoint + fFirstOutset;
291 fPositions.push_back(newPoint);
292 fColors.push_back(fPenumbraColor);
293 this->addEdge(fPathPolygon[0], fFirstOutset, false, doClip);
294
295 for (int i = 1; i < polyCount; ++i) {
296 SkVector normal;
297 if (!compute_normal(fPrevPoint, fPathPolygon[i], fDirection, &normal)) {
298 return false;
299 }
300 normal *= outset;
301 this->addArc(normal, outset, true);
302 this->addEdge(fPathPolygon[i], normal, i == polyCount - 1, doClip);
303 }
304 SkASSERT(this->indexCount());
305
306 // final fan
307 SkASSERT(fPositions.count() >= 3);
308 if (this->addArc(fFirstOutset, outset, false)) {
309 if (fFirstUmbraOutside) {
310 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
311 fFirstVertexIndex + 2);
312 } else {
313 this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
314 fFirstVertexIndex + 1);
315 }
316 } else {
317 // no arc added, fix up by setting first penumbra point position to last one
318 if (fFirstUmbraOutside) {
319 fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
320 } else {
321 fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
322 }
323 }
324
325 return true;
326}
327
328void SkBaseShadowTessellator::computeClipVectorsAndTestCentroid() {
329 SkASSERT(fClipPolygon.count() >= 3);
330 fCurrClipIndex = fClipPolygon.count() - 1;
331
332 // init clip vectors
333 SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
334 SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
335 fClipVectors.push_back(v0);
336
337 // init centroid check
338 bool hiddenCentroid = true;
339 v1 = fCentroid - fClipPolygon[0];
340 SkScalar initCross = v0.cross(v1);
341
342 for (int p = 1; p < fClipPolygon.count(); ++p) {
343 // add to clip vectors
344 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
345 fClipVectors.push_back(v0);
346 // Determine if transformed centroid is inside clipPolygon.
347 v1 = fCentroid - fClipPolygon[p];
348 if (initCross*v0.cross(v1) <= 0) {
349 hiddenCentroid = false;
350 }
351 }
352 SkASSERT(fClipVectors.count() == fClipPolygon.count());
353
354 fTransparent = fTransparent || !hiddenCentroid;
355}
356
357void SkBaseShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
358 bool lastEdge, bool doClip) {
359 // add next umbra point
360 int currUmbraIndex;
361 bool duplicate;
362 if (lastEdge) {
363 duplicate = false;
364 currUmbraIndex = fFirstVertexIndex;
365 fPrevPoint = nextPoint;
366 } else {
367 duplicate = this->addInnerPoint(nextPoint, &currUmbraIndex);
368 }
369 int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
370 ? fPositions.count() - 1
371 : fPositions.count() - 2;
372 if (!duplicate) {
373 // add to center fan if transparent or centroid showing
374 if (fTransparent) {
375 this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
376 // otherwise add to clip ring
377 } else if (doClip) {
378 SkPoint clipPoint;
379 bool isOutside = lastEdge ? fFirstUmbraOutside
380 : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
381 &clipPoint);
382 if (isOutside) {
383 if (!lastEdge) {
384 fPositions.push_back(clipPoint);
385 fColors.push_back(fUmbraColor);
386 }
387 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
388 if (fPrevUmbraOutside) {
389 // fill out quad
390 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
391 fPrevUmbraIndex + 1);
392 }
393 } else if (fPrevUmbraOutside) {
394 // add tri
395 this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
396 }
397
398 fPrevUmbraOutside = isOutside;
399 }
400 }
401
402 // add next penumbra point and quad
403 SkPoint newPoint = nextPoint + nextNormal;
404 fPositions.push_back(newPoint);
405 fColors.push_back(fPenumbraColor);
406
407 if (!duplicate) {
408 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
409 }
410 this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
411
412 fPrevUmbraIndex = currUmbraIndex;
413 fPrevOutset = nextNormal;
414}
415
416bool SkBaseShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
417 SkPoint* clipPoint) {
418 SkVector segmentVector = centroid - umbraPoint;
419
420 int startClipPoint = fCurrClipIndex;
421 do {
422 SkVector dp = umbraPoint - fClipPolygon[fCurrClipIndex];
423 SkScalar denom = fClipVectors[fCurrClipIndex].cross(segmentVector);
424 SkScalar t_num = dp.cross(segmentVector);
425 // if line segments are nearly parallel
426 if (SkScalarNearlyZero(denom)) {
427 // and collinear
428 if (SkScalarNearlyZero(t_num)) {
429 return false;
430 }
431 // otherwise are separate, will try the next poly segment
432 // else if crossing lies within poly segment
433 } else if (t_num >= 0 && t_num <= denom) {
434 SkScalar s_num = dp.cross(fClipVectors[fCurrClipIndex]);
435 // if umbra point is inside the clip polygon
436 if (s_num >= 0 && s_num <= denom) {
437 segmentVector *= s_num / denom;
438 *clipPoint = umbraPoint + segmentVector;
439 return true;
440 }
441 }
442 fCurrClipIndex = (fCurrClipIndex + 1) % fClipPolygon.count();
443 } while (fCurrClipIndex != startClipPoint);
444
445 return false;
446}
447
448bool SkBaseShadowTessellator::addInnerPoint(const SkPoint& pathPoint, int* currUmbraIndex) {
449 SkPoint umbraPoint;
450 if (!fValidUmbra) {
451 SkVector v = fCentroid - pathPoint;
452 v *= 0.95f;
453 umbraPoint = pathPoint + v;
454 } else {
455 umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
456 }
457
458 fPrevPoint = pathPoint;
459
460 // merge "close" points
461 if (fPrevUmbraIndex == -1 ||
462 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
463 // if we've wrapped around, don't add a new point
464 if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
465 *currUmbraIndex = fFirstVertexIndex;
466 } else {
467 *currUmbraIndex = fPositions.count();
468 fPositions.push_back(umbraPoint);
469 fColors.push_back(fUmbraColor);
470 }
471 return false;
472 } else {
473 *currUmbraIndex = fPrevUmbraIndex;
474 return true;
475 }
476}
477
478int SkBaseShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
479 SkScalar minDistance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[fCurrUmbraIndex]);
480 int index = fCurrUmbraIndex;
481 int dir = 1;
482 int next = (index + dir) % fUmbraPolygon.count();
483
484 // init travel direction
485 SkScalar distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
486 if (distance < minDistance) {
487 index = next;
488 minDistance = distance;
489 } else {
490 dir = fUmbraPolygon.count() - 1;
491 }
492
493 // iterate until we find a point that increases the distance
494 next = (index + dir) % fUmbraPolygon.count();
495 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
496 while (distance < minDistance) {
497 index = next;
498 minDistance = distance;
499 next = (index + dir) % fUmbraPolygon.count();
500 distance = SkPointPriv::DistanceToSqd(p, fUmbraPolygon[next]);
501 }
502
503 fCurrUmbraIndex = index;
504 return index;
505}
506
507bool SkBaseShadowTessellator::computeConcaveShadow(SkScalar inset, SkScalar outset) {
508 if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
509 return false;
510 }
511
512 // generate inner ring
513 SkTDArray<int> umbraIndices;
514 umbraIndices.setReserve(fPathPolygon.count());
515 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), inset,
516 &fUmbraPolygon, &umbraIndices)) {
517 // TODO: figure out how to handle this case
518 return false;
519 }
520
521 // generate outer ring
522 SkTDArray<SkPoint> penumbraPolygon;
523 SkTDArray<int> penumbraIndices;
524 penumbraPolygon.setReserve(fUmbraPolygon.count());
525 penumbraIndices.setReserve(fUmbraPolygon.count());
526 if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -outset,
527 &penumbraPolygon, &penumbraIndices)) {
528 // TODO: figure out how to handle this case
529 return false;
530 }
531
532 if (!fUmbraPolygon.count() || !penumbraPolygon.count()) {
533 return false;
534 }
535
536 // attach the rings together
537 this->stitchConcaveRings(fUmbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
538
539 return true;
540}
541
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400542void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
543 SkTDArray<int>* umbraIndices,
544 const SkTDArray<SkPoint>& penumbraPolygon,
545 SkTDArray<int>* penumbraIndices) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400546 // TODO: only create and fill indexMap when fTransparent is true?
547 SkAutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.count());
548
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400549 // find minimum indices
550 int minIndex = 0;
551 int min = (*penumbraIndices)[0];
552 for (int i = 1; i < (*penumbraIndices).count(); ++i) {
553 if ((*penumbraIndices)[i] < min) {
554 min = (*penumbraIndices)[i];
555 minIndex = i;
556 }
557 }
558 int currPenumbra = minIndex;
559
560 minIndex = 0;
561 min = (*umbraIndices)[0];
562 for (int i = 1; i < (*umbraIndices).count(); ++i) {
563 if ((*umbraIndices)[i] < min) {
564 min = (*umbraIndices)[i];
565 minIndex = i;
566 }
567 }
568 int currUmbra = minIndex;
569
570 // now find a case where the indices are equal (there should be at least one)
571 int maxPenumbraIndex = fPathPolygon.count() - 1;
572 int maxUmbraIndex = fPathPolygon.count() - 1;
573 while ((*penumbraIndices)[currPenumbra] != (*umbraIndices)[currUmbra]) {
574 if ((*penumbraIndices)[currPenumbra] < (*umbraIndices)[currUmbra]) {
575 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
576 maxPenumbraIndex = (*penumbraIndices)[currPenumbra];
577 currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
578 } else {
579 (*umbraIndices)[currUmbra] += fPathPolygon.count();
580 maxUmbraIndex = (*umbraIndices)[currUmbra];
581 currUmbra = (currUmbra + 1) % umbraPolygon.count();
582 }
583 }
584
Jim Van Verthd737ab92018-09-10 15:19:01 -0400585 fPositions.push_back(penumbraPolygon[currPenumbra]);
586 fColors.push_back(fPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400587 int prevPenumbraIndex = 0;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400588 fPositions.push_back(umbraPolygon[currUmbra]);
589 fColors.push_back(fUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400590 fPrevUmbraIndex = 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400591 indexMap[currUmbra] = 1;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400592
593 int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
594 int nextUmbra = (currUmbra + 1) % umbraPolygon.count();
595 while ((*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex ||
596 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
597
598 if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) {
599 // advance both one step
Jim Van Verthd737ab92018-09-10 15:19:01 -0400600 fPositions.push_back(penumbraPolygon[nextPenumbra]);
601 fColors.push_back(fPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400602 int currPenumbraIndex = fPositions.count() - 1;
603
Jim Van Verthd737ab92018-09-10 15:19:01 -0400604 fPositions.push_back(umbraPolygon[nextUmbra]);
605 fColors.push_back(fUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400606 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400607 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400608
609 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
610 fPrevUmbraIndex, currUmbraIndex);
611
612 prevPenumbraIndex = currPenumbraIndex;
613 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
614 currPenumbra = nextPenumbra;
615 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
616
617 fPrevUmbraIndex = currUmbraIndex;
618 (*umbraIndices)[currUmbra] += fPathPolygon.count();
619 currUmbra = nextUmbra;
620 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
621 }
622
623 while ((*penumbraIndices)[nextPenumbra] < (*umbraIndices)[nextUmbra] &&
624 (*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex) {
625 // fill out penumbra arc
Jim Van Verthd737ab92018-09-10 15:19:01 -0400626 fPositions.push_back(penumbraPolygon[nextPenumbra]);
627 fColors.push_back(fPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400628 int currPenumbraIndex = fPositions.count() - 1;
629
630 this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
631
632 prevPenumbraIndex = currPenumbraIndex;
633 // this ensures the ordering when we wrap around
634 (*penumbraIndices)[currPenumbra] += fPathPolygon.count();
635 currPenumbra = nextPenumbra;
636 nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
637 }
638
639 while ((*umbraIndices)[nextUmbra] < (*penumbraIndices)[nextPenumbra] &&
640 (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
641 // fill out umbra arc
Jim Van Verthd737ab92018-09-10 15:19:01 -0400642 fPositions.push_back(umbraPolygon[nextUmbra]);
643 fColors.push_back(fUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400644 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400645 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400646
647 this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
648
649 fPrevUmbraIndex = currUmbraIndex;
650 // this ensures the ordering when we wrap around
651 (*umbraIndices)[currUmbra] += fPathPolygon.count();
652 currUmbra = nextUmbra;
653 nextUmbra = (currUmbra + 1) % umbraPolygon.count();
654 }
655 }
656 // finish up by advancing both one step
Jim Van Verthd737ab92018-09-10 15:19:01 -0400657 fPositions.push_back(penumbraPolygon[nextPenumbra]);
658 fColors.push_back(fPenumbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400659 int currPenumbraIndex = fPositions.count() - 1;
660
Jim Van Verthd737ab92018-09-10 15:19:01 -0400661 fPositions.push_back(umbraPolygon[nextUmbra]);
662 fColors.push_back(fUmbraColor);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400663 int currUmbraIndex = fPositions.count() - 1;
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400664 indexMap[nextUmbra] = currUmbraIndex;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400665
666 this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
667 fPrevUmbraIndex, currUmbraIndex);
668
669 if (fTransparent) {
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400670 SkTriangulateSimplePolygon(umbraPolygon.begin(), indexMap, umbraPolygon.count(),
671 &fIndices);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400672 }
673}
674
675
Jim Van Vertha84898d2017-02-06 13:38:23 -0500676// tesselation tolerance values, in device space pixels
Mike Kleinb8b51e62017-02-09 15:22:53 -0500677#if SK_SUPPORT_GPU
Jim Van Vertha84898d2017-02-06 13:38:23 -0500678static const SkScalar kQuadTolerance = 0.2f;
679static const SkScalar kCubicTolerance = 0.2f;
Mike Kleinb8b51e62017-02-09 15:22:53 -0500680#endif
Jim Van Vertha84898d2017-02-06 13:38:23 -0500681static const SkScalar kConicTolerance = 0.5f;
682
Jim Van Verth3645bb02018-06-26 14:58:58 -0400683// clamps the point to the nearest 16th of a pixel
684static void sanitize_point(const SkPoint& in, SkPoint* out) {
685 out->fX = SkScalarRoundToScalar(16.f*in.fX)*0.0625f;
686 out->fY = SkScalarRoundToScalar(16.f*in.fY)*0.0625f;
687}
688
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400689void SkBaseShadowTessellator::handleLine(const SkPoint& p) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400690 SkPoint pSanitized;
691 sanitize_point(p, &pSanitized);
692
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400693 if (fPathPolygon.count() > 0) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400694 if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400695 // skip coincident point
696 return;
697 }
698 }
699
700 if (fPathPolygon.count() > 1) {
701 if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2],
702 fPathPolygon[fPathPolygon.count() - 1],
Jim Van Verth3645bb02018-06-26 14:58:58 -0400703 pSanitized)) {
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400704 // remove collinear point
705 fPathPolygon.pop();
706 }
707 }
708
Jim Van Verthd737ab92018-09-10 15:19:01 -0400709 fPathPolygon.push_back(pSanitized);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400710}
711
Brian Salomonaff27a22017-02-06 15:47:44 -0500712void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500713 m.mapPoints(p, 1);
Jim Van Verthcdaf6612018-06-05 15:21:13 -0400714
Jim Van Vertha84898d2017-02-06 13:38:23 -0500715 this->handleLine(*p);
716}
717
Brian Salomonaff27a22017-02-06 15:47:44 -0500718void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500719#if SK_SUPPORT_GPU
Jim Van Vertha947e292018-02-26 13:54:34 -0500720 // check for degeneracy
721 SkVector v0 = pts[1] - pts[0];
722 SkVector v1 = pts[2] - pts[0];
723 if (SkScalarNearlyZero(v0.cross(v1))) {
724 return;
725 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500726 // TODO: Pull PathUtils out of Ganesh?
727 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400728 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500729 SkPoint* target = fPointBuffer.begin();
730 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
731 kQuadTolerance, &target, maxCount);
732 fPointBuffer.setCount(count);
733 for (int i = 0; i < count; i++) {
734 this->handleLine(fPointBuffer[i]);
735 }
736#else
737 // for now, just to draw something
738 this->handleLine(pts[1]);
739 this->handleLine(pts[2]);
740#endif
741}
742
Brian Salomonaff27a22017-02-06 15:47:44 -0500743void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500744 m.mapPoints(pts, 3);
745 this->handleQuad(pts);
746}
747
Brian Salomonaff27a22017-02-06 15:47:44 -0500748void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500749 m.mapPoints(pts, 4);
750#if SK_SUPPORT_GPU
751 // TODO: Pull PathUtils out of Ganesh?
752 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
Mike Kleincc9856c2018-04-19 09:18:33 -0400753 fPointBuffer.setCount(maxCount);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500754 SkPoint* target = fPointBuffer.begin();
755 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
756 kCubicTolerance, &target, maxCount);
757 fPointBuffer.setCount(count);
758 for (int i = 0; i < count; i++) {
759 this->handleLine(fPointBuffer[i]);
760 }
761#else
762 // for now, just to draw something
763 this->handleLine(pts[1]);
764 this->handleLine(pts[2]);
765 this->handleLine(pts[3]);
766#endif
767}
768
Brian Salomonaff27a22017-02-06 15:47:44 -0500769void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
Jim Van Verthda965502017-04-11 15:29:14 -0400770 if (m.hasPerspective()) {
771 w = SkConic::TransformW(pts, w, m);
772 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500773 m.mapPoints(pts, 3);
774 SkAutoConicToQuads quadder;
775 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
776 SkPoint lastPoint = *(quads++);
777 int count = quadder.countQuads();
778 for (int i = 0; i < count; ++i) {
779 SkPoint quadPts[3];
780 quadPts[0] = lastPoint;
781 quadPts[1] = quads[0];
782 quadPts[2] = i == count - 1 ? pts[2] : quads[1];
783 this->handleQuad(quadPts);
784 lastPoint = quadPts[2];
785 quads += 2;
786 }
787}
788
Jim Van Verthd737ab92018-09-10 15:19:01 -0400789bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc) {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500790 // fill in fan from previous quad
791 SkScalar rotSin, rotCos;
792 int numSteps;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400793 if (!SkComputeRadialSteps(fPrevOutset, nextNormal, offset, &rotSin, &rotCos, &numSteps)) {
Jim Van Verth061cc212018-07-11 14:09:09 -0400794 // recover as best we can
795 numSteps = 0;
796 }
Jim Van Verth76387852017-05-16 16:55:16 -0400797 SkVector prevNormal = fPrevOutset;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400798 for (int i = 0; i < numSteps-1; ++i) {
Jim Van Verthda965502017-04-11 15:29:14 -0400799 SkVector currNormal;
800 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
801 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400802 fPositions.push_back(fPrevPoint + currNormal);
803 fColors.push_back(fPenumbraColor);
Jim Van Verth872da6b2018-04-10 11:24:11 -0400804 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500805
Jim Van Verthda965502017-04-11 15:29:14 -0400806 prevNormal = currNormal;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500807 }
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400808 if (finishArc && numSteps) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400809 fPositions.push_back(fPrevPoint + nextNormal);
810 fColors.push_back(fPenumbraColor);
Jim Van Verth872da6b2018-04-10 11:24:11 -0400811 this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
Jim Van Verthda965502017-04-11 15:29:14 -0400812 }
Jim Van Verth76387852017-05-16 16:55:16 -0400813 fPrevOutset = nextNormal;
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400814
815 return (numSteps > 0);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500816}
817
Jim Van Verth872da6b2018-04-10 11:24:11 -0400818void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
819 auto indices = fIndices.append(3);
820
821 indices[0] = index0;
822 indices[1] = index1;
823 indices[2] = index2;
824}
825
826void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
827 uint16_t index2, uint16_t index3) {
828 auto indices = fIndices.append(6);
829
830 indices[0] = index0;
831 indices[1] = index1;
832 indices[2] = index2;
833
834 indices[3] = index2;
835 indices[4] = index1;
836 indices[5] = index3;
837}
838
Jim Van Vertha84898d2017-02-06 13:38:23 -0500839//////////////////////////////////////////////////////////////////////////////////////////////////
840
Brian Salomonaff27a22017-02-06 15:47:44 -0500841class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500842public:
843 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400844 const SkPoint3& zPlaneParams, bool transparent);
Jim Van Vertha84898d2017-02-06 13:38:23 -0500845
846private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400847 bool computePathPolygon(const SkPath& path, const SkMatrix& ctm);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400848
Jim Van Verthd737ab92018-09-10 15:19:01 -0400849 static constexpr auto kInsetFactor = 0.5f;
Jim Van Verthda965502017-04-11 15:29:14 -0400850
Brian Salomonaff27a22017-02-06 15:47:44 -0500851 typedef SkBaseShadowTessellator INHERITED;
Jim Van Vertha84898d2017-02-06 13:38:23 -0500852};
853
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500854SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
Jim Van Vertha84898d2017-02-06 13:38:23 -0500855 const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400856 const SkPoint3& zPlaneParams,
Jim Van Verthbce74962017-01-25 09:39:46 -0500857 bool transparent)
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400858 : INHERITED(zPlaneParams, transparent) {
Jim Van Verthda965502017-04-11 15:29:14 -0400859 // Set base colors
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400860 SkScalar baseZ = heightFunc(path.getBounds().centerX(), path.getBounds().centerY());
861 SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(baseZ));
Jim Van Verthb4366552017-03-27 14:25:29 -0400862 // umbraColor is the interior value, penumbraColor the exterior value.
863 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
864 // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
865 // the final alpha.
Jim Van Verth060d9822017-05-04 09:58:17 -0400866 fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
867 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthd737ab92018-09-10 15:19:01 -0400868 SkScalar outset = SkDrawShadowMetrics::AmbientBlurRadius(baseZ);
Jim Van Verthda965502017-04-11 15:29:14 -0400869
Jim Van Verth3645bb02018-06-26 14:58:58 -0400870 if (!this->computePathPolygon(path, ctm)) {
871 return;
872 }
873 if (fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
874 fSucceeded = true; // We don't want to try to blur these cases, so we will
875 // return an empty SkVertices instead.
876 return;
877 }
878
Jim Van Verthbce74962017-01-25 09:39:46 -0500879 // Outer ring: 3*numPts
880 // Middle ring: numPts
881 fPositions.setReserve(4 * path.countPoints());
882 fColors.setReserve(4 * path.countPoints());
883 // Outer ring: 12*numPts
884 // Middle ring: 0
885 fIndices.setReserve(12 * path.countPoints());
886
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400887 if (fIsConvex) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400888 fSucceeded = this->computeConvexShadow(kInsetFactor, outset, false);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400889 } else {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400890 fSucceeded = this->computeConcaveShadow(kInsetFactor, outset);
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400891 }
892}
893
Jim Van Verth3645bb02018-06-26 14:58:58 -0400894bool SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) {
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400895 fPathPolygon.setReserve(path.countPoints());
896
897 // walk around the path, tessellate and generate outer ring
898 // if original path is transparent, will accumulate sum of points for centroid
899 SkPath::Iter iter(path, true);
900 SkPoint pts[4];
901 SkPath::Verb verb;
Jim Van Verth3645bb02018-06-26 14:58:58 -0400902 bool verbSeen = false;
903 bool closeSeen = false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400904 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -0400905 if (closeSeen) {
906 return false;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400907 }
Jim Van Verth3645bb02018-06-26 14:58:58 -0400908 switch (verb) {
909 case SkPath::kLine_Verb:
910 this->handleLine(ctm, &pts[1]);
911 break;
912 case SkPath::kQuad_Verb:
913 this->handleQuad(ctm, pts);
914 break;
915 case SkPath::kCubic_Verb:
916 this->handleCubic(ctm, pts);
917 break;
918 case SkPath::kConic_Verb:
919 this->handleConic(ctm, pts, iter.conicWeight());
920 break;
921 case SkPath::kMove_Verb:
922 if (verbSeen) {
923 return false;
924 }
925 break;
926 case SkPath::kClose_Verb:
927 case SkPath::kDone_Verb:
928 closeSeen = true;
929 break;
930 }
931 verbSeen = true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400932 }
933
934 this->finishPathPolygon();
Jim Van Verth3645bb02018-06-26 14:58:58 -0400935 return true;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400936}
937
Jim Van Verth91af7272017-01-27 14:15:54 -0500938///////////////////////////////////////////////////////////////////////////////////////////////////
939
Brian Salomonaff27a22017-02-06 15:47:44 -0500940class SkSpotShadowTessellator : public SkBaseShadowTessellator {
Brian Salomon958fbc42017-01-30 17:01:28 -0500941public:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500942 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400943 const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -0400944 SkScalar lightRadius, bool transparent);
Brian Salomon958fbc42017-01-30 17:01:28 -0500945
Brian Salomon958fbc42017-01-30 17:01:28 -0500946private:
Jim Van Verth3645bb02018-06-26 14:58:58 -0400947 bool computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -0400948 const SkMatrix& shadowTransform);
Jim Van Verthf507c282018-05-11 10:48:20 -0400949 void addToClip(const SkVector& nextPoint);
Jim Van Verthda965502017-04-11 15:29:14 -0400950
Brian Salomonaff27a22017-02-06 15:47:44 -0500951 typedef SkBaseShadowTessellator INHERITED;
Brian Salomon958fbc42017-01-30 17:01:28 -0500952};
953
Jim Van Vertha84898d2017-02-06 13:38:23 -0500954SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -0400955 const SkPoint3& zPlaneParams,
Jim Van Verthb4366552017-03-27 14:25:29 -0400956 const SkPoint3& lightPos, SkScalar lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -0400957 bool transparent)
Jim Van Verthd737ab92018-09-10 15:19:01 -0400958 : INHERITED(zPlaneParams, transparent) {
Jim Van Verthda965502017-04-11 15:29:14 -0400959
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400960 const SkRect& pathBounds = path.getBounds();
Jim Van Verthb4366552017-03-27 14:25:29 -0400961
962 // Set radius and colors
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400963 SkScalar occluderHeight = this->heightFunc(pathBounds.centerX(), pathBounds.centerY());
Jim Van Verth060d9822017-05-04 09:58:17 -0400964 fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
965 fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
Jim Van Verthb4366552017-03-27 14:25:29 -0400966
Jim Van Verth1af03d42017-07-31 09:34:58 -0400967 // Compute the blur radius, scale and translation for the spot shadow.
Jim Van Verthd737ab92018-09-10 15:19:01 -0400968 SkScalar outset;
Jim Van Verthda965502017-04-11 15:29:14 -0400969 SkMatrix shadowTransform;
970 if (!ctm.hasPerspective()) {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400971 SkScalar scale;
972 SkVector translate;
Jim Van Verthd737ab92018-09-10 15:19:01 -0400973 SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, lightPos.fZ,
974 lightRadius, &outset, &scale, &translate);
Jim Van Verthda965502017-04-11 15:29:14 -0400975 shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400976 shadowTransform.preConcat(ctm);
Jim Van Verthda965502017-04-11 15:29:14 -0400977 } else {
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400978 // get rotated quad in 3D
979 SkPoint pts[4];
980 ctm.mapRectToQuad(pts, pathBounds);
981 SkPoint3 pts3D[4];
982 SkScalar z = this->heightFunc(pathBounds.fLeft, pathBounds.fTop);
983 pts3D[0].set(pts[0].fX, pts[0].fY, z);
984 z = this->heightFunc(pathBounds.fRight, pathBounds.fTop);
985 pts3D[1].set(pts[1].fX, pts[1].fY, z);
986 z = this->heightFunc(pathBounds.fRight, pathBounds.fBottom);
987 pts3D[2].set(pts[2].fX, pts[2].fY, z);
988 z = this->heightFunc(pathBounds.fLeft, pathBounds.fBottom);
989 pts3D[3].set(pts[3].fX, pts[3].fY, z);
990
991 // project from light through corners to z=0 plane
992 for (int i = 0; i < 4; ++i) {
Jim Van Verthd737ab92018-09-10 15:19:01 -0400993 SkScalar zRatio = pts3D[i].fZ / (lightPos.fZ - pts3D[i].fZ);
Jim Van Verth0b7645f2018-08-31 12:36:52 -0400994 pts3D[i].fX -= (lightPos.fX - pts3D[i].fX)*zRatio;
995 pts3D[i].fY -= (lightPos.fY - pts3D[i].fY)*zRatio;
996 pts3D[i].fZ = SK_Scalar1;
997 }
998
999 // Generate matrix that projects from [-1,1]x[-1,1] square to projected quad
1000 SkPoint3 h0, h1, h2;
1001 // Compute crossing point between top and bottom edges (gives new x-axis).
1002 h0 = (pts3D[1].cross(pts3D[0])).cross(pts3D[2].cross(pts3D[3]));
1003 // Compute crossing point between left and right edges (gives new y-axis).
1004 h1 = (pts3D[0].cross(pts3D[3])).cross(pts3D[1].cross(pts3D[2]));
1005 // Compute crossing point between diagonals (gives new origin).
1006 h2 = (pts3D[0].cross(pts3D[2])).cross(pts3D[1].cross(pts3D[3]));
1007 // In some cases the crossing points are in the wrong direction
1008 // to map (-1,-1) to pts3D[0], so we need to correct for that.
1009 // Want h0 to be to the right of the left edge.
1010 SkVector3 v = pts3D[3] - pts3D[0];
1011 SkVector3 w = h0 - pts3D[0];
1012 SkScalar perpDot = v.fX*w.fY - v.fY*w.fX;
1013 if (perpDot > 0) {
1014 h0 = -h0;
1015 }
1016 // Want h1 to be above the bottom edge.
1017 v = pts3D[1] - pts3D[0];
1018 perpDot = v.fX*w.fY - v.fY*w.fX;
1019 if (perpDot < 0) {
1020 h1 = -h1;
1021 }
1022 shadowTransform.setAll(h0.fX / h2.fZ, h1.fX / h2.fZ, h2.fX / h2.fZ,
1023 h0.fY / h2.fZ, h1.fY / h2.fZ, h2.fY / h2.fZ,
1024 h0.fZ / h2.fZ, h1.fZ / h2.fZ, 1);
1025 // generate matrix that transforms from bounds to [-1,1]x[-1,1] square
1026 SkMatrix toHomogeneous;
1027 SkScalar xScale = 2/(pathBounds.fRight - pathBounds.fLeft);
1028 SkScalar yScale = 2/(pathBounds.fBottom - pathBounds.fTop);
1029 toHomogeneous.setAll(xScale, 0, -xScale*pathBounds.fLeft - 1,
1030 0, yScale, -yScale*pathBounds.fTop - 1,
1031 0, 0, 1);
1032 shadowTransform.preConcat(toHomogeneous);
1033
Jim Van Verthd737ab92018-09-10 15:19:01 -04001034 outset = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
Jim Van Verthda965502017-04-11 15:29:14 -04001035 }
Jim Van Verthd737ab92018-09-10 15:19:01 -04001036 SkScalar inset = outset;
Jim Van Verthb4366552017-03-27 14:25:29 -04001037
Brian Salomonab664fa2017-03-24 16:07:20 +00001038 // compute rough clip bounds for umbra, plus offset polygon, plus centroid
Jim Van Verth3645bb02018-06-26 14:58:58 -04001039 if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) {
1040 return;
1041 }
1042 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) {
1043 fSucceeded = true; // We don't want to try to blur these cases, so we will
1044 // return an empty SkVertices instead.
Brian Salomon66085ed2017-02-02 15:39:34 -05001045 return;
1046 }
Brian Salomon66085ed2017-02-02 15:39:34 -05001047
Jim Van Verthd737ab92018-09-10 15:19:01 -04001048 // TODO: calculate these reserves better
1049 // Penumbra ring: 3*numPts
1050 // Umbra ring: numPts
1051 // Inner ring: numPts
1052 fPositions.setReserve(5 * path.countPoints());
1053 fColors.setReserve(5 * path.countPoints());
1054 // Penumbra ring: 12*numPts
1055 // Umbra ring: 3*numPts
1056 fIndices.setReserve(15 * path.countPoints());
Jim Van Verthf507c282018-05-11 10:48:20 -04001057
Jim Van Verthf507c282018-05-11 10:48:20 -04001058 if (fIsConvex) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001059 SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
1060 fPathPolygon[0],
1061 fPathPolygon[1]);
1062 SkRect bounds;
1063 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
1064 for (int i = 1; i < fPathPolygon.count(); ++i) {
1065 int j = i + 1;
1066 if (i == fPathPolygon.count() - 1) {
1067 j = 0;
1068 }
1069 SkPoint currPoint = fPathPolygon[i];
1070 SkPoint nextPoint = fPathPolygon[j];
1071 SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
1072 nextPoint);
1073 if (distSq < minDistSq) {
1074 minDistSq = distSq;
1075 }
Brian Salomonab664fa2017-03-24 16:07:20 +00001076 }
Jim Van Verth872da6b2018-04-10 11:24:11 -04001077 static constexpr auto kTolerance = 1.0e-2f;
Jim Van Verthd737ab92018-09-10 15:19:01 -04001078 if (minDistSq < (inset + kTolerance)*(inset + kTolerance)) {
Jim Van Verth872da6b2018-04-10 11:24:11 -04001079 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
Jim Van Verthd737ab92018-09-10 15:19:01 -04001080 SkScalar newInset = SkScalarSqrt(minDistSq) - kTolerance;
1081 SkScalar ratio = 128 * (newInset + inset) / inset;
Jim Van Verth872da6b2018-04-10 11:24:11 -04001082 // they aren't PMColors, but the interpolation algorithm is the same
1083 fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
Jim Van Verthd737ab92018-09-10 15:19:01 -04001084 inset = newInset;
Brian Salomonab664fa2017-03-24 16:07:20 +00001085 }
Jim Van Verthd737ab92018-09-10 15:19:01 -04001086 fSucceeded = this->computeConvexShadow(inset, outset, true);
Jim Van Verth872da6b2018-04-10 11:24:11 -04001087 } else {
Jim Van Verthd737ab92018-09-10 15:19:01 -04001088 fSucceeded = this->computeConcaveShadow(inset, outset);
Jim Van Verth8760e2f2018-06-12 14:21:38 -04001089 }
1090
1091 if (!fSucceeded) {
Jim Van Verthf507c282018-05-11 10:48:20 -04001092 return;
Jim Van Verth91af7272017-01-27 14:15:54 -05001093 }
Jim Van Verthda965502017-04-11 15:29:14 -04001094
Brian Salomon0dda9cb2017-02-03 10:33:25 -05001095 fSucceeded = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001096}
1097
Jim Van Verth3645bb02018-06-26 14:58:58 -04001098bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthda965502017-04-11 15:29:14 -04001099 const SkMatrix& shadowTransform) {
Brian Salomonab664fa2017-03-24 16:07:20 +00001100
1101 fPathPolygon.setReserve(path.countPoints());
Jim Van Verth3645bb02018-06-26 14:58:58 -04001102 fClipPolygon.setReserve(path.countPoints());
Brian Salomonab664fa2017-03-24 16:07:20 +00001103
1104 // Walk around the path and compute clip polygon and path polygon.
1105 // Will also accumulate sum of areas for centroid.
1106 // For Bezier curves, we compute additional interior points on curve.
Jim Van Verth91af7272017-01-27 14:15:54 -05001107 SkPath::Iter iter(path, true);
1108 SkPoint pts[4];
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001109 SkPoint clipPts[4];
Jim Van Verth91af7272017-01-27 14:15:54 -05001110 SkPath::Verb verb;
1111
Brian Salomon66085ed2017-02-02 15:39:34 -05001112 // coefficients to compute cubic Bezier at t = 5/16
Brian Salomonab664fa2017-03-24 16:07:20 +00001113 static constexpr SkScalar kA = 0.32495117187f;
1114 static constexpr SkScalar kB = 0.44311523437f;
1115 static constexpr SkScalar kC = 0.20141601562f;
1116 static constexpr SkScalar kD = 0.03051757812f;
Brian Salomon66085ed2017-02-02 15:39:34 -05001117
1118 SkPoint curvePoint;
1119 SkScalar w;
Jim Van Verth3645bb02018-06-26 14:58:58 -04001120 bool closeSeen = false;
1121 bool verbSeen = false;
Jim Van Verth91af7272017-01-27 14:15:54 -05001122 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Jim Van Verth3645bb02018-06-26 14:58:58 -04001123 if (closeSeen) {
1124 return false;
1125 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001126 switch (verb) {
Jim Van Verth91af7272017-01-27 14:15:54 -05001127 case SkPath::kLine_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001128 ctm.mapPoints(clipPts, &pts[1], 1);
1129 this->addToClip(clipPts[0]);
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001130 this->handleLine(shadowTransform, &pts[1]);
Jim Van Verth91af7272017-01-27 14:15:54 -05001131 break;
1132 case SkPath::kQuad_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001133 ctm.mapPoints(clipPts, pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001134 // point at t = 1/2
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001135 curvePoint.fX = 0.25f*clipPts[0].fX + 0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1136 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 -04001137 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001138 this->addToClip(clipPts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001139 this->handleQuad(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001140 break;
1141 case SkPath::kConic_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001142 ctm.mapPoints(clipPts, pts, 3);
Brian Salomon66085ed2017-02-02 15:39:34 -05001143 w = iter.conicWeight();
Jim Van Vertha84898d2017-02-06 13:38:23 -05001144 // point at t = 1/2
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001145 curvePoint.fX = 0.25f*clipPts[0].fX + w*0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1146 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 -05001147 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
Jim Van Verthf507c282018-05-11 10:48:20 -04001148 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001149 this->addToClip(clipPts[2]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001150 this->handleConic(shadowTransform, pts, w);
Jim Van Verth91af7272017-01-27 14:15:54 -05001151 break;
1152 case SkPath::kCubic_Verb:
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001153 ctm.mapPoints(clipPts, pts, 4);
Brian Salomon66085ed2017-02-02 15:39:34 -05001154 // point at t = 5/16
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001155 curvePoint.fX = kA*clipPts[0].fX + kB*clipPts[1].fX
1156 + kC*clipPts[2].fX + kD*clipPts[3].fX;
1157 curvePoint.fY = kA*clipPts[0].fY + kB*clipPts[1].fY
1158 + kC*clipPts[2].fY + kD*clipPts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001159 this->addToClip(curvePoint);
Brian Salomon66085ed2017-02-02 15:39:34 -05001160 // point at t = 11/16
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001161 curvePoint.fX = kD*clipPts[0].fX + kC*clipPts[1].fX
1162 + kB*clipPts[2].fX + kA*clipPts[3].fX;
1163 curvePoint.fY = kD*clipPts[0].fY + kC*clipPts[1].fY
1164 + kB*clipPts[2].fY + kA*clipPts[3].fY;
Jim Van Verthf507c282018-05-11 10:48:20 -04001165 this->addToClip(curvePoint);
Jim Van Verth0b7645f2018-08-31 12:36:52 -04001166 this->addToClip(clipPts[3]);
Brian Salomonab664fa2017-03-24 16:07:20 +00001167 this->handleCubic(shadowTransform, pts);
Jim Van Verth91af7272017-01-27 14:15:54 -05001168 break;
Brian Salomonab664fa2017-03-24 16:07:20 +00001169 case SkPath::kMove_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001170 if (verbSeen) {
1171 return false;
1172 }
1173 break;
Jim Van Verth91af7272017-01-27 14:15:54 -05001174 case SkPath::kClose_Verb:
Brian Salomonab664fa2017-03-24 16:07:20 +00001175 case SkPath::kDone_Verb:
Jim Van Verth3645bb02018-06-26 14:58:58 -04001176 closeSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001177 break;
1178 default:
1179 SkDEBUGFAIL("unknown verb");
1180 }
Jim Van Verth3645bb02018-06-26 14:58:58 -04001181 verbSeen = true;
Jim Van Verth91af7272017-01-27 14:15:54 -05001182 }
1183
Jim Van Verthcdaf6612018-06-05 15:21:13 -04001184 this->finishPathPolygon();
Jim Van Verth3645bb02018-06-26 14:58:58 -04001185 return true;
Brian Salomon66085ed2017-02-02 15:39:34 -05001186}
1187
Jim Van Verthd737ab92018-09-10 15:19:01 -04001188void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
1189 if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count() - 1])) {
1190 fClipPolygon.push_back(point);
Brian Salomon66085ed2017-02-02 15:39:34 -05001191 }
Jim Van Verth91af7272017-01-27 14:15:54 -05001192}
Brian Salomon958fbc42017-01-30 17:01:28 -05001193
1194///////////////////////////////////////////////////////////////////////////////////////////////////
1195
Brian Salomonaff27a22017-02-06 15:47:44 -05001196sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001197 const SkPoint3& zPlane, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001198 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001199 return nullptr;
1200 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001201 SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001202 return ambientTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001203}
1204
Brian Salomonaff27a22017-02-06 15:47:44 -05001205sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
Jim Van Verthe308a122017-05-08 14:19:30 -04001206 const SkPoint3& zPlane, const SkPoint3& lightPos,
Jim Van Verth060d9822017-05-04 09:58:17 -04001207 SkScalar lightRadius, bool transparent) {
Mike Reed27575e82018-05-17 10:11:31 -04001208 if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() ||
Jim Van Verth1989c492018-05-31 13:15:16 -04001209 !lightPos.isFinite() || !(lightPos.fZ >= SK_ScalarNearlyZero) ||
1210 !SkScalarIsFinite(lightRadius) || !(lightRadius >= SK_ScalarNearlyZero)) {
Mike Reed9d5c6742018-03-06 10:31:27 -05001211 return nullptr;
1212 }
Jim Van Verthe308a122017-05-08 14:19:30 -04001213 SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
Brian Salomonaff27a22017-02-06 15:47:44 -05001214 return spotTess.releaseVertices();
Brian Salomon958fbc42017-01-30 17:01:28 -05001215}