SkShadowTessellator.cpp revision 91af72703830f3946c538b47c6c7c96afc0adde2
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
858abc9e2c56464336472493745e91133819deb96Jim Van Verth#include "GrShadowTessellator.h"
9bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth#include "GrPathUtils.h"
10bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
1158abc9e2c56464336472493745e91133819deb96Jim Van Verth#include "SkGeometry.h"
1258abc9e2c56464336472493745e91133819deb96Jim Van Verth
13bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verthstatic bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar radius, SkScalar dir,
14bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                           SkVector* newNormal) {
15bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkVector normal;
16bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // compute perpendicular
17bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    normal.fX = p0.fY - p1.fY;
18bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    normal.fY = p1.fX - p0.fX;
19bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (!normal.normalize()) {
20bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        return false;
21bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
22bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    normal *= radius*dir;
23bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *newNormal = normal;
24bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    return true;
25bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
26bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
27bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verthstatic void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
28bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                                 SkScalar* rotSin, SkScalar* rotCos, int* n) {
29bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    const SkScalar kRecipPixelsPerArcSegment = 0.25f;
30bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
31bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkScalar rCos = v1.dot(v2);
32bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkScalar rSin = v1.cross(v2);
33bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkScalar theta = SkScalarATan2(rSin, rCos);
34bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
35bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkScalar steps = r*theta*kRecipPixelsPerArcSegment;
36bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
37bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkScalar dTheta = theta / steps;
38bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *rotSin = SkScalarSinCos(dTheta, rotCos);
39bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *n = SkScalarFloorToInt(steps);
40bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
41bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
4291af72703830f3946c538b47c6c7c96afc0adde2Jim Van VerthGrAmbientShadowTessellator::GrAmbientShadowTessellator(const SkPath& path,
43bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                                                       SkScalar radius,
44bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                                                       GrColor umbraColor,
45bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                                                       GrColor penumbraColor,
46bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                                                       bool transparent)
47bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    : fRadius(radius)
48bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    , fUmbraColor(umbraColor)
49bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    , fPenumbraColor(penumbraColor)
50bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    , fTransparent(transparent)
51bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    , fPrevInnerIndex(-1) {
52bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
53bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // Outer ring: 3*numPts
54bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // Middle ring: numPts
55bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fPositions.setReserve(4 * path.countPoints());
56bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fColors.setReserve(4 * path.countPoints());
57bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // Outer ring: 12*numPts
58bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // Middle ring: 0
59bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fIndices.setReserve(12 * path.countPoints());
60bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
61bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fInitPoints.setReserve(3);
62bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
63bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // walk around the path, tessellate and generate outer ring
64bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // if original path is transparent, will accumulate sum of points for centroid
65bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkPath::Iter iter(path, true);
66bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkPoint pts[4];
67bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkPath::Verb verb;
68bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (fTransparent) {
69bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fPositions.push() = SkPoint::Make(0, 0);
70bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fColors.push() = umbraColor;
71bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fCentroidCount = 0;
72bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
73bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
74bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        switch (verb) {
75bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kLine_Verb:
7691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                this->handleLine(pts[1]);
77bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                break;
78bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kQuad_Verb:
7991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                this->handleQuad(pts);
80bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                break;
81bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kCubic_Verb:
8291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                this->handleCubic(pts);
83bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                break;
84bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kConic_Verb:
8591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                this->handleConic(pts, iter.conicWeight());
86bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                break;
87bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kMove_Verb:
88bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kClose_Verb:
89bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            case SkPath::kDone_Verb:
90bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                break;
91bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        }
92bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
93bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
94bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkVector normal;
95bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (compute_normal(fPositions[fPrevInnerIndex], fPositions[fFirstVertex], fRadius, fDirection,
96bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                       &normal)) {
97bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        this->addArc(normal);
98bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
99bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        // close out previous arc
100bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fPositions.push() = fPositions[fPrevInnerIndex] + normal;
101bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fColors.push() = fPenumbraColor;
102bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPrevInnerIndex;
103bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPositions.count() - 2;
104bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPositions.count() - 1;
105bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
106bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        // add final edge
107bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fPositions.push() = fPositions[fFirstVertex] + normal;
108bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fColors.push() = fPenumbraColor;
109bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
110bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPrevInnerIndex;
111bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPositions.count() - 2;
112bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fFirstVertex;
113bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
114bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPositions.count() - 2;
115bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPositions.count() - 1;
116bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fFirstVertex;
117bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
118bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
119bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // finalize centroid
120bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (fTransparent) {
121bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fPositions[0] *= SkScalarFastInvert(fCentroidCount);
122bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
123bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = 0;
124bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPrevInnerIndex;
125bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fFirstVertex;
126bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
127bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
128bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // final fan
129bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (fPositions.count() >= 3) {
130bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fPrevInnerIndex = fFirstVertex;
131bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fPrevNormal = normal;
132bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        this->addArc(fFirstNormal);
133bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
134bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fFirstVertex;
135bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPositions.count() - 1;
136bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fFirstVertex + 1;
137bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
138bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
139bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
140bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth// tesselation tolerance values, in device space pixels
141bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verthstatic const SkScalar kQuadTolerance = 0.2f;
142bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verthstatic const SkScalar kCubicTolerance = 0.2f;
143bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verthstatic const SkScalar kConicTolerance = 0.5f;
144bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
14558abc9e2c56464336472493745e91133819deb96Jim Van Verthvoid GrAmbientShadowTessellator::handleLine(const SkPoint& p)  {
146bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (fInitPoints.count() < 2) {
147bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fInitPoints.push() = p;
148bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        return;
149bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
150bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
151bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (fInitPoints.count() == 2) {
152bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        // determine if cw or ccw
153bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        SkVector v0 = fInitPoints[1] - fInitPoints[0];
154bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        SkVector v1 = p - fInitPoints[0];
155bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        SkScalar perpDot = v0.fX*v1.fY - v0.fY*v1.fX;
156bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        if (SkScalarNearlyZero(perpDot)) {
157bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            // nearly parallel, just treat as straight line and continue
158bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            fInitPoints[1] = p;
159bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            return;
160bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        }
161bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
162bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        // if perpDot > 0, winding is ccw
163bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fDirection = (perpDot > 0) ? -1 : 1;
164bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
165bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        // add first quad
166bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        if (!compute_normal(fInitPoints[0], fInitPoints[1], fRadius, fDirection,
167bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                            &fFirstNormal)) {
168bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            // first two points are incident, make the third point the second and continue
169bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            fInitPoints[1] = p;
170bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            return;
171bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        }
172bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
173bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fFirstVertex = fPositions.count();
174bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fPrevNormal = fFirstNormal;
175bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fPrevInnerIndex = fFirstVertex;
176bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
177bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fPositions.push() = fInitPoints[0];
178bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fColors.push() = fUmbraColor;
179bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fPositions.push() = fInitPoints[0] + fFirstNormal;
180bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fColors.push() = fPenumbraColor;
181bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        if (fTransparent) {
182bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            fPositions[0] += fInitPoints[0];
183bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth            fCentroidCount = 1;
184bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        }
185bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        this->addEdge(fInitPoints[1], fFirstNormal);
186bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
187bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        // to ensure we skip this block next time
188bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fInitPoints.push() = p;
189bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
190bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
191bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkVector normal;
192bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (compute_normal(fPositions[fPrevInnerIndex], p, fRadius, fDirection, &normal)) {
193bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        this->addArc(normal);
19491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        this->finishArcAndAddEdge(p, normal);
195bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
196bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
197bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
19858abc9e2c56464336472493745e91133819deb96Jim Van Verthvoid GrAmbientShadowTessellator::handleQuad(const SkPoint pts[3]) {
199bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
200bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fPointBuffer.setReserve(maxCount);
201bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkPoint* target = fPointBuffer.begin();
202bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
203bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                                                     kQuadTolerance, &target, maxCount);
204bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fPointBuffer.setCount(count);
205bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    for (int i = 0; i < count; i++) {
206bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        this->handleLine(fPointBuffer[i]);
207bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
208bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
209bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
21091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrAmbientShadowTessellator::handleCubic(SkPoint pts[4]) {
211bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
212bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fPointBuffer.setReserve(maxCount);
213bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkPoint* target = fPointBuffer.begin();
214bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
215bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                                                 kCubicTolerance, &target, maxCount);
216bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fPointBuffer.setCount(count);
217bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    for (int i = 0; i < count; i++) {
218bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        this->handleLine(fPointBuffer[i]);
219bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
220bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
221bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
22291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrAmbientShadowTessellator::handleConic(SkPoint pts[3], SkScalar w) {
223bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkAutoConicToQuads quadder;
224bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
225bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkPoint lastPoint = *(quads++);
226bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    int count = quadder.countQuads();
227bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    for (int i = 0; i < count; ++i) {
228bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        SkPoint quadPts[3];
229bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        quadPts[0] = lastPoint;
230bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        quadPts[1] = quads[0];
231bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        quadPts[2] = i == count - 1 ? pts[2] : quads[1];
232bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        this->handleQuad(quadPts);
233bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        lastPoint = quadPts[2];
234bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        quads += 2;
235bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
236bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
237bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
23858abc9e2c56464336472493745e91133819deb96Jim Van Verthvoid GrAmbientShadowTessellator::addArc(const SkVector& nextNormal) {
239bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // fill in fan from previous quad
240bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkScalar rotSin, rotCos;
241bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    int numSteps;
242bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    compute_radial_steps(fPrevNormal, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
243bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    SkVector prevNormal = fPrevNormal;
244bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    for (int i = 0; i < numSteps; ++i) {
245bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        SkVector nextNormal;
246bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        nextNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
247bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        nextNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
248bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fPositions.push() = fPositions[fPrevInnerIndex] + nextNormal;
249bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fColors.push() = fPenumbraColor;
250bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPrevInnerIndex;
251bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPositions.count() - 2;
252bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPositions.count() - 1;
253bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
254bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        prevNormal = nextNormal;
255bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
256bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
257bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
25858abc9e2c56464336472493745e91133819deb96Jim Van Verthvoid GrAmbientShadowTessellator::finishArcAndAddEdge(const SkPoint& nextPoint,
259bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth                                                     const SkVector& nextNormal) {
260bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // close out previous arc
261bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fPositions.push() = fPositions[fPrevInnerIndex] + nextNormal;
262bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fColors.push() = fPenumbraColor;
263bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fIndices.push() = fPrevInnerIndex;
264bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fIndices.push() = fPositions.count() - 2;
265bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fIndices.push() = fPositions.count() - 1;
266bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
267bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    this->addEdge(nextPoint, nextNormal);
268bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
269bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
27058abc9e2c56464336472493745e91133819deb96Jim Van Verthvoid GrAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
271bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // add next quad
272bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fPositions.push() = nextPoint;
273bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fColors.push() = fUmbraColor;
274bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fPositions.push() = nextPoint + nextNormal;
275bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fColors.push() = fPenumbraColor;
276bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
277bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fIndices.push() = fPrevInnerIndex;
278bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fIndices.push() = fPositions.count() - 3;
279bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fIndices.push() = fPositions.count() - 2;
280bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
281bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fIndices.push() = fPositions.count() - 3;
282bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fIndices.push() = fPositions.count() - 1;
283bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    *fIndices.push() = fPositions.count() - 2;
284bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
285bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    // if transparent, add point to first one in array and add to center fan
286bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    if (fTransparent) {
287bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        fPositions[0] += nextPoint;
288bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        ++fCentroidCount;
289bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
290bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = 0;
291bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPrevInnerIndex;
292bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth        *fIndices.push() = fPositions.count() - 2;
293bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    }
294bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth
295bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fPrevInnerIndex = fPositions.count() - 2;
296bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth    fPrevNormal = nextNormal;
297bce7496d7dd9131cc7121389a55f6d512ee7661eJim Van Verth}
29891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
29991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth///////////////////////////////////////////////////////////////////////////////////////////////////
30091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
30191af72703830f3946c538b47c6c7c96afc0adde2Jim Van VerthGrSpotShadowTessellator::GrSpotShadowTessellator(const SkPath& path,
30291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                                                 SkScalar scale, const SkVector& translate,
30391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                                                 SkScalar radius,
30491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                                                 GrColor umbraColor, GrColor penumbraColor,
30591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                                                 bool /* transparent */)
30691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    : fRadius(radius)
30791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    , fUmbraColor(umbraColor)
30891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    , fPenumbraColor(penumbraColor)
30991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    , fPrevInnerIndex(-1) {
31091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
31191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // TODO: calculate these better
31291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // Outer ring: 3*numPts
31391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // Inner ring: numPts
31491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fPositions.setReserve(4 * path.countPoints());
31591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fColors.setReserve(4 * path.countPoints());
31691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // Outer ring: 12*numPts
31791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // Inner ring: 0
31891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fIndices.setReserve(12 * path.countPoints());
31991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
32091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fInitPoints.setReserve(3);
32191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
32291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fClipPolygon.setReserve(path.countPoints());
32391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    this->computeClipBounds(path);
32491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fCentroid *= scale;
32591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fCentroid += translate;
32691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
32791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // walk around the path, tessellate and generate inner and outer rings
32891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPath::Iter iter(path, true);
32991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPoint pts[4];
33091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPath::Verb verb;
33191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fPositions.push() = fCentroid;
33291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fColors.push() = fUmbraColor;
33391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
33491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        switch (verb) {
33591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kLine_Verb:
33691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                this->handleLine(scale, translate, pts[1]);
33791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
33891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kQuad_Verb:
33991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                this->handleQuad(scale, translate, pts);
34091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
34191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kCubic_Verb:
34291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                this->handleCubic(scale, translate, pts);
34391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
34491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kConic_Verb:
34591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                this->handleConic(scale, translate, pts, iter.conicWeight());
34691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
34791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kMove_Verb:
34891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kClose_Verb:
34991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kDone_Verb:
35091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
35191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        }
35291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
35391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
35491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkVector normal;
35591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    if (compute_normal(fPrevPoint, fFirstPoint, fRadius, fDirection,
35691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                        &normal)) {
35791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        this->addArc(normal);
35891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
35991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // close out previous arc
36091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fPositions.push() = fPrevPoint + normal;
36191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fColors.push() = fPenumbraColor;
36291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPrevInnerIndex;
36391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPositions.count() - 2;
36491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPositions.count() - 1;
36591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
36691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // add final edge
36791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fPositions.push() = fFirstPoint + normal;
36891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fColors.push() = fPenumbraColor;
36991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
37091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPrevInnerIndex;
37191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPositions.count() - 2;
37291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fFirstVertex;
37391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
37491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPositions.count() - 2;
37591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPositions.count() - 1;
37691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fFirstVertex;
37791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
37891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // add to center fan
37991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = 0;
38091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPrevInnerIndex;
38191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fFirstVertex;
38291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
38391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
38491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // final fan
38591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    if (fPositions.count() >= 3) {
38691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fPrevInnerIndex = fFirstVertex;
38791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fPrevPoint = fFirstPoint;
38891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fPrevNormal = normal;
38991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        this->addArc(fFirstNormal);
39091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
39191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fFirstVertex;
39291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPositions.count() - 1;
39391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fFirstVertex + 1;
39491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
39591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
39691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
39791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrSpotShadowTessellator::computeClipBounds(const SkPath& path) {
39891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // walk around the path and compute clip polygon
39991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // if original path is transparent, will accumulate sum of points for centroid
40091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPath::Iter iter(path, true);
40191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPoint pts[4];
40291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPath::Verb verb;
40391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
40491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fCentroid = SkPoint::Make(0, 0);
40591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    int centroidCount = 0;
40691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fClipPolygon.reset();
40791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
40891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
40991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        switch (verb) {
41091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kMove_Verb:
41191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
41291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kLine_Verb:
41391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                fCentroid += pts[1];
41491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                centroidCount++;
41591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                *fClipPolygon.push() = pts[1];
41691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
41791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kQuad_Verb:
41891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                fCentroid += pts[1];
41991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                fCentroid += pts[2];
42091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                centroidCount += 2;
42191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                *fClipPolygon.push() = pts[2];
42291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
42391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kConic_Verb:
42491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                fCentroid += pts[1];
42591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                fCentroid += pts[2];
42691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                centroidCount += 2;
42791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                *fClipPolygon.push() = pts[2];
42891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
42991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kCubic_Verb:
43091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                fCentroid += pts[1];
43191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                fCentroid += pts[2];
43291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                fCentroid += pts[3];
43391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                centroidCount += 3;
43491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                *fClipPolygon.push() = pts[3];
43591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
43691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            case SkPath::kClose_Verb:
43791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                break;
43891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            default:
43991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                SkDEBUGFAIL("unknown verb");
44091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        }
44191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
44291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
44391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fCentroid *= SkScalarInvert(centroidCount);
44491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
44591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
44691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
44791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                                        SkPoint* pts, int count) {
44891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // TODO: vectorize
44991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    for (int i = 0; i < count; ++i) {
45091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        pts[i] *= scale;
45191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        pts[i] += xlate;
45291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
45391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
45491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
45591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrSpotShadowTessellator::handleLine(const SkPoint& p) {
45691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    if (fInitPoints.count() < 2) {
45791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fInitPoints.push() = p;
45891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        return;
45991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
46091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
46191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    if (fInitPoints.count() == 2) {
46291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // determine if cw or ccw
46391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        SkVector v0 = fInitPoints[1] - fInitPoints[0];
46491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        SkVector v1 = p - fInitPoints[0];
46591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        SkScalar perpDot = v0.fX*v1.fY - v0.fY*v1.fX;
46691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        if (SkScalarNearlyZero(perpDot)) {
46791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            // nearly parallel, just treat as straight line and continue
46891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            fInitPoints[1] = p;
46991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            return;
47091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        }
47191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
47291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // if perpDot > 0, winding is ccw
47391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fDirection = (perpDot > 0) ? -1 : 1;
47491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
47591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // add first quad
47691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        if (!compute_normal(fInitPoints[0], fInitPoints[1], fRadius, fDirection,
47791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                            &fFirstNormal)) {
47891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            // first two points are incident, make the third point the second and continue
47991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            fInitPoints[1] = p;
48091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth            return;
48191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        }
48291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
48391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fFirstPoint = fInitPoints[0];
48491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fFirstVertex = fPositions.count();
48591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fPrevNormal = fFirstNormal;
48691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fPrevPoint = fFirstPoint;
48791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        fPrevInnerIndex = fFirstVertex;
48891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
48991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        this->addInnerPoint(fFirstPoint, fUmbraColor, fRadius);
49091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        SkPoint newPoint = fFirstPoint + fFirstNormal;
49191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fPositions.push() = newPoint;
49291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fColors.push() = fPenumbraColor;
49391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        this->addEdge(fInitPoints[1], fFirstNormal);
49491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
49591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // to ensure we skip this block next time
49691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fInitPoints.push() = p;
49791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
49891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
49991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkVector normal;
50091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    if (compute_normal(fPrevPoint, p, fRadius, fDirection, &normal)) {
50191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        this->addArc(normal);
50291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        this->finishArcAndAddEdge(p, normal);
50391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
50491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
50591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
50691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrSpotShadowTessellator::handleLine(SkScalar scale, const SkVector& xlate, SkPoint p) {
50791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    this->mapPoints(scale, xlate, &p, 1);
50891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    this->handleLine(p);
50991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
51091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
51191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrSpotShadowTessellator::handleQuad(const SkPoint pts[3]) {
51291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
51391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fPointBuffer.setReserve(maxCount);
51491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPoint* target = fPointBuffer.begin();
51591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
51691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                                                     kQuadTolerance, &target, maxCount);
51791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fPointBuffer.setCount(count);
51891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    for (int i = 0; i < count; i++) {
51991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        this->handleLine(fPointBuffer[i]);
52091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
52191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
52291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
52391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrSpotShadowTessellator::handleQuad(SkScalar scale, const SkVector& xlate, SkPoint pts[3]) {
52491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    this->mapPoints(scale, xlate, pts, 3);
52591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    this->handleQuad(pts);
52691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
52791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
52891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrSpotShadowTessellator::handleCubic(SkScalar scale, const SkVector& xlate, SkPoint pts[4]) {
52991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    this->mapPoints(scale, xlate, pts, 4);
53091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
53191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fPointBuffer.setReserve(maxCount);
53291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPoint* target = fPointBuffer.begin();
53391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
53491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                                                 kCubicTolerance, &target, maxCount);
53591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fPointBuffer.setCount(count);
53691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    for (int i = 0; i < count; i++) {
53791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        this->handleLine(fPointBuffer[i]);
53891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
53991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
54091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
54191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrSpotShadowTessellator::handleConic(SkScalar scale, const SkVector& xlate,
54291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                                          SkPoint pts[3], SkScalar w) {
54391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    this->mapPoints(scale, xlate, pts, 3);
54491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkAutoConicToQuads quadder;
54591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
54691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPoint lastPoint = *(quads++);
54791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    int count = quadder.countQuads();
54891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    for (int i = 0; i < count; ++i) {
54991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        SkPoint quadPts[3];
55091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        quadPts[0] = lastPoint;
55191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        quadPts[1] = quads[0];
55291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        quadPts[2] = i == count - 1 ? pts[2] : quads[1];
55391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        this->handleQuad(quadPts);
55491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        lastPoint = quadPts[2];
55591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        quads += 2;
55691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
55791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
55891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
55991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint, GrColor umbraColor,
56091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                                            SkScalar radius) {
56191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkVector v = fCentroid - pathPoint;
56291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkScalar distance = v.length();
56391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    if (distance < radius) {
56491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fPositions.push() = fCentroid;
56591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fColors.push() = umbraColor; // fix this
56691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        // TODO: deal with fanning from centroid
56791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    } else {
56891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        SkScalar t = radius / distance;
56991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        v *= t;
57091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        SkPoint innerPoint = pathPoint + v;
57191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fPositions.push() = innerPoint;
57291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fColors.push() = umbraColor;
57391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
57491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fPrevPoint = pathPoint;
57591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
57691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
57791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrSpotShadowTessellator::addArc(const SkVector& nextNormal) {
57891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // fill in fan from previous quad
57991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkScalar rotSin, rotCos;
58091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    int numSteps;
58191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    compute_radial_steps(fPrevNormal, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
58291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkVector prevNormal = fPrevNormal;
58391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    for (int i = 0; i < numSteps; ++i) {
58491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        SkVector nextNormal;
58591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        nextNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
58691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        nextNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
58791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fPositions.push() = fPrevPoint + nextNormal;
58891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fColors.push() = fPenumbraColor;
58991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPrevInnerIndex;
59091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPositions.count() - 2;
59191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        *fIndices.push() = fPositions.count() - 1;
59291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
59391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth        prevNormal = nextNormal;
59491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    }
59591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
59691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
59791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrSpotShadowTessellator::finishArcAndAddEdge(const SkPoint& nextPoint,
59891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth                                                  const SkVector& nextNormal) {
59991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // close out previous arc
60091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPoint newPoint = fPrevPoint + nextNormal;
60191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fPositions.push() = newPoint;
60291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fColors.push() = fPenumbraColor;
60391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = fPrevInnerIndex;
60491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = fPositions.count() - 2;
60591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = fPositions.count() - 1;
60691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
60791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    this->addEdge(nextPoint, nextNormal);
60891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
60991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
61091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verthvoid GrSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
61191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // add next quad
61291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    this->addInnerPoint(nextPoint, fUmbraColor, fRadius);
61391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    SkPoint newPoint = nextPoint + nextNormal;
61491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fPositions.push() = newPoint;
61591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fColors.push() = fPenumbraColor;
61691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
61791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = fPrevInnerIndex;
61891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = fPositions.count() - 3;
61991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = fPositions.count() - 2;
62091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
62191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = fPositions.count() - 3;
62291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = fPositions.count() - 1;
62391af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = fPositions.count() - 2;
62491af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
62591af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    // add to center fan
62691af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = 0;
62791af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = fPrevInnerIndex;
62891af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    *fIndices.push() = fPositions.count() - 2;
62991af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth
63091af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fPrevInnerIndex = fPositions.count() - 2;
63191af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth    fPrevNormal = nextNormal;
63291af72703830f3946c538b47c6c7c96afc0adde2Jim Van Verth}
633