Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/utils/SkShadowTessellator.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
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
8
9
#include "src/utils/SkShadowTessellator.h"
10
11
#include "include/core/SkColor.h"
12
#include "include/core/SkMatrix.h"
13
#include "include/core/SkPath.h"
14
#include "include/core/SkPoint.h"
15
#include "include/core/SkPoint3.h"
16
#include "include/core/SkRect.h"
17
#include "include/core/SkTypes.h"
18
#include "include/core/SkVertices.h"
19
#include "include/private/SkColorData.h"
20
#include "include/private/base/SkFloatingPoint.h"
21
#include "include/private/base/SkTDArray.h"
22
#include "include/private/base/SkTemplates.h"
23
#include "src/core/SkDrawShadowInfo.h"
24
#include "src/core/SkGeometry.h"
25
#include "src/core/SkPointPriv.h"
26
#include "src/core/SkRectPriv.h"
27
#include "src/utils/SkPolyUtils.h"
28
29
#include <algorithm>
30
#include <cstdint>
31
32
33
#if defined(SK_GANESH)
34
#include "src/gpu/ganesh/geometry/GrPathUtils.h"
35
#endif
36
37
using namespace skia_private;
38
39
#if !defined(SK_ENABLE_OPTIMIZE_SIZE)
40
41
/**
42
 * Base class
43
 */
44
class SkBaseShadowTessellator {
45
public:
46
    SkBaseShadowTessellator(const SkPoint3& zPlaneParams, const SkRect& bounds, bool transparent);
47
0
    virtual ~SkBaseShadowTessellator() {}
48
49
0
    sk_sp<SkVertices> releaseVertices() {
50
0
        if (!fSucceeded) {
51
0
            return nullptr;
52
0
        }
53
0
        return SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, this->vertexCount(),
54
0
                                    fPositions.begin(), nullptr, fColors.begin(),
55
0
                                    this->indexCount(), fIndices.begin());
56
0
    }
57
58
protected:
59
    inline static constexpr auto kMinHeight = 0.1f;
60
    inline static constexpr auto kPenumbraColor = SK_ColorTRANSPARENT;
61
    inline static constexpr auto kUmbraColor = SK_ColorBLACK;
62
63
0
    int vertexCount() const { return fPositions.size(); }
64
0
    int indexCount() const { return fIndices.size(); }
65
66
    // initialization methods
67
    bool accumulateCentroid(const SkPoint& c, const SkPoint& n);
68
    bool checkConvexity(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2);
69
    void finishPathPolygon();
70
71
    // convex shadow methods
72
    bool computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip);
73
    void computeClipVectorsAndTestCentroid();
74
    bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
75
    void addEdge(const SkVector& nextPoint, const SkVector& nextNormal, SkColor umbraColor,
76
                 const SkTDArray<SkPoint>& umbraPolygon, bool lastEdge, bool doClip);
77
    bool addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor,
78
                       const SkTDArray<SkPoint>& umbraPolygon, int* currUmbraIndex);
79
    int getClosestUmbraIndex(const SkPoint& point, const SkTDArray<SkPoint>& umbraPolygon);
80
81
    // concave shadow methods
82
    bool computeConcaveShadow(SkScalar inset, SkScalar outset);
83
    void stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
84
                            SkTDArray<int>* umbraIndices,
85
                            const SkTDArray<SkPoint>& penumbraPolygon,
86
                            SkTDArray<int>* penumbraIndices);
87
88
    void handleLine(const SkPoint& p);
89
    void handleLine(const SkMatrix& m, SkPoint* p);
90
91
    void handleQuad(const SkPoint pts[3]);
92
    void handleQuad(const SkMatrix& m, SkPoint pts[3]);
93
94
    void handleCubic(const SkMatrix& m, SkPoint pts[4]);
95
96
    void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
97
98
    bool addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc);
99
100
    void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
101
    void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
102
103
0
    SkScalar heightFunc(SkScalar x, SkScalar y) {
104
0
        return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
105
0
    }
106
107
    SkPoint3            fZPlaneParams;
108
109
    // temporary buffer
110
    SkTDArray<SkPoint>  fPointBuffer;
111
112
    SkTDArray<SkPoint>  fPositions;
113
    SkTDArray<SkColor>  fColors;
114
    SkTDArray<uint16_t> fIndices;
115
116
    SkTDArray<SkPoint>   fPathPolygon;
117
    SkTDArray<SkPoint>   fClipPolygon;
118
    SkTDArray<SkVector>  fClipVectors;
119
120
    SkRect              fPathBounds;
121
    SkPoint             fCentroid;
122
    SkScalar            fArea;
123
    SkScalar            fLastArea;
124
    SkScalar            fLastCross;
125
126
    int                 fFirstVertexIndex;
127
    SkVector            fFirstOutset;
128
    SkPoint             fFirstPoint;
129
130
    bool                fSucceeded;
131
    bool                fTransparent;
132
    bool                fIsConvex;
133
    bool                fValidUmbra;
134
135
    SkScalar            fDirection;
136
    int                 fPrevUmbraIndex;
137
    int                 fCurrUmbraIndex;
138
    int                 fCurrClipIndex;
139
    bool                fPrevUmbraOutside;
140
    bool                fFirstUmbraOutside;
141
    SkVector            fPrevOutset;
142
    SkPoint             fPrevPoint;
143
};
144
145
static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
146
0
                           SkVector* newNormal) {
147
0
    SkVector normal;
148
    // compute perpendicular
149
0
    normal.fX = p0.fY - p1.fY;
150
0
    normal.fY = p1.fX - p0.fX;
151
0
    normal *= dir;
152
0
    if (!normal.normalize()) {
153
0
        return false;
154
0
    }
155
0
    *newNormal = normal;
156
0
    return true;
157
0
}
158
159
0
static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
160
0
    static constexpr SkScalar kClose = (SK_Scalar1 / 16);
161
0
    static constexpr SkScalar kCloseSqd = kClose * kClose;
162
163
0
    SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
164
0
    return distSq < kCloseSqd;
165
0
}
166
167
0
static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
168
0
    SkVector v0 = p1 - p0;
169
0
    SkVector v1 = p2 - p1;
170
0
    return v0.cross(v1);
171
0
}
172
173
SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, const SkRect& bounds,
174
                                                 bool transparent)
175
        : fZPlaneParams(zPlaneParams)
