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