blob: d6eb3d2a2be478dced16fc571fe4a7e4a6d8230a [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"
kkinnunen18996512015-04-26 23:18:49 -070021#include "GrStrokeInfo.h"
egdanielaf18a092015-01-05 10:22:28 -080022#include "SkGeometry.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000023#include "SkString.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
mtklein36352bf2015-03-25 18:17:31 -0700516 const char* name() const 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
mtklein36352bf2015-03-25 18:17:31 -0700527 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) 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
joshualittabb52a12015-01-13 15:02:10 -0800545 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800546 this->setupPosition(pb, gpArgs, qe.inPosition()->fName, qe.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800547
548 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800549 this->emitTransforms(args.fPB, gpArgs->fPositionVar, qe.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800550 qe.localMatrix(), args.fTransformsIn, args.fTransformsOut);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000551
joshualittc369e7c2014-10-22 10:56:26 -0700552 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700553
554 SkAssertResult(fsBuilder->enableFeature(
555 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
joshualitt74077b92014-10-24 11:26:03 -0700556 fsBuilder->codeAppendf("float edgeAlpha;");
joshualitt30ba4362014-08-21 20:18:45 -0700557
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000558 // keep the derivative instructions outside the conditional
joshualitt74077b92014-10-24 11:26:03 -0700559 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s.xy);", v.fsIn());
560 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s.xy);", v.fsIn());
561 fsBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000562 // today we know z and w are in device space. We could use derivatives
joshualitt74077b92014-10-24 11:26:03 -0700563 fsBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
564 v.fsIn());
565 fsBuilder->codeAppendf ("} else {");
566 fsBuilder->codeAppendf("vec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,"
567 " 2.0*%s.x*duvdy.x - duvdy.y);",
568 v.fsIn(), v.fsIn());
569 fsBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
570 v.fsIn());
571 fsBuilder->codeAppendf("edgeAlpha = "
572 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000573
joshualitt2dd1ae02014-12-03 06:24:10 -0800574 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000575 }
576
joshualitt9b989322014-12-15 14:16:27 -0800577 static inline void GenKey(const GrGeometryProcessor& gp,
578 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700579 const GrGLSLCaps&,
joshualitt9b989322014-12-15 14:16:27 -0800580 GrProcessorKeyBuilder* b) {
581 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800582 uint32_t key = local.fInputColorType << 16;
583 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 : 0x0;
584 key |= ComputePosKey(gp.viewMatrix()) << 1;
585 b->add32(key);
joshualitt9b989322014-12-15 14:16:27 -0800586 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000587
joshualitt9b989322014-12-15 14:16:27 -0800588 virtual void setData(const GrGLProgramDataManager& pdman,
589 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700590 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800591 this->setUniformViewMatrix(pdman, gp.viewMatrix());
592
joshualitt9b989322014-12-15 14:16:27 -0800593 const BatchTracker& local = bt.cast<BatchTracker>();
594 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
595 GrGLfloat c[4];
596 GrColorToRGBAFloat(local.fColor, c);
597 pdman.set4fv(fColorUniform, 1, c);
598 fColor = local.fColor;
599 }
600 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000601
602 private:
joshualitt9b989322014-12-15 14:16:27 -0800603 GrColor fColor;
604 UniformHandle fColorUniform;
605
joshualitt249af152014-09-15 11:41:13 -0700606 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000607 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000608
joshualitteb2a6762014-12-04 11:35:33 -0800609 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700610 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700611 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800612 GLProcessor::GenKey(*this, bt, caps, b);
613 }
614
joshualittabb52a12015-01-13 15:02:10 -0800615 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700616 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800617 return SkNEW_ARGS(GLProcessor, (*this, bt));
618 }
619
mtklein36352bf2015-03-25 18:17:31 -0700620 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800621 BatchTracker* local = bt->cast<BatchTracker>();
622 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800623 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800624 }
625
joshualitt50cb76b2015-04-28 09:17:05 -0700626 bool onCanMakeEqual(const GrBatchTracker& m,
627 const GrGeometryProcessor& that,
628 const GrBatchTracker& t) const override {
629 const BatchTracker& mine = m.cast<BatchTracker>();
630 const BatchTracker& theirs = t.cast<BatchTracker>();
631 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
632 that, theirs.fUsesLocalCoords) &&
633 CanCombineOutput(mine.fInputColorType, mine.fColor,
634 theirs.fInputColorType, theirs.fColor);
635 }
636
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000637private:
joshualittd27f73e2014-12-29 07:43:36 -0800638 QuadEdgeEffect(GrColor color, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800639 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800640 this->initClassID<QuadEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800641 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
642 fInQuadEdge = &this->addVertexAttrib(Attribute("inQuadEdge", kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000643 }
644
joshualitt50cb76b2015-04-28 09:17:05 -0700645 bool onIsEqual(const GrGeometryProcessor& other) const override {
646 return true;
647 }
648
649 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
650 out->setUnknownSingleComponent();
651 }
652
joshualitt9b989322014-12-15 14:16:27 -0800653 struct BatchTracker {
654 GrGPInput fInputColorType;
655 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800656 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800657 };
658
joshualitt71c92602015-01-14 08:12:47 -0800659 const Attribute* fInPosition;
660 const Attribute* fInQuadEdge;
joshualitt249af152014-09-15 11:41:13 -0700661
joshualittb0a8a372014-09-23 09:50:21 -0700662 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000663
joshualitt2e3b3e32014-12-09 13:31:14 -0800664 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000665};
666
joshualittb0a8a372014-09-23 09:50:21 -0700667GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000668
joshualittb0a8a372014-09-23 09:50:21 -0700669GrGeometryProcessor* QuadEdgeEffect::TestCreate(SkRandom* random,
670 GrContext*,
671 const GrDrawTargetCaps& caps,
672 GrTexture*[]) {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000673 // Doesn't work without derivative instructions.
joshualittd27f73e2014-12-29 07:43:36 -0800674 return caps.shaderDerivativeSupport() ?
675 QuadEdgeEffect::Create(GrRandomColor(random),
676 GrProcessorUnitTest::TestMatrix(random)) : NULL;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000677}
678
679///////////////////////////////////////////////////////////////////////////////
680
joshualitt9853cce2014-11-17 14:22:48 -0800681bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800682 const GrPipelineBuilder*,
joshualitt8059eb92014-12-29 15:10:07 -0800683 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800684 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700685 const GrStrokeInfo& stroke,
robertphillips@google.comfa662942012-05-17 12:20:22 +0000686 bool antiAlias) const {
bsalomon@google.combcce8922013-03-25 15:38:39 +0000687 return (target->caps()->shaderDerivativeSupport() && antiAlias &&
robertphillips@google.come79f3202014-02-11 16:30:21 +0000688 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
robertphillips@google.comfa662942012-05-17 12:20:22 +0000689}
690
joshualitt27f398f2015-02-05 14:39:01 -0800691class AAConvexPathBatch : public GrBatch {
692public:
693 struct Geometry {
694 GrColor fColor;
695 SkMatrix fViewMatrix;
696 SkPath fPath;
joshualitt27f398f2015-02-05 14:39:01 -0800697 };
698
699 static GrBatch* Create(const Geometry& geometry) {
700 return SkNEW_ARGS(AAConvexPathBatch, (geometry));
701 }
702
mtklein36352bf2015-03-25 18:17:31 -0700703 const char* name() const override { return "AAConvexBatch"; }
joshualitt27f398f2015-02-05 14:39:01 -0800704
mtklein36352bf2015-03-25 18:17:31 -0700705 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt27f398f2015-02-05 14:39:01 -0800706 // When this is called on a batch, there is only one geometry bundle
707 out->setKnownFourComponents(fGeoData[0].fColor);
708 }
mtklein36352bf2015-03-25 18:17:31 -0700709 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt27f398f2015-02-05 14:39:01 -0800710 out->setUnknownSingleComponent();
711 }
712
mtklein36352bf2015-03-25 18:17:31 -0700713 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt27f398f2015-02-05 14:39:01 -0800714 // Handle any color overrides
715 if (init.fColorIgnored) {
716 fGeoData[0].fColor = GrColor_ILLEGAL;
717 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
718 fGeoData[0].fColor = init.fOverrideColor;
719 }
720
721 // setup batch properties
722 fBatch.fColorIgnored = init.fColorIgnored;
723 fBatch.fColor = fGeoData[0].fColor;
724 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
725 fBatch.fCoverageIgnored = init.fCoverageIgnored;
726 }
727
mtklein36352bf2015-03-25 18:17:31 -0700728 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt27f398f2015-02-05 14:39:01 -0800729 int instanceCount = fGeoData.count();
730
731 SkMatrix invert;
joshualitt7abe15d2015-02-20 12:40:45 -0800732 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
joshualitt27f398f2015-02-05 14:39:01 -0800733 SkDebugf("Could not invert viewmatrix\n");
734 return;
735 }
736
737 // Setup GrGeometryProcessor
738 SkAutoTUnref<GrGeometryProcessor> quadProcessor(QuadEdgeEffect::Create(this->color(),
739 invert));
740
741 batchTarget->initDraw(quadProcessor, pipeline);
742
743 // TODO remove this when batch is everywhere
744 GrPipelineInfo init;
745 init.fColorIgnored = fBatch.fColorIgnored;
746 init.fOverrideColor = GrColor_ILLEGAL;
747 init.fCoverageIgnored = fBatch.fCoverageIgnored;
748 init.fUsesLocalCoords = this->usesLocalCoords();
749 quadProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init);
750
751 // TODO generate all segments for all paths and use one vertex buffer
752 for (int i = 0; i < instanceCount; i++) {
753 Geometry& args = fGeoData[i];
754
755 // We use the fact that SkPath::transform path does subdivision based on
756 // perspective. Otherwise, we apply the view matrix when copying to the
757 // segment representation.
758 const SkMatrix* viewMatrix = &args.fViewMatrix;
759 if (viewMatrix->hasPerspective()) {
760 args.fPath.transform(*viewMatrix);
761 viewMatrix = &SkMatrix::I();
762 }
763
764 int vertexCount;
765 int indexCount;
766 enum {
767 kPreallocSegmentCnt = 512 / sizeof(Segment),
768 kPreallocDrawCnt = 4,
769 };
770 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
771 SkPoint fanPt;
772
773 if (!get_segments(args.fPath, *viewMatrix, &segments, &fanPt, &vertexCount,
774 &indexCount)) {
775 continue;
776 }
777
778 const GrVertexBuffer* vertexBuffer;
779 int firstVertex;
780
781 size_t vertexStride = quadProcessor->getVertexStride();
782 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
783 vertexCount,
784 &vertexBuffer,
785 &firstVertex);
786
joshualitt4b31de82015-03-05 14:33:41 -0800787 if (!vertices) {
788 SkDebugf("Could not allocate vertices\n");
789 return;
790 }
791
joshualitt27f398f2015-02-05 14:39:01 -0800792 const GrIndexBuffer* indexBuffer;
793 int firstIndex;
794
795 void *indices = batchTarget->indexPool()->makeSpace(indexCount,
796 &indexBuffer,
797 &firstIndex);
798
joshualitt4b31de82015-03-05 14:33:41 -0800799 if (!indices) {
800 SkDebugf("Could not allocate indices\n");
801 return;
802 }
803
joshualitt27f398f2015-02-05 14:39:01 -0800804 QuadVertex* verts = reinterpret_cast<QuadVertex*>(vertices);
805 uint16_t* idxs = reinterpret_cast<uint16_t*>(indices);
806
807 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
808 create_vertices(segments, fanPt, &draws, verts, idxs);
809
joshualitt27f398f2015-02-05 14:39:01 -0800810 GrDrawTarget::DrawInfo info;
811 info.setVertexBuffer(vertexBuffer);
812 info.setIndexBuffer(indexBuffer);
813 info.setPrimitiveType(kTriangles_GrPrimitiveType);
814 info.setStartIndex(firstIndex);
815
816 int vOffset = 0;
817 for (int i = 0; i < draws.count(); ++i) {
818 const Draw& draw = draws[i];
819 info.setStartVertex(vOffset + firstVertex);
820 info.setVertexCount(draw.fVertexCnt);
821 info.setIndexCount(draw.fIndexCnt);
822 batchTarget->draw(info);
823 vOffset += draw.fVertexCnt;
824 }
825 }
826 }
827
828 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
829
830private:
831 AAConvexPathBatch(const Geometry& geometry) {
832 this->initClassID<AAConvexPathBatch>();
833 fGeoData.push_back(geometry);
834 }
835
mtklein36352bf2015-03-25 18:17:31 -0700836 bool onCombineIfPossible(GrBatch* t) override {
joshualitt27f398f2015-02-05 14:39:01 -0800837 AAConvexPathBatch* that = t->cast<AAConvexPathBatch>();
838
839 if (this->color() != that->color()) {
840 return false;
841 }
842
843 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
844 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
845 return false;
846 }
847
848 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
849 return true;
850 }
851
852 GrColor color() const { return fBatch.fColor; }
853 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
854 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
855
856 struct BatchTracker {
857 GrColor fColor;
858 bool fUsesLocalCoords;
859 bool fColorIgnored;
860 bool fCoverageIgnored;
861 };
862
joshualitt27f398f2015-02-05 14:39:01 -0800863 BatchTracker fBatch;
864 SkSTArray<1, Geometry, true> fGeoData;
865};
866
joshualitt9853cce2014-11-17 14:22:48 -0800867bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800868 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800869 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800870 const SkMatrix& vm,
joshualitt27f398f2015-02-05 14:39:01 -0800871 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700872 const GrStrokeInfo&,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000873 bool antiAlias) {
joshualitt27f398f2015-02-05 14:39:01 -0800874 if (path.isEmpty()) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000875 return true;
876 }
bsalomon@google.com4647f902013-03-26 14:45:27 +0000877
joshualitt27f398f2015-02-05 14:39:01 -0800878 // We outset our vertices one pixel and add one more pixel for precision.
879 // TODO create tighter bounds when we start reordering.
880 SkRect devRect = path.getBounds();
881 vm.mapRect(&devRect);
882 devRect.outset(2, 2);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000883
joshualitt27f398f2015-02-05 14:39:01 -0800884 AAConvexPathBatch::Geometry geometry;
885 geometry.fColor = color;
886 geometry.fViewMatrix = vm;
887 geometry.fPath = path;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000888
joshualitt27f398f2015-02-05 14:39:01 -0800889 SkAutoTUnref<GrBatch> batch(AAConvexPathBatch::Create(geometry));
890 target->drawBatch(pipelineBuilder, batch, &devRect);
bsalomon@google.coma8347462012-10-08 18:59:39 +0000891
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000892 return true;
joshualitt27f398f2015-02-05 14:39:01 -0800893
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000894}