176
        , fPathBounds(bounds)
177
        , fCentroid({0, 0})
178
        , fArea(0)
179
        , fLastArea(0)
180
        , fLastCross(0)
181
        , fFirstVertexIndex(-1)
182
        , fSucceeded(false)
183
        , fTransparent(transparent)
184
        , fIsConvex(true)
185
        , fValidUmbra(true)
186
        , fDirection(1)
187
        , fPrevUmbraIndex(-1)
188
        , fCurrUmbraIndex(0)
189
        , fCurrClipIndex(0)
190
        , fPrevUmbraOutside(false)
191
0
        , fFirstUmbraOutside(false) {
192
    // child classes will set reserve for positions, colors and indices
193
0
}
194
195
0
bool SkBaseShadowTessellator::accumulateCentroid(const SkPoint& curr, const SkPoint& next) {
196
0
    if (duplicate_pt(curr, next)) {
197
0
        return false;
198
0
    }
199
200
0
    SkASSERT(!fPathPolygon.empty());
201
0
    SkVector v0 = curr - fPathPolygon[0];
202
0
    SkVector v1 = next - fPathPolygon[0];
203
0
    SkScalar quadArea = v0.cross(v1);
204
0
    fCentroid.fX += (v0.fX + v1.fX) * quadArea;
205
0
    fCentroid.fY += (v0.fY + v1.fY) * quadArea;
206
0
    fArea += quadArea;
207
    // convexity check
208
0
    if (quadArea*fLastArea < 0) {
209
0
        fIsConvex = false;
210
0
    }
211
0
    if (0 != quadArea) {
212
0
        fLastArea = quadArea;
213
0
    }
214
215
0
    return true;
216
0
}
Unexecuted instantiation: SkBaseShadowTessellator::accumulateCentroid(SkPoint const&, SkPoint const&)
Unexecuted instantiation: SkBaseShadowTessellator::accumulateCentroid(SkPoint const&, SkPoint const&)
217
218
bool SkBaseShadowTessellator::checkConvexity(const SkPoint& p0,
219
                                             const SkPoint& p1,
220
0
                                             const SkPoint& p2) {
221
0
    SkScalar cross = perp_dot(p0, p1, p2);
222
    // skip collinear point
223
0
    if (SkScalarNearlyZero(cross)) {
224
0
        return false;
225
0
    }
226
227
    // check for convexity
228
0
    if (fLastCross*cross < 0) {
229
0
        fIsConvex = false;
230
0
    }
231
0
    if (0 != cross) {
232
0
        fLastCross = cross;
233
0
    }
234
235
0
    return true;
236
0
}
237
238
0
void SkBaseShadowTessellator::finishPathPolygon() {
239
0
    if (fPathPolygon.size() > 1) {
240
0
        if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.size() - 1], fPathPolygon[0])) {
241
            // remove coincident point
242
0
            fPathPolygon.pop_back();
243
0
        }
244
0
    }
245
246
0
    if (fPathPolygon.size() > 2) {
247
        // do this before the final convexity check, so we use the correct fPathPolygon[0]
248
0
        fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
249
0
        fCentroid += fPathPolygon[0];
250
0
        if (!checkConvexity(fPathPolygon[fPathPolygon.size() - 2],
251
0
                            fPathPolygon[fPathPolygon.size() - 1],
252
0
                            fPathPolygon[0])) {
253
            // remove collinear point
254
0
            fPathPolygon[0] = fPathPolygon[fPathPolygon.size() - 1];
255
0
            fPathPolygon.pop_back();
256
0
        }
257
0
    }
258
259
    // if area is positive, winding is ccw
260
0
    fDirection = fArea > 0 ? -1 : 1;
261
0
}
262
263
0
bool SkBaseShadowTessellator::computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip) {
264
0
    if (doClip) {
265
0
        this->computeClipVectorsAndTestCentroid();
266
0
    }
267
268
    // adjust inset distance and umbra color if necessary
269
0
    auto umbraColor = kUmbraColor;
270
0
    SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
271
0
                                                                      fPathPolygon[0],
272
0
                                                                      fPathPolygon[1]);
273
0
    SkRect bounds;
274
0
    bounds.setBounds(&fPathPolygon[0], fPathPolygon.size());
275
0
    for (int i = 1; i < fPathPolygon.size(); ++i) {
276
0
        int j = i + 1;
277
0
        if (i == fPathPolygon.size() - 1) {
278
0
            j = 0;
279
0
        }
280
0
        SkPoint currPoint = fPathPolygon[i];
281
0
        SkPoint nextPoint = fPathPolygon[j];
282
0
        SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
283
0
                                                                       nextPoint);
284
0
        if (distSq < minDistSq) {
285
0
            minDistSq = distSq;
286
0
        }
287
0
    }
288
289
0
    SkTDArray<SkPoint> insetPolygon;
290
0
    if (inset > SK_ScalarNearlyZero) {
291
0
        static constexpr auto kTolerance = 1.0e-2f;
292
0
        if (minDistSq < (inset + kTolerance)*(inset + kTolerance)) {
293
            // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
294
0
            auto newInset = SkScalarSqrt(minDistSq) - kTolerance;
295
0
            auto ratio = 128 * (newInset / inset + 1);
296
0
            SkASSERT(SkIsFinite(ratio));
297
            // they aren't PMColors, but the interpolation algorithm is the same
298
0
            umbraColor = SkPMLerp(kUmbraColor, kPenumbraColor, (unsigned)ratio);
299
0
            inset = newInset;
300
0
        }
301
302
        // generate inner ring
303
0
        if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.size(), inset,
304
0
                                  &insetPolygon)) {
305
            // not ideal, but in this case we'll inset using the centroid
306
0
            fValidUmbra = false;
307
0
        }
308
0
    }
309
0
    const SkTDArray<SkPoint>& umbraPolygon = (inset > SK_ScalarNearlyZero) ? insetPolygon
310
0
                                                                           : fPathPolygon;
311
312
    // walk around the path polygon, generate outer ring and connect to inner ring
313
0
    if (fTransparent) {
314
0
        fPositions.push_back(fCentroid);
315
0
        fColors.push_back(umbraColor);
316
0
    }
317
0
    fCurrUmbraIndex = 0;
318
319
    // initial setup
320
    // add first quad
321
0
    int polyCount = fPathPolygon.size();
