blob: 18965b7ec5bb6a282a7c9854d2cb91ee72cc9b57 [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 {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000025 // These enum values are assumed in member functions below.
26 kLine = 0,
27 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000028 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000029
bsalomon@google.com9aed1142012-01-30 14:28:39 +000030 // line uses one pt, quad uses 2 pts
31 GrPoint fPts[2];
32 // normal to edge ending at each pt
33 GrVec fNorms[2];
34 // is the corner where the previous segment meets this segment
35 // sharp. If so, fMid is a normalized bisector facing outward.
36 GrVec fMid;
37
38 int countPoints() {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000039 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
40 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000041 }
42 const SkPoint& endPt() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000043 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
44 return fPts[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000045 };
46 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000047 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
48 return fNorms[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000049 };
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000050};
51
52typedef SkTArray<Segment, true> SegmentArray;
53
bsalomon@google.com9aed1142012-01-30 14:28:39 +000054void center_of_mass(const SegmentArray& segments, SkPoint* c) {
55 GrScalar area = 0;
56 SkPoint center;
57 center.set(0, 0);
58 int count = segments.count();
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000059 SkPoint p0;
60 if (count > 2) {
61 // We translate the polygon so that the first point is at the origin.
62 // This avoids some precision issues with small area polygons far away
63 // from the origin.
64 p0 = segments[0].endPt();
65 SkPoint pi;
66 SkPoint pj;
67 // the first and last interation of the below loop would compute
68 // zeros since the starting / ending point is (0,0). So instead we start
69 // at i=1 and make the last iteration i=count-2.
70 pj = segments[1].endPt() - p0;
71 for (int i = 1; i < count - 1; ++i) {
72 pi = pj;
73 const SkPoint pj = segments[i + 1].endPt() - p0;
74
75 GrScalar t = GrMul(pi.fX, pj.fY) - GrMul(pj.fX, pi.fY);
76 area += t;
77 center.fX += (pi.fX + pj.fX) * t;
78 center.fY += (pi.fY + pj.fY) * t;
79
80 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000081 }
bsalomon@google.com278dc692012-02-15 16:52:51 +000082 // If the poly has no area then we instead return the average of
83 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000084 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +000085 SkPoint avg;
86 avg.set(0, 0);
87 for (int i = 0; i < count; ++i) {
88 const SkPoint& pt = segments[i].endPt();
89 avg.fX += pt.fX;
90 avg.fY += pt.fY;
91 }
92 SkScalar denom = SK_Scalar1 / count;
93 avg.scale(denom);
94 *c = avg;
95 } else {
96 area *= 3;
97 area = GrScalarDiv(GR_Scalar1, area);
98 center.fX = GrScalarMul(center.fX, area);
99 center.fY = GrScalarMul(center.fY, area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +0000100 // undo the translate of p0 to the origin.
101 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000102 }
103 GrAssert(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000104}
105
106void compute_vectors(SegmentArray* segments,
bsalomon@google.com278dc692012-02-15 16:52:51 +0000107 SkPoint* fanPt,
108 SkPath::Direction dir,
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000109 int* vCount,
110 int* iCount) {
111 center_of_mass(*segments, fanPt);
112 int count = segments->count();
113
bsalomon@google.com278dc692012-02-15 16:52:51 +0000114 // Make the normals point towards the outside
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000115 GrPoint::Side normSide;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000116 if (dir == SkPath::kCCW_Direction) {
117 normSide = GrPoint::kRight_Side;
118 } else {
119 normSide = GrPoint::kLeft_Side;
120 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000121
122 *vCount = 0;
123 *iCount = 0;
124 // compute normals at all points
125 for (int a = 0; a < count; ++a) {
126 const Segment& sega = (*segments)[a];
127 int b = (a + 1) % count;
128 Segment& segb = (*segments)[b];
129
130 const GrPoint* prevPt = &sega.endPt();
131 int n = segb.countPoints();
132 for (int p = 0; p < n; ++p) {
133 segb.fNorms[p] = segb.fPts[p] - *prevPt;
134 segb.fNorms[p].normalize();
135 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
136 prevPt = &segb.fPts[p];
137 }
138 if (Segment::kLine == segb.fType) {
139 *vCount += 5;
140 *iCount += 9;
141 } else {
142 *vCount += 6;
143 *iCount += 12;
144 }
145 }
146
147 // compute mid-vectors where segments meet. TODO: Detect shallow corners
148 // and leave out the wedges and close gaps by stitching segments together.
149 for (int a = 0; a < count; ++a) {
150 const Segment& sega = (*segments)[a];
151 int b = (a + 1) % count;
152 Segment& segb = (*segments)[b];
153 segb.fMid = segb.fNorms[0] + sega.endNorm();
154 segb.fMid.normalize();
155 // corner wedges
156 *vCount += 4;
157 *iCount += 6;
158 }
159}
160
bsalomon@google.com9732f622012-01-31 15:19:21 +0000161struct DegenerateTestData {
162 DegenerateTestData() { fStage = kInitial; }
163 bool isDegenerate() const { return kNonDegenerate != fStage; }
164 enum {
165 kInitial,
166 kPoint,
167 kLine,
168 kNonDegenerate
169 } fStage;
170 GrPoint fFirstPoint;
171 GrVec fLineNormal;
172 GrScalar fLineC;
173};
174
175void update_degenerate_test(DegenerateTestData* data, const GrPoint& pt) {
176 static const SkScalar TOL = (SK_Scalar1 / 16);
177 static const SkScalar TOL_SQD = SkScalarMul(TOL, TOL);
178
179 switch (data->fStage) {
180 case DegenerateTestData::kInitial:
181 data->fFirstPoint = pt;
182 data->fStage = DegenerateTestData::kPoint;
183 break;
184 case DegenerateTestData::kPoint:
185 if (pt.distanceToSqd(data->fFirstPoint) > TOL_SQD) {
186 data->fLineNormal = pt - data->fFirstPoint;
187 data->fLineNormal.normalize();
188 data->fLineNormal.setOrthog(data->fLineNormal);
189 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
190 data->fStage = DegenerateTestData::kLine;
191 }
192 break;
193 case DegenerateTestData::kLine:
194 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > TOL) {
195 data->fStage = DegenerateTestData::kNonDegenerate;
196 }
197 case DegenerateTestData::kNonDegenerate:
198 break;
199 default:
200 GrCrash("Unexpected degenerate test stage.");
201 }
202}
203
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000204bool get_segments(const GrPath& path,
bsalomon@google.comc759af32012-03-05 19:55:43 +0000205 const GrMatrix& m,
206 SegmentArray* segments,
207 SkPoint* fanPt,
208 int* vCount,
209 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000210 SkPath::Iter iter(path, true);
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000211 // This renderer overemphasises very thin path regions. We use the distance
212 // to the path from the sample to compute coverage. Every pixel intersected
213 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't
214 // notice that the sample may be close to a very thin area of the path and
215 // thus should be very light. This is particularly egregious for degenerate
216 // line paths. We detect paths that are very close to a line (zero area) and
217 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000218 DegenerateTestData degenerateData;
219
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000220 for (;;) {
221 GrPoint pts[4];
222 GrPathCmd cmd = (GrPathCmd)iter.next(pts);
223 switch (cmd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000224 case kMove_PathCmd:
225 update_degenerate_test(&degenerateData, pts[0]);
226 break;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000227 case kLine_PathCmd: {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000228 update_degenerate_test(&degenerateData, pts[1]);
bsalomon@google.comc759af32012-03-05 19:55:43 +0000229 m.mapPoints(pts + 1, 1);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000230 segments->push_back();
231 segments->back().fType = Segment::kLine;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000232 segments->back().fPts[0] = pts[1];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000233 break;
234 }
235 case kQuadratic_PathCmd:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000236 update_degenerate_test(&degenerateData, pts[1]);
237 update_degenerate_test(&degenerateData, pts[2]);
bsalomon@google.comc759af32012-03-05 19:55:43 +0000238 m.mapPoints(pts + 1, 2);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000239 segments->push_back();
240 segments->back().fType = Segment::kQuad;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000241 segments->back().fPts[0] = pts[1];
242 segments->back().fPts[1] = pts[2];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000243 break;
244 case kCubic_PathCmd: {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000245 update_degenerate_test(&degenerateData, pts[1]);
246 update_degenerate_test(&degenerateData, pts[2]);
247 update_degenerate_test(&degenerateData, pts[3]);
bsalomon@google.comc759af32012-03-05 19:55:43 +0000248 // unlike quads and lines, the pts[0] will also be read (in
249 // convertCubicToQuads).
250 m.mapPoints(pts, 4);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000251 SkSTArray<15, SkPoint, true> quads;
252 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, &quads);
253 int count = quads.count();
254 for (int q = 0; q < count; q += 3) {
255 segments->push_back();
256 segments->back().fType = Segment::kQuad;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000257 segments->back().fPts[0] = quads[q + 1];
258 segments->back().fPts[1] = quads[q + 2];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000259 }
260 break;
261 };
262 case kEnd_PathCmd:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000263 if (degenerateData.isDegenerate()) {
264 return false;
265 } else {
bsalomon@google.com278dc692012-02-15 16:52:51 +0000266 SkPath::Direction dir;
267 GR_DEBUGCODE(bool succeeded = )
268 path.cheapComputeDirection(&dir);
269 GrAssert(succeeded);
270 compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000271 return true;
272 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000273 default:
274 break;
275 }
276 }
277}
278
279struct QuadVertex {
280 GrPoint fPos;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000281 GrPoint fUV;
282 GrScalar fD0;
283 GrScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000284};
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000285
286void create_vertices(const SegmentArray& segments,
287 const SkPoint& fanPt,
288 QuadVertex* verts,
289 uint16_t* idxs) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000290 int v = 0;
291 int i = 0;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000292
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000293 int count = segments.count();
294 for (int a = 0; a < count; ++a) {
295 const Segment& sega = segments[a];
296 int b = (a + 1) % count;
297 const Segment& segb = segments[b];
298
299 // FIXME: These tris are inset in the 1 unit arc around the corner
300 verts[v + 0].fPos = sega.endPt();
301 verts[v + 1].fPos = verts[v + 0].fPos + sega.endNorm();
302 verts[v + 2].fPos = verts[v + 0].fPos + segb.fMid;
303 verts[v + 3].fPos = verts[v + 0].fPos + segb.fNorms[0];
304 verts[v + 0].fUV.set(0,0);
305 verts[v + 1].fUV.set(0,-SK_Scalar1);
306 verts[v + 2].fUV.set(0,-SK_Scalar1);
307 verts[v + 3].fUV.set(0,-SK_Scalar1);
308 verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
309 verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
310 verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
311 verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
312
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000313 idxs[i + 0] = v + 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000314 idxs[i + 1] = v + 2;
315 idxs[i + 2] = v + 1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000316 idxs[i + 3] = v + 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000317 idxs[i + 4] = v + 3;
318 idxs[i + 5] = v + 2;
319
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000320 v += 4;
321 i += 6;
322
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000323 if (Segment::kLine == segb.fType) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000324 verts[v + 0].fPos = fanPt;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000325 verts[v + 1].fPos = sega.endPt();
326 verts[v + 2].fPos = segb.fPts[0];
327
328 verts[v + 3].fPos = verts[v + 1].fPos + segb.fNorms[0];
329 verts[v + 4].fPos = verts[v + 2].fPos + segb.fNorms[0];
330
331 // we draw the line edge as a degenerate quad (u is 0, v is the
332 // signed distance to the edge)
333 GrScalar dist = fanPt.distanceToLineBetween(verts[v + 1].fPos,
334 verts[v + 2].fPos);
335 verts[v + 0].fUV.set(0, dist);
336 verts[v + 1].fUV.set(0, 0);
337 verts[v + 2].fUV.set(0, 0);
338 verts[v + 3].fUV.set(0, -SK_Scalar1);
339 verts[v + 4].fUV.set(0, -SK_Scalar1);
340
341 verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
342 verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
343 verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
344 verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
345 verts[v + 4].fD0 = verts[v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000346
347 idxs[i + 0] = v + 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000348 idxs[i + 1] = v + 2;
349 idxs[i + 2] = v + 1;
350
351 idxs[i + 3] = v + 3;
352 idxs[i + 4] = v + 1;
353 idxs[i + 5] = v + 2;
354
355 idxs[i + 6] = v + 4;
356 idxs[i + 7] = v + 3;
bsalomon@google.com06809612012-01-21 15:03:39 +0000357 idxs[i + 8] = v + 2;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000358
bsalomon@google.com06809612012-01-21 15:03:39 +0000359 v += 5;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000360 i += 9;
bsalomon@google.com06809612012-01-21 15:03:39 +0000361 } else {
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000362 GrPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000363
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000364 GrVec midVec = segb.fNorms[0] + segb.fNorms[1];
365 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000366
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000367 verts[v + 0].fPos = fanPt;
368 verts[v + 1].fPos = qpts[0];
369 verts[v + 2].fPos = qpts[2];
370 verts[v + 3].fPos = qpts[0] + segb.fNorms[0];
371 verts[v + 4].fPos = qpts[2] + segb.fNorms[1];
372 verts[v + 5].fPos = qpts[1] + midVec;
373
374 GrScalar c = segb.fNorms[0].dot(qpts[0]);
375 verts[v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
376 verts[v + 1].fD0 = 0.f;
377 verts[v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
378 verts[v + 3].fD0 = -GR_ScalarMax/100;
379 verts[v + 4].fD0 = -GR_ScalarMax/100;
380 verts[v + 5].fD0 = -GR_ScalarMax/100;
381
382 c = segb.fNorms[1].dot(qpts[2]);
383 verts[v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
384 verts[v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
385 verts[v + 2].fD1 = 0.f;
386 verts[v + 3].fD1 = -GR_ScalarMax/100;
387 verts[v + 4].fD1 = -GR_ScalarMax/100;
388 verts[v + 5].fD1 = -GR_ScalarMax/100;
389
bsalomon@google.com06809612012-01-21 15:03:39 +0000390 GrMatrix toUV;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000391 GrPathUtils::quadDesignSpaceToUVCoordsMatrix(qpts, &toUV);
392 toUV.mapPointsWithStride(&verts[v].fUV,
393 &verts[v].fPos,
394 sizeof(QuadVertex),
395 6);
bsalomon@google.com06809612012-01-21 15:03:39 +0000396
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000397 idxs[i + 0] = v + 3;
398 idxs[i + 1] = v + 1;
399 idxs[i + 2] = v + 2;
400 idxs[i + 3] = v + 4;
401 idxs[i + 4] = v + 3;
402 idxs[i + 5] = v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000403
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000404 idxs[i + 6] = v + 5;
405 idxs[i + 7] = v + 3;
406 idxs[i + 8] = v + 4;
407
408 idxs[i + 9] = v + 0;
409 idxs[i + 10] = v + 2;
410 idxs[i + 11] = v + 1;
411
412 v += 6;
413 i += 12;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000414 }
415 }
416}
417
418}
419
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000420bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
421 GrPathFill fill,
422 const GrDrawTarget* target,
423 bool antiAlias) const {
424 if (!target->getCaps().fShaderDerivativeSupport || !antiAlias ||
425 kHairLine_PathFill == fill || GrIsFillInverted(fill) ||
426 !path.isConvex()) {
427 return false;
428 } else {
429 return true;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000430 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000431}
432
433bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath,
434 GrPathFill fill,
435 const GrVec* translate,
436 GrDrawTarget* target,
437 GrDrawState::StageMask stageMask,
438 bool antiAlias) {
439
bsalomon@google.comc759af32012-03-05 19:55:43 +0000440 const SkPath* path = &origPath;
441 if (path->isEmpty()) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000442 return true;
443 }
444 GrDrawState* drawState = target->drawState();
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000445
446 GrDrawTarget::AutoStateRestore asr;
447 GrMatrix vm = drawState->getViewMatrix();
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000448 if (NULL != translate) {
449 vm.postTranslate(translate->fX, translate->fY);
450 }
451 asr.set(target);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000452 GrMatrix ivm;
453 if (vm.invert(&ivm)) {
454 drawState->preConcatSamplerMatrices(stageMask, ivm);
455 }
456 drawState->setViewMatrix(GrMatrix::I());
457
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000458 GrVertexLayout layout = 0;
459 for (int s = 0; s < GrDrawState::kNumStages; ++s) {
460 if ((1 << s) & stageMask) {
461 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
462 }
463 }
464 layout |= GrDrawTarget::kEdge_VertexLayoutBit;
465
bsalomon@google.comc759af32012-03-05 19:55:43 +0000466 // We use the fact that SkPath::transform path does subdivision based on
467 // perspective. Otherwise, we apply the view matrix when copying to the
468 // segment representation.
469 SkPath tmpPath;
470 if (vm.hasPerspective()) {
471 origPath.transform(vm, &tmpPath);
472 path = &tmpPath;
473 vm.reset();
474 }
475
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000476 QuadVertex *verts;
477 uint16_t* idxs;
478
bsalomon@google.com06809612012-01-21 15:03:39 +0000479 int vCount;
480 int iCount;
bsalomon@google.com68a5b262012-03-05 18:24:07 +0000481 enum {
482 kPreallocSegmentCnt = 512 / sizeof(Segment),
483 };
484 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000485 SkPoint fanPt;
bsalomon@google.comc759af32012-03-05 19:55:43 +0000486
487 if (!get_segments(*path, vm, &segments, &fanPt, &vCount, &iCount)) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000488 return false;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000489 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000490
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000491 if (!target->reserveVertexSpace(layout,
492 vCount,
493 reinterpret_cast<void**>(&verts))) {
494 return false;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000495 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000496 if (!target->reserveIndexSpace(iCount, reinterpret_cast<void**>(&idxs))) {
497 target->resetVertexSource();
498 return false;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000499 }
500
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000501 create_vertices(segments, fanPt, verts, idxs);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000502
503 drawState->setVertexEdgeType(GrDrawState::kQuad_EdgeType);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000504 target->drawIndexed(kTriangles_PrimitiveType,
505 0, // start vertex
506 0, // start index
507 vCount,
508 iCount);
509 return true;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000510}
511