blob: 60749d81c0108748532e1749a95f3ba98c9d00b9 [file] [log] [blame]
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +00001
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "GrAAConvexPathRenderer.h"
10
11#include "GrContext.h"
12#include "GrDrawState.h"
13#include "GrPathUtils.h"
14#include "SkString.h"
15#include "SkTrace.h"
16
17
18GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
19}
20
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000021namespace {
22
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000023struct Segment {
24 enum {
25 kLine,
26 kQuad
27 } fType;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000028 // line uses one pt, quad uses 2 pts
29 GrPoint fPts[2];
30 // normal to edge ending at each pt
31 GrVec fNorms[2];
32 // is the corner where the previous segment meets this segment
33 // sharp. If so, fMid is a normalized bisector facing outward.
34 GrVec fMid;
35
36 int countPoints() {
37 return (kLine == fType) ? 1 : 2;
38 }
39 const SkPoint& endPt() const {
40 return (kLine == fType) ? fPts[0] : fPts[1];
41 };
42 const SkPoint& endNorm() const {
43 return (kLine == fType) ? fNorms[0] : fNorms[1];
44 };
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000045};
46
47typedef SkTArray<Segment, true> SegmentArray;
48
bsalomon@google.com9aed1142012-01-30 14:28:39 +000049void center_of_mass(const SegmentArray& segments, SkPoint* c) {
50 GrScalar area = 0;
51 SkPoint center;
52 center.set(0, 0);
53 int count = segments.count();
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000054 SkPoint p0;
55 if (count > 2) {
56 // We translate the polygon so that the first point is at the origin.
57 // This avoids some precision issues with small area polygons far away
58 // from the origin.
59 p0 = segments[0].endPt();
60 SkPoint pi;
61 SkPoint pj;
62 // the first and last interation of the below loop would compute
63 // zeros since the starting / ending point is (0,0). So instead we start
64 // at i=1 and make the last iteration i=count-2.
65 pj = segments[1].endPt() - p0;
66 for (int i = 1; i < count - 1; ++i) {
67 pi = pj;
68 const SkPoint pj = segments[i + 1].endPt() - p0;
69
70 GrScalar t = GrMul(pi.fX, pj.fY) - GrMul(pj.fX, pi.fY);
71 area += t;
72 center.fX += (pi.fX + pj.fX) * t;
73 center.fY += (pi.fY + pj.fY) * t;
74
75 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000076 }
bsalomon@google.com278dc692012-02-15 16:52:51 +000077 // If the poly has no area then we instead return the average of
78 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000079 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +000080 SkPoint avg;
81 avg.set(0, 0);
82 for (int i = 0; i < count; ++i) {
83 const SkPoint& pt = segments[i].endPt();
84 avg.fX += pt.fX;
85 avg.fY += pt.fY;
86 }
87 SkScalar denom = SK_Scalar1 / count;
88 avg.scale(denom);
89 *c = avg;
90 } else {
91 area *= 3;
92 area = GrScalarDiv(GR_Scalar1, area);
93 center.fX = GrScalarMul(center.fX, area);
94 center.fY = GrScalarMul(center.fY, area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000095 // undo the translate of p0 to the origin.
96 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +000097 }
98 GrAssert(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
bsalomon@google.com9aed1142012-01-30 14:28:39 +000099}
100
101void compute_vectors(SegmentArray* segments,
bsalomon@google.com278dc692012-02-15 16:52:51 +0000102 SkPoint* fanPt,
103 SkPath::Direction dir,
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000104 int* vCount,
105 int* iCount) {
106 center_of_mass(*segments, fanPt);
107 int count = segments->count();
108
bsalomon@google.com278dc692012-02-15 16:52:51 +0000109 // Make the normals point towards the outside
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000110 GrPoint::Side normSide;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000111 if (dir == SkPath::kCCW_Direction) {
112 normSide = GrPoint::kRight_Side;
113 } else {
114 normSide = GrPoint::kLeft_Side;
115 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000116
117 *vCount = 0;
118 *iCount = 0;
119 // compute normals at all points
120 for (int a = 0; a < count; ++a) {
121 const Segment& sega = (*segments)[a];
122 int b = (a + 1) % count;
123 Segment& segb = (*segments)[b];
124
125 const GrPoint* prevPt = &sega.endPt();
126 int n = segb.countPoints();
127 for (int p = 0; p < n; ++p) {
128 segb.fNorms[p] = segb.fPts[p] - *prevPt;
129 segb.fNorms[p].normalize();
130 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
131 prevPt = &segb.fPts[p];
132 }
133 if (Segment::kLine == segb.fType) {
134 *vCount += 5;
135 *iCount += 9;
136 } else {
137 *vCount += 6;
138 *iCount += 12;
139 }
140 }
141
142 // compute mid-vectors where segments meet. TODO: Detect shallow corners
143 // and leave out the wedges and close gaps by stitching segments together.
144 for (int a = 0; a < count; ++a) {
145 const Segment& sega = (*segments)[a];
146 int b = (a + 1) % count;
147 Segment& segb = (*segments)[b];
148 segb.fMid = segb.fNorms[0] + sega.endNorm();
149 segb.fMid.normalize();
150 // corner wedges
151 *vCount += 4;
152 *iCount += 6;
153 }
154}
155
bsalomon@google.com9732f622012-01-31 15:19:21 +0000156struct DegenerateTestData {
157 DegenerateTestData() { fStage = kInitial; }
158 bool isDegenerate() const { return kNonDegenerate != fStage; }
159 enum {
160 kInitial,
161 kPoint,
162 kLine,
163 kNonDegenerate
164 } fStage;
165 GrPoint fFirstPoint;
166 GrVec fLineNormal;
167 GrScalar fLineC;
168};
169
170void update_degenerate_test(DegenerateTestData* data, const GrPoint& pt) {
171 static const SkScalar TOL = (SK_Scalar1 / 16);
172 static const SkScalar TOL_SQD = SkScalarMul(TOL, TOL);
173
174 switch (data->fStage) {
175 case DegenerateTestData::kInitial:
176 data->fFirstPoint = pt;
177 data->fStage = DegenerateTestData::kPoint;
178 break;
179 case DegenerateTestData::kPoint:
180 if (pt.distanceToSqd(data->fFirstPoint) > TOL_SQD) {
181 data->fLineNormal = pt - data->fFirstPoint;
182 data->fLineNormal.normalize();
183 data->fLineNormal.setOrthog(data->fLineNormal);
184 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
185 data->fStage = DegenerateTestData::kLine;
186 }
187 break;
188 case DegenerateTestData::kLine:
189 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > TOL) {
190 data->fStage = DegenerateTestData::kNonDegenerate;
191 }
192 case DegenerateTestData::kNonDegenerate:
193 break;
194 default:
195 GrCrash("Unexpected degenerate test stage.");
196 }
197}
198
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000199bool get_segments(const GrPath& path,
200 SegmentArray* segments,
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000201 SkPoint* fanPt,
202 int* vCount,
203 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000204 SkPath::Iter iter(path, true);
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000205 // This renderer overemphasises very thin path regions. We use the distance
206 // to the path from the sample to compute coverage. Every pixel intersected
207 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't
208 // notice that the sample may be close to a very thin area of the path and
209 // thus should be very light. This is particularly egregious for degenerate
210 // line paths. We detect paths that are very close to a line (zero area) and
211 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000212 DegenerateTestData degenerateData;
213
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000214 for (;;) {
215 GrPoint pts[4];
216 GrPathCmd cmd = (GrPathCmd)iter.next(pts);
217 switch (cmd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000218 case kMove_PathCmd:
219 update_degenerate_test(&degenerateData, pts[0]);
220 break;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000221 case kLine_PathCmd: {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000222 update_degenerate_test(&degenerateData, pts[1]);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000223 segments->push_back();
224 segments->back().fType = Segment::kLine;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000225 segments->back().fPts[0] = pts[1];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000226 break;
227 }
228 case kQuadratic_PathCmd:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000229 update_degenerate_test(&degenerateData, pts[1]);
230 update_degenerate_test(&degenerateData, pts[2]);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000231 segments->push_back();
232 segments->back().fType = Segment::kQuad;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000233 segments->back().fPts[0] = pts[1];
234 segments->back().fPts[1] = pts[2];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000235 break;
236 case kCubic_PathCmd: {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000237 update_degenerate_test(&degenerateData, pts[1]);
238 update_degenerate_test(&degenerateData, pts[2]);
239 update_degenerate_test(&degenerateData, pts[3]);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000240 SkSTArray<15, SkPoint, true> quads;
241 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, &quads);
242 int count = quads.count();
243 for (int q = 0; q < count; q += 3) {
244 segments->push_back();
245 segments->back().fType = Segment::kQuad;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000246 segments->back().fPts[0] = quads[q + 1];
247 segments->back().fPts[1] = quads[q + 2];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000248 }
249 break;
250 };
251 case kEnd_PathCmd:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000252 if (degenerateData.isDegenerate()) {
253 return false;
254 } else {
bsalomon@google.com278dc692012-02-15 16:52:51 +0000255 SkPath::Direction dir;
256 GR_DEBUGCODE(bool succeeded = )
257 path.cheapComputeDirection(&dir);
258 GrAssert(succeeded);
259 compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000260 return true;
261 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000262 default:
263 break;
264 }
265 }
266}
267
268struct QuadVertex {
269 GrPoint fPos;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000270 GrPoint fUV;
271 GrScalar fD0;
272 GrScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000273};
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000274
275void create_vertices(const SegmentArray& segments,
276 const SkPoint& fanPt,
277 QuadVertex* verts,
278 uint16_t* idxs) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000279 int v = 0;
280 int i = 0;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000281
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000282 int count = segments.count();
283 for (int a = 0; a < count; ++a) {
284 const Segment& sega = segments[a];
285 int b = (a + 1) % count;
286 const Segment& segb = segments[b];
287
288 // FIXME: These tris are inset in the 1 unit arc around the corner
289 verts[v + 0].fPos = sega.endPt();
290 verts[v + 1].fPos = verts[v + 0].fPos + sega.endNorm();
291 verts[v + 2].fPos = verts[v + 0].fPos + segb.fMid;
292 verts[v + 3].fPos = verts[v + 0].fPos + segb.fNorms[0];
293 verts[v + 0].fUV.set(0,0);
294 verts[v + 1].fUV.set(0,-SK_Scalar1);
295 verts[v + 2].fUV.set(0,-SK_Scalar1);
296 verts[v + 3].fUV.set(0,-SK_Scalar1);
297 verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
298 verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
299 verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
300 verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
301
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000302 idxs[i + 0] = v + 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000303 idxs[i + 1] = v + 2;
304 idxs[i + 2] = v + 1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000305 idxs[i + 3] = v + 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000306 idxs[i + 4] = v + 3;
307 idxs[i + 5] = v + 2;
308
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000309 v += 4;
310 i += 6;
311
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000312 if (Segment::kLine == segb.fType) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000313 verts[v + 0].fPos = fanPt;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000314 verts[v + 1].fPos = sega.endPt();
315 verts[v + 2].fPos = segb.fPts[0];
316
317 verts[v + 3].fPos = verts[v + 1].fPos + segb.fNorms[0];
318 verts[v + 4].fPos = verts[v + 2].fPos + segb.fNorms[0];
319
320 // we draw the line edge as a degenerate quad (u is 0, v is the
321 // signed distance to the edge)
322 GrScalar dist = fanPt.distanceToLineBetween(verts[v + 1].fPos,
323 verts[v + 2].fPos);
324 verts[v + 0].fUV.set(0, dist);
325 verts[v + 1].fUV.set(0, 0);
326 verts[v + 2].fUV.set(0, 0);
327 verts[v + 3].fUV.set(0, -SK_Scalar1);
328 verts[v + 4].fUV.set(0, -SK_Scalar1);
329
330 verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
331 verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
332 verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
333 verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
334 verts[v + 4].fD0 = verts[v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000335
336 idxs[i + 0] = v + 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000337 idxs[i + 1] = v + 2;
338 idxs[i + 2] = v + 1;
339
340 idxs[i + 3] = v + 3;
341 idxs[i + 4] = v + 1;
342 idxs[i + 5] = v + 2;
343
344 idxs[i + 6] = v + 4;
345 idxs[i + 7] = v + 3;
bsalomon@google.com06809612012-01-21 15:03:39 +0000346 idxs[i + 8] = v + 2;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000347
bsalomon@google.com06809612012-01-21 15:03:39 +0000348 v += 5;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000349 i += 9;
bsalomon@google.com06809612012-01-21 15:03:39 +0000350 } else {
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000351 GrPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000352
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000353 GrVec midVec = segb.fNorms[0] + segb.fNorms[1];
354 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000355
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000356 verts[v + 0].fPos = fanPt;
357 verts[v + 1].fPos = qpts[0];
358 verts[v + 2].fPos = qpts[2];
359 verts[v + 3].fPos = qpts[0] + segb.fNorms[0];
360 verts[v + 4].fPos = qpts[2] + segb.fNorms[1];
361 verts[v + 5].fPos = qpts[1] + midVec;
362
363 GrScalar c = segb.fNorms[0].dot(qpts[0]);
364 verts[v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
365 verts[v + 1].fD0 = 0.f;
366 verts[v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
367 verts[v + 3].fD0 = -GR_ScalarMax/100;
368 verts[v + 4].fD0 = -GR_ScalarMax/100;
369 verts[v + 5].fD0 = -GR_ScalarMax/100;
370
371 c = segb.fNorms[1].dot(qpts[2]);
372 verts[v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
373 verts[v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
374 verts[v + 2].fD1 = 0.f;
375 verts[v + 3].fD1 = -GR_ScalarMax/100;
376 verts[v + 4].fD1 = -GR_ScalarMax/100;
377 verts[v + 5].fD1 = -GR_ScalarMax/100;
378
bsalomon@google.com06809612012-01-21 15:03:39 +0000379 GrMatrix toUV;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000380 GrPathUtils::quadDesignSpaceToUVCoordsMatrix(qpts, &toUV);
381 toUV.mapPointsWithStride(&verts[v].fUV,
382 &verts[v].fPos,
383 sizeof(QuadVertex),
384 6);
bsalomon@google.com06809612012-01-21 15:03:39 +0000385
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000386 idxs[i + 0] = v + 3;
387 idxs[i + 1] = v + 1;
388 idxs[i + 2] = v + 2;
389 idxs[i + 3] = v + 4;
390 idxs[i + 4] = v + 3;
391 idxs[i + 5] = v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000392
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000393 idxs[i + 6] = v + 5;
394 idxs[i + 7] = v + 3;
395 idxs[i + 8] = v + 4;
396
397 idxs[i + 9] = v + 0;
398 idxs[i + 10] = v + 2;
399 idxs[i + 11] = v + 1;
400
401 v += 6;
402 i += 12;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000403 }
404 }
405}
406
407}
408
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000409bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
410 GrPathFill fill,
411 const GrDrawTarget* target,
412 bool antiAlias) const {
413 if (!target->getCaps().fShaderDerivativeSupport || !antiAlias ||
414 kHairLine_PathFill == fill || GrIsFillInverted(fill) ||
415 !path.isConvex()) {
416 return false;
417 } else {
418 return true;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000419 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000420}
421
422bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath,
423 GrPathFill fill,
424 const GrVec* translate,
425 GrDrawTarget* target,
426 GrDrawState::StageMask stageMask,
427 bool antiAlias) {
428
429
430 if (origPath.isEmpty()) {
431 return true;
432 }
433 GrDrawState* drawState = target->drawState();
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000434
435 GrDrawTarget::AutoStateRestore asr;
436 GrMatrix vm = drawState->getViewMatrix();
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000437 if (NULL != translate) {
438 vm.postTranslate(translate->fX, translate->fY);
439 }
440 asr.set(target);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000441 GrMatrix ivm;
442 if (vm.invert(&ivm)) {
443 drawState->preConcatSamplerMatrices(stageMask, ivm);
444 }
445 drawState->setViewMatrix(GrMatrix::I());
446
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000447 SkPath path;
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000448 origPath.transform(vm, &path);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000449
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000450 GrVertexLayout layout = 0;
451 for (int s = 0; s < GrDrawState::kNumStages; ++s) {
452 if ((1 << s) & stageMask) {
453 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
454 }
455 }
456 layout |= GrDrawTarget::kEdge_VertexLayoutBit;
457
458 QuadVertex *verts;
459 uint16_t* idxs;
460
bsalomon@google.com06809612012-01-21 15:03:39 +0000461 int vCount;
462 int iCount;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000463 SegmentArray segments;
464 SkPoint fanPt;
465 if (!get_segments(path, &segments, &fanPt, &vCount, &iCount)) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000466 return false;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000467 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000468
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000469 if (!target->reserveVertexSpace(layout,
470 vCount,
471 reinterpret_cast<void**>(&verts))) {
472 return false;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000473 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000474 if (!target->reserveIndexSpace(iCount, reinterpret_cast<void**>(&idxs))) {
475 target->resetVertexSource();
476 return false;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000477 }
478
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000479 create_vertices(segments, fanPt, verts, idxs);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000480
481 drawState->setVertexEdgeType(GrDrawState::kQuad_EdgeType);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000482 target->drawIndexed(kTriangles_PrimitiveType,
483 0, // start vertex
484 0, // start index
485 vCount,
486 iCount);
487 return true;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000488}
489