blob: 9f24190889def1b7736fde1eae589c431f2f0fc0 [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.com4647f902013-03-26 14:45:27 +000019#include "effects/GrEdgeEffect.h"
20
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000021GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
22}
23
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000024namespace {
25
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000026struct Segment {
27 enum {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000028 // These enum values are assumed in member functions below.
29 kLine = 0,
30 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000031 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000032
bsalomon@google.com9aed1142012-01-30 14:28:39 +000033 // line uses one pt, quad uses 2 pts
34 GrPoint fPts[2];
35 // normal to edge ending at each pt
36 GrVec fNorms[2];
37 // is the corner where the previous segment meets this segment
38 // sharp. If so, fMid is a normalized bisector facing outward.
39 GrVec fMid;
40
41 int countPoints() {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000042 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
43 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000044 }
45 const SkPoint& endPt() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000046 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
47 return fPts[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000048 };
49 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000050 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
51 return fNorms[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000052 };
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000053};
54
55typedef SkTArray<Segment, true> SegmentArray;
56
bsalomon@google.com9aed1142012-01-30 14:28:39 +000057void center_of_mass(const SegmentArray& segments, SkPoint* c) {
bsalomon@google.com81712882012-11-01 17:12:34 +000058 SkScalar area = 0;
vandebo@chromium.org6390c722012-03-28 21:03:22 +000059 SkPoint center = {0, 0};
bsalomon@google.com9aed1142012-01-30 14:28:39 +000060 int count = segments.count();
vandebo@chromium.org6390c722012-03-28 21:03:22 +000061 SkPoint p0 = {0, 0};
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000062 if (count > 2) {
63 // We translate the polygon so that the first point is at the origin.
64 // This avoids some precision issues with small area polygons far away
65 // from the origin.
66 p0 = segments[0].endPt();
67 SkPoint pi;
68 SkPoint pj;
bsalomon@google.coma51ab842012-07-10 19:53:34 +000069 // the first and last iteration of the below loop would compute
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000070 // zeros since the starting / ending point is (0,0). So instead we start
71 // at i=1 and make the last iteration i=count-2.
72 pj = segments[1].endPt() - p0;
73 for (int i = 1; i < count - 1; ++i) {
74 pi = pj;
75 const SkPoint pj = segments[i + 1].endPt() - p0;
76
bsalomon@google.com81712882012-11-01 17:12:34 +000077 SkScalar t = SkScalarMul(pi.fX, pj.fY) - SkScalarMul(pj.fX, pi.fY);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000078 area += t;
79 center.fX += (pi.fX + pj.fX) * t;
80 center.fY += (pi.fY + pj.fY) * t;
81
82 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000083 }
bsalomon@google.com278dc692012-02-15 16:52:51 +000084 // If the poly has no area then we instead return the average of
85 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000086 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +000087 SkPoint avg;
88 avg.set(0, 0);
89 for (int i = 0; i < count; ++i) {
90 const SkPoint& pt = segments[i].endPt();
91 avg.fX += pt.fX;
92 avg.fY += pt.fY;
93 }
94 SkScalar denom = SK_Scalar1 / count;
95 avg.scale(denom);
96 *c = avg;
97 } else {
98 area *= 3;
bsalomon@google.com81712882012-11-01 17:12:34 +000099 area = SkScalarDiv(SK_Scalar1, area);
100 center.fX = SkScalarMul(center.fX, area);
101 center.fY = SkScalarMul(center.fY, area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +0000102 // undo the translate of p0 to the origin.
103 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000104 }
105 GrAssert(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000106}
107
108void compute_vectors(SegmentArray* segments,
bsalomon@google.com278dc692012-02-15 16:52:51 +0000109 SkPoint* fanPt,
110 SkPath::Direction dir,
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000111 int* vCount,
112 int* iCount) {
113 center_of_mass(*segments, fanPt);
114 int count = segments->count();
115
bsalomon@google.com278dc692012-02-15 16:52:51 +0000116 // Make the normals point towards the outside
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000117 GrPoint::Side normSide;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000118 if (dir == SkPath::kCCW_Direction) {
119 normSide = GrPoint::kRight_Side;
120 } else {
121 normSide = GrPoint::kLeft_Side;
122 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000123
124 *vCount = 0;
125 *iCount = 0;
126 // compute normals at all points
127 for (int a = 0; a < count; ++a) {
128 const Segment& sega = (*segments)[a];
129 int b = (a + 1) % count;
130 Segment& segb = (*segments)[b];
131
132 const GrPoint* prevPt = &sega.endPt();
133 int n = segb.countPoints();
134 for (int p = 0; p < n; ++p) {
135 segb.fNorms[p] = segb.fPts[p] - *prevPt;
136 segb.fNorms[p].normalize();
137 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
138 prevPt = &segb.fPts[p];
139 }
140 if (Segment::kLine == segb.fType) {
141 *vCount += 5;
142 *iCount += 9;
143 } else {
144 *vCount += 6;
145 *iCount += 12;
146 }
147 }
148
149 // compute mid-vectors where segments meet. TODO: Detect shallow corners
150 // and leave out the wedges and close gaps by stitching segments together.
151 for (int a = 0; a < count; ++a) {
152 const Segment& sega = (*segments)[a];
153 int b = (a + 1) % count;
154 Segment& segb = (*segments)[b];
155 segb.fMid = segb.fNorms[0] + sega.endNorm();
156 segb.fMid.normalize();
157 // corner wedges
158 *vCount += 4;
159 *iCount += 6;
160 }
161}
162
bsalomon@google.com9732f622012-01-31 15:19:21 +0000163struct DegenerateTestData {
164 DegenerateTestData() { fStage = kInitial; }
165 bool isDegenerate() const { return kNonDegenerate != fStage; }
166 enum {
167 kInitial,
168 kPoint,
169 kLine,
170 kNonDegenerate
171 } fStage;
172 GrPoint fFirstPoint;
173 GrVec fLineNormal;
bsalomon@google.com81712882012-11-01 17:12:34 +0000174 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000175};
176
177void update_degenerate_test(DegenerateTestData* data, const GrPoint& pt) {
178 static const SkScalar TOL = (SK_Scalar1 / 16);
179 static const SkScalar TOL_SQD = SkScalarMul(TOL, TOL);
180
181 switch (data->fStage) {
182 case DegenerateTestData::kInitial:
183 data->fFirstPoint = pt;
184 data->fStage = DegenerateTestData::kPoint;
185 break;
186 case DegenerateTestData::kPoint:
187 if (pt.distanceToSqd(data->fFirstPoint) > TOL_SQD) {
188 data->fLineNormal = pt - data->fFirstPoint;
189 data->fLineNormal.normalize();
190 data->fLineNormal.setOrthog(data->fLineNormal);
191 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
192 data->fStage = DegenerateTestData::kLine;
193 }
194 break;
195 case DegenerateTestData::kLine:
196 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > TOL) {
197 data->fStage = DegenerateTestData::kNonDegenerate;
198 }
199 case DegenerateTestData::kNonDegenerate:
200 break;
201 default:
202 GrCrash("Unexpected degenerate test stage.");
203 }
204}
205
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000206inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Direction* dir) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000207 if (!path.cheapComputeDirection(dir)) {
208 return false;
209 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000210 // check whether m reverses the orientation
211 GrAssert(!m.hasPerspective());
bsalomon@google.com81712882012-11-01 17:12:34 +0000212 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
213 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000214 if (det2x2 < 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000215 *dir = SkPath::OppositeDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000216 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000217 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000218}
219
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000220bool get_segments(const SkPath& path,
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000221 const SkMatrix& m,
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000222 SegmentArray* segments,
223 SkPoint* fanPt,
224 int* vCount,
225 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000226 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000227 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000228 // to the path from the sample to compute coverage. Every pixel intersected
229 // 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 +0000230 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000231 // thus should be very light. This is particularly egregious for degenerate
232 // line paths. We detect paths that are very close to a line (zero area) and
233 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000234 DegenerateTestData degenerateData;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000235 SkPath::Direction dir;
236 // get_direction can fail for some degenerate paths.
237 if (!get_direction(path, m, &dir)) {
238 return false;
239 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000240
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000241 for (;;) {
242 GrPoint pts[4];
243 GrPathCmd cmd = (GrPathCmd)iter.next(pts);
244 switch (cmd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000245 case kMove_PathCmd:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000246 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000247 update_degenerate_test(&degenerateData, pts[0]);
248 break;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000249 case kLine_PathCmd: {
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000250 m.mapPoints(pts + 1, 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000251 update_degenerate_test(&degenerateData, pts[1]);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000252 segments->push_back();
253 segments->back().fType = Segment::kLine;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000254 segments->back().fPts[0] = pts[1];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000255 break;
256 }
257 case kQuadratic_PathCmd:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000258 m.mapPoints(pts + 1, 2);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000259 update_degenerate_test(&degenerateData, pts[1]);
260 update_degenerate_test(&degenerateData, pts[2]);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000261 segments->push_back();
262 segments->back().fType = Segment::kQuad;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000263 segments->back().fPts[0] = pts[1];
264 segments->back().fPts[1] = pts[2];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000265 break;
266 case kCubic_PathCmd: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000267 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000268 update_degenerate_test(&degenerateData, pts[1]);
269 update_degenerate_test(&degenerateData, pts[2]);
270 update_degenerate_test(&degenerateData, pts[3]);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000271 // unlike quads and lines, the pts[0] will also be read (in
272 // convertCubicToQuads).
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000273 SkSTArray<15, SkPoint, true> quads;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000274 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000275 int count = quads.count();
276 for (int q = 0; q < count; q += 3) {
277 segments->push_back();
278 segments->back().fType = Segment::kQuad;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000279 segments->back().fPts[0] = quads[q + 1];
280 segments->back().fPts[1] = quads[q + 2];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000281 }
282 break;
283 };
284 case kEnd_PathCmd:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000285 if (degenerateData.isDegenerate()) {
286 return false;
287 } else {
bsalomon@google.com278dc692012-02-15 16:52:51 +0000288 compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000289 return true;
290 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000291 default:
292 break;
293 }
294 }
295}
296
297struct QuadVertex {
298 GrPoint fPos;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000299 GrPoint fUV;
bsalomon@google.com81712882012-11-01 17:12:34 +0000300 SkScalar fD0;
301 SkScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000302};
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000303
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000304void create_vertices(const SegmentArray& segments,
305 const SkPoint& fanPt,
306 QuadVertex* verts,
307 uint16_t* idxs) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000308 int v = 0;
309 int i = 0;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000310
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000311 int count = segments.count();
312 for (int a = 0; a < count; ++a) {
313 const Segment& sega = segments[a];
314 int b = (a + 1) % count;
315 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000316
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000317 // FIXME: These tris are inset in the 1 unit arc around the corner
318 verts[v + 0].fPos = sega.endPt();
319 verts[v + 1].fPos = verts[v + 0].fPos + sega.endNorm();
320 verts[v + 2].fPos = verts[v + 0].fPos + segb.fMid;
321 verts[v + 3].fPos = verts[v + 0].fPos + segb.fNorms[0];
322 verts[v + 0].fUV.set(0,0);
323 verts[v + 1].fUV.set(0,-SK_Scalar1);
324 verts[v + 2].fUV.set(0,-SK_Scalar1);
325 verts[v + 3].fUV.set(0,-SK_Scalar1);
326 verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
327 verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
328 verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
329 verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000330
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000331 idxs[i + 0] = v + 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000332 idxs[i + 1] = v + 2;
333 idxs[i + 2] = v + 1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000334 idxs[i + 3] = v + 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000335 idxs[i + 4] = v + 3;
336 idxs[i + 5] = v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000337
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000338 v += 4;
339 i += 6;
340
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000341 if (Segment::kLine == segb.fType) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000342 verts[v + 0].fPos = fanPt;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000343 verts[v + 1].fPos = sega.endPt();
344 verts[v + 2].fPos = segb.fPts[0];
345
346 verts[v + 3].fPos = verts[v + 1].fPos + segb.fNorms[0];
347 verts[v + 4].fPos = verts[v + 2].fPos + segb.fNorms[0];
348
349 // we draw the line edge as a degenerate quad (u is 0, v is the
350 // signed distance to the edge)
bsalomon@google.com81712882012-11-01 17:12:34 +0000351 SkScalar dist = fanPt.distanceToLineBetween(verts[v + 1].fPos,
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000352 verts[v + 2].fPos);
353 verts[v + 0].fUV.set(0, dist);
354 verts[v + 1].fUV.set(0, 0);
355 verts[v + 2].fUV.set(0, 0);
356 verts[v + 3].fUV.set(0, -SK_Scalar1);
357 verts[v + 4].fUV.set(0, -SK_Scalar1);
358
359 verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
360 verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
361 verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
362 verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
363 verts[v + 4].fD0 = verts[v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000364
365 idxs[i + 0] = v + 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000366 idxs[i + 1] = v + 2;
367 idxs[i + 2] = v + 1;
368
369 idxs[i + 3] = v + 3;
370 idxs[i + 4] = v + 1;
371 idxs[i + 5] = v + 2;
372
373 idxs[i + 6] = v + 4;
374 idxs[i + 7] = v + 3;
bsalomon@google.com06809612012-01-21 15:03:39 +0000375 idxs[i + 8] = v + 2;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000376
bsalomon@google.com06809612012-01-21 15:03:39 +0000377 v += 5;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000378 i += 9;
bsalomon@google.com06809612012-01-21 15:03:39 +0000379 } else {
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000380 GrPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000381
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000382 GrVec midVec = segb.fNorms[0] + segb.fNorms[1];
383 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000384
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000385 verts[v + 0].fPos = fanPt;
386 verts[v + 1].fPos = qpts[0];
387 verts[v + 2].fPos = qpts[2];
388 verts[v + 3].fPos = qpts[0] + segb.fNorms[0];
389 verts[v + 4].fPos = qpts[2] + segb.fNorms[1];
390 verts[v + 5].fPos = qpts[1] + midVec;
391
bsalomon@google.com81712882012-11-01 17:12:34 +0000392 SkScalar c = segb.fNorms[0].dot(qpts[0]);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000393 verts[v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
394 verts[v + 1].fD0 = 0.f;
395 verts[v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
bsalomon@google.com81712882012-11-01 17:12:34 +0000396 verts[v + 3].fD0 = -SK_ScalarMax/100;
397 verts[v + 4].fD0 = -SK_ScalarMax/100;
398 verts[v + 5].fD0 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000399
400 c = segb.fNorms[1].dot(qpts[2]);
401 verts[v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
402 verts[v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
403 verts[v + 2].fD1 = 0.f;
bsalomon@google.com81712882012-11-01 17:12:34 +0000404 verts[v + 3].fD1 = -SK_ScalarMax/100;
405 verts[v + 4].fD1 = -SK_ScalarMax/100;
406 verts[v + 5].fD1 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000407
bsalomon@google.com19713172012-03-15 13:51:08 +0000408 GrPathUtils::QuadUVMatrix toUV(qpts);
409 toUV.apply<6, sizeof(QuadVertex), sizeof(GrPoint)>(verts + v);
bsalomon@google.com06809612012-01-21 15:03:39 +0000410
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000411 idxs[i + 0] = v + 3;
412 idxs[i + 1] = v + 1;
413 idxs[i + 2] = v + 2;
414 idxs[i + 3] = v + 4;
415 idxs[i + 4] = v + 3;
416 idxs[i + 5] = v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000417
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000418 idxs[i + 6] = v + 5;
419 idxs[i + 7] = v + 3;
420 idxs[i + 8] = v + 4;
421
422 idxs[i + 9] = v + 0;
423 idxs[i + 10] = v + 2;
424 idxs[i + 11] = v + 1;
425
426 v += 6;
427 i += 12;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000428 }
429 }
430}
431
432}
433
robertphillips@google.comfa662942012-05-17 12:20:22 +0000434bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000435 const SkStrokeRec& stroke,
robertphillips@google.comfa662942012-05-17 12:20:22 +0000436 const GrDrawTarget* target,
437 bool antiAlias) const {
bsalomon@google.combcce8922013-03-25 15:38:39 +0000438 return (target->caps()->shaderDerivativeSupport() && antiAlias &&
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000439 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
robertphillips@google.comfa662942012-05-17 12:20:22 +0000440}
441
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000442bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath,
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000443 const SkStrokeRec&,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000444 GrDrawTarget* target,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000445 bool antiAlias) {
446
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000447 const SkPath* path = &origPath;
448 if (path->isEmpty()) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000449 return true;
450 }
bsalomon@google.com4647f902013-03-26 14:45:27 +0000451
452 GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kPreserve_ASRInit);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000453 GrDrawState* drawState = target->drawState();
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000454
bsalomon@google.coma8347462012-10-08 18:59:39 +0000455 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
456 if (!adcd.succeeded()) {
bsalomon@google.come3d32162012-07-20 13:37:06 +0000457 return false;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000458 }
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000459 const SkMatrix* vm = &adcd.getOriginalMatrix();
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000460
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000461 // We use the fact that SkPath::transform path does subdivision based on
462 // perspective. Otherwise, we apply the view matrix when copying to the
463 // segment representation.
464 SkPath tmpPath;
bsalomon@google.coma8347462012-10-08 18:59:39 +0000465 if (vm->hasPerspective()) {
466 origPath.transform(*vm, &tmpPath);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000467 path = &tmpPath;
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000468 vm = &SkMatrix::I();
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000469 }
470
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000471 QuadVertex *verts;
472 uint16_t* idxs;
473
bsalomon@google.com06809612012-01-21 15:03:39 +0000474 int vCount;
475 int iCount;
bsalomon@google.com68a5b262012-03-05 18:24:07 +0000476 enum {
477 kPreallocSegmentCnt = 512 / sizeof(Segment),
478 };
479 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000480 SkPoint fanPt;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000481
bsalomon@google.coma8347462012-10-08 18:59:39 +0000482 if (!get_segments(*path, *vm, &segments, &fanPt, &vCount, &iCount)) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000483 return false;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000484 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000485
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000486 // position + edge
487 static const GrVertexAttrib kAttribs[] = {
jvanverth@google.com3b0d6312013-03-01 20:30:01 +0000488 {kVec2f_GrVertexAttribType, 0},
489 {kVec4f_GrVertexAttribType, sizeof(GrPoint)}
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000490 };
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000491
492 drawState->setVertexAttribs(kAttribs, SK_ARRAY_COUNT(kAttribs));
493 drawState->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0);
bsalomon@google.com4647f902013-03-26 14:45:27 +0000494 drawState->setAttribBindings(GrDrawState::kDefault_AttribBindings);
495
496 enum {
497 // the edge effects share this stage with glyph rendering
498 // (kGlyphMaskStage in GrTextContext) && SW path rendering
499 // (kPathMaskStage in GrSWMaskHelper)
500 kEdgeEffectStage = GrPaint::kTotalStages,
501 };
502 static const int kEdgeAttrIndex = 1;
503 GrEffectRef* quadEffect = GrEdgeEffect::Create(GrEdgeEffect::kQuad_EdgeType);
504 drawState->setEffect(kEdgeEffectStage, quadEffect, kEdgeAttrIndex)->unref();
505
jvanverth@google.comb75b0a02013-02-05 20:33:30 +0000506 GrDrawTarget::AutoReleaseGeometry arg(target, vCount, iCount);
bsalomon@google.comb3729422012-03-07 19:13:28 +0000507 if (!arg.succeeded()) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000508 return false;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000509 }
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000510 GrAssert(sizeof(QuadVertex) == drawState->getVertexSize());
bsalomon@google.comb3729422012-03-07 19:13:28 +0000511 verts = reinterpret_cast<QuadVertex*>(arg.vertices());
512 idxs = reinterpret_cast<uint16_t*>(arg.indices());
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000513
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000514 create_vertices(segments, fanPt, verts, idxs);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000515
bsalomon@google.com47059542012-06-06 20:51:20 +0000516 target->drawIndexed(kTriangles_GrPrimitiveType,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000517 0, // start vertex
518 0, // start index
519 vCount,
520 iCount);
bsalomon@google.coma8347462012-10-08 18:59:39 +0000521
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000522 return true;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000523}