blob: f4a6266bc17b7bb48bf77df73b1e439c98359e1c [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
joshualitt27f398f2015-02-05 14:39:01 -080011#include "GrBatch.h"
12#include "GrBatchTarget.h"
13#include "GrBufferAllocPool.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000014#include "GrContext.h"
bsalomon@google.comc26d94f2013-03-25 18:19:00 +000015#include "GrDrawTargetCaps.h"
joshualitteb2a6762014-12-04 11:35:33 -080016#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080017#include "GrInvariantOutput.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000018#include "GrPathUtils.h"
egdaniel8dd688b2015-01-22 10:16:09 -080019#include "GrProcessor.h"
20#include "GrPipelineBuilder.h"
egdanielaf18a092015-01-05 10:22:28 -080021#include "SkGeometry.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000022#include "SkString.h"
sugoi@google.com5f74cf82012-12-17 21:16:45 +000023#include "SkStrokeRec.h"
commit-bot@chromium.org933e65d2014-03-20 20:00:24 +000024#include "SkTraceEvent.h"
joshualittb0a8a372014-09-23 09:50:21 -070025#include "gl/GrGLProcessor.h"
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000026#include "gl/GrGLSL.h"
joshualitt249af152014-09-15 11:41:13 -070027#include "gl/GrGLGeometryProcessor.h"
joshualitteb2a6762014-12-04 11:35:33 -080028#include "gl/builders/GrGLProgramBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000029
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000030GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
31}
32
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000033struct Segment {
34 enum {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000035 // These enum values are assumed in member functions below.
36 kLine = 0,
37 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000038 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000039
bsalomon@google.com9aed1142012-01-30 14:28:39 +000040 // line uses one pt, quad uses 2 pts
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000041 SkPoint fPts[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000042 // normal to edge ending at each pt
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000043 SkVector fNorms[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000044 // is the corner where the previous segment meets this segment
45 // sharp. If so, fMid is a normalized bisector facing outward.
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000046 SkVector fMid;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000047
48 int countPoints() {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000049 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
50 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000051 }
52 const SkPoint& endPt() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000053 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
54 return fPts[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000055 };
56 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000057 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
58 return fNorms[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000059 };
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000060};
61
62typedef SkTArray<Segment, true> SegmentArray;
63
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +000064static void center_of_mass(const SegmentArray& segments, SkPoint* c) {
bsalomon@google.com81712882012-11-01 17:12:34 +000065 SkScalar area = 0;
vandebo@chromium.org6390c722012-03-28 21:03:22 +000066 SkPoint center = {0, 0};
bsalomon@google.com9aed1142012-01-30 14:28:39 +000067 int count = segments.count();
vandebo@chromium.org6390c722012-03-28 21:03:22 +000068 SkPoint p0 = {0, 0};
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000069 if (count > 2) {
70 // We translate the polygon so that the first point is at the origin.
71 // This avoids some precision issues with small area polygons far away
72 // from the origin.
73 p0 = segments[0].endPt();
74 SkPoint pi;
75 SkPoint pj;
bsalomon@google.coma51ab842012-07-10 19:53:34 +000076 // the first and last iteration of the below loop would compute
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000077 // zeros since the starting / ending point is (0,0). So instead we start
78 // at i=1 and make the last iteration i=count-2.
79 pj = segments[1].endPt() - p0;
80 for (int i = 1; i < count - 1; ++i) {
81 pi = pj;
82 const SkPoint pj = segments[i + 1].endPt() - p0;
83
bsalomon@google.com81712882012-11-01 17:12:34 +000084 SkScalar t = SkScalarMul(pi.fX, pj.fY) - SkScalarMul(pj.fX, pi.fY);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000085 area += t;
86 center.fX += (pi.fX + pj.fX) * t;
87 center.fY += (pi.fY + pj.fY) * t;
88
89 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000090 }
bsalomon@google.com278dc692012-02-15 16:52:51 +000091 // If the poly has no area then we instead return the average of
92 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000093 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +000094 SkPoint avg;
95 avg.set(0, 0);
96 for (int i = 0; i < count; ++i) {
97 const SkPoint& pt = segments[i].endPt();
98 avg.fX += pt.fX;
99 avg.fY += pt.fY;
100 }
101 SkScalar denom = SK_Scalar1 / count;
102 avg.scale(denom);
103 *c = avg;
104 } else {
105 area *= 3;
bsalomon@google.com81712882012-11-01 17:12:34 +0000106 area = SkScalarDiv(SK_Scalar1, area);
107 center.fX = SkScalarMul(center.fX, area);
108 center.fY = SkScalarMul(center.fY, area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +0000109 // undo the translate of p0 to the origin.
110 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000111 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000112 SkASSERT(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000113}
114
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000115static void compute_vectors(SegmentArray* segments,
116 SkPoint* fanPt,
117 SkPath::Direction dir,
118 int* vCount,
119 int* iCount) {
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000120 center_of_mass(*segments, fanPt);
121 int count = segments->count();
122
bsalomon@google.com278dc692012-02-15 16:52:51 +0000123 // Make the normals point towards the outside
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000124 SkPoint::Side normSide;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000125 if (dir == SkPath::kCCW_Direction) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000126 normSide = SkPoint::kRight_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000127 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000128 normSide = SkPoint::kLeft_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000129 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000130
131 *vCount = 0;
132 *iCount = 0;
133 // compute normals at all points
134 for (int a = 0; a < count; ++a) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000135 Segment& sega = (*segments)[a];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000136 int b = (a + 1) % count;
137 Segment& segb = (*segments)[b];
138
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000139 const SkPoint* prevPt = &sega.endPt();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000140 int n = segb.countPoints();
141 for (int p = 0; p < n; ++p) {
142 segb.fNorms[p] = segb.fPts[p] - *prevPt;
143 segb.fNorms[p].normalize();
144 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
145 prevPt = &segb.fPts[p];
146 }
147 if (Segment::kLine == segb.fType) {
148 *vCount += 5;
149 *iCount += 9;
150 } else {
151 *vCount += 6;
152 *iCount += 12;
153 }
154 }
155
156 // compute mid-vectors where segments meet. TODO: Detect shallow corners
157 // and leave out the wedges and close gaps by stitching segments together.
158 for (int a = 0; a < count; ++a) {
159 const Segment& sega = (*segments)[a];
160 int b = (a + 1) % count;
161 Segment& segb = (*segments)[b];
162 segb.fMid = segb.fNorms[0] + sega.endNorm();
163 segb.fMid.normalize();
164 // corner wedges
165 *vCount += 4;
166 *iCount += 6;
167 }
168}
169
bsalomon@google.com9732f622012-01-31 15:19:21 +0000170struct DegenerateTestData {
171 DegenerateTestData() { fStage = kInitial; }
172 bool isDegenerate() const { return kNonDegenerate != fStage; }
173 enum {
174 kInitial,
175 kPoint,
176 kLine,
177 kNonDegenerate
178 } fStage;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000179 SkPoint fFirstPoint;
180 SkVector fLineNormal;
bsalomon@google.com81712882012-11-01 17:12:34 +0000181 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000182};
183
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000184static const SkScalar kClose = (SK_Scalar1 / 16);
185static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000186
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000187static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000188 switch (data->fStage) {
189 case DegenerateTestData::kInitial:
190 data->fFirstPoint = pt;
191 data->fStage = DegenerateTestData::kPoint;
192 break;
193 case DegenerateTestData::kPoint:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000194 if (pt.distanceToSqd(data->fFirstPoint) > kCloseSqd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000195 data->fLineNormal = pt - data->fFirstPoint;
196 data->fLineNormal.normalize();
197 data->fLineNormal.setOrthog(data->fLineNormal);
198 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
199 data->fStage = DegenerateTestData::kLine;
200 }
201 break;
202 case DegenerateTestData::kLine:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000203 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000204 data->fStage = DegenerateTestData::kNonDegenerate;
205 }
206 case DegenerateTestData::kNonDegenerate:
207 break;
208 default:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000209 SkFAIL("Unexpected degenerate test stage.");
bsalomon@google.com9732f622012-01-31 15:19:21 +0000210 }
211}
212
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000213static inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Direction* dir) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000214 if (!path.cheapComputeDirection(dir)) {
215 return false;
216 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000217 // check whether m reverses the orientation
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000218 SkASSERT(!m.hasPerspective());
bsalomon@google.com81712882012-11-01 17:12:34 +0000219 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
220 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000221 if (det2x2 < 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000222 *dir = SkPath::OppositeDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000223 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000224 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000225}
226
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000227static inline void add_line_to_segment(const SkPoint& pt,
joshualitt27f398f2015-02-05 14:39:01 -0800228 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000229 segments->push_back();
230 segments->back().fType = Segment::kLine;
231 segments->back().fPts[0] = pt;
232}
233
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000234static inline void add_quad_segment(const SkPoint pts[3],
joshualitt27f398f2015-02-05 14:39:01 -0800235 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000236 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
237 if (pts[0] != pts[2]) {
joshualitt27f398f2015-02-05 14:39:01 -0800238 add_line_to_segment(pts[2], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000239 }
240 } else {
241 segments->push_back();
242 segments->back().fType = Segment::kQuad;
243 segments->back().fPts[0] = pts[1];
244 segments->back().fPts[1] = pts[2];
245 }
246}
247
248static inline void add_cubic_segments(const SkPoint pts[4],
249 SkPath::Direction dir,
joshualitt27f398f2015-02-05 14:39:01 -0800250 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000251 SkSTArray<15, SkPoint, true> quads;
252 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
253 int count = quads.count();
254 for (int q = 0; q < count; q += 3) {
joshualitt27f398f2015-02-05 14:39:01 -0800255 add_quad_segment(&quads[q], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000256 }
257}
258
259static bool get_segments(const SkPath& path,
260 const SkMatrix& m,
261 SegmentArray* segments,
262 SkPoint* fanPt,
263 int* vCount,
joshualitt27f398f2015-02-05 14:39:01 -0800264 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000265 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000266 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000267 // to the path from the sample to compute coverage. Every pixel intersected
268 // 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 +0000269 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000270 // thus should be very light. This is particularly egregious for degenerate
271 // line paths. We detect paths that are very close to a line (zero area) and
272 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000273 DegenerateTestData degenerateData;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000274 SkPath::Direction dir;
275 // get_direction can fail for some degenerate paths.
276 if (!get_direction(path, m, &dir)) {
277 return false;
278 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000279
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000280 for (;;) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000281 SkPoint pts[4];
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000282 SkPath::Verb verb = iter.next(pts);
283 switch (verb) {
284 case SkPath::kMove_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000285 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000286 update_degenerate_test(&degenerateData, pts[0]);
287 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000288 case SkPath::kLine_Verb: {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000289 m.mapPoints(&pts[1], 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000290 update_degenerate_test(&degenerateData, pts[1]);
joshualitt27f398f2015-02-05 14:39:01 -0800291 add_line_to_segment(pts[1], segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000292 break;
293 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000294 case SkPath::kQuad_Verb:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000295 m.mapPoints(pts, 3);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000296 update_degenerate_test(&degenerateData, pts[1]);
297 update_degenerate_test(&degenerateData, pts[2]);
joshualitt27f398f2015-02-05 14:39:01 -0800298 add_quad_segment(pts, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000299 break;
egdanielaf18a092015-01-05 10:22:28 -0800300 case SkPath::kConic_Verb: {
301 m.mapPoints(pts, 3);
302 SkScalar weight = iter.conicWeight();
303 SkAutoConicToQuads converter;
304 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5f);
305 for (int i = 0; i < converter.countQuads(); ++i) {
306 update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
307 update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
joshualitt27f398f2015-02-05 14:39:01 -0800308 add_quad_segment(quadPts + 2*i, segments);
egdanielaf18a092015-01-05 10:22:28 -0800309 }
310 break;
311 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000312 case SkPath::kCubic_Verb: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000313 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000314 update_degenerate_test(&degenerateData, pts[1]);
315 update_degenerate_test(&degenerateData, pts[2]);
316 update_degenerate_test(&degenerateData, pts[3]);
joshualitt27f398f2015-02-05 14:39:01 -0800317 add_cubic_segments(pts, dir, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000318 break;
319 };
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000320 case SkPath::kDone_Verb:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000321 if (degenerateData.isDegenerate()) {
322 return false;
323 } else {
bsalomon@google.com278dc692012-02-15 16:52:51 +0000324 compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000325 return true;
326 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000327 default:
328 break;
329 }
330 }
331}
332
333struct QuadVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000334 SkPoint fPos;
335 SkPoint fUV;
bsalomon@google.com81712882012-11-01 17:12:34 +0000336 SkScalar fD0;
337 SkScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000338};
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000339
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000340struct Draw {
341 Draw() : fVertexCnt(0), fIndexCnt(0) {}
342 int fVertexCnt;
343 int fIndexCnt;
344};
345
346typedef SkTArray<Draw, true> DrawArray;
347
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000348static void create_vertices(const SegmentArray& segments,
349 const SkPoint& fanPt,
350 DrawArray* draws,
351 QuadVertex* verts,
352 uint16_t* idxs) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000353 Draw* draw = &draws->push_back();
354 // alias just to make vert/index assignments easier to read.
355 int* v = &draw->fVertexCnt;
356 int* i = &draw->fIndexCnt;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000357
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000358 int count = segments.count();
359 for (int a = 0; a < count; ++a) {
360 const Segment& sega = segments[a];
361 int b = (a + 1) % count;
362 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000363
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000364 // Check whether adding the verts for this segment to the current draw would cause index
365 // values to overflow.
366 int vCount = 4;
367 if (Segment::kLine == segb.fType) {
368 vCount += 5;
369 } else {
370 vCount += 6;
371 }
372 if (draw->fVertexCnt + vCount > (1 << 16)) {
373 verts += *v;
374 idxs += *i;
375 draw = &draws->push_back();
376 v = &draw->fVertexCnt;
377 i = &draw->fIndexCnt;
378 }
379
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000380 // FIXME: These tris are inset in the 1 unit arc around the corner
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000381 verts[*v + 0].fPos = sega.endPt();
382 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
383 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
384 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
385 verts[*v + 0].fUV.set(0,0);
386 verts[*v + 1].fUV.set(0,-SK_Scalar1);
387 verts[*v + 2].fUV.set(0,-SK_Scalar1);
388 verts[*v + 3].fUV.set(0,-SK_Scalar1);
389 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;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000393
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000394 idxs[*i + 0] = *v + 0;
395 idxs[*i + 1] = *v + 2;
396 idxs[*i + 2] = *v + 1;
397 idxs[*i + 3] = *v + 0;
398 idxs[*i + 4] = *v + 3;
399 idxs[*i + 5] = *v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000400
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000401 *v += 4;
402 *i += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000403
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000404 if (Segment::kLine == segb.fType) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000405 verts[*v + 0].fPos = fanPt;
406 verts[*v + 1].fPos = sega.endPt();
407 verts[*v + 2].fPos = segb.fPts[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000408
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000409 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
410 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000411
412 // we draw the line edge as a degenerate quad (u is 0, v is the
413 // signed distance to the edge)
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000414 SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos,
415 verts[*v + 2].fPos);
416 verts[*v + 0].fUV.set(0, dist);
417 verts[*v + 1].fUV.set(0, 0);
418 verts[*v + 2].fUV.set(0, 0);
419 verts[*v + 3].fUV.set(0, -SK_Scalar1);
420 verts[*v + 4].fUV.set(0, -SK_Scalar1);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000421
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000422 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
423 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
424 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
425 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
426 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000427
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000428 idxs[*i + 0] = *v + 0;
429 idxs[*i + 1] = *v + 2;
430 idxs[*i + 2] = *v + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000431
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000432 idxs[*i + 3] = *v + 3;
433 idxs[*i + 4] = *v + 1;
434 idxs[*i + 5] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000435
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000436 idxs[*i + 6] = *v + 4;
437 idxs[*i + 7] = *v + 3;
438 idxs[*i + 8] = *v + 2;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000439
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000440 *v += 5;
441 *i += 9;
bsalomon@google.com06809612012-01-21 15:03:39 +0000442 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000443 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000444
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000445 SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000446 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000447
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000448 verts[*v + 0].fPos = fanPt;
449 verts[*v + 1].fPos = qpts[0];
450 verts[*v + 2].fPos = qpts[2];
451 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
452 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
453 verts[*v + 5].fPos = qpts[1] + midVec;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000454
bsalomon@google.com81712882012-11-01 17:12:34 +0000455 SkScalar c = segb.fNorms[0].dot(qpts[0]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000456 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
457 verts[*v + 1].fD0 = 0.f;
458 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
459 verts[*v + 3].fD0 = -SK_ScalarMax/100;
460 verts[*v + 4].fD0 = -SK_ScalarMax/100;
461 verts[*v + 5].fD0 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000462
463 c = segb.fNorms[1].dot(qpts[2]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000464 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
465 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
466 verts[*v + 2].fD1 = 0.f;
467 verts[*v + 3].fD1 = -SK_ScalarMax/100;
468 verts[*v + 4].fD1 = -SK_ScalarMax/100;
469 verts[*v + 5].fD1 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000470
bsalomon@google.com19713172012-03-15 13:51:08 +0000471 GrPathUtils::QuadUVMatrix toUV(qpts);
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000472 toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v);
bsalomon@google.com06809612012-01-21 15:03:39 +0000473
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000474 idxs[*i + 0] = *v + 3;
475 idxs[*i + 1] = *v + 1;
476 idxs[*i + 2] = *v + 2;
477 idxs[*i + 3] = *v + 4;
478 idxs[*i + 4] = *v + 3;
479 idxs[*i + 5] = *v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000480
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000481 idxs[*i + 6] = *v + 5;
482 idxs[*i + 7] = *v + 3;
483 idxs[*i + 8] = *v + 4;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000484
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000485 idxs[*i + 9] = *v + 0;
486 idxs[*i + 10] = *v + 2;
487 idxs[*i + 11] = *v + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000488
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000489 *v += 6;
490 *i += 12;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000491 }
492 }
493}
494
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000495///////////////////////////////////////////////////////////////////////////////
496
497/*
498 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
499 * two components of the vertex attribute. Coverage is based on signed
500 * distance with negative being inside, positive outside. The edge is specified in
501 * window space (y-down). If either the third or fourth component of the interpolated
502 * 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 +0000503 * attempt to trim to a portion of the infinite quad.
504 * Requires shader derivative instruction support.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000505 */
506
joshualitt249af152014-09-15 11:41:13 -0700507class QuadEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000508public:
509
joshualittd27f73e2014-12-29 07:43:36 -0800510 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& localMatrix) {
511 return SkNEW_ARGS(QuadEdgeEffect, (color, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000512 }
513
514 virtual ~QuadEdgeEffect() {}
515
mtklein72c9faa2015-01-09 10:06:39 -0800516 const char* name() const SK_OVERRIDE { return "QuadEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000517
joshualitt71c92602015-01-14 08:12:47 -0800518 const Attribute* inPosition() const { return fInPosition; }
519 const Attribute* inQuadEdge() const { return fInQuadEdge; }
joshualitt249af152014-09-15 11:41:13 -0700520
joshualittb0a8a372014-09-23 09:50:21 -0700521 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000522 public:
joshualitteb2a6762014-12-04 11:35:33 -0800523 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800524 const GrBatchTracker&)
525 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000526
robertphillips46d36f02015-01-18 08:14:14 -0800527 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE {
joshualitt2dd1ae02014-12-03 06:24:10 -0800528 const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800529 GrGLGPBuilder* pb = args.fPB;
530 GrGLVertexBuilder* vsBuilder = pb->getVertexShaderBuilder();
joshualitt2dd1ae02014-12-03 06:24:10 -0800531
joshualittabb52a12015-01-13 15:02:10 -0800532 // emit attributes
533 vsBuilder->emitAttributes(qe);
534
joshualitt74077b92014-10-24 11:26:03 -0700535 GrGLVertToFrag v(kVec4f_GrSLType);
536 args.fPB->addVarying("QuadEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -0800537 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.inQuadEdge()->fName);
538
joshualitt9b989322014-12-15 14:16:27 -0800539 const BatchTracker& local = args.fBT.cast<BatchTracker>();
540
541 // Setup pass through color
542 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
543 &fColorUniform);
544
joshualittee2af952014-12-30 09:04:15 -0800545 // setup uniform viewMatrix
546 this->addUniformViewMatrix(pb);
547
joshualittabb52a12015-01-13 15:02:10 -0800548 // Setup position
robertphillips46d36f02015-01-18 08:14:14 -0800549 SetupPosition(vsBuilder, gpArgs, qe.inPosition()->fName,
550 qe.viewMatrix(), this->uViewM());
joshualittabb52a12015-01-13 15:02:10 -0800551
552 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800553 this->emitTransforms(args.fPB, gpArgs->fPositionVar, qe.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800554 qe.localMatrix(), args.fTransformsIn, args.fTransformsOut);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000555
joshualittc369e7c2014-10-22 10:56:26 -0700556 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700557
558 SkAssertResult(fsBuilder->enableFeature(
559 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
joshualitt74077b92014-10-24 11:26:03 -0700560 fsBuilder->codeAppendf("float edgeAlpha;");
joshualitt30ba4362014-08-21 20:18:45 -0700561
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000562 // keep the derivative instructions outside the conditional
joshualitt74077b92014-10-24 11:26:03 -0700563 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s.xy);", v.fsIn());
564 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s.xy);", v.fsIn());
565 fsBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000566 // today we know z and w are in device space. We could use derivatives
joshualitt74077b92014-10-24 11:26:03 -0700567 fsBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
568 v.fsIn());
569 fsBuilder->codeAppendf ("} else {");
570 fsBuilder->codeAppendf("vec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,"
571 " 2.0*%s.x*duvdy.x - duvdy.y);",
572 v.fsIn(), v.fsIn());
573 fsBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
574 v.fsIn());
575 fsBuilder->codeAppendf("edgeAlpha = "
576 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000577
joshualitt2dd1ae02014-12-03 06:24:10 -0800578 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000579 }
580
joshualitt9b989322014-12-15 14:16:27 -0800581 static inline void GenKey(const GrGeometryProcessor& gp,
582 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800583 const GrGLCaps&,
joshualitt9b989322014-12-15 14:16:27 -0800584 GrProcessorKeyBuilder* b) {
585 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800586 uint32_t key = local.fInputColorType << 16;
587 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 : 0x0;
588 key |= ComputePosKey(gp.viewMatrix()) << 1;
589 b->add32(key);
joshualitt9b989322014-12-15 14:16:27 -0800590 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000591
joshualitt9b989322014-12-15 14:16:27 -0800592 virtual void setData(const GrGLProgramDataManager& pdman,
593 const GrPrimitiveProcessor& gp,
594 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800595 this->setUniformViewMatrix(pdman, gp.viewMatrix());
596
joshualitt9b989322014-12-15 14:16:27 -0800597 const BatchTracker& local = bt.cast<BatchTracker>();
598 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
599 GrGLfloat c[4];
600 GrColorToRGBAFloat(local.fColor, c);
601 pdman.set4fv(fColorUniform, 1, c);
602 fColor = local.fColor;
603 }
604 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000605
606 private:
joshualitt9b989322014-12-15 14:16:27 -0800607 GrColor fColor;
608 UniformHandle fColorUniform;
609
joshualitt249af152014-09-15 11:41:13 -0700610 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000611 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000612
joshualitteb2a6762014-12-04 11:35:33 -0800613 virtual void getGLProcessorKey(const GrBatchTracker& bt,
614 const GrGLCaps& caps,
615 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
616 GLProcessor::GenKey(*this, bt, caps, b);
617 }
618
joshualittabb52a12015-01-13 15:02:10 -0800619 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
620 const GrGLCaps&) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800621 return SkNEW_ARGS(GLProcessor, (*this, bt));
622 }
623
joshualitt4d8da812015-01-28 12:53:54 -0800624 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800625 BatchTracker* local = bt->cast<BatchTracker>();
626 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800627 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800628 }
629
joshualitt290c09b2014-12-19 13:45:20 -0800630 bool onCanMakeEqual(const GrBatchTracker& m,
631 const GrGeometryProcessor& that,
632 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800633 const BatchTracker& mine = m.cast<BatchTracker>();
634 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800635 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
636 that, theirs.fUsesLocalCoords) &&
637 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800638 theirs.fInputColorType, theirs.fColor);
639 }
640
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000641private:
joshualittd27f73e2014-12-29 07:43:36 -0800642 QuadEdgeEffect(GrColor color, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800643 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800644 this->initClassID<QuadEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800645 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
646 fInQuadEdge = &this->addVertexAttrib(Attribute("inQuadEdge", kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000647 }
648
mtklein72c9faa2015-01-09 10:06:39 -0800649 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000650 return true;
651 }
652
mtklein72c9faa2015-01-09 10:06:39 -0800653 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800654 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700655 }
656
joshualitt9b989322014-12-15 14:16:27 -0800657 struct BatchTracker {
658 GrGPInput fInputColorType;
659 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800660 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800661 };
662
joshualitt71c92602015-01-14 08:12:47 -0800663 const Attribute* fInPosition;
664 const Attribute* fInQuadEdge;
joshualitt249af152014-09-15 11:41:13 -0700665
joshualittb0a8a372014-09-23 09:50:21 -0700666 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000667
joshualitt2e3b3e32014-12-09 13:31:14 -0800668 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000669};
670
joshualittb0a8a372014-09-23 09:50:21 -0700671GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000672
joshualittb0a8a372014-09-23 09:50:21 -0700673GrGeometryProcessor* QuadEdgeEffect::TestCreate(SkRandom* random,
674 GrContext*,
675 const GrDrawTargetCaps& caps,
676 GrTexture*[]) {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000677 // Doesn't work without derivative instructions.
joshualittd27f73e2014-12-29 07:43:36 -0800678 return caps.shaderDerivativeSupport() ?
679 QuadEdgeEffect::Create(GrRandomColor(random),
680 GrProcessorUnitTest::TestMatrix(random)) : NULL;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000681}
682
683///////////////////////////////////////////////////////////////////////////////
684
joshualitt9853cce2014-11-17 14:22:48 -0800685bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800686 const GrPipelineBuilder*,
joshualitt8059eb92014-12-29 15:10:07 -0800687 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800688 const SkPath& path,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000689 const SkStrokeRec& stroke,
robertphillips@google.comfa662942012-05-17 12:20:22 +0000690 bool antiAlias) const {
bsalomon@google.combcce8922013-03-25 15:38:39 +0000691 return (target->caps()->shaderDerivativeSupport() && antiAlias &&
robertphillips@google.come79f3202014-02-11 16:30:21 +0000692 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
robertphillips@google.comfa662942012-05-17 12:20:22 +0000693}
694
joshualitt27f398f2015-02-05 14:39:01 -0800695class AAConvexPathBatch : public GrBatch {
696public:
697 struct Geometry {
698 GrColor fColor;
699 SkMatrix fViewMatrix;
700 SkPath fPath;
701 SkDEBUGCODE(SkRect fDevBounds;)
702 };
703
704 static GrBatch* Create(const Geometry& geometry) {
705 return SkNEW_ARGS(AAConvexPathBatch, (geometry));
706 }
707
708 const char* name() const SK_OVERRIDE { return "AAConvexBatch"; }
709
710 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
711 // When this is called on a batch, there is only one geometry bundle
712 out->setKnownFourComponents(fGeoData[0].fColor);
713 }
714 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
715 out->setUnknownSingleComponent();
716 }
717
718 void initBatchOpt(const GrBatchOpt& batchOpt) {
719 fBatchOpt = batchOpt;
720 }
721
722 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
723 // Handle any color overrides
724 if (init.fColorIgnored) {
725 fGeoData[0].fColor = GrColor_ILLEGAL;
726 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
727 fGeoData[0].fColor = init.fOverrideColor;
728 }
729
730 // setup batch properties
731 fBatch.fColorIgnored = init.fColorIgnored;
732 fBatch.fColor = fGeoData[0].fColor;
733 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
734 fBatch.fCoverageIgnored = init.fCoverageIgnored;
735 }
736
737 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
738 int instanceCount = fGeoData.count();
739
740 SkMatrix invert;
741 if (!this->viewMatrix().invert(&invert)) {
742 SkDebugf("Could not invert viewmatrix\n");
743 return;
744 }
745
746 // Setup GrGeometryProcessor
747 SkAutoTUnref<GrGeometryProcessor> quadProcessor(QuadEdgeEffect::Create(this->color(),
748 invert));
749
750 batchTarget->initDraw(quadProcessor, pipeline);
751
752 // TODO remove this when batch is everywhere
753 GrPipelineInfo init;
754 init.fColorIgnored = fBatch.fColorIgnored;
755 init.fOverrideColor = GrColor_ILLEGAL;
756 init.fCoverageIgnored = fBatch.fCoverageIgnored;
757 init.fUsesLocalCoords = this->usesLocalCoords();
758 quadProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init);
759
760 // TODO generate all segments for all paths and use one vertex buffer
761 for (int i = 0; i < instanceCount; i++) {
762 Geometry& args = fGeoData[i];
763
764 // We use the fact that SkPath::transform path does subdivision based on
765 // perspective. Otherwise, we apply the view matrix when copying to the
766 // segment representation.
767 const SkMatrix* viewMatrix = &args.fViewMatrix;
768 if (viewMatrix->hasPerspective()) {
769 args.fPath.transform(*viewMatrix);
770 viewMatrix = &SkMatrix::I();
771 }
772
773 int vertexCount;
774 int indexCount;
775 enum {
776 kPreallocSegmentCnt = 512 / sizeof(Segment),
777 kPreallocDrawCnt = 4,
778 };
779 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
780 SkPoint fanPt;
781
782 if (!get_segments(args.fPath, *viewMatrix, &segments, &fanPt, &vertexCount,
783 &indexCount)) {
784 continue;
785 }
786
787 const GrVertexBuffer* vertexBuffer;
788 int firstVertex;
789
790 size_t vertexStride = quadProcessor->getVertexStride();
791 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
792 vertexCount,
793 &vertexBuffer,
794 &firstVertex);
795
796 const GrIndexBuffer* indexBuffer;
797 int firstIndex;
798
799 void *indices = batchTarget->indexPool()->makeSpace(indexCount,
800 &indexBuffer,
801 &firstIndex);
802
803 QuadVertex* verts = reinterpret_cast<QuadVertex*>(vertices);
804 uint16_t* idxs = reinterpret_cast<uint16_t*>(indices);
805
806 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
807 create_vertices(segments, fanPt, &draws, verts, idxs);
808
809#ifdef SK_DEBUG
810 // Check devBounds
811 SkRect actualBounds;
812 actualBounds.set(verts[0].fPos, verts[1].fPos);
813 for (int i = 2; i < vertexCount; ++i) {
814 actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY);
815 }
816 SkASSERT(args.fDevBounds.contains(actualBounds));
817#endif
818
819 GrDrawTarget::DrawInfo info;
820 info.setVertexBuffer(vertexBuffer);
821 info.setIndexBuffer(indexBuffer);
822 info.setPrimitiveType(kTriangles_GrPrimitiveType);
823 info.setStartIndex(firstIndex);
824
825 int vOffset = 0;
826 for (int i = 0; i < draws.count(); ++i) {
827 const Draw& draw = draws[i];
828 info.setStartVertex(vOffset + firstVertex);
829 info.setVertexCount(draw.fVertexCnt);
830 info.setIndexCount(draw.fIndexCnt);
831 batchTarget->draw(info);
832 vOffset += draw.fVertexCnt;
833 }
834 }
835 }
836
837 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
838
839private:
840 AAConvexPathBatch(const Geometry& geometry) {
841 this->initClassID<AAConvexPathBatch>();
842 fGeoData.push_back(geometry);
843 }
844
845 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
846 AAConvexPathBatch* that = t->cast<AAConvexPathBatch>();
847
848 if (this->color() != that->color()) {
849 return false;
850 }
851
852 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
853 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
854 return false;
855 }
856
857 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
858 return true;
859 }
860
861 GrColor color() const { return fBatch.fColor; }
862 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
863 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
864
865 struct BatchTracker {
866 GrColor fColor;
867 bool fUsesLocalCoords;
868 bool fColorIgnored;
869 bool fCoverageIgnored;
870 };
871
872 GrBatchOpt fBatchOpt;
873 BatchTracker fBatch;
874 SkSTArray<1, Geometry, true> fGeoData;
875};
876
joshualitt9853cce2014-11-17 14:22:48 -0800877bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800878 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800879 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800880 const SkMatrix& vm,
joshualitt27f398f2015-02-05 14:39:01 -0800881 const SkPath& path,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000882 const SkStrokeRec&,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000883 bool antiAlias) {
joshualitt27f398f2015-02-05 14:39:01 -0800884 if (path.isEmpty()) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000885 return true;
886 }
bsalomon@google.com4647f902013-03-26 14:45:27 +0000887
joshualitt27f398f2015-02-05 14:39:01 -0800888 // We outset our vertices one pixel and add one more pixel for precision.
889 // TODO create tighter bounds when we start reordering.
890 SkRect devRect = path.getBounds();
891 vm.mapRect(&devRect);
892 devRect.outset(2, 2);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000893
joshualitt27f398f2015-02-05 14:39:01 -0800894 AAConvexPathBatch::Geometry geometry;
895 geometry.fColor = color;
896 geometry.fViewMatrix = vm;
897 geometry.fPath = path;
898 SkDEBUGCODE(geometry.fDevBounds = devRect;)
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000899
joshualitt27f398f2015-02-05 14:39:01 -0800900 SkAutoTUnref<GrBatch> batch(AAConvexPathBatch::Create(geometry));
901 target->drawBatch(pipelineBuilder, batch, &devRect);
bsalomon@google.coma8347462012-10-08 18:59:39 +0000902
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000903 return true;
joshualitt27f398f2015-02-05 14:39:01 -0800904
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000905}