blob: cff63248673f3d0029a595281d80f150ed338848 [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"
bsalomon@google.comc26d94f2013-03-25 18:19:00 +000013#include "GrDrawTargetCaps.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000014#include "GrPathUtils.h"
15#include "SkString.h"
sugoi@google.com5f74cf82012-12-17 21:16:45 +000016#include "SkStrokeRec.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000017#include "SkTrace.h"
18
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000019GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
20}
21
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000022namespace {
23
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000024struct Segment {
25 enum {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000026 // These enum values are assumed in member functions below.
27 kLine = 0,
28 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000029 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000030
bsalomon@google.com9aed1142012-01-30 14:28:39 +000031 // line uses one pt, quad uses 2 pts
32 GrPoint fPts[2];
33 // normal to edge ending at each pt
34 GrVec fNorms[2];
35 // is the corner where the previous segment meets this segment
36 // sharp. If so, fMid is a normalized bisector facing outward.
37 GrVec fMid;
38
39 int countPoints() {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000040 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
41 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000042 }
43 const SkPoint& endPt() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000044 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
45 return fPts[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000046 };
47 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000048 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
49 return fNorms[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000050 };
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000051};
52
53typedef SkTArray<Segment, true> SegmentArray;
54
bsalomon@google.com9aed1142012-01-30 14:28:39 +000055void center_of_mass(const SegmentArray& segments, SkPoint* c) {
bsalomon@google.com81712882012-11-01 17:12:34 +000056 SkScalar area = 0;
vandebo@chromium.org6390c722012-03-28 21:03:22 +000057 SkPoint center = {0, 0};
bsalomon@google.com9aed1142012-01-30 14:28:39 +000058 int count = segments.count();
vandebo@chromium.org6390c722012-03-28 21:03:22 +000059 SkPoint p0 = {0, 0};
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000060 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;
bsalomon@google.coma51ab842012-07-10 19:53:34 +000067 // the first and last iteration of the below loop would compute
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000068 // 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
bsalomon@google.com81712882012-11-01 17:12:34 +000075 SkScalar t = SkScalarMul(pi.fX, pj.fY) - SkScalarMul(pj.fX, pi.fY);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000076 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;
bsalomon@google.com81712882012-11-01 17:12:34 +000097 area = SkScalarDiv(SK_Scalar1, area);
98 center.fX = SkScalarMul(center.fX, area);
99 center.fY = SkScalarMul(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;
bsalomon@google.com81712882012-11-01 17:12:34 +0000172 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000173};
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.comb9086a02012-11-01 18:02:54 +0000204inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Direction* dir) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000205 if (!path.cheapComputeDirection(dir)) {
206 return false;
207 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000208 // check whether m reverses the orientation
209 GrAssert(!m.hasPerspective());
bsalomon@google.com81712882012-11-01 17:12:34 +0000210 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
211 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000212 if (det2x2 < 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000213 *dir = SkPath::OppositeDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000214 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000215 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000216}
217
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000218bool get_segments(const SkPath& path,
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000219 const SkMatrix& m,
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000220 SegmentArray* segments,
221 SkPoint* fanPt,
222 int* vCount,
223 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000224 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000225 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000226 // to the path from the sample to compute coverage. Every pixel intersected
227 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000228 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000229 // thus should be very light. This is particularly egregious for degenerate
230 // line paths. We detect paths that are very close to a line (zero area) and
231 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000232 DegenerateTestData degenerateData;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000233 SkPath::Direction dir;
234 // get_direction can fail for some degenerate paths.
235 if (!get_direction(path, m, &dir)) {
236 return false;
237 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000238
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000239 for (;;) {
240 GrPoint pts[4];
241 GrPathCmd cmd = (GrPathCmd)iter.next(pts);
242 switch (cmd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000243 case kMove_PathCmd:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000244 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000245 update_degenerate_test(&degenerateData, pts[0]);
246 break;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000247 case kLine_PathCmd: {
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000248 m.mapPoints(pts + 1, 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000249 update_degenerate_test(&degenerateData, pts[1]);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000250 segments->push_back();
251 segments->back().fType = Segment::kLine;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000252 segments->back().fPts[0] = pts[1];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000253 break;
254 }
255 case kQuadratic_PathCmd:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000256 m.mapPoints(pts + 1, 2);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000257 update_degenerate_test(&degenerateData, pts[1]);
258 update_degenerate_test(&degenerateData, pts[2]);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000259 segments->push_back();
260 segments->back().fType = Segment::kQuad;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000261 segments->back().fPts[0] = pts[1];
262 segments->back().fPts[1] = pts[2];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000263 break;
264 case kCubic_PathCmd: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000265 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000266 update_degenerate_test(&degenerateData, pts[1]);
267 update_degenerate_test(&degenerateData, pts[2]);
268 update_degenerate_test(&degenerateData, pts[3]);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000269 // unlike quads and lines, the pts[0] will also be read (in
270 // convertCubicToQuads).
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000271 SkSTArray<15, SkPoint, true> quads;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000272 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000273 int count = quads.count();
274 for (int q = 0; q < count; q += 3) {
275 segments->push_back();
276 segments->back().fType = Segment::kQuad;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000277 segments->back().fPts[0] = quads[q + 1];
278 segments->back().fPts[1] = quads[q + 2];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000279 }
280 break;
281 };
282 case kEnd_PathCmd:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000283 if (degenerateData.isDegenerate()) {
284 return false;
285 } else {
bsalomon@google.com278dc692012-02-15 16:52:51 +0000286 compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000287 return true;
288 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000289 default:
290 break;
291 }
292 }
293}
294
295struct QuadVertex {
296 GrPoint fPos;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000297 GrPoint fUV;
bsalomon@google.com81712882012-11-01 17:12:34 +0000298 SkScalar fD0;
299 SkScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000300};
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000301
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000302void create_vertices(const SegmentArray& segments,
303 const SkPoint& fanPt,
304 QuadVertex* verts,
305 uint16_t* idxs) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000306 int v = 0;
307 int i = 0;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000308
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000309 int count = segments.count();
310 for (int a = 0; a < count; ++a) {
311 const Segment& sega = segments[a];
312 int b = (a + 1) % count;
313 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000314
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000315 // FIXME: These tris are inset in the 1 unit arc around the corner
316 verts[v + 0].fPos = sega.endPt();
317 verts[v + 1].fPos = verts[v + 0].fPos + sega.endNorm();
318 verts[v + 2].fPos = verts[v + 0].fPos + segb.fMid;
319 verts[v + 3].fPos = verts[v + 0].fPos + segb.fNorms[0];
320 verts[v + 0].fUV.set(0,0);
321 verts[v + 1].fUV.set(0,-SK_Scalar1);
322 verts[v + 2].fUV.set(0,-SK_Scalar1);
323 verts[v + 3].fUV.set(0,-SK_Scalar1);
324 verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
325 verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
326 verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
327 verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000328
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000329 idxs[i + 0] = v + 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000330 idxs[i + 1] = v + 2;
331 idxs[i + 2] = v + 1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000332 idxs[i + 3] = v + 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000333 idxs[i + 4] = v + 3;
334 idxs[i + 5] = v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000335
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000336 v += 4;
337 i += 6;
338
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000339 if (Segment::kLine == segb.fType) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000340 verts[v + 0].fPos = fanPt;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000341 verts[v + 1].fPos = sega.endPt();
342 verts[v + 2].fPos = segb.fPts[0];
343
344 verts[v + 3].fPos = verts[v + 1].fPos + segb.fNorms[0];
345 verts[v + 4].fPos = verts[v + 2].fPos + segb.fNorms[0];
346
347 // we draw the line edge as a degenerate quad (u is 0, v is the
348 // signed distance to the edge)
bsalomon@google.com81712882012-11-01 17:12:34 +0000349 SkScalar dist = fanPt.distanceToLineBetween(verts[v + 1].fPos,
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000350 verts[v + 2].fPos);
351 verts[v + 0].fUV.set(0, dist);
352 verts[v + 1].fUV.set(0, 0);
353 verts[v + 2].fUV.set(0, 0);
354 verts[v + 3].fUV.set(0, -SK_Scalar1);
355 verts[v + 4].fUV.set(0, -SK_Scalar1);
356
357 verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
358 verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
359 verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
360 verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
361 verts[v + 4].fD0 = verts[v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000362
363 idxs[i + 0] = v + 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000364 idxs[i + 1] = v + 2;
365 idxs[i + 2] = v + 1;
366
367 idxs[i + 3] = v + 3;
368 idxs[i + 4] = v + 1;
369 idxs[i + 5] = v + 2;
370
371 idxs[i + 6] = v + 4;
372 idxs[i + 7] = v + 3;
bsalomon@google.com06809612012-01-21 15:03:39 +0000373 idxs[i + 8] = v + 2;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000374
bsalomon@google.com06809612012-01-21 15:03:39 +0000375 v += 5;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000376 i += 9;
bsalomon@google.com06809612012-01-21 15:03:39 +0000377 } else {
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000378 GrPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000379
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000380 GrVec midVec = segb.fNorms[0] + segb.fNorms[1];
381 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000382
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000383 verts[v + 0].fPos = fanPt;
384 verts[v + 1].fPos = qpts[0];
385 verts[v + 2].fPos = qpts[2];
386 verts[v + 3].fPos = qpts[0] + segb.fNorms[0];
387 verts[v + 4].fPos = qpts[2] + segb.fNorms[1];
388 verts[v + 5].fPos = qpts[1] + midVec;
389
bsalomon@google.com81712882012-11-01 17:12:34 +0000390 SkScalar c = segb.fNorms[0].dot(qpts[0]);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000391 verts[v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
392 verts[v + 1].fD0 = 0.f;
393 verts[v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
bsalomon@google.com81712882012-11-01 17:12:34 +0000394 verts[v + 3].fD0 = -SK_ScalarMax/100;
395 verts[v + 4].fD0 = -SK_ScalarMax/100;
396 verts[v + 5].fD0 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000397
398 c = segb.fNorms[1].dot(qpts[2]);
399 verts[v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
400 verts[v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
401 verts[v + 2].fD1 = 0.f;
bsalomon@google.com81712882012-11-01 17:12:34 +0000402 verts[v + 3].fD1 = -SK_ScalarMax/100;
403 verts[v + 4].fD1 = -SK_ScalarMax/100;
404 verts[v + 5].fD1 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000405
bsalomon@google.com19713172012-03-15 13:51:08 +0000406 GrPathUtils::QuadUVMatrix toUV(qpts);
407 toUV.apply<6, sizeof(QuadVertex), sizeof(GrPoint)>(verts + v);
bsalomon@google.com06809612012-01-21 15:03:39 +0000408
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000409 idxs[i + 0] = v + 3;
410 idxs[i + 1] = v + 1;
411 idxs[i + 2] = v + 2;
412 idxs[i + 3] = v + 4;
413 idxs[i + 4] = v + 3;
414 idxs[i + 5] = v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000415
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000416 idxs[i + 6] = v + 5;
417 idxs[i + 7] = v + 3;
418 idxs[i + 8] = v + 4;
419
420 idxs[i + 9] = v + 0;
421 idxs[i + 10] = v + 2;
422 idxs[i + 11] = v + 1;
423
424 v += 6;
425 i += 12;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000426 }
427 }
428}
429
430}
431
robertphillips@google.comfa662942012-05-17 12:20:22 +0000432bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000433 const SkStrokeRec& stroke,
robertphillips@google.comfa662942012-05-17 12:20:22 +0000434 const GrDrawTarget* target,
435 bool antiAlias) const {
bsalomon@google.combcce8922013-03-25 15:38:39 +0000436 return (target->caps()->shaderDerivativeSupport() && antiAlias &&
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000437 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
robertphillips@google.comfa662942012-05-17 12:20:22 +0000438}
439
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000440bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath,
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000441 const SkStrokeRec&,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000442 GrDrawTarget* target,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000443 bool antiAlias) {
444
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000445 const SkPath* path = &origPath;
446 if (path->isEmpty()) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000447 return true;
448 }
449 GrDrawState* drawState = target->drawState();
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000450
bsalomon@google.coma8347462012-10-08 18:59:39 +0000451 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
452 if (!adcd.succeeded()) {
bsalomon@google.come3d32162012-07-20 13:37:06 +0000453 return false;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000454 }
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000455 const SkMatrix* vm = &adcd.getOriginalMatrix();
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000456
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000457 // We use the fact that SkPath::transform path does subdivision based on
458 // perspective. Otherwise, we apply the view matrix when copying to the
459 // segment representation.
460 SkPath tmpPath;
bsalomon@google.coma8347462012-10-08 18:59:39 +0000461 if (vm->hasPerspective()) {
462 origPath.transform(*vm, &tmpPath);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000463 path = &tmpPath;
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000464 vm = &SkMatrix::I();
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000465 }
466
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000467 QuadVertex *verts;
468 uint16_t* idxs;
469
bsalomon@google.com06809612012-01-21 15:03:39 +0000470 int vCount;
471 int iCount;
bsalomon@google.com68a5b262012-03-05 18:24:07 +0000472 enum {
473 kPreallocSegmentCnt = 512 / sizeof(Segment),
474 };
475 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000476 SkPoint fanPt;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000477
bsalomon@google.coma8347462012-10-08 18:59:39 +0000478 if (!get_segments(*path, *vm, &segments, &fanPt, &vCount, &iCount)) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000479 return false;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000480 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000481
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000482 // position + edge
483 static const GrVertexAttrib kAttribs[] = {
jvanverth@google.com3b0d6312013-03-01 20:30:01 +0000484 {kVec2f_GrVertexAttribType, 0},
485 {kVec4f_GrVertexAttribType, sizeof(GrPoint)}
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000486 };
487 static const GrAttribBindings bindings = GrDrawState::kEdge_AttribBindingsBit;
488
489 drawState->setVertexAttribs(kAttribs, SK_ARRAY_COUNT(kAttribs));
490 drawState->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0);
491 drawState->setAttribIndex(GrDrawState::kEdge_AttribIndex, 1);
492 drawState->setAttribBindings(bindings);
jvanverth@google.comb75b0a02013-02-05 20:33:30 +0000493 GrDrawTarget::AutoReleaseGeometry arg(target, vCount, iCount);
bsalomon@google.comb3729422012-03-07 19:13:28 +0000494 if (!arg.succeeded()) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000495 return false;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000496 }
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000497 GrAssert(sizeof(QuadVertex) == drawState->getVertexSize());
bsalomon@google.comb3729422012-03-07 19:13:28 +0000498 verts = reinterpret_cast<QuadVertex*>(arg.vertices());
499 idxs = reinterpret_cast<uint16_t*>(arg.indices());
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000500
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000501 create_vertices(segments, fanPt, verts, idxs);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000502
bsalomon@google.coma8347462012-10-08 18:59:39 +0000503 GrDrawState::VertexEdgeType oldEdgeType = drawState->getVertexEdgeType();
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000504 drawState->setVertexEdgeType(GrDrawState::kQuad_EdgeType);
bsalomon@google.com47059542012-06-06 20:51:20 +0000505 target->drawIndexed(kTriangles_GrPrimitiveType,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000506 0, // start vertex
507 0, // start index
508 vCount,
509 iCount);
bsalomon@google.coma8347462012-10-08 18:59:39 +0000510 drawState->setVertexEdgeType(oldEdgeType);
511
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000512 return true;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000513}