322
0
    if (!compute_normal(fPathPolygon[polyCount - 1], fPathPolygon[0], fDirection, &fFirstOutset)) {
323
        // polygon should be sanitized by this point, so this is unrecoverable
324
0
        return false;
325
0
    }
326
327
0
    fFirstOutset *= outset;
328
0
    fFirstPoint = fPathPolygon[polyCount - 1];
329
0
    fFirstVertexIndex = fPositions.size();
330
0
    fPrevOutset = fFirstOutset;
331
0
    fPrevPoint = fFirstPoint;
332
0
    fPrevUmbraIndex = -1;
333
334
0
    this->addInnerPoint(fFirstPoint, umbraColor, umbraPolygon, &fPrevUmbraIndex);
335
336
0
    if (!fTransparent && doClip) {
337
0
        SkPoint clipPoint;
338
0
        bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
339
0
                                              fCentroid, &clipPoint);
340
0
        if (isOutside) {
341
0
            fPositions.push_back(clipPoint);
342
0
            fColors.push_back(umbraColor);
343
0
        }
344
0
        fPrevUmbraOutside = isOutside;
345
0
        fFirstUmbraOutside = isOutside;
346
0
    }
347
348
0
    SkPoint newPoint = fFirstPoint + fFirstOutset;
349
0
    fPositions.push_back(newPoint);
350
0
    fColors.push_back(kPenumbraColor);
351
0
    this->addEdge(fPathPolygon[0], fFirstOutset, umbraColor, umbraPolygon, false, doClip);
352
353
0
    for (int i = 1; i < polyCount; ++i) {
354
0
        SkVector normal;
355
0
        if (!compute_normal(fPrevPoint, fPathPolygon[i], fDirection, &normal)) {
356
0
            return false;
357
0
        }
358
0
        normal *= outset;
359
0
        this->addArc(normal, outset, true);
360
0
        this->addEdge(fPathPolygon[i], normal, umbraColor, umbraPolygon,
361
0
                      i == polyCount - 1, doClip);
362
0
    }
363
0
    SkASSERT(this->indexCount());
364
365
    // final fan
366
0
    SkASSERT(fPositions.size() >= 3);
367
0
    if (this->addArc(fFirstOutset, outset, false)) {
368
0
        if (fFirstUmbraOutside) {
369
0
            this->appendTriangle(fFirstVertexIndex, fPositions.size() - 1,
370
0
                                 fFirstVertexIndex + 2);
371
0
        } else {
372
0
            this->appendTriangle(fFirstVertexIndex, fPositions.size() - 1,
373
0
                                 fFirstVertexIndex + 1);
374
0
        }
375
0
    } else {
376
        // no arc added, fix up by setting first penumbra point position to last one
377
0
        if (fFirstUmbraOutside) {
378
0
            fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.size() - 1];
379
0
        } else {
380
0
            fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.size() - 1];
381
0
        }
382
0
    }
383
384
0
    return true;
385
0
}
Unexecuted instantiation: SkBaseShadowTessellator::computeConvexShadow(float, float, bool)
Unexecuted instantiation: SkBaseShadowTessellator::computeConvexShadow(float, float, bool)
386
387
0
void SkBaseShadowTessellator::computeClipVectorsAndTestCentroid() {
388
0
    SkASSERT(fClipPolygon.size() >= 3);
389
0
    fCurrClipIndex = fClipPolygon.size() - 1;
390
391
    // init clip vectors
392
0
    SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
393
0
    SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
394
0
    fClipVectors.push_back(v0);
395
396
    // init centroid check
397
0
    bool hiddenCentroid = true;
398
0
    v1 = fCentroid - fClipPolygon[0];
399
0
    SkScalar initCross = v0.cross(v1);
400
401
0
    for (int p = 1; p < fClipPolygon.size(); ++p) {
402
        // add to clip vectors
403
0
        v0 = fClipPolygon[(p + 1) % fClipPolygon.size()] - fClipPolygon[p];
404
0
        fClipVectors.push_back(v0);
405
        // Determine if transformed centroid is inside clipPolygon.
406
0
        v1 = fCentroid - fClipPolygon[p];
407
0
        if (initCross*v0.cross(v1) <= 0) {
408
0
            hiddenCentroid = false;
409
0
        }
410
0
    }
411
0
    SkASSERT(fClipVectors.size() == fClipPolygon.size());
412
413
0
    fTransparent = fTransparent || !hiddenCentroid;
414
0
}
Unexecuted instantiation: SkBaseShadowTessellator::computeClipVectorsAndTestCentroid()
Unexecuted instantiation: SkBaseShadowTessellator::computeClipVectorsAndTestCentroid()
415
416
void SkBaseShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal,
417
                                      SkColor umbraColor, const SkTDArray<SkPoint>& umbraPolygon,
418
0
                                      bool lastEdge, bool doClip) {
419
    // add next umbra point
420
0
    int currUmbraIndex;
421
0
    bool duplicate;
422
0
    if (lastEdge) {
423
0
        duplicate = false;
424
0
        currUmbraIndex = fFirstVertexIndex;
425
0
        fPrevPoint = nextPoint;
426
0
    } else {
427
0
        duplicate = this->addInnerPoint(nextPoint, umbraColor, umbraPolygon, &currUmbraIndex);
428
0
    }
429
0
    int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex)
430
0
        ? fPositions.size() - 1
431
0
        : fPositions.size() - 2;
432
0
    if (!duplicate) {
433
        // add to center fan if transparent or centroid showing
434
0
        if (fTransparent) {
435
0
            this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
436
            // otherwise add to clip ring
437
0
        } else if (doClip) {
438
0
            SkPoint clipPoint;
439
0
            bool isOutside = lastEdge ? fFirstUmbraOutside
440
0
                : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
441
0
                                       &clipPoint);
442
0
            if (isOutside) {
443
0
                if (!lastEdge) {
444
0
                    fPositions.push_back(clipPoint);
445
0
                    fColors.push_back(umbraColor);
446
0
                }
447
0
                this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
448
0
                if (fPrevUmbraOutside) {
449
                    // fill out quad
450
0
                    this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
451
0
                                         fPrevUmbraIndex + 1);
452
0
                }
453
0
            } else if (fPrevUmbraOutside) {
454
                // add tri
455
0
                this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
456
0
            }
457
458
0
            fPrevUmbraOutside = isOutside;
459
0
        }
