blob: e9a58ac3855ab19e9a8517b36132e2421d9e29c6 [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
robertphillipse16dfdb2015-05-08 04:46:51 -070011#include "GrAAConvexTessellator.h"
joshualitt27f398f2015-02-05 14:39:01 -080012#include "GrBatch.h"
13#include "GrBatchTarget.h"
joshualitt8e5c1772015-05-11 08:58:52 -070014#include "GrBatchTest.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000015#include "GrContext.h"
robertphillipse16dfdb2015-05-08 04:46:51 -070016#include "GrDefaultGeoProcFactory.h"
bsalomon@google.comc26d94f2013-03-25 18:19:00 +000017#include "GrDrawTargetCaps.h"
joshualitteb2a6762014-12-04 11:35:33 -080018#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080019#include "GrInvariantOutput.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000020#include "GrPathUtils.h"
egdaniel8dd688b2015-01-22 10:16:09 -080021#include "GrProcessor.h"
22#include "GrPipelineBuilder.h"
kkinnunen18996512015-04-26 23:18:49 -070023#include "GrStrokeInfo.h"
egdanielaf18a092015-01-05 10:22:28 -080024#include "SkGeometry.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000025#include "SkString.h"
commit-bot@chromium.org933e65d2014-03-20 20:00:24 +000026#include "SkTraceEvent.h"
joshualittb0a8a372014-09-23 09:50:21 -070027#include "gl/GrGLProcessor.h"
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000028#include "gl/GrGLSL.h"
joshualitt249af152014-09-15 11:41:13 -070029#include "gl/GrGLGeometryProcessor.h"
joshualitteb2a6762014-12-04 11:35:33 -080030#include "gl/builders/GrGLProgramBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000031
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000032GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
33}
34
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000035struct Segment {
36 enum {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000037 // These enum values are assumed in member functions below.
38 kLine = 0,
39 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000040 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000041
bsalomon@google.com9aed1142012-01-30 14:28:39 +000042 // line uses one pt, quad uses 2 pts
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000043 SkPoint fPts[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000044 // normal to edge ending at each pt
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000045 SkVector fNorms[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000046 // is the corner where the previous segment meets this segment
47 // sharp. If so, fMid is a normalized bisector facing outward.
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000048 SkVector fMid;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000049
50 int countPoints() {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000051 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
52 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000053 }
54 const SkPoint& endPt() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000055 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
56 return fPts[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000057 };
58 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000059 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
60 return fNorms[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000061 };
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000062};
63
64typedef SkTArray<Segment, true> SegmentArray;
65
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +000066static void center_of_mass(const SegmentArray& segments, SkPoint* c) {
bsalomon@google.com81712882012-11-01 17:12:34 +000067 SkScalar area = 0;
vandebo@chromium.org6390c722012-03-28 21:03:22 +000068 SkPoint center = {0, 0};
bsalomon@google.com9aed1142012-01-30 14:28:39 +000069 int count = segments.count();
vandebo@chromium.org6390c722012-03-28 21:03:22 +000070 SkPoint p0 = {0, 0};
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000071 if (count > 2) {
72 // We translate the polygon so that the first point is at the origin.
73 // This avoids some precision issues with small area polygons far away
74 // from the origin.
75 p0 = segments[0].endPt();
76 SkPoint pi;
77 SkPoint pj;
bsalomon@google.coma51ab842012-07-10 19:53:34 +000078 // the first and last iteration of the below loop would compute
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000079 // zeros since the starting / ending point is (0,0). So instead we start
80 // at i=1 and make the last iteration i=count-2.
81 pj = segments[1].endPt() - p0;
82 for (int i = 1; i < count - 1; ++i) {
83 pi = pj;
84 const SkPoint pj = segments[i + 1].endPt() - p0;
85
bsalomon@google.com81712882012-11-01 17:12:34 +000086 SkScalar t = SkScalarMul(pi.fX, pj.fY) - SkScalarMul(pj.fX, pi.fY);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000087 area += t;
88 center.fX += (pi.fX + pj.fX) * t;
89 center.fY += (pi.fY + pj.fY) * t;
90
91 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000092 }
bsalomon@google.com278dc692012-02-15 16:52:51 +000093 // If the poly has no area then we instead return the average of
94 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000095 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +000096 SkPoint avg;
97 avg.set(0, 0);
98 for (int i = 0; i < count; ++i) {
99 const SkPoint& pt = segments[i].endPt();
100 avg.fX += pt.fX;
101 avg.fY += pt.fY;
102 }
103 SkScalar denom = SK_Scalar1 / count;
104 avg.scale(denom);
105 *c = avg;
106 } else {
107 area *= 3;
reed80ea19c2015-05-12 10:37:34 -0700108 area = SkScalarInvert(area);
bsalomon@google.com81712882012-11-01 17:12:34 +0000109 center.fX = SkScalarMul(center.fX, area);
110 center.fY = SkScalarMul(center.fY, area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +0000111 // undo the translate of p0 to the origin.
112 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000113 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000114 SkASSERT(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000115}
116
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000117static void compute_vectors(SegmentArray* segments,
118 SkPoint* fanPt,
119 SkPath::Direction dir,
120 int* vCount,
121 int* iCount) {
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000122 center_of_mass(*segments, fanPt);
123 int count = segments->count();
124
bsalomon@google.com278dc692012-02-15 16:52:51 +0000125 // Make the normals point towards the outside
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000126 SkPoint::Side normSide;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000127 if (dir == SkPath::kCCW_Direction) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000128 normSide = SkPoint::kRight_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000129 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000130 normSide = SkPoint::kLeft_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000131 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000132
133 *vCount = 0;
134 *iCount = 0;
135 // compute normals at all points
136 for (int a = 0; a < count; ++a) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000137 Segment& sega = (*segments)[a];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000138 int b = (a + 1) % count;
139 Segment& segb = (*segments)[b];
140
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000141 const SkPoint* prevPt = &sega.endPt();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000142 int n = segb.countPoints();
143 for (int p = 0; p < n; ++p) {
144 segb.fNorms[p] = segb.fPts[p] - *prevPt;
145 segb.fNorms[p].normalize();
146 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
147 prevPt = &segb.fPts[p];
148 }
149 if (Segment::kLine == segb.fType) {
150 *vCount += 5;
151 *iCount += 9;
152 } else {
153 *vCount += 6;
154 *iCount += 12;
155 }
156 }
157
158 // compute mid-vectors where segments meet. TODO: Detect shallow corners
159 // and leave out the wedges and close gaps by stitching segments together.
160 for (int a = 0; a < count; ++a) {
161 const Segment& sega = (*segments)[a];
162 int b = (a + 1) % count;
163 Segment& segb = (*segments)[b];
164 segb.fMid = segb.fNorms[0] + sega.endNorm();
165 segb.fMid.normalize();
166 // corner wedges
167 *vCount += 4;
168 *iCount += 6;
169 }
170}
171
bsalomon@google.com9732f622012-01-31 15:19:21 +0000172struct DegenerateTestData {
173 DegenerateTestData() { fStage = kInitial; }
174 bool isDegenerate() const { return kNonDegenerate != fStage; }
175 enum {
176 kInitial,
177 kPoint,
178 kLine,
179 kNonDegenerate
180 } fStage;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000181 SkPoint fFirstPoint;
182 SkVector fLineNormal;
bsalomon@google.com81712882012-11-01 17:12:34 +0000183 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000184};
185
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000186static const SkScalar kClose = (SK_Scalar1 / 16);
187static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000188
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000189static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000190 switch (data->fStage) {
191 case DegenerateTestData::kInitial:
192 data->fFirstPoint = pt;
193 data->fStage = DegenerateTestData::kPoint;
194 break;
195 case DegenerateTestData::kPoint:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000196 if (pt.distanceToSqd(data->fFirstPoint) > kCloseSqd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000197 data->fLineNormal = pt - data->fFirstPoint;
198 data->fLineNormal.normalize();
199 data->fLineNormal.setOrthog(data->fLineNormal);
200 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
201 data->fStage = DegenerateTestData::kLine;
202 }
203 break;
204 case DegenerateTestData::kLine:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000205 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000206 data->fStage = DegenerateTestData::kNonDegenerate;
207 }
208 case DegenerateTestData::kNonDegenerate:
209 break;
210 default:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000211 SkFAIL("Unexpected degenerate test stage.");
bsalomon@google.com9732f622012-01-31 15:19:21 +0000212 }
213}
214
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000215static inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Direction* dir) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000216 if (!path.cheapComputeDirection(dir)) {
217 return false;
218 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000219 // check whether m reverses the orientation
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000220 SkASSERT(!m.hasPerspective());
bsalomon@google.com81712882012-11-01 17:12:34 +0000221 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
222 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000223 if (det2x2 < 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000224 *dir = SkPath::OppositeDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000225 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000226 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000227}
228
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000229static inline void add_line_to_segment(const SkPoint& pt,
joshualitt27f398f2015-02-05 14:39:01 -0800230 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000231 segments->push_back();
232 segments->back().fType = Segment::kLine;
233 segments->back().fPts[0] = pt;
234}
235
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000236static inline void add_quad_segment(const SkPoint pts[3],
joshualitt27f398f2015-02-05 14:39:01 -0800237 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000238 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
239 if (pts[0] != pts[2]) {
joshualitt27f398f2015-02-05 14:39:01 -0800240 add_line_to_segment(pts[2], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000241 }
242 } else {
243 segments->push_back();
244 segments->back().fType = Segment::kQuad;
245 segments->back().fPts[0] = pts[1];
246 segments->back().fPts[1] = pts[2];
247 }
248}
249
250static inline void add_cubic_segments(const SkPoint pts[4],
251 SkPath::Direction dir,
joshualitt27f398f2015-02-05 14:39:01 -0800252 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000253 SkSTArray<15, SkPoint, true> quads;
254 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
255 int count = quads.count();
256 for (int q = 0; q < count; q += 3) {
joshualitt27f398f2015-02-05 14:39:01 -0800257 add_quad_segment(&quads[q], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000258 }
259}
260
261static bool get_segments(const SkPath& path,
262 const SkMatrix& m,
263 SegmentArray* segments,
264 SkPoint* fanPt,
265 int* vCount,
joshualitt27f398f2015-02-05 14:39:01 -0800266 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000267 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000268 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000269 // to the path from the sample to compute coverage. Every pixel intersected
270 // 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 +0000271 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000272 // thus should be very light. This is particularly egregious for degenerate
273 // line paths. We detect paths that are very close to a line (zero area) and
274 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000275 DegenerateTestData degenerateData;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000276 SkPath::Direction dir;
277 // get_direction can fail for some degenerate paths.
278 if (!get_direction(path, m, &dir)) {
279 return false;
280 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000281
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000282 for (;;) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000283 SkPoint pts[4];
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000284 SkPath::Verb verb = iter.next(pts);
285 switch (verb) {
286 case SkPath::kMove_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000287 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000288 update_degenerate_test(&degenerateData, pts[0]);
289 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000290 case SkPath::kLine_Verb: {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000291 m.mapPoints(&pts[1], 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000292 update_degenerate_test(&degenerateData, pts[1]);
joshualitt27f398f2015-02-05 14:39:01 -0800293 add_line_to_segment(pts[1], segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000294 break;
295 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000296 case SkPath::kQuad_Verb:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000297 m.mapPoints(pts, 3);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000298 update_degenerate_test(&degenerateData, pts[1]);
299 update_degenerate_test(&degenerateData, pts[2]);
joshualitt27f398f2015-02-05 14:39:01 -0800300 add_quad_segment(pts, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000301 break;
egdanielaf18a092015-01-05 10:22:28 -0800302 case SkPath::kConic_Verb: {
303 m.mapPoints(pts, 3);
304 SkScalar weight = iter.conicWeight();
305 SkAutoConicToQuads converter;
306 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5f);
307 for (int i = 0; i < converter.countQuads(); ++i) {
308 update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
309 update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
joshualitt27f398f2015-02-05 14:39:01 -0800310 add_quad_segment(quadPts + 2*i, segments);
egdanielaf18a092015-01-05 10:22:28 -0800311 }
312 break;
313 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000314 case SkPath::kCubic_Verb: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000315 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000316 update_degenerate_test(&degenerateData, pts[1]);
317 update_degenerate_test(&degenerateData, pts[2]);
318 update_degenerate_test(&degenerateData, pts[3]);
joshualitt27f398f2015-02-05 14:39:01 -0800319 add_cubic_segments(pts, dir, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000320 break;
321 };
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000322 case SkPath::kDone_Verb:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000323 if (degenerateData.isDegenerate()) {
324 return false;
325 } else {
bsalomon@google.com278dc692012-02-15 16:52:51 +0000326 compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000327 return true;
328 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000329 default:
330 break;
331 }
332 }
333}
334
335struct QuadVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000336 SkPoint fPos;
337 SkPoint fUV;
bsalomon@google.com81712882012-11-01 17:12:34 +0000338 SkScalar fD0;
339 SkScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000340};
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000341
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000342struct Draw {
343 Draw() : fVertexCnt(0), fIndexCnt(0) {}
344 int fVertexCnt;
345 int fIndexCnt;
346};
347
348typedef SkTArray<Draw, true> DrawArray;
349
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000350static void create_vertices(const SegmentArray& segments,
351 const SkPoint& fanPt,
352 DrawArray* draws,
353 QuadVertex* verts,
354 uint16_t* idxs) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000355 Draw* draw = &draws->push_back();
356 // alias just to make vert/index assignments easier to read.
357 int* v = &draw->fVertexCnt;
358 int* i = &draw->fIndexCnt;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000359
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000360 int count = segments.count();
361 for (int a = 0; a < count; ++a) {
362 const Segment& sega = segments[a];
363 int b = (a + 1) % count;
364 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000365
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000366 // Check whether adding the verts for this segment to the current draw would cause index
367 // values to overflow.
368 int vCount = 4;
369 if (Segment::kLine == segb.fType) {
370 vCount += 5;
371 } else {
372 vCount += 6;
373 }
374 if (draw->fVertexCnt + vCount > (1 << 16)) {
375 verts += *v;
376 idxs += *i;
377 draw = &draws->push_back();
378 v = &draw->fVertexCnt;
379 i = &draw->fIndexCnt;
380 }
381
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000382 // FIXME: These tris are inset in the 1 unit arc around the corner
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000383 verts[*v + 0].fPos = sega.endPt();
384 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
385 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
386 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
387 verts[*v + 0].fUV.set(0,0);
388 verts[*v + 1].fUV.set(0,-SK_Scalar1);
389 verts[*v + 2].fUV.set(0,-SK_Scalar1);
390 verts[*v + 3].fUV.set(0,-SK_Scalar1);
391 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
392 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
393 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
394 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000395
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000396 idxs[*i + 0] = *v + 0;
397 idxs[*i + 1] = *v + 2;
398 idxs[*i + 2] = *v + 1;
399 idxs[*i + 3] = *v + 0;
400 idxs[*i + 4] = *v + 3;
401 idxs[*i + 5] = *v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000402
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000403 *v += 4;
404 *i += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000405
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000406 if (Segment::kLine == segb.fType) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000407 verts[*v + 0].fPos = fanPt;
408 verts[*v + 1].fPos = sega.endPt();
409 verts[*v + 2].fPos = segb.fPts[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000410
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000411 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
412 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000413
414 // we draw the line edge as a degenerate quad (u is 0, v is the
415 // signed distance to the edge)
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000416 SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos,
417 verts[*v + 2].fPos);
418 verts[*v + 0].fUV.set(0, dist);
419 verts[*v + 1].fUV.set(0, 0);
420 verts[*v + 2].fUV.set(0, 0);
421 verts[*v + 3].fUV.set(0, -SK_Scalar1);
422 verts[*v + 4].fUV.set(0, -SK_Scalar1);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000423
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000424 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
425 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
426 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
427 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
428 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000429
cdalton35964822015-04-29 10:14:03 -0700430 idxs[*i + 0] = *v + 3;
431 idxs[*i + 1] = *v + 1;
432 idxs[*i + 2] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000433
cdalton35964822015-04-29 10:14:03 -0700434 idxs[*i + 3] = *v + 4;
435 idxs[*i + 4] = *v + 3;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000436 idxs[*i + 5] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000437
cdalton35964822015-04-29 10:14:03 -0700438 *i += 6;
439
440 // Draw the interior fan if it exists.
441 // TODO: Detect and combine colinear segments. This will ensure we catch every case
442 // with no interior, and that the resulting shared edge uses the same endpoints.
443 if (count >= 3) {
444 idxs[*i + 0] = *v + 0;
445 idxs[*i + 1] = *v + 2;
446 idxs[*i + 2] = *v + 1;
447
448 *i += 3;
449 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000450
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000451 *v += 5;
bsalomon@google.com06809612012-01-21 15:03:39 +0000452 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000453 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000454
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000455 SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000456 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000457
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000458 verts[*v + 0].fPos = fanPt;
459 verts[*v + 1].fPos = qpts[0];
460 verts[*v + 2].fPos = qpts[2];
461 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
462 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
463 verts[*v + 5].fPos = qpts[1] + midVec;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000464
bsalomon@google.com81712882012-11-01 17:12:34 +0000465 SkScalar c = segb.fNorms[0].dot(qpts[0]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000466 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
467 verts[*v + 1].fD0 = 0.f;
468 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
469 verts[*v + 3].fD0 = -SK_ScalarMax/100;
470 verts[*v + 4].fD0 = -SK_ScalarMax/100;
471 verts[*v + 5].fD0 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000472
473 c = segb.fNorms[1].dot(qpts[2]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000474 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
475 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
476 verts[*v + 2].fD1 = 0.f;
477 verts[*v + 3].fD1 = -SK_ScalarMax/100;
478 verts[*v + 4].fD1 = -SK_ScalarMax/100;
479 verts[*v + 5].fD1 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000480
bsalomon@google.com19713172012-03-15 13:51:08 +0000481 GrPathUtils::QuadUVMatrix toUV(qpts);
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000482 toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v);
bsalomon@google.com06809612012-01-21 15:03:39 +0000483
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000484 idxs[*i + 0] = *v + 3;
485 idxs[*i + 1] = *v + 1;
486 idxs[*i + 2] = *v + 2;
487 idxs[*i + 3] = *v + 4;
488 idxs[*i + 4] = *v + 3;
489 idxs[*i + 5] = *v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000490
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000491 idxs[*i + 6] = *v + 5;
492 idxs[*i + 7] = *v + 3;
493 idxs[*i + 8] = *v + 4;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000494
cdalton35964822015-04-29 10:14:03 -0700495 *i += 9;
496
497 // Draw the interior fan if it exists.
498 // TODO: Detect and combine colinear segments. This will ensure we catch every case
499 // with no interior, and that the resulting shared edge uses the same endpoints.
500 if (count >= 3) {
501 idxs[*i + 0] = *v + 0;
502 idxs[*i + 1] = *v + 2;
503 idxs[*i + 2] = *v + 1;
504
505 *i += 3;
506 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000507
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000508 *v += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000509 }
510 }
511}
512
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000513///////////////////////////////////////////////////////////////////////////////
514
515/*
516 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
517 * two components of the vertex attribute. Coverage is based on signed
518 * distance with negative being inside, positive outside. The edge is specified in
519 * window space (y-down). If either the third or fourth component of the interpolated
520 * 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 +0000521 * attempt to trim to a portion of the infinite quad.
522 * Requires shader derivative instruction support.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000523 */
524
joshualitt249af152014-09-15 11:41:13 -0700525class QuadEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000526public:
527
joshualittd27f73e2014-12-29 07:43:36 -0800528 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& localMatrix) {
529 return SkNEW_ARGS(QuadEdgeEffect, (color, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000530 }
531
532 virtual ~QuadEdgeEffect() {}
533
mtklein36352bf2015-03-25 18:17:31 -0700534 const char* name() const override { return "QuadEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000535
joshualitt71c92602015-01-14 08:12:47 -0800536 const Attribute* inPosition() const { return fInPosition; }
537 const Attribute* inQuadEdge() const { return fInQuadEdge; }
joshualitt88c23fc2015-05-13 14:18:07 -0700538 GrColor color() const { return fColor; }
joshualitt249af152014-09-15 11:41:13 -0700539
joshualittb0a8a372014-09-23 09:50:21 -0700540 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000541 public:
joshualitteb2a6762014-12-04 11:35:33 -0800542 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800543 const GrBatchTracker&)
544 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000545
mtklein36352bf2015-03-25 18:17:31 -0700546 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800547 const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800548 GrGLGPBuilder* pb = args.fPB;
549 GrGLVertexBuilder* vsBuilder = pb->getVertexShaderBuilder();
joshualitt2dd1ae02014-12-03 06:24:10 -0800550
joshualittabb52a12015-01-13 15:02:10 -0800551 // emit attributes
552 vsBuilder->emitAttributes(qe);
553
joshualitt74077b92014-10-24 11:26:03 -0700554 GrGLVertToFrag v(kVec4f_GrSLType);
555 args.fPB->addVarying("QuadEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -0800556 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.inQuadEdge()->fName);
557
joshualitt9b989322014-12-15 14:16:27 -0800558 const BatchTracker& local = args.fBT.cast<BatchTracker>();
559
560 // Setup pass through color
561 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
562 &fColorUniform);
563
joshualittabb52a12015-01-13 15:02:10 -0800564 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800565 this->setupPosition(pb, gpArgs, qe.inPosition()->fName, qe.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800566
567 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800568 this->emitTransforms(args.fPB, gpArgs->fPositionVar, qe.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800569 qe.localMatrix(), args.fTransformsIn, args.fTransformsOut);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000570
egdaniel29bee0f2015-04-29 11:54:42 -0700571 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700572
573 SkAssertResult(fsBuilder->enableFeature(
574 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
joshualitt74077b92014-10-24 11:26:03 -0700575 fsBuilder->codeAppendf("float edgeAlpha;");
joshualitt30ba4362014-08-21 20:18:45 -0700576
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000577 // keep the derivative instructions outside the conditional
joshualitt74077b92014-10-24 11:26:03 -0700578 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s.xy);", v.fsIn());
579 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s.xy);", v.fsIn());
580 fsBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000581 // today we know z and w are in device space. We could use derivatives
joshualitt74077b92014-10-24 11:26:03 -0700582 fsBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
583 v.fsIn());
584 fsBuilder->codeAppendf ("} else {");
585 fsBuilder->codeAppendf("vec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,"
586 " 2.0*%s.x*duvdy.x - duvdy.y);",
587 v.fsIn(), v.fsIn());
588 fsBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
589 v.fsIn());
590 fsBuilder->codeAppendf("edgeAlpha = "
591 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000592
joshualitt2dd1ae02014-12-03 06:24:10 -0800593 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000594 }
595
joshualitt9b989322014-12-15 14:16:27 -0800596 static inline void GenKey(const GrGeometryProcessor& gp,
597 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700598 const GrGLSLCaps&,
joshualitt9b989322014-12-15 14:16:27 -0800599 GrProcessorKeyBuilder* b) {
600 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800601 uint32_t key = local.fInputColorType << 16;
602 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 : 0x0;
603 key |= ComputePosKey(gp.viewMatrix()) << 1;
604 b->add32(key);
joshualitt9b989322014-12-15 14:16:27 -0800605 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000606
joshualitt9b989322014-12-15 14:16:27 -0800607 virtual void setData(const GrGLProgramDataManager& pdman,
608 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700609 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800610 this->setUniformViewMatrix(pdman, gp.viewMatrix());
611
joshualitt9b989322014-12-15 14:16:27 -0800612 const BatchTracker& local = bt.cast<BatchTracker>();
613 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
614 GrGLfloat c[4];
615 GrColorToRGBAFloat(local.fColor, c);
616 pdman.set4fv(fColorUniform, 1, c);
617 fColor = local.fColor;
618 }
619 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000620
621 private:
joshualitt9b989322014-12-15 14:16:27 -0800622 GrColor fColor;
623 UniformHandle fColorUniform;
624
joshualitt249af152014-09-15 11:41:13 -0700625 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000626 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000627
joshualitteb2a6762014-12-04 11:35:33 -0800628 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700629 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700630 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800631 GLProcessor::GenKey(*this, bt, caps, b);
632 }
633
joshualittabb52a12015-01-13 15:02:10 -0800634 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700635 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800636 return SkNEW_ARGS(GLProcessor, (*this, bt));
637 }
638
mtklein36352bf2015-03-25 18:17:31 -0700639 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800640 BatchTracker* local = bt->cast<BatchTracker>();
641 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800642 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800643 }
644
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000645private:
joshualittd27f73e2014-12-29 07:43:36 -0800646 QuadEdgeEffect(GrColor color, const SkMatrix& localMatrix)
joshualitt88c23fc2015-05-13 14:18:07 -0700647 : INHERITED(SkMatrix::I(), localMatrix)
648 , fColor(color) {
joshualitteb2a6762014-12-04 11:35:33 -0800649 this->initClassID<QuadEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800650 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
651 fInQuadEdge = &this->addVertexAttrib(Attribute("inQuadEdge", kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000652 }
653
joshualitt9b989322014-12-15 14:16:27 -0800654 struct BatchTracker {
655 GrGPInput fInputColorType;
656 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800657 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800658 };
659
joshualitt71c92602015-01-14 08:12:47 -0800660 const Attribute* fInPosition;
661 const Attribute* fInQuadEdge;
joshualitt88c23fc2015-05-13 14:18:07 -0700662 GrColor fColor;
joshualitt249af152014-09-15 11:41:13 -0700663
joshualittb0a8a372014-09-23 09:50:21 -0700664 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000665
joshualitt2e3b3e32014-12-09 13:31:14 -0800666 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000667};
668
joshualittb0a8a372014-09-23 09:50:21 -0700669GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000670
joshualittb0a8a372014-09-23 09:50:21 -0700671GrGeometryProcessor* QuadEdgeEffect::TestCreate(SkRandom* random,
672 GrContext*,
673 const GrDrawTargetCaps& caps,
674 GrTexture*[]) {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000675 // Doesn't work without derivative instructions.
jvanverthe9c0fc62015-04-29 11:18:05 -0700676 return caps.shaderCaps()->shaderDerivativeSupport() ?
joshualittd27f73e2014-12-29 07:43:36 -0800677 QuadEdgeEffect::Create(GrRandomColor(random),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700678 GrTest::TestMatrix(random)) : NULL;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000679}
680
681///////////////////////////////////////////////////////////////////////////////
682
joshualitt9853cce2014-11-17 14:22:48 -0800683bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800684 const GrPipelineBuilder*,
joshualitt8059eb92014-12-29 15:10:07 -0800685 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800686 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700687 const GrStrokeInfo& stroke,
robertphillips@google.comfa662942012-05-17 12:20:22 +0000688 bool antiAlias) const {
jvanverthe9c0fc62015-04-29 11:18:05 -0700689 return (target->caps()->shaderCaps()->shaderDerivativeSupport() && antiAlias &&
robertphillips@google.come79f3202014-02-11 16:30:21 +0000690 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
robertphillips@google.comfa662942012-05-17 12:20:22 +0000691}
692
robertphillipse16dfdb2015-05-08 04:46:51 -0700693// extract the result vertices and indices from the GrAAConvexTessellator
694static void extract_verts(const GrAAConvexTessellator& tess,
695 void* vertices,
696 size_t vertexStride,
697 GrColor color,
698 uint16_t* idxs,
699 bool tweakAlphaForCoverage) {
700 intptr_t verts = reinterpret_cast<intptr_t>(vertices);
701
702 for (int i = 0; i < tess.numPts(); ++i) {
703 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
704 }
705
706 // Make 'verts' point to the colors
707 verts += sizeof(SkPoint);
708 for (int i = 0; i < tess.numPts(); ++i) {
709 SkASSERT(tess.depth(i) >= -0.5f && tess.depth(i) <= 0.5f);
710 if (tweakAlphaForCoverage) {
711 SkASSERT(SkScalarRoundToInt(255.0f * (tess.depth(i) + 0.5f)) <= 255);
712 unsigned scale = SkScalarRoundToInt(255.0f * (tess.depth(i) + 0.5f));
713 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
714 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
715 } else {
716 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
717 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
718 tess.depth(i) + 0.5f;
719 }
720 }
721
722 for (int i = 0; i < tess.numIndices(); ++i) {
723 idxs[i] = tess.index(i);
724 }
725}
726
727static const GrGeometryProcessor* create_fill_gp(bool tweakAlphaForCoverage,
728 const SkMatrix& localMatrix) {
729 uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
730 if (!tweakAlphaForCoverage) {
731 flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
732 }
733
joshualitt1ba8cc92015-05-13 12:24:23 -0700734 return GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix);
robertphillipse16dfdb2015-05-08 04:46:51 -0700735}
736
joshualitt27f398f2015-02-05 14:39:01 -0800737class AAConvexPathBatch : public GrBatch {
738public:
739 struct Geometry {
740 GrColor fColor;
741 SkMatrix fViewMatrix;
742 SkPath fPath;
joshualitt27f398f2015-02-05 14:39:01 -0800743 };
744
745 static GrBatch* Create(const Geometry& geometry) {
746 return SkNEW_ARGS(AAConvexPathBatch, (geometry));
747 }
748
mtklein36352bf2015-03-25 18:17:31 -0700749 const char* name() const override { return "AAConvexBatch"; }
joshualitt27f398f2015-02-05 14:39:01 -0800750
mtklein36352bf2015-03-25 18:17:31 -0700751 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt27f398f2015-02-05 14:39:01 -0800752 // When this is called on a batch, there is only one geometry bundle
753 out->setKnownFourComponents(fGeoData[0].fColor);
754 }
mtklein36352bf2015-03-25 18:17:31 -0700755 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt27f398f2015-02-05 14:39:01 -0800756 out->setUnknownSingleComponent();
757 }
758
mtklein36352bf2015-03-25 18:17:31 -0700759 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt27f398f2015-02-05 14:39:01 -0800760 // Handle any color overrides
761 if (init.fColorIgnored) {
762 fGeoData[0].fColor = GrColor_ILLEGAL;
763 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
764 fGeoData[0].fColor = init.fOverrideColor;
765 }
766
767 // setup batch properties
768 fBatch.fColorIgnored = init.fColorIgnored;
769 fBatch.fColor = fGeoData[0].fColor;
770 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
771 fBatch.fCoverageIgnored = init.fCoverageIgnored;
robertphillipse16dfdb2015-05-08 04:46:51 -0700772 fBatch.fLinesOnly = SkPath::kLine_SegmentMask == fGeoData[0].fPath.getSegmentMasks();
773 fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
774 }
775
776 void generateGeometryLinesOnly(GrBatchTarget* batchTarget, const GrPipeline* pipeline) {
777 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
778
779 SkMatrix invert;
780 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
781 SkDebugf("Could not invert viewmatrix\n");
782 return;
783 }
784
785 // Setup GrGeometryProcessor
786 SkAutoTUnref<const GrGeometryProcessor> gp(
787 create_fill_gp(canTweakAlphaForCoverage, invert));
788
789 batchTarget->initDraw(gp, pipeline);
790
791 // TODO remove this when batch is everywhere
792 GrPipelineInfo init;
793 init.fColorIgnored = fBatch.fColorIgnored;
794 init.fOverrideColor = GrColor_ILLEGAL;
795 init.fCoverageIgnored = fBatch.fCoverageIgnored;
796 init.fUsesLocalCoords = this->usesLocalCoords();
797 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
798
799 size_t vertexStride = gp->getVertexStride();
800
801 SkASSERT(canTweakAlphaForCoverage ?
802 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
803 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
804
805 GrAAConvexTessellator tess;
806
807 int instanceCount = fGeoData.count();
808
809 for (int i = 0; i < instanceCount; i++) {
810 tess.rewind();
811
812 Geometry& args = fGeoData[i];
813
814 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
815 continue;
816 }
817
818 const GrVertexBuffer* vertexBuffer;
819 int firstVertex;
820
821 void* verts = batchTarget->makeVertSpace(vertexStride, tess.numPts(),
822 &vertexBuffer, &firstVertex);
823 if (!verts) {
824 SkDebugf("Could not allocate vertices\n");
825 return;
826 }
827
828 const GrIndexBuffer* indexBuffer;
829 int firstIndex;
830
831 uint16_t* idxs = batchTarget->makeIndexSpace(tess.numIndices(),
832 &indexBuffer, &firstIndex);
833 if (!idxs) {
834 SkDebugf("Could not allocate indices\n");
835 return;
836 }
837
838 extract_verts(tess, verts, vertexStride, args.fColor, idxs, canTweakAlphaForCoverage);
839
840 GrVertices info;
841 info.initIndexed(kTriangles_GrPrimitiveType,
842 vertexBuffer, indexBuffer,
843 firstVertex, firstIndex,
844 tess.numPts(), tess.numIndices());
845 batchTarget->draw(info);
846 }
joshualitt27f398f2015-02-05 14:39:01 -0800847 }
848
mtklein36352bf2015-03-25 18:17:31 -0700849 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
robertphillipse16dfdb2015-05-08 04:46:51 -0700850#ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
851 if (this->linesOnly()) {
852 this->generateGeometryLinesOnly(batchTarget, pipeline);
853 return;
854 }
855#endif
856
joshualitt27f398f2015-02-05 14:39:01 -0800857 int instanceCount = fGeoData.count();
858
859 SkMatrix invert;
joshualitt7abe15d2015-02-20 12:40:45 -0800860 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
joshualitt27f398f2015-02-05 14:39:01 -0800861 SkDebugf("Could not invert viewmatrix\n");
862 return;
863 }
864
865 // Setup GrGeometryProcessor
866 SkAutoTUnref<GrGeometryProcessor> quadProcessor(QuadEdgeEffect::Create(this->color(),
867 invert));
868
869 batchTarget->initDraw(quadProcessor, pipeline);
870
871 // TODO remove this when batch is everywhere
872 GrPipelineInfo init;
873 init.fColorIgnored = fBatch.fColorIgnored;
874 init.fOverrideColor = GrColor_ILLEGAL;
875 init.fCoverageIgnored = fBatch.fCoverageIgnored;
876 init.fUsesLocalCoords = this->usesLocalCoords();
877 quadProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init);
878
879 // TODO generate all segments for all paths and use one vertex buffer
880 for (int i = 0; i < instanceCount; i++) {
881 Geometry& args = fGeoData[i];
882
883 // We use the fact that SkPath::transform path does subdivision based on
884 // perspective. Otherwise, we apply the view matrix when copying to the
885 // segment representation.
886 const SkMatrix* viewMatrix = &args.fViewMatrix;
887 if (viewMatrix->hasPerspective()) {
888 args.fPath.transform(*viewMatrix);
889 viewMatrix = &SkMatrix::I();
890 }
891
892 int vertexCount;
893 int indexCount;
894 enum {
895 kPreallocSegmentCnt = 512 / sizeof(Segment),
896 kPreallocDrawCnt = 4,
897 };
898 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
899 SkPoint fanPt;
900
901 if (!get_segments(args.fPath, *viewMatrix, &segments, &fanPt, &vertexCount,
902 &indexCount)) {
903 continue;
904 }
905
906 const GrVertexBuffer* vertexBuffer;
907 int firstVertex;
908
909 size_t vertexStride = quadProcessor->getVertexStride();
bsalomone64eb572015-05-07 11:35:55 -0700910 QuadVertex* verts = reinterpret_cast<QuadVertex*>(batchTarget->makeVertSpace(
911 vertexStride, vertexCount, &vertexBuffer, &firstVertex));
912
913 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800914 SkDebugf("Could not allocate vertices\n");
915 return;
916 }
917
joshualitt27f398f2015-02-05 14:39:01 -0800918 const GrIndexBuffer* indexBuffer;
919 int firstIndex;
920
robertphillipse40d3972015-05-07 09:51:43 -0700921 uint16_t *idxs = batchTarget->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
922 if (!idxs) {
joshualitt4b31de82015-03-05 14:33:41 -0800923 SkDebugf("Could not allocate indices\n");
924 return;
925 }
926
joshualitt27f398f2015-02-05 14:39:01 -0800927 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
928 create_vertices(segments, fanPt, &draws, verts, idxs);
929
bsalomone64eb572015-05-07 11:35:55 -0700930 GrVertices vertices;
joshualitt27f398f2015-02-05 14:39:01 -0800931
joshualitt27f398f2015-02-05 14:39:01 -0800932 for (int i = 0; i < draws.count(); ++i) {
933 const Draw& draw = draws[i];
bsalomone64eb572015-05-07 11:35:55 -0700934 vertices.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer,
935 firstVertex, firstIndex, draw.fVertexCnt, draw.fIndexCnt);
936 batchTarget->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -0700937 firstVertex += draw.fVertexCnt;
938 firstIndex += draw.fIndexCnt;
joshualitt27f398f2015-02-05 14:39:01 -0800939 }
940 }
941 }
942
943 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
944
945private:
946 AAConvexPathBatch(const Geometry& geometry) {
947 this->initClassID<AAConvexPathBatch>();
948 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700949
950 // compute bounds
951 fBounds = geometry.fPath.getBounds();
952 geometry.fViewMatrix.mapRect(&fBounds);
joshualitt27f398f2015-02-05 14:39:01 -0800953 }
954
mtklein36352bf2015-03-25 18:17:31 -0700955 bool onCombineIfPossible(GrBatch* t) override {
joshualitt27f398f2015-02-05 14:39:01 -0800956 AAConvexPathBatch* that = t->cast<AAConvexPathBatch>();
957
958 if (this->color() != that->color()) {
959 return false;
960 }
961
962 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
963 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
964 return false;
965 }
966
robertphillipse16dfdb2015-05-08 04:46:51 -0700967 if (this->linesOnly() != that->linesOnly()) {
968 return false;
969 }
970
971 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
972 // not tweaking
973 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
974 fBatch.fCanTweakAlphaForCoverage = false;
975 }
976
joshualitt27f398f2015-02-05 14:39:01 -0800977 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700978 this->joinBounds(that->bounds());
joshualitt27f398f2015-02-05 14:39:01 -0800979 return true;
980 }
981
982 GrColor color() const { return fBatch.fColor; }
robertphillipse16dfdb2015-05-08 04:46:51 -0700983 bool linesOnly() const { return fBatch.fLinesOnly; }
joshualitt27f398f2015-02-05 14:39:01 -0800984 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
robertphillipse16dfdb2015-05-08 04:46:51 -0700985 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
joshualitt27f398f2015-02-05 14:39:01 -0800986 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
987
988 struct BatchTracker {
989 GrColor fColor;
990 bool fUsesLocalCoords;
991 bool fColorIgnored;
992 bool fCoverageIgnored;
robertphillipse16dfdb2015-05-08 04:46:51 -0700993 bool fLinesOnly;
994 bool fCanTweakAlphaForCoverage;
joshualitt27f398f2015-02-05 14:39:01 -0800995 };
996
joshualitt27f398f2015-02-05 14:39:01 -0800997 BatchTracker fBatch;
998 SkSTArray<1, Geometry, true> fGeoData;
999};
1000
joshualitt9853cce2014-11-17 14:22:48 -08001001bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08001002 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001003 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001004 const SkMatrix& vm,
joshualitt27f398f2015-02-05 14:39:01 -08001005 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -07001006 const GrStrokeInfo&,
bsalomon@google.comc2099d22012-03-02 21:26:50 +00001007 bool antiAlias) {
joshualitt27f398f2015-02-05 14:39:01 -08001008 if (path.isEmpty()) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +00001009 return true;
1010 }
bsalomon@google.com4647f902013-03-26 14:45:27 +00001011
joshualitt27f398f2015-02-05 14:39:01 -08001012 AAConvexPathBatch::Geometry geometry;
1013 geometry.fColor = color;
1014 geometry.fViewMatrix = vm;
1015 geometry.fPath = path;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +00001016
joshualitt27f398f2015-02-05 14:39:01 -08001017 SkAutoTUnref<GrBatch> batch(AAConvexPathBatch::Create(geometry));
joshualitt99c7c072015-05-01 13:43:30 -07001018 target->drawBatch(pipelineBuilder, batch);
bsalomon@google.coma8347462012-10-08 18:59:39 +00001019
bsalomon@google.comc2099d22012-03-02 21:26:50 +00001020 return true;
joshualitt27f398f2015-02-05 14:39:01 -08001021
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +00001022}
joshualitt8e5c1772015-05-11 08:58:52 -07001023
1024///////////////////////////////////////////////////////////////////////////////////////////////////
1025
1026#ifdef GR_TEST_UTILS
1027
joshualitt6c891102015-05-13 08:51:49 -07001028BATCH_TEST_DEFINE(AAConvexPathBatch) {
joshualitt8e5c1772015-05-11 08:58:52 -07001029 AAConvexPathBatch::Geometry geometry;
1030 geometry.fColor = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001031 geometry.fViewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt8e5c1772015-05-11 08:58:52 -07001032 geometry.fPath = GrTest::TestPathConvex(random);
1033
1034 return AAConvexPathBatch::Create(geometry);
1035}
1036
1037#endif