1bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth/*
2bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth * Copyright 2017 Google Inc.
3bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth *
4bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth * Use of this source code is governed by a BSD-style license that can be
5bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth * found in the LICENSE file.
6bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth */
7bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
8efe3dedbb3493b738abdb56041b093245e4e8711Jim Van Verth#include "SkShadowTessellator.h"
966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon#include "SkColorPriv.h"
1058abc9e2c56464336472493745e91133819deb96Jim Van Verth#include "SkGeometry.h"
11ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon#include "SkInsetConvexPolygon.h"
1285dc96b8cef031cc38d80f63056581c41c57ff7dJim Van Verth#include "SkPath.h"
13aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon#include "SkVertices.h"
1485dc96b8cef031cc38d80f63056581c41c57ff7dJim Van Verth
1585dc96b8cef031cc38d80f63056581c41c57ff7dJim Van Verth#if SK_SUPPORT_GPU
1685dc96b8cef031cc38d80f63056581c41c57ff7dJim Van Verth#include "GrPathUtils.h"
1785dc96b8cef031cc38d80f63056581c41c57ff7dJim Van Verth#endif
1858abc9e2c56464336472493745e91133819deb96Jim Van Verth
19958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
20a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth/**
21a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth * Base class
22a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth */
23aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomonclass SkBaseShadowTessellator {
24958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomonpublic:
25e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth    SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent);
26aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon    virtual ~SkBaseShadowTessellator() {}
270dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon
28aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon    sk_sp<SkVertices> releaseVertices() {
29aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon        if (!fSucceeded) {
30aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon            return nullptr;
31aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon        }
32887cdf112809727c51890ba8b98b3ddce22249f0Mike Reed        return SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, this->vertexCount(),
3397eb4feb112967ba7fcb00d6995adda1002873c2Mike Reed                                    fPositions.begin(), nullptr, fColors.begin(),
3497eb4feb112967ba7fcb00d6995adda1002873c2Mike Reed                                    this->indexCount(), fIndices.begin());
351a8b79a79e8aa6d4723588fd1125cb3b08886af8Brian Salomon    }
36958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
37a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verthprotected:
38da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    static constexpr auto kMinHeight = 0.1f;
39da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
40aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon    int vertexCount() const { return fPositions.count(); }
41aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon    int indexCount() const { return fIndices.count(); }
42aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon
43da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    bool setZOffset(const SkRect& bounds, bool perspective);
44da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
45a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    virtual void handleLine(const SkPoint& p) = 0;
46a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    void handleLine(const SkMatrix& m, SkPoint* p);
47958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
48958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon    void handleQuad(const SkPoint pts[3]);
49a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    void handleQuad(const SkMatrix& m, SkPoint pts[3]);
50958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
51a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    void handleCubic(const SkMatrix& m, SkPoint pts[4]);
52958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
53a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
54958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
55da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    bool setTransformedHeightFunc(const SkMatrix& ctm);
56da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
57e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth    bool addArc(const SkVector& nextNormal, bool finishArc);
58958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
59e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth    SkScalar heightFunc(SkScalar x, SkScalar y) {
60e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth        return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
61e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth    }
62e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth
63e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth    SkPoint3                                fZPlaneParams;
64da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    std::function<SkScalar(const SkPoint&)> fTransformedHeightFunc;
65da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    SkScalar                                fZOffset;
66da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    // members for perspective height function
674c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth    SkPoint3                                fTransformedZParams;
68da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    SkScalar                                fPartialDeterminants[3];
69b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth
70da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    // first two points
71a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkTDArray<SkPoint>  fInitPoints;
72a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    // temporary buffer
73a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkTDArray<SkPoint>  fPointBuffer;
74958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
75958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon    SkTDArray<SkPoint>  fPositions;
76958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon    SkTDArray<SkColor>  fColors;
77958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon    SkTDArray<uint16_t> fIndices;
78958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
797638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    int                 fFirstVertexIndex;
807638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    SkVector            fFirstOutset;
81a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkPoint             fFirstPoint;
820dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon
830dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon    bool                fSucceeded;
84a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    bool                fTransparent;
85a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
86a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkColor             fUmbraColor;
87a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkColor             fPenumbraColor;
88a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
89a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkScalar            fRadius;
90a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkScalar            fDirection;
91a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    int                 fPrevUmbraIndex;
927638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    SkVector            fPrevOutset;
93a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkPoint             fPrevPoint;
94958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon};
95958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
96da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verthstatic bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
97bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                           SkVector* newNormal) {
98bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkVector normal;
99bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // compute perpendicular
100bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    normal.fX = p0.fY - p1.fY;
101bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    normal.fY = p1.fX - p0.fX;
102da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    normal *= dir;
103bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (!normal.normalize()) {
104bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        return false;
105bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
106bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *newNormal = normal;
107bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    return true;
108bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
109bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
110bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verthstatic void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
111bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                                 SkScalar* rotSin, SkScalar* rotCos, int* n) {
112e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth    const SkScalar kRecipPixelsPerArcSegment = 0.125f;
113bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
114bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkScalar rCos = v1.dot(v2);
115bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkScalar rSin = v1.cross(v2);
116bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkScalar theta = SkScalarATan2(rSin, rCos);
117bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
118e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth    int steps = SkScalarFloorToInt(r*theta*kRecipPixelsPerArcSegment);
119bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
120bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkScalar dTheta = theta / steps;
121bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *rotSin = SkScalarSinCos(dTheta, rotCos);
122e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth    *n = steps;
123bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
124bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
125e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van VerthSkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
126e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth        : fZPlaneParams(zPlaneParams)
127da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        , fZOffset(0)
1287638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        , fFirstVertexIndex(-1)
129aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon        , fSucceeded(false)
130aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon        , fTransparent(transparent)
131aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon        , fDirection(1)
132aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon        , fPrevUmbraIndex(-1) {
133a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    fInitPoints.setReserve(3);
134a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
135a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    // child classes will set reserve for positions, colors and indices
136a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth}
137a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
138da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verthbool SkBaseShadowTessellator::setZOffset(const SkRect& bounds, bool perspective) {
139e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth    SkScalar minZ = this->heightFunc(bounds.fLeft, bounds.fTop);
140da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    if (perspective) {
141e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth        SkScalar z = this->heightFunc(bounds.fLeft, bounds.fBottom);
142da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        if (z < minZ) {
143da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            minZ = z;
144da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        }
145e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth        z = this->heightFunc(bounds.fRight, bounds.fTop);
146da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        if (z < minZ) {
147da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            minZ = z;
148da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        }
149e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth        z = this->heightFunc(bounds.fRight, bounds.fBottom);
150da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        if (z < minZ) {
151da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            minZ = z;
152da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        }
153da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
154da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
155da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    if (minZ < kMinHeight) {
156da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fZOffset = -minZ + kMinHeight;
157da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        return true;
158da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
159da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
160da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    return false;
161da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth}
162da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
163a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth// tesselation tolerance values, in device space pixels
164b8b51e6b2f431c89907139bec52ed64b7ed303edMike Klein#if SK_SUPPORT_GPU
165a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verthstatic const SkScalar kQuadTolerance = 0.2f;
166a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verthstatic const SkScalar kCubicTolerance = 0.2f;
167b8b51e6b2f431c89907139bec52ed64b7ed303edMike Klein#endif
168a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verthstatic const SkScalar kConicTolerance = 0.5f;
169a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
170aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomonvoid SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
171a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    m.mapPoints(p, 1);
172a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    this->handleLine(*p);
173a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth}
174a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
175aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomonvoid SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) {
176a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth#if SK_SUPPORT_GPU
177a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    // TODO: Pull PathUtils out of Ganesh?
178a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
179a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    fPointBuffer.setReserve(maxCount);
180a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkPoint* target = fPointBuffer.begin();
181a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
182a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth                                                     kQuadTolerance, &target, maxCount);
183a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    fPointBuffer.setCount(count);
184a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    for (int i = 0; i < count; i++) {
185a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        this->handleLine(fPointBuffer[i]);
186a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    }
187a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth#else
188a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    // for now, just to draw something
189a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    this->handleLine(pts[1]);
190a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    this->handleLine(pts[2]);
191a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth#endif
192a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth}
193a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
194aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomonvoid SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
195a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    m.mapPoints(pts, 3);
196a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    this->handleQuad(pts);
197a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth}
198a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
199aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomonvoid SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
200a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    m.mapPoints(pts, 4);
201a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth#if SK_SUPPORT_GPU
202a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    // TODO: Pull PathUtils out of Ganesh?
203a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
204a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    fPointBuffer.setReserve(maxCount);
205a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkPoint* target = fPointBuffer.begin();
206a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
207a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth                                                 kCubicTolerance, &target, maxCount);
208a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    fPointBuffer.setCount(count);
209a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    for (int i = 0; i < count; i++) {
210a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        this->handleLine(fPointBuffer[i]);
211a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    }
212a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth#else
213a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    // for now, just to draw something
214a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    this->handleLine(pts[1]);
215a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    this->handleLine(pts[2]);
216a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    this->handleLine(pts[3]);
217a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth#endif
218a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth}
219a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
220aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomonvoid SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
221da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    if (m.hasPerspective()) {
222da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        w = SkConic::TransformW(pts, w, m);
223da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
224a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    m.mapPoints(pts, 3);
225a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkAutoConicToQuads quadder;
226a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
227a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkPoint lastPoint = *(quads++);
228a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    int count = quadder.countQuads();
229a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    for (int i = 0; i < count; ++i) {
230a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        SkPoint quadPts[3];
231a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        quadPts[0] = lastPoint;
232a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        quadPts[1] = quads[0];
233a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        quadPts[2] = i == count - 1 ? pts[2] : quads[1];
234a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        this->handleQuad(quadPts);
235a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        lastPoint = quadPts[2];
236a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        quads += 2;
237a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    }
238a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth}
239a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
240e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verthbool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) {
241a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    // fill in fan from previous quad
242a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkScalar rotSin, rotCos;
243a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    int numSteps;
2447638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    compute_radial_steps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
2457638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    SkVector prevNormal = fPrevOutset;
246e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth    for (int i = 0; i < numSteps-1; ++i) {
247da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkVector currNormal;
248da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
249da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
250da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fPositions.push() = fPrevPoint + currNormal;
251a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        *fColors.push() = fPenumbraColor;
252a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        *fIndices.push() = fPrevUmbraIndex;
253a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        *fIndices.push() = fPositions.count() - 1;
254e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth        *fIndices.push() = fPositions.count() - 2;
255a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
256da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        prevNormal = currNormal;
257a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    }
258e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth    if (finishArc && numSteps) {
259da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fPositions.push() = fPrevPoint + nextNormal;
260da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fColors.push() = fPenumbraColor;
261da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPrevUmbraIndex;
262da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPositions.count() - 1;
263e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth        *fIndices.push() = fPositions.count() - 2;
264da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
2657638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    fPrevOutset = nextNormal;
266e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth
267e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth    return (numSteps > 0);
268a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth}
269a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
270da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verthbool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
2714c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth    if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
272da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fTransformedHeightFunc = [this](const SkPoint& p) {
273e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth            return fZPlaneParams.fZ;
274da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        };
275da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    } else {
276da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkMatrix ctmInverse;
277da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        if (!ctm.invert(&ctmInverse)) {
278da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            return false;
279da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        }
280da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        // multiply by transpose
2814c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth        fTransformedZParams = SkPoint3::Make(
282e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth            ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
283e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth            ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
284e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth            ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
285e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth
286e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth            ctmInverse[SkMatrix::kMSkewX] * fZPlaneParams.fX +
287e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth            ctmInverse[SkMatrix::kMScaleY] * fZPlaneParams.fY +
288e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth            ctmInverse[SkMatrix::kMPersp1] * fZPlaneParams.fZ,
289e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth
290e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth            ctmInverse[SkMatrix::kMTransX] * fZPlaneParams.fX +
291e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth            ctmInverse[SkMatrix::kMTransY] * fZPlaneParams.fY +
292e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth            ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
293e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth        );
294da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
2954c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth        if (ctm.hasPerspective()) {
2964c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
2974c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            // so pre-compute those values that are independent of X and Y.
2984c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
2994c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
3004c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                                      ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
3014c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
3024c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                                      ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
3034c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
3044c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                                      ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
3054c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
3064c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                                      ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
3074c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                                      ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
3084c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth
3094c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
3104c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
3114c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            fTransformedZParams.fX *= ctmDeterminant;
3124c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            fTransformedZParams.fY *= ctmDeterminant;
3134c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            fTransformedZParams.fZ *= ctmDeterminant;
3144c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth
3154c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            fTransformedHeightFunc = [this](const SkPoint& p) {
3164c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                SkScalar denom = p.fX * fPartialDeterminants[0] +
3174c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                                 p.fY * fPartialDeterminants[1] +
3184c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                                 fPartialDeterminants[2];
3194c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                SkScalar w = SkScalarFastInvert(denom);
3204c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                return fZOffset + w*(fTransformedZParams.fX * p.fX +
3214c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                                     fTransformedZParams.fY * p.fY +
3224c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                                     fTransformedZParams.fZ);
3234c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            };
3244c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth        } else {
3254c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            fTransformedHeightFunc = [this](const SkPoint& p) {
3264c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                return fZOffset + fTransformedZParams.fX * p.fX +
3274c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth                       fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
3284c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth            };
3294c9b893953a4dddc20edafd7583523c94c01c889Jim Van Verth        }
330da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
331a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
332da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    return true;
333a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth}
334a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
335a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
336a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth//////////////////////////////////////////////////////////////////////////////////////////////////
337a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
338aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomonclass SkAmbientShadowTessellator : public SkBaseShadowTessellator {
339a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verthpublic:
340a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
341e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth                               const SkPoint3& zPlaneParams, bool transparent);
342a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
343a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verthprivate:
344a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    void handleLine(const SkPoint& p) override;
345da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
346da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
347da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    static constexpr auto kHeightFactor = 1.0f / 128.0f;
348da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    static constexpr auto kGeomFactor = 64.0f;
349da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    static constexpr auto kMaxEdgeLenSqr = 20 * 20;
3507638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    static constexpr auto kInsetFactor = -0.5f;
351a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
352da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    SkScalar offset(SkScalar z) {
353da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        return z * kHeightFactor * kGeomFactor;
354da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
355da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    SkColor umbraColor(SkScalar z) {
356da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkScalar umbraAlpha = SkScalarInvert((1.0f + SkTMax(z*kHeightFactor, 0.0f)));
357060d9820364b0cf09c7eb3bda449f24c3dcba2e2Jim Van Verth        return SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
358da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
359da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
360a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    int                 fCentroidCount;
3617638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    bool                fSplitFirstEdge;
3627638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    bool                fSplitPreviousEdge;
363a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
364aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon    typedef SkBaseShadowTessellator INHERITED;
365a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth};
366a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
367efe3dedbb3493b738abdb56041b093245e4e8711Jim Van VerthSkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
368a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth                                                       const SkMatrix& ctm,
369e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth                                                       const SkPoint3& zPlaneParams,
370bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                                                       bool transparent)
3717638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        : INHERITED(zPlaneParams, transparent)
3727638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        , fSplitFirstEdge(false)
3737638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        , fSplitPreviousEdge(false) {
374da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    // Set base colors
375b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth    SkScalar occluderHeight = heightFunc(0, 0);
376b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth    SkScalar umbraAlpha = SkScalarInvert((1.0f + SkTMax(occluderHeight*kHeightFactor, 0.0f)));
377b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth    // umbraColor is the interior value, penumbraColor the exterior value.
378b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth    // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
379b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth    // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
380b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth    // the final alpha.
381060d9820364b0cf09c7eb3bda449f24c3dcba2e2Jim Van Verth    fUmbraColor = SkColorSetARGB(umbraAlpha * 255.9999f, 0, 0, 0);
382060d9820364b0cf09c7eb3bda449f24c3dcba2e2Jim Van Verth    fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
383b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth
384da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    // make sure we're not below the canvas plane
385da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    this->setZOffset(path.getBounds(), ctm.hasPerspective());
386da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
387da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    this->setTransformedHeightFunc(ctm);
388da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
389bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // Outer ring: 3*numPts
390bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // Middle ring: numPts
391bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fPositions.setReserve(4 * path.countPoints());
392bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fColors.setReserve(4 * path.countPoints());
393bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // Outer ring: 12*numPts
394bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // Middle ring: 0
395bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fIndices.setReserve(12 * path.countPoints());
396bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
397bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // walk around the path, tessellate and generate outer ring
398bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // if original path is transparent, will accumulate sum of points for centroid
399bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkPath::Iter iter(path, true);
400bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkPoint pts[4];
401bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkPath::Verb verb;
402bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (fTransparent) {
403bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fPositions.push() = SkPoint::Make(0, 0);
404b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth        *fColors.push() = fUmbraColor;
405bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fCentroidCount = 0;
406bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
407bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
408bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        switch (verb) {
409bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kLine_Verb:
410a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth                this->INHERITED::handleLine(ctm, &pts[1]);
411bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                break;
412bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kQuad_Verb:
413a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth                this->handleQuad(ctm, pts);
414bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                break;
415bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kCubic_Verb:
416a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth                this->handleCubic(ctm, pts);
417bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                break;
418bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kConic_Verb:
419a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth                this->handleConic(ctm, pts, iter.conicWeight());
420bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                break;
421bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kMove_Verb:
422bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kClose_Verb:
423bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kDone_Verb:
424bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                break;
425bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        }
426bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
427bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
4280dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon    if (!this->indexCount()) {
4290dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon        return;
4300dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon    }
4310dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon
4327638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    // Finish up
433bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkVector normal;
434da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
435da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkScalar z = fTransformedHeightFunc(fPrevPoint);
436da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fRadius = this->offset(z);
437da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkVector scaledNormal(normal);
438da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        scaledNormal *= fRadius;
439da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        this->addArc(scaledNormal, true);
440da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
4417638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        // fix-up the last and first umbra points
4427638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        SkVector inset = normal;
4437638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        // adding to an average, so multiply by an additional half
4447638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        inset *= 0.5f*kInsetFactor;
4457638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fPositions[fPrevUmbraIndex] += inset;
4467638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fPositions[fFirstVertexIndex] += inset;
4477638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        // we multiply by another half because now we're adding to an average of an average
4487638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        inset *= 0.5f;
4497638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        if (fSplitPreviousEdge) {
4507638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            fPositions[fPrevUmbraIndex - 2] += inset;
4517638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        }
4527638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        if (fSplitFirstEdge) {
4537638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            fPositions[fFirstVertexIndex + 2] += inset;
4547638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        }
4557638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth
456da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        // set up for final edge
457da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        z = fTransformedHeightFunc(fFirstPoint);
458da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        normal *= this->offset(z);
459da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
460da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        // make sure we don't end up with a sharp alpha edge along the quad diagonal
4617638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        if (fColors[fPrevUmbraIndex] != fColors[fFirstVertexIndex] &&
462da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            fFirstPoint.distanceToSqd(fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
4637638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            SkPoint centerPoint = fPositions[fPrevUmbraIndex] + fPositions[fFirstVertexIndex];
4647638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            centerPoint *= 0.5f;
4657638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            *fPositions.push() = centerPoint;
4667638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            *fColors.push() = SkPMLerp(fColors[fFirstVertexIndex], fColors[fPrevUmbraIndex], 128);
4677638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            centerPoint = fPositions[fPositions.count()-2] + fPositions[fFirstVertexIndex+1];
468da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            centerPoint *= 0.5f;
469da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fPositions.push() = centerPoint;
470da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fColors.push() = fPenumbraColor;
471bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
4727638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
4737638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fPrevUmbraIndex;
4747638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fPositions.count() - 3;
4757638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fPositions.count() - 2;
476bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
4777638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fPositions.count() - 3;
4787638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fPositions.count() - 1;
4797638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fPositions.count() - 2;
4807638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            } else {
4817638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fPrevUmbraIndex;
4827638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fPositions.count() - 2;
4837638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fPositions.count() - 1;
4847638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth
4857638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fPrevUmbraIndex;
4867638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fPositions.count() - 1;
4877638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fPositions.count() - 3;
4887638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            }
489da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
4901c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth            // if transparent, add point to first one in array and add to center fan
4911c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth            if (fTransparent) {
4921c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth                fPositions[0] += centerPoint;
4931c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth                ++fCentroidCount;
4941c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth
4951c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth                *fIndices.push() = 0;
4961c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth                *fIndices.push() = fPrevUmbraIndex;
4971c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth                *fIndices.push() = fPositions.count() - 2;
4981c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth            }
4991c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth
500da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            fPrevUmbraIndex = fPositions.count() - 2;
501da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        }
502da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
503da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        // final edge
504a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        *fPositions.push() = fFirstPoint + normal;
505bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fColors.push() = fPenumbraColor;
506bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
5077638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        if (fColors[fPrevUmbraIndex] > fColors[fFirstVertexIndex]) {
508da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPrevUmbraIndex;
509da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 2;
5107638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            *fIndices.push() = fFirstVertexIndex;
511bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
512da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 2;
513da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 1;
5147638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            *fIndices.push() = fFirstVertexIndex;
515da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        } else {
516da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPrevUmbraIndex;
517da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 2;
518da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 1;
519da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
520da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPrevUmbraIndex;
521da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 1;
5227638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            *fIndices.push() = fFirstVertexIndex;
523da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        }
5247638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fPrevOutset = normal;
525bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
526bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
527bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // finalize centroid
528bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (fTransparent) {
529bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fPositions[0] *= SkScalarFastInvert(fCentroidCount);
5301c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth        fColors[0] = this->umbraColor(fTransformedHeightFunc(fPositions[0]));
531bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
532bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = 0;
53366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        *fIndices.push() = fPrevUmbraIndex;
5347638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        *fIndices.push() = fFirstVertexIndex;
535bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
536bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
537bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // final fan
538bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (fPositions.count() >= 3) {
5397638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fPrevUmbraIndex = fFirstVertexIndex;
540a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        fPrevPoint = fFirstPoint;
541da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fRadius = this->offset(fTransformedHeightFunc(fPrevPoint));
5427638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        if (this->addArc(fFirstOutset, false)) {
5437638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            *fIndices.push() = fFirstVertexIndex;
544e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth            *fIndices.push() = fPositions.count() - 1;
5457638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            *fIndices.push() = fFirstVertexIndex + 1;
546e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth        } else {
547e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth            // arc is too small, set the first penumbra point to be the same position
548e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth            // as the last one
5497638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
550e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth        }
551bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
5520dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon    fSucceeded = true;
553bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
554bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
555efe3dedbb3493b738abdb56041b093245e4e8711Jim Van Verthvoid SkAmbientShadowTessellator::handleLine(const SkPoint& p)  {
556bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (fInitPoints.count() < 2) {
557bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fInitPoints.push() = p;
558bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        return;
559bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
560bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
561bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (fInitPoints.count() == 2) {
562bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        // determine if cw or ccw
563bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        SkVector v0 = fInitPoints[1] - fInitPoints[0];
564bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        SkVector v1 = p - fInitPoints[0];
565bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        SkScalar perpDot = v0.fX*v1.fY - v0.fY*v1.fX;
566bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        if (SkScalarNearlyZero(perpDot)) {
567bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            // nearly parallel, just treat as straight line and continue
568bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            fInitPoints[1] = p;
569bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            return;
570bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        }
571bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
572bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        // if perpDot > 0, winding is ccw
573bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fDirection = (perpDot > 0) ? -1 : 1;
574bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
575bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        // add first quad
576da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkVector normal;
577da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &normal)) {
578bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            // first two points are incident, make the third point the second and continue
579bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            fInitPoints[1] = p;
580bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            return;
581bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        }
582bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
583a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        fFirstPoint = fInitPoints[0];
5847638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fFirstVertexIndex = fPositions.count();
585da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkScalar z = fTransformedHeightFunc(fFirstPoint);
5867638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fFirstOutset = normal;
5877638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fFirstOutset *= this->offset(z);
588da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
5897638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fPrevOutset = fFirstOutset;
590a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth        fPrevPoint = fFirstPoint;
5917638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fPrevUmbraIndex = fFirstVertexIndex;
592bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
593da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fPositions.push() = fFirstPoint;
594da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fColors.push() = this->umbraColor(z);
5957638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        *fPositions.push() = fFirstPoint + fFirstOutset;
596bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fColors.push() = fPenumbraColor;
597bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        if (fTransparent) {
598da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            fPositions[0] += fFirstPoint;
599bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            fCentroidCount = 1;
600bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        }
601da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
602da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        // add the first quad
603da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        z = fTransformedHeightFunc(fInitPoints[1]);
604da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fRadius = this->offset(z);
605da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fUmbraColor = this->umbraColor(z);
606da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        this->addEdge(fInitPoints[1], normal);
607bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
608bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        // to ensure we skip this block next time
609bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fInitPoints.push() = p;
610bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
611bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
612bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkVector normal;
6137638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
614da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkVector scaledNormal = normal;
615da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        scaledNormal *= fRadius;
616da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        this->addArc(scaledNormal, true);
617da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkScalar z = fTransformedHeightFunc(p);
618da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fRadius = this->offset(z);
619da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fUmbraColor = this->umbraColor(z);
620da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        this->addEdge(p, normal);
621bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
622bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
623bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
624efe3dedbb3493b738abdb56041b093245e4e8711Jim Van Verthvoid SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
6257638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    // We compute the inset in two stages: first we inset by half the current normal,
6267638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    // then on the next addEdge() we add half of the next normal to get an average of the two
6277638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    SkVector insetNormal = nextNormal;
6287638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    insetNormal *= 0.5f*kInsetFactor;
6297638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth
6307638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    // Adding the other half of the average for the previous edge
6317638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    fPositions[fPrevUmbraIndex] += insetNormal;
6327638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth
6337638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    SkPoint umbraPoint = nextPoint + insetNormal;
6347638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    SkVector outsetNormal = nextNormal;
6357638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    outsetNormal *= fRadius;
6367638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    SkPoint penumbraPoint = nextPoint + outsetNormal;
6377638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth
6387638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    // For split edges, we're adding an average of two averages, so we multiply by another half
6397638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    if (fSplitPreviousEdge) {
6407638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        insetNormal *= 0.5f;
6417638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fPositions[fPrevUmbraIndex - 2] += insetNormal;
6427638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    }
6437638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth
6447638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    // Split the edge to make sure we don't end up with a sharp alpha edge along the quad diagonal
645da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    if (fColors[fPrevUmbraIndex] != fUmbraColor &&
646da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        nextPoint.distanceToSqd(fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) {
6477638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth
6487638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        // This is lacking 1/4 of the next inset -- we'll add it the next time we call addEdge()
6497638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        SkPoint centerPoint = fPositions[fPrevUmbraIndex] + umbraPoint;
650da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        centerPoint *= 0.5f;
651da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fPositions.push() = centerPoint;
652da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fColors.push() = SkPMLerp(fUmbraColor, fColors[fPrevUmbraIndex], 128);
6537638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        centerPoint = fPositions[fPositions.count()-2] + penumbraPoint;
6547638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        centerPoint *= 0.5f;
6557638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        *fPositions.push() = centerPoint;
656da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fColors.push() = fPenumbraColor;
657da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
658da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        // set triangularization to get best interpolation of color
659da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
660da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPrevUmbraIndex;
661da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 3;
662da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 2;
663da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
664da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 3;
665da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 1;
666da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 2;
667da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        } else {
668da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPrevUmbraIndex;
669da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 2;
670da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 1;
671da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
672da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPrevUmbraIndex;
673da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 1;
674da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            *fIndices.push() = fPositions.count() - 3;
675da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        }
676da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
6771c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth        // if transparent, add point to first one in array and add to center fan
6781c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth        if (fTransparent) {
6791c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth            fPositions[0] += centerPoint;
6801c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth            ++fCentroidCount;
6811c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth
6821c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth            *fIndices.push() = 0;
6831c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth            *fIndices.push() = fPrevUmbraIndex;
6841c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth            *fIndices.push() = fPositions.count() - 2;
6851c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth        }
6861c4c1144c980e672b6744adb3f30e11cb2d54750Jim Van Verth
6877638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fSplitPreviousEdge = true;
6887638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        if (fPrevUmbraIndex == fFirstVertexIndex) {
6897638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            fSplitFirstEdge = true;
6907638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        }
691da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fPrevUmbraIndex = fPositions.count() - 2;
6927638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    } else {
6937638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fSplitPreviousEdge = false;
694da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
695da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
696bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // add next quad
6977638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    *fPositions.push() = umbraPoint;
698bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fColors.push() = fUmbraColor;
6997638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    *fPositions.push() = penumbraPoint;
700bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fColors.push() = fPenumbraColor;
701bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
702da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    // set triangularization to get best interpolation of color
703da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
704da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPrevUmbraIndex;
705da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPositions.count() - 3;
706da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPositions.count() - 2;
707bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
708da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPositions.count() - 3;
709da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPositions.count() - 1;
710da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPositions.count() - 2;
711da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    } else {
712da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPrevUmbraIndex;
713da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPositions.count() - 2;
714da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPositions.count() - 1;
715da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
716da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPrevUmbraIndex;
717da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPositions.count() - 1;
718da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        *fIndices.push() = fPositions.count() - 3;
719da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
720bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
721bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // if transparent, add point to first one in array and add to center fan
722bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (fTransparent) {
723bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fPositions[0] += nextPoint;
724bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        ++fCentroidCount;
725bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
726bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = 0;
72766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        *fIndices.push() = fPrevUmbraIndex;
728bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPositions.count() - 2;
729bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
730bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
73166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    fPrevUmbraIndex = fPositions.count() - 2;
732a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    fPrevPoint = nextPoint;
7337638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    fPrevOutset = outsetNormal;
734bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
73591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
73691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth///////////////////////////////////////////////////////////////////////////////////////////////////
73791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
738aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomonclass SkSpotShadowTessellator : public SkBaseShadowTessellator {
739958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomonpublic:
740a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
741e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth                            const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
742060d9820364b0cf09c7eb3bda449f24c3dcba2e2Jim Van Verth                            SkScalar lightRadius, bool transparent);
743958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
744958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomonprivate:
745ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    void computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
746da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth                                    const SkMatrix& shadowTransform);
747ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    void computeClipVectorsAndTestCentroid();
74866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
749ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    int getClosestUmbraPoint(const SkPoint& point);
750958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
751a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    void handleLine(const SkPoint& p) override;
752b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth    bool handlePolyPoint(const SkPoint& p);
753958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
754958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon    void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
755ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    bool addInnerPoint(const SkPoint& pathPoint);
756da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
757da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
758da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    SkScalar offset(SkScalar z) {
759da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
760da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        return fLightRadius*zRatio;
761da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
762da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
763da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    SkScalar            fLightZ;
764da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    SkScalar            fLightRadius;
765da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    SkScalar            fOffsetAdjust;
766958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
767958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon    SkTDArray<SkPoint>  fClipPolygon;
76866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    SkTDArray<SkVector> fClipVectors;
769a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    SkPoint             fCentroid;
770ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    SkScalar            fArea;
771a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth
772ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    SkTDArray<SkPoint>  fPathPolygon;
773ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    SkTDArray<SkPoint>  fUmbraPolygon;
774ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    int                 fCurrClipPoint;
775ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    int                 fCurrUmbraPoint;
77666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    bool                fPrevUmbraOutside;
77766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    bool                fFirstUmbraOutside;
778a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth    bool                fValidUmbra;
779958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
780aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon    typedef SkBaseShadowTessellator INHERITED;
781958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon};
782958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
783a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van VerthSkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
784e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth                                                 const SkPoint3& zPlaneParams,
785b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth                                                 const SkPoint3& lightPos, SkScalar lightRadius,
786060d9820364b0cf09c7eb3bda449f24c3dcba2e2Jim Van Verth                                                 bool transparent)
787e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth    : INHERITED(zPlaneParams, transparent)
788da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    , fLightZ(lightPos.fZ)
789da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    , fLightRadius(lightRadius)
790da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    , fOffsetAdjust(0)
791da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    , fCurrClipPoint(0)
792da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    , fPrevUmbraOutside(false)
793da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    , fFirstUmbraOutside(false)
794da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    , fValidUmbra(true) {
795da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
796da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    // make sure we're not below the canvas plane
797da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    if (this->setZOffset(path.getBounds(), ctm.hasPerspective())) {
798da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        // Adjust light height and radius
799da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fLightRadius *= (fLightZ + fZOffset) / fLightZ;
800da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fLightZ += fZOffset;
801da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
802b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth
803b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth    // Set radius and colors
804b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth    SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
805e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth    SkScalar occluderHeight = this->heightFunc(center.fX, center.fY) + fZOffset;
806da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    float zRatio = SkTPin(occluderHeight / (fLightZ - occluderHeight), 0.0f, 0.95f);
807b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth    SkScalar radius = lightRadius * zRatio;
808b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth    fRadius = radius;
809060d9820364b0cf09c7eb3bda449f24c3dcba2e2Jim Van Verth    fUmbraColor = SkColorSetARGB(255, 0, 0, 0);
810060d9820364b0cf09c7eb3bda449f24c3dcba2e2Jim Van Verth    fPenumbraColor = SkColorSetARGB(0, 0, 0, 0);
811b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth
812b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth    // Compute the scale and translation for the spot shadow.
813da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    SkMatrix shadowTransform;
814da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    if (!ctm.hasPerspective()) {
815da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkScalar scale = fLightZ / (fLightZ - occluderHeight);
816da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkVector translate = SkVector::Make(-zRatio * lightPos.fX, -zRatio * lightPos.fY);
817da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
818da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    } else {
819da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        // For perspective, we have a scale, a z-shear, and another projective divide --
820da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        // this varies at each point so we can't use an affine transform.
821da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        // We'll just apply this to each generated point in turn.
822da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        shadowTransform.reset();
823da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        // Also can't cull the center (for now).
824da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fTransparent = true;
825da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
826da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    SkMatrix fullTransform = SkMatrix::Concat(shadowTransform, ctm);
827da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
828da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    // Set up our reverse mapping
829da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    this->setTransformedHeightFunc(fullTransform);
830b436655ad5c40a04b65c1642d0a0e781ce296c96Jim Van Verth
831ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // TODO: calculate these reserves better
83266085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    // Penumbra ring: 3*numPts
83366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    // Umbra ring: numPts
83491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // Inner ring: numPts
83566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    fPositions.setReserve(5 * path.countPoints());
83666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    fColors.setReserve(5 * path.countPoints());
83766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    // Penumbra ring: 12*numPts
83866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    // Umbra ring: 3*numPts
83966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    fIndices.setReserve(15 * path.countPoints());
840e7c85c45c4c0a97adc6711bb12ecacc36af4ba11Brian Salomon    fClipPolygon.setReserve(path.countPoints());
841ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
842ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // compute rough clip bounds for umbra, plus offset polygon, plus centroid
843da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    this->computeClipAndPathPolygons(path, ctm, shadowTransform);
844ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3) {
84566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        return;
84666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    }
84766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
848ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // check to see if umbra collapses
849ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    SkScalar minDistSq = fCentroid.distanceToLineSegmentBetweenSqd(fPathPolygon[0],
850ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon                                                                   fPathPolygon[1]);
851da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    SkRect bounds;
852da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
853ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    for (int i = 1; i < fPathPolygon.count(); ++i) {
854ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        int j = i + 1;
855ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        if (i == fPathPolygon.count() - 1) {
856ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon            j = 0;
857ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        }
858ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        SkPoint currPoint = fPathPolygon[i];
859ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        SkPoint nextPoint = fPathPolygon[j];
860ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        SkScalar distSq = fCentroid.distanceToLineSegmentBetweenSqd(currPoint, nextPoint);
861ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        if (distSq < minDistSq) {
862ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon            minDistSq = distSq;
863ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        }
864ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    }
865ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    static constexpr auto kTolerance = 1.0e-2f;
866ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
867ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
868ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
869da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fOffsetAdjust = newRadius - radius;
870b6069dfba7b7ab563a3fccb2f38307e47035300cJim Van Verth        SkScalar ratio = 128 * (newRadius + radius) / radius;
871ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        // they aren't PMColors, but the interpolation algorithm is the same
872ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
873ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        radius = newRadius;
874ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    }
875e5f5bf5175e426ebb6aa234f4387831c898f20adJim Van Verth
876ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // compute vectors for clip tests
877ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    this->computeClipVectorsAndTestCentroid();
878ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
879ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // generate inner ring
880da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
881da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth                              &fUmbraPolygon)) {
882ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        // this shouldn't happen, but just in case we'll inset using the centroid
883ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        fValidUmbra = false;
884ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    }
885ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
886ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // walk around the path polygon, generate outer ring and connect to inner ring
88766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    if (fTransparent) {
88866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        *fPositions.push() = fCentroid;
88966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        *fColors.push() = fUmbraColor;
89066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    }
891ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    fCurrUmbraPoint = 0;
892ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    for (int i = 0; i < fPathPolygon.count(); ++i) {
893b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth        if (!this->handlePolyPoint(fPathPolygon[i])) {
894b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth            return;
895b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth        }
89691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
89791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
8980dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon    if (!this->indexCount()) {
8990dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon        return;
9000dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon    }
9010dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon
902ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // finish up the final verts
90391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkVector normal;
904da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
905da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        normal *= fRadius;
906da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        this->addArc(normal, true);
90791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
90866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        // add to center fan
90966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        if (fTransparent) {
91066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            *fIndices.push() = 0;
91166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            *fIndices.push() = fPrevUmbraIndex;
9127638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            *fIndices.push() = fFirstVertexIndex;
91366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            // or to clip ring
91466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        } else {
91566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            if (fFirstUmbraOutside) {
91666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fIndices.push() = fPrevUmbraIndex;
9177638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fFirstVertexIndex;
9187638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fFirstVertexIndex + 1;
91966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                if (fPrevUmbraOutside) {
92066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                    // fill out quad
92166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                    *fIndices.push() = fPrevUmbraIndex;
9227638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                    *fIndices.push() = fFirstVertexIndex + 1;
92366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                    *fIndices.push() = fPrevUmbraIndex + 1;
92466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                }
92566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            } else if (fPrevUmbraOutside) {
92666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                // add tri
92766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fIndices.push() = fPrevUmbraIndex;
9287638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fFirstVertexIndex;
92966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fIndices.push() = fPrevUmbraIndex + 1;
93066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            }
93166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        }
93266085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
93391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // add final edge
93491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fPositions.push() = fFirstPoint + normal;
93591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fColors.push() = fPenumbraColor;
93691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
93766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        *fIndices.push() = fPrevUmbraIndex;
93891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPositions.count() - 2;
9397638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        *fIndices.push() = fFirstVertexIndex;
94091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
94191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPositions.count() - 2;
94291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPositions.count() - 1;
9437638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        *fIndices.push() = fFirstVertexIndex;
944da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
9457638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fPrevOutset = normal;
94691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
94791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
94891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // final fan
94991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    if (fPositions.count() >= 3) {
9507638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fPrevUmbraIndex = fFirstVertexIndex;
95191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fPrevPoint = fFirstPoint;
9527638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        if (this->addArc(fFirstOutset, false)) {
9537638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth            *fIndices.push() = fFirstVertexIndex;
954e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth            *fIndices.push() = fPositions.count() - 1;
955e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth            if (fFirstUmbraOutside) {
9567638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fFirstVertexIndex + 2;
957e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth            } else {
9587638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                *fIndices.push() = fFirstVertexIndex + 1;
959e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth            }
96066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        } else {
961e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth            // no arc added, fix up by setting first penumbra point position to last one
962e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth            if (fFirstUmbraOutside) {
9637638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
964e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth            } else {
9657638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth                fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
966e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth            }
96766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        }
96891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
969da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
970da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    if (ctm.hasPerspective()) {
971da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        for (int i = 0; i < fPositions.count(); ++i) {
972da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
973da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            SkScalar factor = SkScalarInvert(fLightZ - pathZ);
974da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
975da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            fPositions[i].fY = (fPositions[i].fY*fLightZ - lightPos.fY*pathZ)*factor;
976da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        }
977da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth#ifdef DRAW_CENTROID
978da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkScalar pathZ = fTransformedHeightFunc(fCentroid);
979da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        SkScalar factor = SkScalarInvert(fLightZ - pathZ);
980da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fCentroid.fX = (fCentroid.fX*fLightZ - lightPos.fX*pathZ)*factor;
981da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        fCentroid.fY = (fCentroid.fY*fLightZ - lightPos.fY*pathZ)*factor;
982da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth#endif
983da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    }
984da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth#ifdef DRAW_CENTROID
985da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fPositions.push() = fCentroid + SkVector::Make(-2, -2);
986da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
987da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fPositions.push() = fCentroid + SkVector::Make(2, -2);
988da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
989da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fPositions.push() = fCentroid + SkVector::Make(-2, 2);
990da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
991da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fPositions.push() = fCentroid + SkVector::Make(2, 2);
992da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fColors.push() = SkColorSetARGB(255, 0, 255, 255);
993da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
994da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fIndices.push() = fPositions.count() - 4;
995da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fIndices.push() = fPositions.count() - 2;
996da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fIndices.push() = fPositions.count() - 1;
997da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
998da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fIndices.push() = fPositions.count() - 4;
999da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fIndices.push() = fPositions.count() - 1;
1000da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    *fIndices.push() = fPositions.count() - 3;
1001da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth#endif
1002da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth
10030dda9cb881900241c1c2193ddf3bede72cda898bBrian Salomon    fSucceeded = true;
100491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
100591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
1006ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomonvoid SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
1007da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth                                                         const SkMatrix& shadowTransform) {
1008ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1009ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    fPathPolygon.setReserve(path.countPoints());
1010ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1011ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // Walk around the path and compute clip polygon and path polygon.
1012ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // Will also accumulate sum of areas for centroid.
1013ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // For Bezier curves, we compute additional interior points on curve.
101491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPath::Iter iter(path, true);
101591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPoint pts[4];
101691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPath::Verb verb;
101791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
1018e7c85c45c4c0a97adc6711bb12ecacc36af4ba11Brian Salomon    fClipPolygon.reset();
1019e5f5bf5175e426ebb6aa234f4387831c898f20adJim Van Verth
1020ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // init centroid
1021ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    fCentroid = SkPoint::Make(0, 0);
1022ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    fArea = 0;
1023ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
102466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    // coefficients to compute cubic Bezier at t = 5/16
1025ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    static constexpr SkScalar kA = 0.32495117187f;
1026ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    static constexpr SkScalar kB = 0.44311523437f;
1027ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    static constexpr SkScalar kC = 0.20141601562f;
1028ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    static constexpr SkScalar kD = 0.03051757812f;
102966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
103066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    SkPoint curvePoint;
103166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    SkScalar w;
103291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
103391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        switch (verb) {
103491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kLine_Verb:
1035a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth                ctm.mapPoints(&pts[1], 1);
103691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                *fClipPolygon.push() = pts[1];
1037ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon                this->INHERITED::handleLine(shadowTransform, &pts[1]);
103891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
103991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kQuad_Verb:
1040a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth                ctm.mapPoints(pts, 3);
104166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                // point at t = 1/2
104266085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
104366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY;
104466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fClipPolygon.push() = curvePoint;
104566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fClipPolygon.push() = pts[2];
1046ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon                this->handleQuad(shadowTransform, pts);
104791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
104891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kConic_Verb:
1049a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth                ctm.mapPoints(pts, 3);
105066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                w = iter.conicWeight();
1051a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth                // point at t = 1/2
105266085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
105366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
105466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
105566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fClipPolygon.push() = curvePoint;
105666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fClipPolygon.push() = pts[2];
1057ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon                this->handleConic(shadowTransform, pts, w);
105891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
105991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kCubic_Verb:
1060a84898dbb8d8f7cb8c3e9bdfb4c31d85dff1922fJim Van Verth                ctm.mapPoints(pts, 4);
106166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                // point at t = 5/16
106266085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
106366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY;
106466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fClipPolygon.push() = curvePoint;
106566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                // point at t = 11/16
106666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
106766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                curvePoint.fY = kD*pts[0].fY + kC*pts[1].fY + kB*pts[2].fY + kA*pts[3].fY;
106866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fClipPolygon.push() = curvePoint;
106966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fClipPolygon.push() = pts[3];
1070ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon                this->handleCubic(shadowTransform, pts);
107191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
1072ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon            case SkPath::kMove_Verb:
107391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kClose_Verb:
1074ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon            case SkPath::kDone_Verb:
107591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
107691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            default:
107791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                SkDEBUGFAIL("unknown verb");
107891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        }
107991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
108091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
1081ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // finish centroid
1082ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    if (fPathPolygon.count() > 0) {
1083ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        SkPoint currPoint = fPathPolygon[fPathPolygon.count() - 1];
1084ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        SkPoint nextPoint = fPathPolygon[0];
1085ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        SkScalar quadArea = currPoint.cross(nextPoint);
1086ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        fCentroid.fX += (currPoint.fX + nextPoint.fX) * quadArea;
1087ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        fCentroid.fY += (currPoint.fY + nextPoint.fY) * quadArea;
1088ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        fArea += quadArea;
1089ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        fCentroid *= SK_Scalar1 / (3 * fArea);
1090ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    }
1091ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1092ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    fCurrClipPoint = fClipPolygon.count() - 1;
109366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon}
109466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
1095ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomonvoid SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
109666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    SkASSERT(fClipPolygon.count() >= 3);
109766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
1098ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // init clip vectors
109966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
110066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    *fClipVectors.push() = v0;
110166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
110266085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    // init centroid check
110366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    bool hiddenCentroid = true;
1104ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    SkVector v1 = fCentroid - fClipPolygon[0];
110566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    SkScalar initCross = v0.cross(v1);
110666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
110766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    for (int p = 1; p < fClipPolygon.count(); ++p) {
1108ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        // add to clip vectors
110966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
111066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        *fClipVectors.push() = v0;
111166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        // Determine if transformed centroid is inside clipPolygon.
1112ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        v1 = fCentroid - fClipPolygon[p];
111366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        if (initCross*v0.cross(v1) <= 0) {
111466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            hiddenCentroid = false;
111566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        }
111666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    }
111766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    SkASSERT(fClipVectors.count() == fClipPolygon.count());
111866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
1119ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    fTransparent = fTransparent || !hiddenCentroid;
112066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon}
112166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
112266085ed41779211f9e7e17aa493d4585d73445feBrian Salomonbool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
112366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                                             SkPoint* clipPoint) {
112466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    SkVector segmentVector = centroid - umbraPoint;
112566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
1126ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    int startClipPoint = fCurrClipPoint;
112766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    do {
1128ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
1129ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
113066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        SkScalar t_num = dp.cross(segmentVector);
113166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        // if line segments are nearly parallel
113266085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        if (SkScalarNearlyZero(denom)) {
113366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            // and collinear
113466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            if (SkScalarNearlyZero(t_num)) {
113566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                return false;
113666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            }
113766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            // otherwise are separate, will try the next poly segment
113866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        // else if crossing lies within poly segment
113966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        } else if (t_num >= 0 && t_num <= denom) {
1140ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon            SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
114166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            // if umbra point is inside the clip polygon
1142da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth            if (s_num >= 0 && s_num <= denom) {
114366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                segmentVector *= s_num/denom;
114466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *clipPoint = umbraPoint + segmentVector;
114566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                return true;
114666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            }
114766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        }
1148ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
1149ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    } while (fCurrClipPoint != startClipPoint);
115066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
115166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    return false;
115291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
115391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
1154ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomonint SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
1155ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    SkScalar minDistance = p.distanceToSqd(fUmbraPolygon[fCurrUmbraPoint]);
1156ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    int index = fCurrUmbraPoint;
1157ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    int dir = 1;
1158ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    int next = (index + dir) % fUmbraPolygon.count();
1159ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1160ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // init travel direction
1161ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    SkScalar distance = p.distanceToSqd(fUmbraPolygon[next]);
1162ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    if (distance < minDistance) {
1163ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        index = next;
1164ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        minDistance = distance;
1165ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    } else {
1166ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        dir = fUmbraPolygon.count()-1;
1167ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    }
1168ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1169ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // iterate until we find a point that increases the distance
1170ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    next = (index + dir) % fUmbraPolygon.count();
1171ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    distance = p.distanceToSqd(fUmbraPolygon[next]);
1172ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    while (distance < minDistance) {
1173ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        index = next;
1174ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        minDistance = distance;
1175ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        next = (index + dir) % fUmbraPolygon.count();
1176ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        distance = p.distanceToSqd(fUmbraPolygon[next]);
1177ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    }
1178ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1179ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    fCurrUmbraPoint = index;
1180ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    return index;
1181ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon}
1182ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1183efe3dedbb3493b738abdb56041b093245e4e8711Jim Van Verthvoid SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
118491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                                        SkPoint* pts, int count) {
118591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // TODO: vectorize
118691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    for (int i = 0; i < count; ++i) {
118791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        pts[i] *= scale;
118891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        pts[i] += xlate;
118991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
119091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
119191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
1192ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomonstatic bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
1193ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    static constexpr SkScalar kClose = (SK_Scalar1 / 16);
1194ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    static constexpr SkScalar kCloseSqd = kClose*kClose;
1195ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1196ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    SkScalar distSq = p0.distanceToSqd(p1);
1197ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    return distSq < kCloseSqd;
1198ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon}
1199ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1200b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verthstatic SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
1201ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    SkVector v0 = p1 - p0;
1202ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    SkVector v1 = p2 - p0;
1203b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth    return v0.cross(v1);
1204b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth}
1205b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth
1206b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verthstatic bool is_collinear(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
1207b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth    return (SkScalarNearlyZero(perp_dot(p0, p1, p2)));
1208ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon}
1209ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1210efe3dedbb3493b738abdb56041b093245e4e8711Jim Van Verthvoid SkSpotShadowTessellator::handleLine(const SkPoint& p) {
1211ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // remove coincident points and add to centroid
1212ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    if (fPathPolygon.count() > 0) {
1213ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        const SkPoint& lastPoint = fPathPolygon[fPathPolygon.count() - 1];
1214ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        if (duplicate_pt(p, lastPoint)) {
1215ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon            return;
1216ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        }
1217ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        SkScalar quadArea = lastPoint.cross(p);
1218ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        fCentroid.fX += (p.fX + lastPoint.fX) * quadArea;
1219ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        fCentroid.fY += (p.fY + lastPoint.fY) * quadArea;
1220ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        fArea += quadArea;
1221ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    }
1222ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1223ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // try to remove collinear points
1224ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    if (fPathPolygon.count() > 1 && is_collinear(fPathPolygon[fPathPolygon.count()-2],
1225ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon                                                 fPathPolygon[fPathPolygon.count()-1],
1226ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon                                                 p)) {
1227ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        fPathPolygon[fPathPolygon.count() - 1] = p;
1228ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    } else {
1229ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        *fPathPolygon.push() = p;
1230ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    }
1231ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon}
1232ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1233b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verthbool SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p) {
123491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    if (fInitPoints.count() < 2) {
123591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fInitPoints.push() = p;
1236b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth        return true;
123791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
123891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
123991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    if (fInitPoints.count() == 2) {
124091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // determine if cw or ccw
1241b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth        SkScalar perpDot = perp_dot(fInitPoints[0], fInitPoints[1], p);
124291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        if (SkScalarNearlyZero(perpDot)) {
124391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            // nearly parallel, just treat as straight line and continue
124491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            fInitPoints[1] = p;
1245b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth            return true;
124691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        }
124791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
124891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // if perpDot > 0, winding is ccw
124991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fDirection = (perpDot > 0) ? -1 : 1;
125091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
125191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // add first quad
12527638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &fFirstOutset)) {
125391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            // first two points are incident, make the third point the second and continue
125491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            fInitPoints[1] = p;
1255b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth            return true;
125691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        }
125791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
12587638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fFirstOutset *= fRadius;
125991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fFirstPoint = fInitPoints[0];
12607638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fFirstVertexIndex = fPositions.count();
12617638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fPrevOutset = fFirstOutset;
126291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fPrevPoint = fFirstPoint;
1263e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth        fPrevUmbraIndex = -1;
126466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
126566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        this->addInnerPoint(fFirstPoint);
12667638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        fPrevUmbraIndex = fFirstVertexIndex;
126766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
126866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        if (!fTransparent) {
126966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            SkPoint clipPoint;
1270b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth            bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex],
1271b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth                                                  fCentroid, &clipPoint);
127266085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            if (isOutside) {
127366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fPositions.push() = clipPoint;
127466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fColors.push() = fUmbraColor;
127566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            }
127666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            fPrevUmbraOutside = isOutside;
127766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            fFirstUmbraOutside = isOutside;
127866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        }
127991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
12807638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        SkPoint newPoint = fFirstPoint + fFirstOutset;
128191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fPositions.push() = newPoint;
128291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fColors.push() = fPenumbraColor;
12837638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth        this->addEdge(fInitPoints[1], fFirstOutset);
128491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
128591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // to ensure we skip this block next time
128691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fInitPoints.push() = p;
128791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
128891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
1289b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth    // if concave, abort
1290b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth    SkScalar perpDot = perp_dot(fInitPoints[1], fInitPoints[2], p);
1291b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth    if (fDirection*perpDot > 0) {
1292b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth        return false;
1293b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth    }
1294b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth
129591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkVector normal;
1296da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth    if (compute_normal(fPrevPoint, p, fDirection, &normal)) {
1297da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        normal *= fRadius;
1298da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        this->addArc(normal, true);
1299da96550d3941cb794a799c73506a1c5b695c70a1Jim Van Verth        this->addEdge(p, normal);
1300b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth        fInitPoints[1] = fInitPoints[2];
1301b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth        fInitPoints[2] = p;
130291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
1303b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth
1304b55eb28a17f0e12ab75d60ae6a3872e964d315e6Jim Van Verth    return true;
130591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
130691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
1307ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomonbool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint) {
1308ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    SkPoint umbraPoint;
1309ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    if (!fValidUmbra) {
1310ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        SkVector v = fCentroid - pathPoint;
1311ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        v *= 0.95f;
1312ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        umbraPoint = pathPoint + v;
131391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    } else {
1314ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
131591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
131666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
131791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fPrevPoint = pathPoint;
1318ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1319ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    // merge "close" points
1320e7e1d9d039b12a86b9a595871a2bd13fe1c28f72Jim Van Verth    if (fPrevUmbraIndex == -1 ||
1321ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
1322ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        *fPositions.push() = umbraPoint;
1323ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        *fColors.push() = fUmbraColor;
1324ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon
1325ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        return false;
1326ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    } else {
1327ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        return true;
1328ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    }
132991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
133091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
1331efe3dedbb3493b738abdb56041b093245e4e8711Jim Van Verthvoid SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
133266085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    // add next umbra point
1333ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    bool duplicate = this->addInnerPoint(nextPoint);
1334ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    int prevPenumbraIndex = duplicate ? fPositions.count()-1 : fPositions.count()-2;
1335ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    int currUmbraIndex = duplicate ? fPrevUmbraIndex : fPositions.count()-1;
133666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
1337ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    if (!duplicate) {
1338ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        // add to center fan if transparent or centroid showing
1339ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        if (fTransparent) {
1340ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon            *fIndices.push() = 0;
1341ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon            *fIndices.push() = fPrevUmbraIndex;
1342ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon            *fIndices.push() = currUmbraIndex;
1343ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        // otherwise add to clip ring
1344ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        } else {
134566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            SkPoint clipPoint;
1346ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon            bool isOutside = this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
1347ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon                                                  &clipPoint);
134866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            if (isOutside) {
134966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fPositions.push() = clipPoint;
135066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fColors.push() = fUmbraColor;
135166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
135266085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fIndices.push() = fPrevUmbraIndex;
135366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fIndices.push() = currUmbraIndex;
135466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fIndices.push() = currUmbraIndex + 1;
135566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                if (fPrevUmbraOutside) {
135666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                    // fill out quad
135766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                    *fIndices.push() = fPrevUmbraIndex;
135866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                    *fIndices.push() = currUmbraIndex + 1;
135966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                    *fIndices.push() = fPrevUmbraIndex + 1;
136066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                }
136166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            } else if (fPrevUmbraOutside) {
136266085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                // add tri
136366085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fIndices.push() = fPrevUmbraIndex;
136466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fIndices.push() = currUmbraIndex;
136566085ed41779211f9e7e17aa493d4585d73445feBrian Salomon                *fIndices.push() = fPrevUmbraIndex + 1;
136666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            }
136766085ed41779211f9e7e17aa493d4585d73445feBrian Salomon            fPrevUmbraOutside = isOutside;
136866085ed41779211f9e7e17aa493d4585d73445feBrian Salomon        }
136966085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    }
137066085ed41779211f9e7e17aa493d4585d73445feBrian Salomon
137166085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    // add next penumbra point and quad
137291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPoint newPoint = nextPoint + nextNormal;
137391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fPositions.push() = newPoint;
137491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fColors.push() = fPenumbraColor;
137591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
1376ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    if (!duplicate) {
1377ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        *fIndices.push() = fPrevUmbraIndex;
1378ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        *fIndices.push() = prevPenumbraIndex;
1379ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon        *fIndices.push() = currUmbraIndex;
1380ab664fa5b5fb96dd1079c090534330ca7e8a10efBrian Salomon    }
138191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
138266085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    *fIndices.push() = prevPenumbraIndex;
138391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = fPositions.count() - 1;
138466085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    *fIndices.push() = currUmbraIndex;
138591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
138666085ed41779211f9e7e17aa493d4585d73445feBrian Salomon    fPrevUmbraIndex = currUmbraIndex;
13877638785bea9fa6ce3505c3393ad3d3aff9af2d0dJim Van Verth    fPrevOutset = nextNormal;
138891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
1389958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
1390958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon///////////////////////////////////////////////////////////////////////////////////////////////////
1391958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
1392aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomonsk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
1393e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth                                                   const SkPoint3& zPlane, bool transparent) {
1394e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth    SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
1395aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon    return ambientTess.releaseVertices();
1396958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon}
1397958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon
1398aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomonsk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
1399e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth                                                const SkPoint3& zPlane, const SkPoint3& lightPos,
1400060d9820364b0cf09c7eb3bda449f24c3dcba2e2Jim Van Verth                                                SkScalar lightRadius,  bool transparent) {
1401e308a122ef996a64a21a6339e3b50b9edfdf654fJim Van Verth    SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent);
1402aff27a23ad4b38851429066dbfb43cfa7199e37cBrian Salomon    return spotTess.releaseVertices();
1403958fbc460a1e680c6a9979e140da8bfc00b8831dBrian Salomon}
1404