460
0
    }
461
462
    // add next penumbra point and quad
463
0
    SkPoint newPoint = nextPoint + nextNormal;
464
0
    fPositions.push_back(newPoint);
465
0
    fColors.push_back(kPenumbraColor);
466
467
0
    if (!duplicate) {
468
0
        this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
469
0
    }
470
0
    this->appendTriangle(prevPenumbraIndex, fPositions.size() - 1, currUmbraIndex);
471
472
0
    fPrevUmbraIndex = currUmbraIndex;
473
0
    fPrevOutset = nextNormal;
474
0
}
475
476
bool SkBaseShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
477
0
                                             SkPoint* clipPoint) {
478
0
    SkVector segmentVector = centroid - umbraPoint;
479
480
0
    int startClipPoint = fCurrClipIndex;
481
0
    do {
482
0
        SkVector dp = umbraPoint - fClipPolygon[fCurrClipIndex];
483
0
        SkScalar denom = fClipVectors[fCurrClipIndex].cross(segmentVector);
484
0
        SkScalar t_num = dp.cross(segmentVector);
485
        // if line segments are nearly parallel
486
0
        if (SkScalarNearlyZero(denom)) {
487
            // and collinear
488
0
            if (SkScalarNearlyZero(t_num)) {
489
0
                return false;
490
0
            }
491
            // otherwise are separate, will try the next poly segment
492
            // else if crossing lies within poly segment
493
0
        } else if (t_num >= 0 && t_num <= denom) {
494
0
            SkScalar s_num = dp.cross(fClipVectors[fCurrClipIndex]);
495
            // if umbra point is inside the clip polygon
496
0
            if (s_num >= 0 && s_num <= denom) {
497
0
                segmentVector *= s_num / denom;
498
0
                *clipPoint = umbraPoint + segmentVector;
499
0
                return true;
500
0
            }
501
0
        }
502
0
        fCurrClipIndex = (fCurrClipIndex + 1) % fClipPolygon.size();
503
0
    } while (fCurrClipIndex != startClipPoint);
504
505
0
    return false;
506
0
}
507
508
bool SkBaseShadowTessellator::addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor,
509
                                            const SkTDArray<SkPoint>& umbraPolygon,
510
0
                                            int* currUmbraIndex) {
511
0
    SkPoint umbraPoint;
512
0
    if (!fValidUmbra) {
513
0
        SkVector v = fCentroid - pathPoint;
514
0
        v *= 0.95f;
515
0
        umbraPoint = pathPoint + v;
516
0
    } else {
517
0
        umbraPoint = umbraPolygon[this->getClosestUmbraIndex(pathPoint, umbraPolygon)];
518
0
    }
519
520
0
    fPrevPoint = pathPoint;
521
522
    // merge "close" points
523
0
    if (fPrevUmbraIndex == -1 ||
524
0
        !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
525
        // if we've wrapped around, don't add a new point
526
0
        if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
527
0
            *currUmbraIndex = fFirstVertexIndex;
528
0
        } else {
529
0
            *currUmbraIndex = fPositions.size();
530
0
            fPositions.push_back(umbraPoint);
531
0
            fColors.push_back(umbraColor);
532
0
        }
533
0
        return false;
534
0
    } else {
535
0
        *currUmbraIndex = fPrevUmbraIndex;
536
0
        return true;
537
0
    }
538
0
}
539
540
int SkBaseShadowTessellator::getClosestUmbraIndex(const SkPoint& p,
541
0
                                                  const SkTDArray<SkPoint>& umbraPolygon) {
542
0
    SkScalar minDistance = SkPointPriv::DistanceToSqd(p, umbraPolygon[fCurrUmbraIndex]);
543
0
    int index = fCurrUmbraIndex;
544
0
    int dir = 1;
545
0
    int next = (index + dir) % umbraPolygon.size();
546
547
    // init travel direction
548
0
    SkScalar distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
549
0
    if (distance < minDistance) {
550
0
        index = next;
551
0
        minDistance = distance;
552
0
    } else {
553
0
        dir = umbraPolygon.size() - 1;
554
0
    }
555
556
    // iterate until we find a point that increases the distance
557
0
    next = (index + dir) % umbraPolygon.size();
558
0
    distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
559
0
    while (distance < minDistance) {
560
0
        index = next;
561
0
        minDistance = distance;
562
0
        next = (index + dir) % umbraPolygon.size();
563
0
        distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
564
0
    }
565
566
0
    fCurrUmbraIndex = index;
567
0
    return index;
568
0
}
569
570
0
bool SkBaseShadowTessellator::computeConcaveShadow(SkScalar inset, SkScalar outset) {
571
0
    if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.size())) {
572
0
        return false;
573
0
    }
574
575
    // shouldn't inset more than the half bounds of the polygon
576
0
    inset = std::min(inset, std::min(SkTAbs(SkRectPriv::HalfWidth(fPathBounds)),
577
0
                                     SkTAbs(SkRectPriv::HalfHeight(fPathBounds))));
578
    // generate inner ring
579
0
    SkTDArray<SkPoint> umbraPolygon;
580
0
    SkTDArray<int> umbraIndices;
581
0
    umbraIndices.reserve(fPathPolygon.size());
582
0
    if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.size(), fPathBounds, inset,
583
0
                               &umbraPolygon, &umbraIndices)) {
584
        // TODO: figure out how to handle this case
585
0
        return false;
586
0
    }
587
588
    // generate outer ring
589
0
    SkTDArray<SkPoint> penumbraPolygon;
590
0
    SkTDArray<int> penumbraIndices;
591
0
    penumbraPolygon.reserve(umbraPolygon.size());
592
0
    penumbraIndices.reserve(umbraPolygon.size());
593
0
    if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.size(), fPathBounds, -outset,
594
0
                               &penumbraPolygon, &penumbraIndices)) {
595
        // TODO: figure out how to handle this case
596
0
        return false;
597
0
    }
598
599
0
    if (umbraPolygon.empty() || penumbraPolygon.empty()) {
600
0
        return false;
601
0
    }
602
603
    // attach the rings together
604
0
    this->stitchConcaveRings(umbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
605
606
0
    return true;
607
0
}
608
609
void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
610
                                                 SkTDArray<int>* umbraIndices,
611
                                                 const SkTDArray<SkPoint>& penumbraPolygon,
