blob: 77980e468b6f7e71585f3c16fd9dfcafee3bebae [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"
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000014#include "GrEffect.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000015#include "GrPathUtils.h"
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000016#include "GrTBackendEffectFactory.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000017#include "SkString.h"
sugoi@google.com5f74cf82012-12-17 21:16:45 +000018#include "SkStrokeRec.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000019#include "SkTrace.h"
20
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000021#include "gl/GrGLEffect.h"
22#include "gl/GrGLSL.h"
bsalomon@google.com4647f902013-03-26 14:45:27 +000023
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000024GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
25}
26
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000027namespace {
28
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000029struct Segment {
30 enum {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000031 // These enum values are assumed in member functions below.
32 kLine = 0,
33 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000034 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000035
bsalomon@google.com9aed1142012-01-30 14:28:39 +000036 // line uses one pt, quad uses 2 pts
37 GrPoint fPts[2];
38 // normal to edge ending at each pt
39 GrVec fNorms[2];
40 // is the corner where the previous segment meets this segment
41 // sharp. If so, fMid is a normalized bisector facing outward.
42 GrVec fMid;
43
44 int countPoints() {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000045 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
46 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000047 }
48 const SkPoint& endPt() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000049 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
50 return fPts[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000051 };
52 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000053 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
54 return fNorms[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000055 };
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000056};
57
58typedef SkTArray<Segment, true> SegmentArray;
59
bsalomon@google.com9aed1142012-01-30 14:28:39 +000060void center_of_mass(const SegmentArray& segments, SkPoint* c) {
bsalomon@google.com81712882012-11-01 17:12:34 +000061 SkScalar area = 0;
vandebo@chromium.org6390c722012-03-28 21:03:22 +000062 SkPoint center = {0, 0};
bsalomon@google.com9aed1142012-01-30 14:28:39 +000063 int count = segments.count();
vandebo@chromium.org6390c722012-03-28 21:03:22 +000064 SkPoint p0 = {0, 0};
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000065 if (count > 2) {
66 // We translate the polygon so that the first point is at the origin.
67 // This avoids some precision issues with small area polygons far away
68 // from the origin.
69 p0 = segments[0].endPt();
70 SkPoint pi;
71 SkPoint pj;
bsalomon@google.coma51ab842012-07-10 19:53:34 +000072 // the first and last iteration of the below loop would compute
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000073 // zeros since the starting / ending point is (0,0). So instead we start
74 // at i=1 and make the last iteration i=count-2.
75 pj = segments[1].endPt() - p0;
76 for (int i = 1; i < count - 1; ++i) {
77 pi = pj;
78 const SkPoint pj = segments[i + 1].endPt() - p0;
79
bsalomon@google.com81712882012-11-01 17:12:34 +000080 SkScalar t = SkScalarMul(pi.fX, pj.fY) - SkScalarMul(pj.fX, pi.fY);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000081 area += t;
82 center.fX += (pi.fX + pj.fX) * t;
83 center.fY += (pi.fY + pj.fY) * t;
84
85 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000086 }
bsalomon@google.com278dc692012-02-15 16:52:51 +000087 // If the poly has no area then we instead return the average of
88 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000089 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +000090 SkPoint avg;
91 avg.set(0, 0);
92 for (int i = 0; i < count; ++i) {
93 const SkPoint& pt = segments[i].endPt();
94 avg.fX += pt.fX;
95 avg.fY += pt.fY;
96 }
97 SkScalar denom = SK_Scalar1 / count;
98 avg.scale(denom);
99 *c = avg;
100 } else {
101 area *= 3;
bsalomon@google.com81712882012-11-01 17:12:34 +0000102 area = SkScalarDiv(SK_Scalar1, area);
103 center.fX = SkScalarMul(center.fX, area);
104 center.fY = SkScalarMul(center.fY, area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +0000105 // undo the translate of p0 to the origin.
106 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000107 }
108 GrAssert(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000109}
110
111void compute_vectors(SegmentArray* segments,
bsalomon@google.com278dc692012-02-15 16:52:51 +0000112 SkPoint* fanPt,
113 SkPath::Direction dir,
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000114 int* vCount,
115 int* iCount) {
116 center_of_mass(*segments, fanPt);
117 int count = segments->count();
118
bsalomon@google.com278dc692012-02-15 16:52:51 +0000119 // Make the normals point towards the outside
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000120 GrPoint::Side normSide;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000121 if (dir == SkPath::kCCW_Direction) {
122 normSide = GrPoint::kRight_Side;
123 } else {
124 normSide = GrPoint::kLeft_Side;
125 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000126
127 *vCount = 0;
128 *iCount = 0;
129 // compute normals at all points
130 for (int a = 0; a < count; ++a) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000131 Segment& sega = (*segments)[a];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000132 int b = (a + 1) % count;
133 Segment& segb = (*segments)[b];
134
135 const GrPoint* prevPt = &sega.endPt();
136 int n = segb.countPoints();
137 for (int p = 0; p < n; ++p) {
138 segb.fNorms[p] = segb.fPts[p] - *prevPt;
139 segb.fNorms[p].normalize();
140 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
141 prevPt = &segb.fPts[p];
142 }
143 if (Segment::kLine == segb.fType) {
144 *vCount += 5;
145 *iCount += 9;
146 } else {
147 *vCount += 6;
148 *iCount += 12;
149 }
150 }
151
152 // compute mid-vectors where segments meet. TODO: Detect shallow corners
153 // and leave out the wedges and close gaps by stitching segments together.
154 for (int a = 0; a < count; ++a) {
155 const Segment& sega = (*segments)[a];
156 int b = (a + 1) % count;
157 Segment& segb = (*segments)[b];
158 segb.fMid = segb.fNorms[0] + sega.endNorm();
159 segb.fMid.normalize();
160 // corner wedges
161 *vCount += 4;
162 *iCount += 6;
163 }
164}
165
bsalomon@google.com9732f622012-01-31 15:19:21 +0000166struct DegenerateTestData {
167 DegenerateTestData() { fStage = kInitial; }
168 bool isDegenerate() const { return kNonDegenerate != fStage; }
169 enum {
170 kInitial,
171 kPoint,
172 kLine,
173 kNonDegenerate
174 } fStage;
175 GrPoint fFirstPoint;
176 GrVec fLineNormal;
bsalomon@google.com81712882012-11-01 17:12:34 +0000177 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000178};
179
180void update_degenerate_test(DegenerateTestData* data, const GrPoint& pt) {
181 static const SkScalar TOL = (SK_Scalar1 / 16);
182 static const SkScalar TOL_SQD = SkScalarMul(TOL, TOL);
183
184 switch (data->fStage) {
185 case DegenerateTestData::kInitial:
186 data->fFirstPoint = pt;
187 data->fStage = DegenerateTestData::kPoint;
188 break;
189 case DegenerateTestData::kPoint:
190 if (pt.distanceToSqd(data->fFirstPoint) > TOL_SQD) {
191 data->fLineNormal = pt - data->fFirstPoint;
192 data->fLineNormal.normalize();
193 data->fLineNormal.setOrthog(data->fLineNormal);
194 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
195 data->fStage = DegenerateTestData::kLine;
196 }
197 break;
198 case DegenerateTestData::kLine:
199 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > TOL) {
200 data->fStage = DegenerateTestData::kNonDegenerate;
201 }
202 case DegenerateTestData::kNonDegenerate:
203 break;
204 default:
205 GrCrash("Unexpected degenerate test stage.");
206 }
207}
208
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000209inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Direction* dir) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000210 if (!path.cheapComputeDirection(dir)) {
211 return false;
212 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000213 // check whether m reverses the orientation
214 GrAssert(!m.hasPerspective());
bsalomon@google.com81712882012-11-01 17:12:34 +0000215 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
216 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000217 if (det2x2 < 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000218 *dir = SkPath::OppositeDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000219 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000220 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000221}
222
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000223bool get_segments(const SkPath& path,
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000224 const SkMatrix& m,
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000225 SegmentArray* segments,
226 SkPoint* fanPt,
227 int* vCount,
228 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000229 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000230 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000231 // to the path from the sample to compute coverage. Every pixel intersected
232 // 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 +0000233 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000234 // thus should be very light. This is particularly egregious for degenerate
235 // line paths. We detect paths that are very close to a line (zero area) and
236 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000237 DegenerateTestData degenerateData;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000238 SkPath::Direction dir;
239 // get_direction can fail for some degenerate paths.
240 if (!get_direction(path, m, &dir)) {
241 return false;
242 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000243
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000244 for (;;) {
245 GrPoint pts[4];
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000246 SkPath::Verb verb = iter.next(pts);
247 switch (verb) {
248 case SkPath::kMove_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000249 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000250 update_degenerate_test(&degenerateData, pts[0]);
251 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000252 case SkPath::kLine_Verb: {
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000253 m.mapPoints(pts + 1, 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000254 update_degenerate_test(&degenerateData, pts[1]);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000255 segments->push_back();
256 segments->back().fType = Segment::kLine;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000257 segments->back().fPts[0] = pts[1];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000258 break;
259 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000260 case SkPath::kQuad_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000261 m.mapPoints(pts + 1, 2);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000262 update_degenerate_test(&degenerateData, pts[1]);
263 update_degenerate_test(&degenerateData, pts[2]);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000264 segments->push_back();
265 segments->back().fType = Segment::kQuad;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000266 segments->back().fPts[0] = pts[1];
267 segments->back().fPts[1] = pts[2];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000268 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000269 case SkPath::kCubic_Verb: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000270 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000271 update_degenerate_test(&degenerateData, pts[1]);
272 update_degenerate_test(&degenerateData, pts[2]);
273 update_degenerate_test(&degenerateData, pts[3]);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000274 // unlike quads and lines, the pts[0] will also be read (in
275 // convertCubicToQuads).
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000276 SkSTArray<15, SkPoint, true> quads;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000277 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000278 int count = quads.count();
279 for (int q = 0; q < count; q += 3) {
280 segments->push_back();
281 segments->back().fType = Segment::kQuad;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000282 segments->back().fPts[0] = quads[q + 1];
283 segments->back().fPts[1] = quads[q + 2];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000284 }
285 break;
286 };
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000287 case SkPath::kDone_Verb:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000288 if (degenerateData.isDegenerate()) {
289 return false;
290 } else {
bsalomon@google.com278dc692012-02-15 16:52:51 +0000291 compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000292 return true;
293 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000294 default:
295 break;
296 }
297 }
298}
299
300struct QuadVertex {
301 GrPoint fPos;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000302 GrPoint fUV;
bsalomon@google.com81712882012-11-01 17:12:34 +0000303 SkScalar fD0;
304 SkScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000305};
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000306
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000307struct Draw {
308 Draw() : fVertexCnt(0), fIndexCnt(0) {}
309 int fVertexCnt;
310 int fIndexCnt;
311};
312
313typedef SkTArray<Draw, true> DrawArray;
314
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000315void create_vertices(const SegmentArray& segments,
316 const SkPoint& fanPt,
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000317 DrawArray* draws,
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000318 QuadVertex* verts,
319 uint16_t* idxs) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000320 Draw* draw = &draws->push_back();
321 // alias just to make vert/index assignments easier to read.
322 int* v = &draw->fVertexCnt;
323 int* i = &draw->fIndexCnt;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000324
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000325 int count = segments.count();
326 for (int a = 0; a < count; ++a) {
327 const Segment& sega = segments[a];
328 int b = (a + 1) % count;
329 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000330
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000331 // Check whether adding the verts for this segment to the current draw would cause index
332 // values to overflow.
333 int vCount = 4;
334 if (Segment::kLine == segb.fType) {
335 vCount += 5;
336 } else {
337 vCount += 6;
338 }
339 if (draw->fVertexCnt + vCount > (1 << 16)) {
340 verts += *v;
341 idxs += *i;
342 draw = &draws->push_back();
343 v = &draw->fVertexCnt;
344 i = &draw->fIndexCnt;
345 }
346
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000347 // FIXME: These tris are inset in the 1 unit arc around the corner
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000348 verts[*v + 0].fPos = sega.endPt();
349 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
350 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
351 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
352 verts[*v + 0].fUV.set(0,0);
353 verts[*v + 1].fUV.set(0,-SK_Scalar1);
354 verts[*v + 2].fUV.set(0,-SK_Scalar1);
355 verts[*v + 3].fUV.set(0,-SK_Scalar1);
356 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
357 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
358 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
359 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000360
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000361 idxs[*i + 0] = *v + 0;
362 idxs[*i + 1] = *v + 2;
363 idxs[*i + 2] = *v + 1;
364 idxs[*i + 3] = *v + 0;
365 idxs[*i + 4] = *v + 3;
366 idxs[*i + 5] = *v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000367
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000368 *v += 4;
369 *i += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000370
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000371 if (Segment::kLine == segb.fType) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000372 verts[*v + 0].fPos = fanPt;
373 verts[*v + 1].fPos = sega.endPt();
374 verts[*v + 2].fPos = segb.fPts[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000375
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000376 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
377 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000378
379 // we draw the line edge as a degenerate quad (u is 0, v is the
380 // signed distance to the edge)
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000381 SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos,
382 verts[*v + 2].fPos);
383 verts[*v + 0].fUV.set(0, dist);
384 verts[*v + 1].fUV.set(0, 0);
385 verts[*v + 2].fUV.set(0, 0);
386 verts[*v + 3].fUV.set(0, -SK_Scalar1);
387 verts[*v + 4].fUV.set(0, -SK_Scalar1);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000388
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000389 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
390 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
391 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
392 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
393 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000394
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000395 idxs[*i + 0] = *v + 0;
396 idxs[*i + 1] = *v + 2;
397 idxs[*i + 2] = *v + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000398
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000399 idxs[*i + 3] = *v + 3;
400 idxs[*i + 4] = *v + 1;
401 idxs[*i + 5] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000402
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000403 idxs[*i + 6] = *v + 4;
404 idxs[*i + 7] = *v + 3;
405 idxs[*i + 8] = *v + 2;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000406
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000407 *v += 5;
408 *i += 9;
bsalomon@google.com06809612012-01-21 15:03:39 +0000409 } else {
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000410 GrPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000411
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000412 GrVec midVec = segb.fNorms[0] + segb.fNorms[1];
413 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000414
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000415 verts[*v + 0].fPos = fanPt;
416 verts[*v + 1].fPos = qpts[0];
417 verts[*v + 2].fPos = qpts[2];
418 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
419 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
420 verts[*v + 5].fPos = qpts[1] + midVec;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000421
bsalomon@google.com81712882012-11-01 17:12:34 +0000422 SkScalar c = segb.fNorms[0].dot(qpts[0]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000423 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
424 verts[*v + 1].fD0 = 0.f;
425 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
426 verts[*v + 3].fD0 = -SK_ScalarMax/100;
427 verts[*v + 4].fD0 = -SK_ScalarMax/100;
428 verts[*v + 5].fD0 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000429
430 c = segb.fNorms[1].dot(qpts[2]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000431 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
432 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
433 verts[*v + 2].fD1 = 0.f;
434 verts[*v + 3].fD1 = -SK_ScalarMax/100;
435 verts[*v + 4].fD1 = -SK_ScalarMax/100;
436 verts[*v + 5].fD1 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000437
bsalomon@google.com19713172012-03-15 13:51:08 +0000438 GrPathUtils::QuadUVMatrix toUV(qpts);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000439 toUV.apply<6, sizeof(QuadVertex), sizeof(GrPoint)>(verts + *v);
bsalomon@google.com06809612012-01-21 15:03:39 +0000440
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000441 idxs[*i + 0] = *v + 3;
442 idxs[*i + 1] = *v + 1;
443 idxs[*i + 2] = *v + 2;
444 idxs[*i + 3] = *v + 4;
445 idxs[*i + 4] = *v + 3;
446 idxs[*i + 5] = *v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000447
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000448 idxs[*i + 6] = *v + 5;
449 idxs[*i + 7] = *v + 3;
450 idxs[*i + 8] = *v + 4;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000451
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000452 idxs[*i + 9] = *v + 0;
453 idxs[*i + 10] = *v + 2;
454 idxs[*i + 11] = *v + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000455
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000456 *v += 6;
457 *i += 12;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000458 }
459 }
460}
461
462}
463
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000464///////////////////////////////////////////////////////////////////////////////
465
466/*
467 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
468 * two components of the vertex attribute. Coverage is based on signed
469 * distance with negative being inside, positive outside. The edge is specified in
470 * window space (y-down). If either the third or fourth component of the interpolated
471 * vertex coord is > 0 then the pixel is considered outside the edge. This is used to
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000472 * attempt to trim to a portion of the infinite quad.
473 * Requires shader derivative instruction support.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000474 */
475
476class QuadEdgeEffect : public GrEffect {
477public:
478
479 static GrEffectRef* Create() {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000480 GR_CREATE_STATIC_EFFECT(gQuadEdgeEffect, QuadEdgeEffect, ());
481 gQuadEdgeEffect->ref();
482 return gQuadEdgeEffect;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000483 }
484
485 virtual ~QuadEdgeEffect() {}
486
487 static const char* Name() { return "QuadEdge"; }
488
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000489 virtual void getConstantColorComponents(GrColor* color,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000490 uint32_t* validFlags) const SK_OVERRIDE {
491 *validFlags = 0;
492 }
493
494 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
495 return GrTBackendEffectFactory<QuadEdgeEffect>::getInstance();
496 }
497
498 class GLEffect : public GrGLEffect {
499 public:
500 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
501 : INHERITED (factory) {}
502
503 virtual void emitCode(GrGLShaderBuilder* builder,
504 const GrDrawEffect& drawEffect,
505 EffectKey key,
506 const char* outputColor,
507 const char* inputColor,
508 const TextureSamplerArray& samplers) SK_OVERRIDE {
509 const char *vsName, *fsName;
510 const SkString* attrName =
511 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
512 builder->fsCodeAppendf("\t\tfloat edgeAlpha;\n");
513
514 SkAssertResult(builder->enableFeature(
515 GrGLShaderBuilder::kStandardDerivatives_GLSLFeature));
516 builder->addVarying(kVec4f_GrSLType, "QuadEdge", &vsName, &fsName);
517
518 // keep the derivative instructions outside the conditional
519 builder->fsCodeAppendf("\t\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
520 builder->fsCodeAppendf("\t\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
521 builder->fsCodeAppendf("\t\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName);
522 // today we know z and w are in device space. We could use derivatives
523 builder->fsCodeAppendf("\t\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName,
524 fsName);
525 builder->fsCodeAppendf ("\t\t} else {\n");
526 builder->fsCodeAppendf("\t\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
527 "\t\t\t 2.0*%s.x*duvdy.x - duvdy.y);\n",
528 fsName, fsName);
529 builder->fsCodeAppendf("\t\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName,
530 fsName);
531 builder->fsCodeAppendf("\t\t\tedgeAlpha = "
532 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n\t\t}\n");
533
534 SkString modulate;
bsalomon@google.com018f1792013-04-18 19:36:09 +0000535 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000536 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
537
538 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
539 }
540
541 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
542 return 0x0;
543 }
544
545 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
546
547 private:
548 typedef GrGLEffect INHERITED;
549 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000550
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000551private:
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000552 QuadEdgeEffect() {
553 this->addVertexAttrib(kVec4f_GrSLType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000554 }
555
556 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
557 return true;
558 }
559
560 GR_DECLARE_EFFECT_TEST;
561
562 typedef GrEffect INHERITED;
563};
564
565GR_DEFINE_EFFECT_TEST(QuadEdgeEffect);
566
567GrEffectRef* QuadEdgeEffect::TestCreate(SkMWCRandom* random,
568 GrContext*,
569 const GrDrawTargetCaps& caps,
570 GrTexture*[]) {
571 // Doesn't work without derivative instructions.
572 return caps.shaderDerivativeSupport() ? QuadEdgeEffect::Create() : NULL;
573}
574
575///////////////////////////////////////////////////////////////////////////////
576
robertphillips@google.comfa662942012-05-17 12:20:22 +0000577bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000578 const SkStrokeRec& stroke,
robertphillips@google.comfa662942012-05-17 12:20:22 +0000579 const GrDrawTarget* target,
580 bool antiAlias) const {
bsalomon@google.combcce8922013-03-25 15:38:39 +0000581 return (target->caps()->shaderDerivativeSupport() && antiAlias &&
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000582 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
robertphillips@google.comfa662942012-05-17 12:20:22 +0000583}
584
robertphillips@google.com42903302013-04-20 12:26:07 +0000585namespace {
586
587// position + edge
588extern const GrVertexAttrib gPathAttribs[] = {
589 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
590 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
591};
592
593};
594
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000595bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath,
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000596 const SkStrokeRec&,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000597 GrDrawTarget* target,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000598 bool antiAlias) {
599
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000600 const SkPath* path = &origPath;
601 if (path->isEmpty()) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000602 return true;
603 }
bsalomon@google.com4647f902013-03-26 14:45:27 +0000604
bsalomon@google.com137f1342013-05-29 21:27:53 +0000605 SkMatrix viewMatrix = target->getDrawState().getViewMatrix();
606 GrDrawTarget::AutoStateRestore asr;
607 if (!asr.setIdentity(target, GrDrawTarget::kPreserve_ASRInit)) {
bsalomon@google.come3d32162012-07-20 13:37:06 +0000608 return false;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000609 }
bsalomon@google.com137f1342013-05-29 21:27:53 +0000610 GrDrawState* drawState = target->drawState();
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000611
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000612 // We use the fact that SkPath::transform path does subdivision based on
613 // perspective. Otherwise, we apply the view matrix when copying to the
614 // segment representation.
615 SkPath tmpPath;
bsalomon@google.com137f1342013-05-29 21:27:53 +0000616 if (viewMatrix.hasPerspective()) {
617 origPath.transform(viewMatrix, &tmpPath);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000618 path = &tmpPath;
bsalomon@google.com137f1342013-05-29 21:27:53 +0000619 viewMatrix = SkMatrix::I();
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000620 }
621
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000622 QuadVertex *verts;
623 uint16_t* idxs;
624
bsalomon@google.com06809612012-01-21 15:03:39 +0000625 int vCount;
626 int iCount;
bsalomon@google.com68a5b262012-03-05 18:24:07 +0000627 enum {
628 kPreallocSegmentCnt = 512 / sizeof(Segment),
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000629 kPreallocDrawCnt = 4,
bsalomon@google.com68a5b262012-03-05 18:24:07 +0000630 };
631 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000632 SkPoint fanPt;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000633
bsalomon@google.com137f1342013-05-29 21:27:53 +0000634 if (!get_segments(*path, viewMatrix, &segments, &fanPt, &vCount, &iCount)) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000635 return false;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000636 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000637
robertphillips@google.com42903302013-04-20 12:26:07 +0000638 drawState->setVertexAttribs<gPathAttribs>(SK_ARRAY_COUNT(gPathAttribs));
bsalomon@google.com4647f902013-03-26 14:45:27 +0000639
640 enum {
641 // the edge effects share this stage with glyph rendering
642 // (kGlyphMaskStage in GrTextContext) && SW path rendering
643 // (kPathMaskStage in GrSWMaskHelper)
644 kEdgeEffectStage = GrPaint::kTotalStages,
645 };
646 static const int kEdgeAttrIndex = 1;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000647 GrEffectRef* quadEffect = QuadEdgeEffect::Create();
bsalomon@google.com4647f902013-03-26 14:45:27 +0000648 drawState->setEffect(kEdgeEffectStage, quadEffect, kEdgeAttrIndex)->unref();
649
jvanverth@google.comb75b0a02013-02-05 20:33:30 +0000650 GrDrawTarget::AutoReleaseGeometry arg(target, vCount, iCount);
bsalomon@google.comb3729422012-03-07 19:13:28 +0000651 if (!arg.succeeded()) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000652 return false;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000653 }
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000654 GrAssert(sizeof(QuadVertex) == drawState->getVertexSize());
bsalomon@google.comb3729422012-03-07 19:13:28 +0000655 verts = reinterpret_cast<QuadVertex*>(arg.vertices());
656 idxs = reinterpret_cast<uint16_t*>(arg.indices());
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000657
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000658 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
659 create_vertices(segments, fanPt, &draws, verts, idxs);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000660
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000661 // This is valid because all the computed verts are within 1 pixel of the path control points.
662 SkRect devBounds;
bsalomon@google.com137f1342013-05-29 21:27:53 +0000663 devBounds = path->getBounds();
664 viewMatrix.mapRect(&devBounds);
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000665 devBounds.outset(SK_Scalar1, SK_Scalar1);
666
667 // Check devBounds
668#if GR_DEBUG
669 SkRect tolDevBounds = devBounds;
670 tolDevBounds.outset(SK_Scalar1 / 10000, SK_Scalar1 / 10000);
671 SkRect actualBounds;
672 actualBounds.set(verts[0].fPos, verts[1].fPos);
673 for (int i = 2; i < vCount; ++i) {
674 actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY);
675 }
676 GrAssert(tolDevBounds.contains(actualBounds));
677#endif
678
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000679 int vOffset = 0;
680 for (int i = 0; i < draws.count(); ++i) {
681 const Draw& draw = draws[i];
682 target->drawIndexed(kTriangles_GrPrimitiveType,
683 vOffset, // start vertex
684 0, // start index
685 draw.fVertexCnt,
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000686 draw.fIndexCnt,
687 &devBounds);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000688 vOffset += draw.fVertexCnt;
689 }
bsalomon@google.coma8347462012-10-08 18:59:39 +0000690
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000691 return true;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000692}