612
0
                                                 SkTDArray<int>* penumbraIndices) {
613
    // TODO: only create and fill indexMap when fTransparent is true?
614
0
    AutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.size());
615
616
    // find minimum indices
617
0
    int minIndex = 0;
618
0
    int min = (*penumbraIndices)[0];
619
0
    for (int i = 1; i < (*penumbraIndices).size(); ++i) {
620
0
        if ((*penumbraIndices)[i] < min) {
621
0
            min = (*penumbraIndices)[i];
622
0
            minIndex = i;
623
0
        }
624
0
    }
625
0
    int currPenumbra = minIndex;
626
627
0
    minIndex = 0;
628
0
    min = (*umbraIndices)[0];
629
0
    for (int i = 1; i < (*umbraIndices).size(); ++i) {
630
0
        if ((*umbraIndices)[i] < min) {
631
0
            min = (*umbraIndices)[i];
632
0
            minIndex = i;
633
0
        }
634
0
    }
635
0
    int currUmbra = minIndex;
636
637
    // now find a case where the indices are equal (there should be at least one)
638
0
    int maxPenumbraIndex = fPathPolygon.size() - 1;
639
0
    int maxUmbraIndex = fPathPolygon.size() - 1;
640
0
    while ((*penumbraIndices)[currPenumbra] != (*umbraIndices)[currUmbra]) {
641
0
        if ((*penumbraIndices)[currPenumbra] < (*umbraIndices)[currUmbra]) {
642
0
            (*penumbraIndices)[currPenumbra] += fPathPolygon.size();
643
0
            maxPenumbraIndex = (*penumbraIndices)[currPenumbra];
644
0
            currPenumbra = (currPenumbra + 1) % penumbraPolygon.size();
645
0
        } else {
646
0
            (*umbraIndices)[currUmbra] += fPathPolygon.size();
647
0
            maxUmbraIndex = (*umbraIndices)[currUmbra];
648
0
            currUmbra = (currUmbra + 1) % umbraPolygon.size();
649
0
        }
650
0
    }
651
652
0
    fPositions.push_back(penumbraPolygon[currPenumbra]);
653
0
    fColors.push_back(kPenumbraColor);
654
0
    int prevPenumbraIndex = 0;
655
0
    fPositions.push_back(umbraPolygon[currUmbra]);
656
0
    fColors.push_back(kUmbraColor);
657
0
    fPrevUmbraIndex = 1;
658
0
    indexMap[currUmbra] = 1;
659
660
0
    int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.size();
661
0
    int nextUmbra = (currUmbra + 1) % umbraPolygon.size();
662
0
    while ((*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex ||
663
0
           (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
664
665
0
        if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) {
666
            // advance both one step
667
0
            fPositions.push_back(penumbraPolygon[nextPenumbra]);
668
0
            fColors.push_back(kPenumbraColor);
669
0
            int currPenumbraIndex = fPositions.size() - 1;
670
671
0
            fPositions.push_back(umbraPolygon[nextUmbra]);
672
0
            fColors.push_back(kUmbraColor);
673
0
            int currUmbraIndex = fPositions.size() - 1;
674
0
            indexMap[nextUmbra] = currUmbraIndex;
675
676
0
            this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
677
0
                             fPrevUmbraIndex, currUmbraIndex);
678
679
0
            prevPenumbraIndex = currPenumbraIndex;
680
0
            (*penumbraIndices)[currPenumbra] += fPathPolygon.size();
681
0
            currPenumbra = nextPenumbra;
682
0
            nextPenumbra = (currPenumbra + 1) % penumbraPolygon.size();
683
684
0
            fPrevUmbraIndex = currUmbraIndex;
685
0
            (*umbraIndices)[currUmbra] += fPathPolygon.size();
686
0
            currUmbra = nextUmbra;
687
0
            nextUmbra = (currUmbra + 1) % umbraPolygon.size();
688
0
        }
689
690
0
        while ((*penumbraIndices)[nextPenumbra] < (*umbraIndices)[nextUmbra] &&
691
0
               (*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex) {
692
            // fill out penumbra arc
693
0
            fPositions.push_back(penumbraPolygon[nextPenumbra]);
694
0
            fColors.push_back(kPenumbraColor);
695
0
            int currPenumbraIndex = fPositions.size() - 1;
696
697
0
            this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
698
699
0
            prevPenumbraIndex = currPenumbraIndex;
700
            // this ensures the ordering when we wrap around
701
0
            (*penumbraIndices)[currPenumbra] += fPathPolygon.size();
702
0
            currPenumbra = nextPenumbra;
703
0
            nextPenumbra = (currPenumbra + 1) % penumbraPolygon.size();
704
0
        }
705
706
0
        while ((*umbraIndices)[nextUmbra] < (*penumbraIndices)[nextPenumbra] &&
707
0
               (*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
708
            // fill out umbra arc
709
0
            fPositions.push_back(umbraPolygon[nextUmbra]);
710
0
            fColors.push_back(kUmbraColor);
711
0
            int currUmbraIndex = fPositions.size() - 1;
712
0
            indexMap[nextUmbra] = currUmbraIndex;
713
714
0
            this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
715
716
0
            fPrevUmbraIndex = currUmbraIndex;
717
            // this ensures the ordering when we wrap around
718
0
            (*umbraIndices)[currUmbra] += fPathPolygon.size();
719
0
            currUmbra = nextUmbra;
720
0
            nextUmbra = (currUmbra + 1) % umbraPolygon.size();
721
0
        }
722
0
    }
723
    // finish up by advancing both one step
724
0
    fPositions.push_back(penumbraPolygon[nextPenumbra]);
725
0
    fColors.push_back(kPenumbraColor);
726
0
    int currPenumbraIndex = fPositions.size() - 1;
727
728
0
    fPositions.push_back(umbraPolygon[nextUmbra]);
729
0
    fColors.push_back(kUmbraColor);
730
0
    int currUmbraIndex = fPositions.size() - 1;
731
0
    indexMap[nextUmbra] = currUmbraIndex;
732
733
0
    this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
734
0
                     fPrevUmbraIndex, currUmbraIndex);
735
736
0
    if (fTransparent) {
737
0
        SkTriangulateSimplePolygon(umbraPolygon.begin(), indexMap, umbraPolygon.size(),
738
0
                                   &fIndices);
739
0
    }
740
0
}
741
742
743
// tesselation tolerance values, in device space pixels
744
#if defined(SK_GANESH)
745
static constexpr SkScalar kQuadTolerance = 0.2f;
746
static constexpr SkScalar kCubicTolerance = 0.2f;
747
static constexpr SkScalar kQuadToleranceSqd = kQuadTolerance * kQuadTolerance;
748
static constexpr SkScalar kCubicToleranceSqd = kCubicTolerance * kCubicTolerance;
749
#endif
750
static constexpr SkScalar kConicTolerance = 0.25f;
751
752
// clamps the point to the nearest 16th of a pixel
753
0
static void sanitize_point(const SkPoint& in, SkPoint* out) {
754
0
    out->fX = SkScalarRoundToScalar(16.f*in.fX)*0.0625f;
755
0
    out->fY = SkScalarRoundToScalar(16.f*in.fY)*0.0625f;
756
0
}
757
758
0
void SkBaseShadowTessellator::handleLine(const SkPoint& p) {
759
0
    SkPoint pSanitized;
760
0
    sanitize_point(p, &pSanitized);
761
762
0
    if (!fPathPolygon.empty()) {
763
0
        if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.size() - 1], pSanitized)) {
764
            // skip coincident point
765
0
            return;
766
0
        }
767
0
    }
768
769
0
    if (fPathPolygon.size() > 1) {
770
0
        if (!checkConvexity(fPathPolygon[fPathPolygon.size() - 2],
771
0
                            fPathPolygon[fPathPolygon.size() - 1],
772
0
                            pSanitized)) {
773
            // remove collinear point
774
0
            fPathPolygon.pop_back();
775
            // it's possible that the previous point is coincident with the new one now
776
0
            if (duplicate_pt(fPathPolygon[fPathPolygon.size() - 1], pSanitized)) {
777
0
                fPathPolygon.pop_back();
778
0
            }
779
0
        }
780
0
    }
781
782
0
    fPathPolygon.push_back(pSanitized);
783
0
}
784
785
0
void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
786
0
    m.mapPoints(p, 1);
787
788
0
    this->handleLine(*p);
789
0
}
790
791
0
void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
792
0
#if defined(SK_GANESH)
793
    // check for degeneracy
794
0
    SkVector v0 = pts[1] - pts[0];
795
0
    SkVector v1 = pts[2] - pts[0];
796
0
    if (SkScalarNearlyZero(v0.cross(v1))) {
797
0
        return;
798
0
    }
799
    // TODO: Pull PathUtils out of Ganesh?
800
0
    int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
801
0
    fPointBuffer.resize(maxCount);
802
0
    SkPoint* target = fPointBuffer.begin();
803
0
    int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
804
0
                                                     kQuadToleranceSqd, &target, maxCount);
805
0
    fPointBuffer.resize(count);
806
0
    for (int i = 0; i < count; i++) {
807
0
        this->handleLine(fPointBuffer[i]);
808
0
    }
809
#else
810
    // for now, just to draw something
811
    this->handleLine(pts[1]);
812
    this->handleLine(pts[2]);
813
#endif
814
0
}
815
816
0
void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
817
0
    m.mapPoints(pts, 3);
818
0
    this->handleQuad(pts);
819
0
}
820
821
0
void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
822
0
    m.mapPoints(pts, 4);
823
0
#if defined(SK_GANESH)
824
    // TODO: Pull PathUtils out of Ganesh?
825
0
    int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
826
0
    fPointBuffer.resize(maxCount);
827
0
    SkPoint* target = fPointBuffer.begin();
828
0
    int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
829
0
                                                 kCubicToleranceSqd, &target, maxCount);
830
0
    fPointBuffer.resize(count);
831
0
    for (int i = 0; i < count; i++) {
832
0
        this->handleLine(fPointBuffer[i]);
833
0
    }
834
#else
835
    // for now, just to draw something
836
    this->handleLine(pts[1]);
837
    this->handleLine(pts[2]);
838
    this->handleLine(pts[3]);
839
#endif
840
0
}
841
842
0
void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
843
0
    if (m.hasPerspective()) {
844
0
        w = SkConic::TransformW(pts, w, m);
845
0
    }
846
0
    m.mapPoints(pts, 3);
847
0
    SkAutoConicToQuads quadder;
848
0
    const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
849
0
    SkPoint lastPoint = *(quads++);
850
0
    int count = quadder.countQuads();
851
0
    for (int i = 0; i < count; ++i) {
852
0
        SkPoint quadPts[3];
853
0
        quadPts[0] = lastPoint;
854
0
        quadPts[1] = quads[0];
855
0
        quadPts[2] = i == count - 1 ? pts[2] : quads[1];
856
0
        this->handleQuad(quadPts);
857
0
        lastPoint = quadPts[2];
858
0
        quads += 2;
859
0
    }
860
0
}
861
862
0
bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc) {
863
    // fill in fan from previous quad
864
0
    SkScalar rotSin, rotCos;
865
0
    int numSteps;
866
0
    if (!SkComputeRadialSteps(fPrevOutset, nextNormal, offset, &rotSin, &rotCos, &numSteps)) {
867
        // recover as best we can
868
0
        numSteps = 0;
869
0
    }
870
0
    SkVector prevNormal = fPrevOutset;
871
0
    for (int i = 0; i < numSteps-1; ++i) {
872
0
        SkVector currNormal;
873
0
        currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
874
0
        currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
875
0
        fPositions.push_back(fPrevPoint + currNormal);
876
0
        fColors.push_back(kPenumbraColor);
877
0
        this->appendTriangle(fPrevUmbraIndex, fPositions.size() - 1, fPositions.size() - 2);
878
879
0
        prevNormal = currNormal;
880
0
    }
881
0
    if (finishArc && numSteps) {
882
0
        fPositions.push_back(fPrevPoint + nextNormal);
883
0
        fColors.push_back(kPenumbraColor);
884
0
        this->appendTriangle(fPrevUmbraIndex, fPositions.size() - 1, fPositions.size() - 2);
885
0
    }
886
0
    fPrevOutset = nextNormal;
887
888
0
    return (numSteps > 0);
889
0
}
890
891
0
void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
892
0
    auto indices = fIndices.append(3);
893
894
0
    indices[0] = index0;
895
0
    indices[1] = index1;
896
0
    indices[2] = index2;
897
0
}
898
899
void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
900
0
                                         uint16_t index2, uint16_t index3) {
901
0
    auto indices = fIndices.append(6);
902
903
0
    indices[0] = index0;
904
0
    indices[1] = index1;
905
0
    indices[2] = index2;
906
907
0
    indices[3] = index2;
908
0
    indices[4] = index1;
909
0
    indices[5] = index3;
910
0
}
911
912
//////////////////////////////////////////////////////////////////////////////////////////////////
913
914
class SkAmbientShadowTessellator : public SkBaseShadowTessellator {
915
public:
916
    SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
917
                               const SkPoint3& zPlaneParams, bool transparent);
918
919
private:
920
    bool computePathPolygon(const SkPath& path, const SkMatrix& ctm);
921
922
    using INHERITED = SkBaseShadowTessellator;
923
};
924
925
SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
926
                                                       const SkMatrix& ctm,
927
                                                       const SkPoint3& zPlaneParams,
928
                                                       bool transparent)
929
0
        : INHERITED(zPlaneParams, path.getBounds(), transparent) {
930
    // Set base colors
931
0
    auto baseZ = heightFunc(fPathBounds.centerX(), fPathBounds.centerY());
932
    // umbraColor is the interior value, penumbraColor the exterior value.
933
0
    auto outset = SkDrawShadowMetrics::AmbientBlurRadius(baseZ);
934
0
    auto inset = outset * SkDrawShadowMetrics::AmbientRecipAlpha(baseZ) - outset;
935
936
0
    if (!this->computePathPolygon(path, ctm)) {
937
0
        return;
938
0
    }
939
0
    if (fPathPolygon.size() < 3 || !SkIsFinite(fArea)) {
940
0
        fSucceeded = true; // We don't want to try to blur these cases, so we will
941
                           // return an empty SkVertices instead.
942
0
        return;
943
0
    }
944
945
    // Outer ring: 3*numPts
946
    // Middle ring: numPts
947
0
    fPositions.reserve(4 * path.countPoints());
948
0
    fColors.reserve(4 * path.countPoints());
949
    // Outer ring: 12*numPts
950
    // Middle ring: 0
951
0
    fIndices.reserve(12 * path.countPoints());
952
953
0
    if (fIsConvex) {
954
0
        fSucceeded = this->computeConvexShadow(inset, outset, false);
955
0
    } else {
956
0
        fSucceeded = this->computeConcaveShadow(inset, outset);
957
0
    }
958
0
}
959
960
0
bool SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) {
961
0
    fPathPolygon.reserve(path.countPoints());
962
963
    // walk around the path, tessellate and generate outer ring
964
    // if original path is transparent, will accumulate sum of points for centroid
965
0
    SkPath::Iter iter(path, true);
966
0
    SkPoint pts[4];
967
0
    SkPath::Verb verb;
968
0
    bool verbSeen = false;
969
0
    bool closeSeen = false;
970
0
    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
971
0
        if (closeSeen) {
972
0
            return false;
973
0
        }
974
0
        switch (verb) {
975
0
            case SkPath::kLine_Verb:
976
0
                this->handleLine(ctm, &pts[1]);
977
0
                break;
978
0
            case SkPath::kQuad_Verb:
979
0
                this->handleQuad(ctm, pts);
980
0
                break;
981
0
            case SkPath::kCubic_Verb:
982
0
                this->handleCubic(ctm, pts);
983
0
                break;
984
0
            case SkPath::kConic_Verb:
985
0
                this->handleConic(ctm, pts, iter.conicWeight());
986
0
                break;
987
0
            case SkPath::kMove_Verb:
988
0
                if (verbSeen) {
989
0
                    return false;
990
0
                }
991
0
                break;
992
0
            case SkPath::kClose_Verb:
993
0
            case SkPath::kDone_Verb:
994
0
                closeSeen = true;
995
0
                break;
996
0
        }
997
0
        verbSeen = true;
998
0
    }
999
1000
0
    this->finishPathPolygon();
1001
0
    return true;
1002
0
}
1003
1004
///////////////////////////////////////////////////////////////////////////////////////////////////
1005
1006
class SkSpotShadowTessellator : public SkBaseShadowTessellator {
1007
public:
1008
    SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
1009
                            const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
1010
                            SkScalar lightRadius, bool transparent, bool directional);
1011
1012
private:
1013
    bool computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
1014
                                    const SkMatrix& shadowTransform);
1015
    void addToClip(const SkVector& nextPoint);
1016
1017
    using INHERITED = SkBaseShadowTessellator;
1018
};
1019
1020
SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
1021
                                                 const SkPoint3& zPlaneParams,
1022
                                                 const SkPoint3& lightPos, SkScalar lightRadius,
1023
                                                 bool transparent, bool directional)
1024
0
    : INHERITED(zPlaneParams, path.getBounds(), transparent) {
1025
1026
    // Compute the blur radius, scale and translation for the spot shadow.
1027
0
    SkMatrix shadowTransform;
1028
0
    SkScalar outset;
1029
0
    if (!SkDrawShadowMetrics::GetSpotShadowTransform(lightPos, lightRadius, ctm, zPlaneParams,
1030
0
                                                     path.getBounds(), directional,
1031
0
                                                     &shadowTransform, &outset)) {
1032
0
        return;
1033
0
    }
1034
0
    SkScalar inset = outset;
1035
1036
    // compute rough clip bounds for umbra, plus offset polygon, plus centroid
1037
0
    if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) {
1038
0
        return;
1039
0
    }
1040
0
    if (fClipPolygon.size() < 3 || fPathPolygon.size() < 3 || !SkIsFinite(fArea)) {
1041
0
        fSucceeded = true; // We don't want to try to blur these cases, so we will
1042
                           // return an empty SkVertices instead.
1043
0
        return;
1044
0
    }
1045
1046
    // TODO: calculate these reserves better
1047
    // Penumbra ring: 3*numPts
1048
    // Umbra ring: numPts
1049
    // Inner ring: numPts
1050
0
    fPositions.reserve(5 * path.countPoints());
1051
0
    fColors.reserve(5 * path.countPoints());
1052
    // Penumbra ring: 12*numPts
1053
    // Umbra ring: 3*numPts
1054
0
    fIndices.reserve(15 * path.countPoints());
1055
1056
0
    if (fIsConvex) {
1057
0
        fSucceeded = this->computeConvexShadow(inset, outset, true);
1058
0
    } else {
1059
0
        fSucceeded = this->computeConcaveShadow(inset, outset);
1060
0
    }
1061
1062
0
    if (!fSucceeded) {
1063
0
        return;
1064
0
    }
1065
1066
0
    fSucceeded = true;
1067
0
}
1068
1069
bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
1070
0
                                                         const SkMatrix& shadowTransform) {
1071
1072
0
    fPathPolygon.reserve(path.countPoints());
1073
0
    fClipPolygon.reserve(path.countPoints());
1074
1075
    // Walk around the path and compute clip polygon and path polygon.
1076
    // Will also accumulate sum of areas for centroid.
1077
    // For Bezier curves, we compute additional interior points on curve.
1078
0
    SkPath::Iter iter(path, true);
1079
0
    SkPoint pts[4];
1080
0
    SkPoint clipPts[4];
1081
0
    SkPath::Verb verb;
1082
1083
    // coefficients to compute cubic Bezier at t = 5/16
1084
0
    static constexpr SkScalar kA = 0.32495117187f;
1085
0
    static constexpr SkScalar kB = 0.44311523437f;
1086
0
    static constexpr SkScalar kC = 0.20141601562f;
1087
0
    static constexpr SkScalar kD = 0.03051757812f;
1088
1089
0
    SkPoint curvePoint;
1090
0
    SkScalar w;
1091
0
    bool closeSeen = false;
1092
0
    bool verbSeen = false;
1093
0
    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1094
0
        if (closeSeen) {
1095
0
            return false;
1096
0
        }
1097
0
        switch (verb) {
1098
0
            case SkPath::kLine_Verb:
1099
0
                ctm.mapPoints(clipPts, &pts[1], 1);
1100
0
                this->addToClip(clipPts[0]);
1101
0
                this->handleLine(shadowTransform, &pts[1]);
1102
0
                break;
1103
0
            case SkPath::kQuad_Verb:
1104
0
                ctm.mapPoints(clipPts, pts, 3);
1105
                // point at t = 1/2
1106
0
                curvePoint.fX = 0.25f*clipPts[0].fX + 0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1107
0
                curvePoint.fY = 0.25f*clipPts[0].fY + 0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY;
1108
0
                this->addToClip(curvePoint);
1109
0
                this->addToClip(clipPts[2]);
1110
0
                this->handleQuad(shadowTransform, pts);
1111
0
                break;
1112
0
            case SkPath::kConic_Verb:
1113
0
                ctm.mapPoints(clipPts, pts, 3);
1114
0
                w = iter.conicWeight();
1115
                // point at t = 1/2
1116
0
                curvePoint.fX = 0.25f*clipPts[0].fX + w*0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
1117
0
                curvePoint.fY = 0.25f*clipPts[0].fY + w*0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY;
1118
0
                curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
1119
0
                this->addToClip(curvePoint);
1120
0
                this->addToClip(clipPts[2]);
1121
0
                this->handleConic(shadowTransform, pts, w);
1122
0
                break;
1123
0
            case SkPath::kCubic_Verb:
1124
0
                ctm.mapPoints(clipPts, pts, 4);
1125
                // point at t = 5/16
1126
0
                curvePoint.fX = kA*clipPts[0].fX + kB*clipPts[1].fX
1127
0
                              + kC*clipPts[2].fX + kD*clipPts[3].fX;
1128
0
                curvePoint.fY = kA*clipPts[0].fY + kB*clipPts[1].fY
1129
0
                              + kC*clipPts[2].fY + kD*clipPts[3].fY;
1130
0
                this->addToClip(curvePoint);
1131
                // point at t = 11/16
1132
0
                curvePoint.fX = kD*clipPts[0].fX + kC*clipPts[1].fX
1133
0
                              + kB*clipPts[2].fX + kA*clipPts[3].fX;
1134
0
                curvePoint.fY = kD*clipPts[0].fY + kC*clipPts[1].fY
1135
0
                              + kB*clipPts[2].fY + kA*clipPts[3].fY;
1136
0
                this->addToClip(curvePoint);
1137
0
                this->addToClip(clipPts[3]);
1138
0
                this->handleCubic(shadowTransform, pts);
1139
0
                break;
1140
0
            case SkPath::kMove_Verb:
1141
0
                if (verbSeen) {
1142
0
                    return false;
1143
0
                }
1144
0
                break;
1145
0
            case SkPath::kClose_Verb:
1146
0
            case SkPath::kDone_Verb:
1147
0
                closeSeen = true;
1148
0
                break;
1149
0
            default:
1150
0
                SkDEBUGFAIL("unknown verb");
1151
0
        }
1152
0
        verbSeen = true;
1153
0
    }
1154
1155
0
    this->finishPathPolygon();
1156
0
    return true;
1157
0
}
Unexecuted instantiation: SkSpotShadowTessellator::computeClipAndPathPolygons(SkPath const&, SkMatrix const&, SkMatrix const&)
Unexecuted instantiation: SkSpotShadowTessellator::computeClipAndPathPolygons(SkPath const&, SkMatrix const&, SkMatrix const&)
1158
1159
0
void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
1160
0
    if (fClipPolygon.empty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.size() - 1])) {
1161
0
        fClipPolygon.push_back(point);
1162
0
    }
1163
0
}
1164
1165
///////////////////////////////////////////////////////////////////////////////////////////////////
1166
1167
sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
1168
0
                                                   const SkPoint3& zPlane, bool transparent) {
1169
0
    if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) {
1170
0
        return nullptr;
1171
0
    }
1172
0
    SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
1173
0
    return ambientTess.releaseVertices();
1174
0
}
1175
1176
sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
1177
                                                const SkPoint3& zPlane, const SkPoint3& lightPos,
1178
                                                SkScalar lightRadius,  bool transparent,
1179
0
                                                bool directional) {
1180
0
    if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() ||
1181
0
        !lightPos.isFinite() || !(lightPos.fZ >= SK_ScalarNearlyZero) ||
1182
0
        !SkIsFinite(lightRadius) || !(lightRadius >= SK_ScalarNearlyZero)) {
1183
0
        return nullptr;
1184
0
    }
1185
0
    SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent,
1186
0
                                     directional);
1187
0
    return spotTess.releaseVertices();
1188
0
}
1189
1190
#endif // !defined(SK_ENABLE_OPTIMIZE_SIZE)
1191