blob: 45a4ca37831532d86f42b9f5507d934fe4222c0d [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"
bsalomoneb1cb5c2015-05-22 08:01:09 -070015#include "GrCaps.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000016#include "GrContext.h"
robertphillipse16dfdb2015-05-08 04:46:51 -070017#include "GrDefaultGeoProcFactory.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"
reed026beb52015-06-10 14:23:15 -070025#include "SkPathPriv.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000026#include "SkString.h"
commit-bot@chromium.org933e65d2014-03-20 20:00:24 +000027#include "SkTraceEvent.h"
joshualittb0a8a372014-09-23 09:50:21 -070028#include "gl/GrGLProcessor.h"
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000029#include "gl/GrGLSL.h"
joshualitt249af152014-09-15 11:41:13 -070030#include "gl/GrGLGeometryProcessor.h"
joshualitteb2a6762014-12-04 11:35:33 -080031#include "gl/builders/GrGLProgramBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000032
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000033GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
34}
35
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000036struct Segment {
37 enum {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000038 // These enum values are assumed in member functions below.
39 kLine = 0,
40 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000041 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000042
bsalomon@google.com9aed1142012-01-30 14:28:39 +000043 // line uses one pt, quad uses 2 pts
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000044 SkPoint fPts[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000045 // normal to edge ending at each pt
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000046 SkVector fNorms[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000047 // is the corner where the previous segment meets this segment
48 // sharp. If so, fMid is a normalized bisector facing outward.
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000049 SkVector fMid;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000050
51 int countPoints() {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000052 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
53 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000054 }
55 const SkPoint& endPt() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000056 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
57 return fPts[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000058 };
59 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000060 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
61 return fNorms[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000062 };
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000063};
64
65typedef SkTArray<Segment, true> SegmentArray;
66
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +000067static void center_of_mass(const SegmentArray& segments, SkPoint* c) {
bsalomon@google.com81712882012-11-01 17:12:34 +000068 SkScalar area = 0;
vandebo@chromium.org6390c722012-03-28 21:03:22 +000069 SkPoint center = {0, 0};
bsalomon@google.com9aed1142012-01-30 14:28:39 +000070 int count = segments.count();
vandebo@chromium.org6390c722012-03-28 21:03:22 +000071 SkPoint p0 = {0, 0};
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000072 if (count > 2) {
73 // We translate the polygon so that the first point is at the origin.
74 // This avoids some precision issues with small area polygons far away
75 // from the origin.
76 p0 = segments[0].endPt();
77 SkPoint pi;
78 SkPoint pj;
bsalomon@google.coma51ab842012-07-10 19:53:34 +000079 // the first and last iteration of the below loop would compute
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000080 // zeros since the starting / ending point is (0,0). So instead we start
81 // at i=1 and make the last iteration i=count-2.
82 pj = segments[1].endPt() - p0;
83 for (int i = 1; i < count - 1; ++i) {
84 pi = pj;
85 const SkPoint pj = segments[i + 1].endPt() - p0;
86
bsalomon@google.com81712882012-11-01 17:12:34 +000087 SkScalar t = SkScalarMul(pi.fX, pj.fY) - SkScalarMul(pj.fX, pi.fY);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000088 area += t;
89 center.fX += (pi.fX + pj.fX) * t;
90 center.fY += (pi.fY + pj.fY) * t;
91
92 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000093 }
bsalomon@google.com278dc692012-02-15 16:52:51 +000094 // If the poly has no area then we instead return the average of
95 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000096 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +000097 SkPoint avg;
98 avg.set(0, 0);
99 for (int i = 0; i < count; ++i) {
100 const SkPoint& pt = segments[i].endPt();
101 avg.fX += pt.fX;
102 avg.fY += pt.fY;
103 }
104 SkScalar denom = SK_Scalar1 / count;
105 avg.scale(denom);
106 *c = avg;
107 } else {
108 area *= 3;
reed80ea19c2015-05-12 10:37:34 -0700109 area = SkScalarInvert(area);
bsalomon@google.com81712882012-11-01 17:12:34 +0000110 center.fX = SkScalarMul(center.fX, area);
111 center.fY = SkScalarMul(center.fY, area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +0000112 // undo the translate of p0 to the origin.
113 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000114 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000115 SkASSERT(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000116}
117
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000118static void compute_vectors(SegmentArray* segments,
119 SkPoint* fanPt,
reed026beb52015-06-10 14:23:15 -0700120 SkPathPriv::FirstDirection dir,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000121 int* vCount,
122 int* iCount) {
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000123 center_of_mass(*segments, fanPt);
124 int count = segments->count();
125
bsalomon@google.com278dc692012-02-15 16:52:51 +0000126 // Make the normals point towards the outside
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000127 SkPoint::Side normSide;
reed026beb52015-06-10 14:23:15 -0700128 if (dir == SkPathPriv::kCCW_FirstDirection) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000129 normSide = SkPoint::kRight_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000130 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000131 normSide = SkPoint::kLeft_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000132 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000133
134 *vCount = 0;
135 *iCount = 0;
136 // compute normals at all points
137 for (int a = 0; a < count; ++a) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000138 Segment& sega = (*segments)[a];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000139 int b = (a + 1) % count;
140 Segment& segb = (*segments)[b];
141
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000142 const SkPoint* prevPt = &sega.endPt();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000143 int n = segb.countPoints();
144 for (int p = 0; p < n; ++p) {
145 segb.fNorms[p] = segb.fPts[p] - *prevPt;
146 segb.fNorms[p].normalize();
147 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
148 prevPt = &segb.fPts[p];
149 }
150 if (Segment::kLine == segb.fType) {
151 *vCount += 5;
152 *iCount += 9;
153 } else {
154 *vCount += 6;
155 *iCount += 12;
156 }
157 }
158
159 // compute mid-vectors where segments meet. TODO: Detect shallow corners
160 // and leave out the wedges and close gaps by stitching segments together.
161 for (int a = 0; a < count; ++a) {
162 const Segment& sega = (*segments)[a];
163 int b = (a + 1) % count;
164 Segment& segb = (*segments)[b];
165 segb.fMid = segb.fNorms[0] + sega.endNorm();
166 segb.fMid.normalize();
167 // corner wedges
168 *vCount += 4;
169 *iCount += 6;
170 }
171}
172
bsalomon@google.com9732f622012-01-31 15:19:21 +0000173struct DegenerateTestData {
174 DegenerateTestData() { fStage = kInitial; }
175 bool isDegenerate() const { return kNonDegenerate != fStage; }
176 enum {
177 kInitial,
178 kPoint,
179 kLine,
180 kNonDegenerate
181 } fStage;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000182 SkPoint fFirstPoint;
183 SkVector fLineNormal;
bsalomon@google.com81712882012-11-01 17:12:34 +0000184 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000185};
186
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000187static const SkScalar kClose = (SK_Scalar1 / 16);
188static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000189
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000190static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000191 switch (data->fStage) {
192 case DegenerateTestData::kInitial:
193 data->fFirstPoint = pt;
194 data->fStage = DegenerateTestData::kPoint;
195 break;
196 case DegenerateTestData::kPoint:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000197 if (pt.distanceToSqd(data->fFirstPoint) > kCloseSqd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000198 data->fLineNormal = pt - data->fFirstPoint;
199 data->fLineNormal.normalize();
200 data->fLineNormal.setOrthog(data->fLineNormal);
201 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
202 data->fStage = DegenerateTestData::kLine;
203 }
204 break;
205 case DegenerateTestData::kLine:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000206 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000207 data->fStage = DegenerateTestData::kNonDegenerate;
208 }
209 case DegenerateTestData::kNonDegenerate:
210 break;
211 default:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000212 SkFAIL("Unexpected degenerate test stage.");
bsalomon@google.com9732f622012-01-31 15:19:21 +0000213 }
214}
215
reed026beb52015-06-10 14:23:15 -0700216static inline bool get_direction(const SkPath& path, const SkMatrix& m,
217 SkPathPriv::FirstDirection* dir) {
218 if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000219 return false;
220 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000221 // check whether m reverses the orientation
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000222 SkASSERT(!m.hasPerspective());
bsalomon@google.com81712882012-11-01 17:12:34 +0000223 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
224 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000225 if (det2x2 < 0) {
reed026beb52015-06-10 14:23:15 -0700226 *dir = SkPathPriv::OppositeFirstDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000227 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000228 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000229}
230
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000231static inline void add_line_to_segment(const SkPoint& pt,
joshualitt27f398f2015-02-05 14:39:01 -0800232 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000233 segments->push_back();
234 segments->back().fType = Segment::kLine;
235 segments->back().fPts[0] = pt;
236}
237
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000238static inline void add_quad_segment(const SkPoint pts[3],
joshualitt27f398f2015-02-05 14:39:01 -0800239 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000240 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
241 if (pts[0] != pts[2]) {
joshualitt27f398f2015-02-05 14:39:01 -0800242 add_line_to_segment(pts[2], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000243 }
244 } else {
245 segments->push_back();
246 segments->back().fType = Segment::kQuad;
247 segments->back().fPts[0] = pts[1];
248 segments->back().fPts[1] = pts[2];
249 }
250}
251
252static inline void add_cubic_segments(const SkPoint pts[4],
reed026beb52015-06-10 14:23:15 -0700253 SkPathPriv::FirstDirection dir,
joshualitt27f398f2015-02-05 14:39:01 -0800254 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000255 SkSTArray<15, SkPoint, true> quads;
256 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
257 int count = quads.count();
258 for (int q = 0; q < count; q += 3) {
joshualitt27f398f2015-02-05 14:39:01 -0800259 add_quad_segment(&quads[q], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000260 }
261}
262
263static bool get_segments(const SkPath& path,
264 const SkMatrix& m,
265 SegmentArray* segments,
266 SkPoint* fanPt,
267 int* vCount,
joshualitt27f398f2015-02-05 14:39:01 -0800268 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000269 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000270 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000271 // to the path from the sample to compute coverage. Every pixel intersected
272 // 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 +0000273 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000274 // thus should be very light. This is particularly egregious for degenerate
275 // line paths. We detect paths that are very close to a line (zero area) and
276 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000277 DegenerateTestData degenerateData;
reed026beb52015-06-10 14:23:15 -0700278 SkPathPriv::FirstDirection dir;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000279 // get_direction can fail for some degenerate paths.
280 if (!get_direction(path, m, &dir)) {
281 return false;
282 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000283
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000284 for (;;) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000285 SkPoint pts[4];
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000286 SkPath::Verb verb = iter.next(pts);
287 switch (verb) {
288 case SkPath::kMove_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000289 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000290 update_degenerate_test(&degenerateData, pts[0]);
291 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000292 case SkPath::kLine_Verb: {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000293 m.mapPoints(&pts[1], 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000294 update_degenerate_test(&degenerateData, pts[1]);
joshualitt27f398f2015-02-05 14:39:01 -0800295 add_line_to_segment(pts[1], segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000296 break;
297 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000298 case SkPath::kQuad_Verb:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000299 m.mapPoints(pts, 3);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000300 update_degenerate_test(&degenerateData, pts[1]);
301 update_degenerate_test(&degenerateData, pts[2]);
joshualitt27f398f2015-02-05 14:39:01 -0800302 add_quad_segment(pts, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000303 break;
egdanielaf18a092015-01-05 10:22:28 -0800304 case SkPath::kConic_Verb: {
305 m.mapPoints(pts, 3);
306 SkScalar weight = iter.conicWeight();
307 SkAutoConicToQuads converter;
308 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5f);
309 for (int i = 0; i < converter.countQuads(); ++i) {
310 update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
311 update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
joshualitt27f398f2015-02-05 14:39:01 -0800312 add_quad_segment(quadPts + 2*i, segments);
egdanielaf18a092015-01-05 10:22:28 -0800313 }
314 break;
315 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000316 case SkPath::kCubic_Verb: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000317 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000318 update_degenerate_test(&degenerateData, pts[1]);
319 update_degenerate_test(&degenerateData, pts[2]);
320 update_degenerate_test(&degenerateData, pts[3]);
joshualitt27f398f2015-02-05 14:39:01 -0800321 add_cubic_segments(pts, dir, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000322 break;
323 };
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000324 case SkPath::kDone_Verb:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000325 if (degenerateData.isDegenerate()) {
326 return false;
327 } else {
bsalomon@google.com278dc692012-02-15 16:52:51 +0000328 compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000329 return true;
330 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000331 default:
332 break;
333 }
334 }
335}
336
337struct QuadVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000338 SkPoint fPos;
339 SkPoint fUV;
bsalomon@google.com81712882012-11-01 17:12:34 +0000340 SkScalar fD0;
341 SkScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000342};
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000343
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000344struct Draw {
345 Draw() : fVertexCnt(0), fIndexCnt(0) {}
346 int fVertexCnt;
347 int fIndexCnt;
348};
349
350typedef SkTArray<Draw, true> DrawArray;
351
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000352static void create_vertices(const SegmentArray& segments,
353 const SkPoint& fanPt,
354 DrawArray* draws,
355 QuadVertex* verts,
356 uint16_t* idxs) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000357 Draw* draw = &draws->push_back();
358 // alias just to make vert/index assignments easier to read.
359 int* v = &draw->fVertexCnt;
360 int* i = &draw->fIndexCnt;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000361
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000362 int count = segments.count();
363 for (int a = 0; a < count; ++a) {
364 const Segment& sega = segments[a];
365 int b = (a + 1) % count;
366 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000367
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000368 // Check whether adding the verts for this segment to the current draw would cause index
369 // values to overflow.
370 int vCount = 4;
371 if (Segment::kLine == segb.fType) {
372 vCount += 5;
373 } else {
374 vCount += 6;
375 }
376 if (draw->fVertexCnt + vCount > (1 << 16)) {
377 verts += *v;
378 idxs += *i;
379 draw = &draws->push_back();
380 v = &draw->fVertexCnt;
381 i = &draw->fIndexCnt;
382 }
383
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000384 // FIXME: These tris are inset in the 1 unit arc around the corner
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000385 verts[*v + 0].fPos = sega.endPt();
386 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
387 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
388 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
389 verts[*v + 0].fUV.set(0,0);
390 verts[*v + 1].fUV.set(0,-SK_Scalar1);
391 verts[*v + 2].fUV.set(0,-SK_Scalar1);
392 verts[*v + 3].fUV.set(0,-SK_Scalar1);
393 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
394 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
395 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
396 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000397
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000398 idxs[*i + 0] = *v + 0;
399 idxs[*i + 1] = *v + 2;
400 idxs[*i + 2] = *v + 1;
401 idxs[*i + 3] = *v + 0;
402 idxs[*i + 4] = *v + 3;
403 idxs[*i + 5] = *v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000404
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000405 *v += 4;
406 *i += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000407
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000408 if (Segment::kLine == segb.fType) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000409 verts[*v + 0].fPos = fanPt;
410 verts[*v + 1].fPos = sega.endPt();
411 verts[*v + 2].fPos = segb.fPts[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000412
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000413 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
414 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000415
416 // we draw the line edge as a degenerate quad (u is 0, v is the
417 // signed distance to the edge)
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000418 SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos,
419 verts[*v + 2].fPos);
420 verts[*v + 0].fUV.set(0, dist);
421 verts[*v + 1].fUV.set(0, 0);
422 verts[*v + 2].fUV.set(0, 0);
423 verts[*v + 3].fUV.set(0, -SK_Scalar1);
424 verts[*v + 4].fUV.set(0, -SK_Scalar1);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000425
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000426 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
427 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
428 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
429 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
430 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000431
cdalton35964822015-04-29 10:14:03 -0700432 idxs[*i + 0] = *v + 3;
433 idxs[*i + 1] = *v + 1;
434 idxs[*i + 2] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000435
cdalton35964822015-04-29 10:14:03 -0700436 idxs[*i + 3] = *v + 4;
437 idxs[*i + 4] = *v + 3;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000438 idxs[*i + 5] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000439
cdalton35964822015-04-29 10:14:03 -0700440 *i += 6;
441
442 // Draw the interior fan if it exists.
443 // TODO: Detect and combine colinear segments. This will ensure we catch every case
444 // with no interior, and that the resulting shared edge uses the same endpoints.
445 if (count >= 3) {
446 idxs[*i + 0] = *v + 0;
447 idxs[*i + 1] = *v + 2;
448 idxs[*i + 2] = *v + 1;
449
450 *i += 3;
451 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000452
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000453 *v += 5;
bsalomon@google.com06809612012-01-21 15:03:39 +0000454 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000455 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000456
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000457 SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000458 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000459
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000460 verts[*v + 0].fPos = fanPt;
461 verts[*v + 1].fPos = qpts[0];
462 verts[*v + 2].fPos = qpts[2];
463 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
464 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
465 verts[*v + 5].fPos = qpts[1] + midVec;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000466
bsalomon@google.com81712882012-11-01 17:12:34 +0000467 SkScalar c = segb.fNorms[0].dot(qpts[0]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000468 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
469 verts[*v + 1].fD0 = 0.f;
470 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
471 verts[*v + 3].fD0 = -SK_ScalarMax/100;
472 verts[*v + 4].fD0 = -SK_ScalarMax/100;
473 verts[*v + 5].fD0 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000474
475 c = segb.fNorms[1].dot(qpts[2]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000476 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
477 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
478 verts[*v + 2].fD1 = 0.f;
479 verts[*v + 3].fD1 = -SK_ScalarMax/100;
480 verts[*v + 4].fD1 = -SK_ScalarMax/100;
481 verts[*v + 5].fD1 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000482
bsalomon@google.com19713172012-03-15 13:51:08 +0000483 GrPathUtils::QuadUVMatrix toUV(qpts);
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000484 toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v);
bsalomon@google.com06809612012-01-21 15:03:39 +0000485
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000486 idxs[*i + 0] = *v + 3;
487 idxs[*i + 1] = *v + 1;
488 idxs[*i + 2] = *v + 2;
489 idxs[*i + 3] = *v + 4;
490 idxs[*i + 4] = *v + 3;
491 idxs[*i + 5] = *v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000492
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000493 idxs[*i + 6] = *v + 5;
494 idxs[*i + 7] = *v + 3;
495 idxs[*i + 8] = *v + 4;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000496
cdalton35964822015-04-29 10:14:03 -0700497 *i += 9;
498
499 // Draw the interior fan if it exists.
500 // TODO: Detect and combine colinear segments. This will ensure we catch every case
501 // with no interior, and that the resulting shared edge uses the same endpoints.
502 if (count >= 3) {
503 idxs[*i + 0] = *v + 0;
504 idxs[*i + 1] = *v + 2;
505 idxs[*i + 2] = *v + 1;
506
507 *i += 3;
508 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000509
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000510 *v += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000511 }
512 }
513}
514
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000515///////////////////////////////////////////////////////////////////////////////
516
517/*
518 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
519 * two components of the vertex attribute. Coverage is based on signed
520 * distance with negative being inside, positive outside. The edge is specified in
521 * window space (y-down). If either the third or fourth component of the interpolated
522 * 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 +0000523 * attempt to trim to a portion of the infinite quad.
524 * Requires shader derivative instruction support.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000525 */
526
joshualitt249af152014-09-15 11:41:13 -0700527class QuadEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000528public:
529
joshualittb8c241a2015-05-19 08:23:30 -0700530 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& localMatrix,
531 bool usesLocalCoords) {
532 return SkNEW_ARGS(QuadEdgeEffect, (color, localMatrix, usesLocalCoords));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000533 }
534
535 virtual ~QuadEdgeEffect() {}
536
mtklein36352bf2015-03-25 18:17:31 -0700537 const char* name() const override { return "QuadEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000538
joshualitt71c92602015-01-14 08:12:47 -0800539 const Attribute* inPosition() const { return fInPosition; }
540 const Attribute* inQuadEdge() const { return fInQuadEdge; }
joshualitt88c23fc2015-05-13 14:18:07 -0700541 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700542 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -0700543 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700544 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700545
joshualittb0a8a372014-09-23 09:50:21 -0700546 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000547 public:
joshualitteb2a6762014-12-04 11:35:33 -0800548 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800549 const GrBatchTracker&)
550 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000551
mtklein36352bf2015-03-25 18:17:31 -0700552 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800553 const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800554 GrGLGPBuilder* pb = args.fPB;
555 GrGLVertexBuilder* vsBuilder = pb->getVertexShaderBuilder();
joshualitt2dd1ae02014-12-03 06:24:10 -0800556
joshualittabb52a12015-01-13 15:02:10 -0800557 // emit attributes
558 vsBuilder->emitAttributes(qe);
559
joshualitt74077b92014-10-24 11:26:03 -0700560 GrGLVertToFrag v(kVec4f_GrSLType);
561 args.fPB->addVarying("QuadEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -0800562 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.inQuadEdge()->fName);
563
joshualitt9b989322014-12-15 14:16:27 -0800564 // Setup pass through color
joshualittb8c241a2015-05-19 08:23:30 -0700565 if (!qe.colorIgnored()) {
566 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
567 }
joshualitt9b989322014-12-15 14:16:27 -0800568
joshualittabb52a12015-01-13 15:02:10 -0800569 // Setup position
joshualitte578a952015-05-14 10:09:13 -0700570 this->setupPosition(pb, gpArgs, qe.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800571
572 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800573 this->emitTransforms(args.fPB, gpArgs->fPositionVar, qe.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800574 qe.localMatrix(), args.fTransformsIn, args.fTransformsOut);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000575
egdaniel29bee0f2015-04-29 11:54:42 -0700576 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700577
578 SkAssertResult(fsBuilder->enableFeature(
579 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
joshualitt74077b92014-10-24 11:26:03 -0700580 fsBuilder->codeAppendf("float edgeAlpha;");
joshualitt30ba4362014-08-21 20:18:45 -0700581
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000582 // keep the derivative instructions outside the conditional
joshualitt74077b92014-10-24 11:26:03 -0700583 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s.xy);", v.fsIn());
584 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s.xy);", v.fsIn());
585 fsBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000586 // today we know z and w are in device space. We could use derivatives
joshualitt74077b92014-10-24 11:26:03 -0700587 fsBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
588 v.fsIn());
589 fsBuilder->codeAppendf ("} else {");
590 fsBuilder->codeAppendf("vec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,"
591 " 2.0*%s.x*duvdy.x - duvdy.y);",
592 v.fsIn(), v.fsIn());
593 fsBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
594 v.fsIn());
595 fsBuilder->codeAppendf("edgeAlpha = "
596 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000597
joshualitt2dd1ae02014-12-03 06:24:10 -0800598 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000599 }
600
joshualitt9b989322014-12-15 14:16:27 -0800601 static inline void GenKey(const GrGeometryProcessor& gp,
602 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700603 const GrGLSLCaps&,
joshualitt9b989322014-12-15 14:16:27 -0800604 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700605 const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
joshualittb8c241a2015-05-19 08:23:30 -0700606 uint32_t key = 0;
607 key |= qee.usesLocalCoords() && qee.localMatrix().hasPerspective() ? 0x1 : 0x0;
608 key |= qee.colorIgnored() ? 0x2 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800609 b->add32(key);
joshualitt9b989322014-12-15 14:16:27 -0800610 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000611
joshualitt9b989322014-12-15 14:16:27 -0800612 virtual void setData(const GrGLProgramDataManager& pdman,
613 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700614 const GrBatchTracker& bt) override {
joshualittb8c241a2015-05-19 08:23:30 -0700615 const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
616 if (qe.color() != fColor) {
joshualitt9b989322014-12-15 14:16:27 -0800617 GrGLfloat c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700618 GrColorToRGBAFloat(qe.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800619 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700620 fColor = qe.color();
joshualitt9b989322014-12-15 14:16:27 -0800621 }
622 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000623
joshualitte3ababe2015-05-15 07:56:07 -0700624 void setTransformData(const GrPrimitiveProcessor& primProc,
625 const GrGLProgramDataManager& pdman,
626 int index,
627 const SkTArray<const GrCoordTransform*, true>& transforms) override {
628 this->setTransformDataHelper<QuadEdgeEffect>(primProc, pdman, index, transforms);
629 }
630
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000631 private:
joshualitt9b989322014-12-15 14:16:27 -0800632 GrColor fColor;
633 UniformHandle fColorUniform;
634
joshualitt249af152014-09-15 11:41:13 -0700635 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000636 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000637
joshualitteb2a6762014-12-04 11:35:33 -0800638 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700639 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700640 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800641 GLProcessor::GenKey(*this, bt, caps, b);
642 }
643
joshualittabb52a12015-01-13 15:02:10 -0800644 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700645 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800646 return SkNEW_ARGS(GLProcessor, (*this, bt));
647 }
648
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000649private:
joshualittb8c241a2015-05-19 08:23:30 -0700650 QuadEdgeEffect(GrColor color, const SkMatrix& localMatrix, bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700651 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700652 , fLocalMatrix(localMatrix)
653 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800654 this->initClassID<QuadEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800655 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
656 fInQuadEdge = &this->addVertexAttrib(Attribute("inQuadEdge", kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000657 }
658
joshualitt71c92602015-01-14 08:12:47 -0800659 const Attribute* fInPosition;
660 const Attribute* fInQuadEdge;
joshualitt88c23fc2015-05-13 14:18:07 -0700661 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700662 SkMatrix fLocalMatrix;
joshualittb8c241a2015-05-19 08:23:30 -0700663 bool fUsesLocalCoords;
joshualitt249af152014-09-15 11:41:13 -0700664
joshualittb0a8a372014-09-23 09:50:21 -0700665 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000666
joshualitt2e3b3e32014-12-09 13:31:14 -0800667 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000668};
669
joshualittb0a8a372014-09-23 09:50:21 -0700670GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000671
joshualittb0a8a372014-09-23 09:50:21 -0700672GrGeometryProcessor* QuadEdgeEffect::TestCreate(SkRandom* random,
673 GrContext*,
bsalomon4b91f762015-05-19 09:29:46 -0700674 const GrCaps& caps,
joshualittb0a8a372014-09-23 09:50:21 -0700675 GrTexture*[]) {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000676 // Doesn't work without derivative instructions.
jvanverthe9c0fc62015-04-29 11:18:05 -0700677 return caps.shaderCaps()->shaderDerivativeSupport() ?
joshualittd27f73e2014-12-29 07:43:36 -0800678 QuadEdgeEffect::Create(GrRandomColor(random),
joshualittb8c241a2015-05-19 08:23:30 -0700679 GrTest::TestMatrix(random),
680 random->nextBool()) : NULL;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000681}
682
683///////////////////////////////////////////////////////////////////////////////
684
joshualitt9853cce2014-11-17 14:22:48 -0800685bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800686 const GrPipelineBuilder*,
joshualitt8059eb92014-12-29 15:10:07 -0800687 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800688 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700689 const GrStrokeInfo& stroke,
robertphillips@google.comfa662942012-05-17 12:20:22 +0000690 bool antiAlias) const {
jvanverthe9c0fc62015-04-29 11:18:05 -0700691 return (target->caps()->shaderCaps()->shaderDerivativeSupport() && antiAlias &&
robertphillips@google.come79f3202014-02-11 16:30:21 +0000692 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
robertphillips@google.comfa662942012-05-17 12:20:22 +0000693}
694
robertphillipse16dfdb2015-05-08 04:46:51 -0700695// extract the result vertices and indices from the GrAAConvexTessellator
696static void extract_verts(const GrAAConvexTessellator& tess,
697 void* vertices,
698 size_t vertexStride,
699 GrColor color,
700 uint16_t* idxs,
701 bool tweakAlphaForCoverage) {
702 intptr_t verts = reinterpret_cast<intptr_t>(vertices);
703
704 for (int i = 0; i < tess.numPts(); ++i) {
705 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
706 }
707
708 // Make 'verts' point to the colors
709 verts += sizeof(SkPoint);
710 for (int i = 0; i < tess.numPts(); ++i) {
711 SkASSERT(tess.depth(i) >= -0.5f && tess.depth(i) <= 0.5f);
712 if (tweakAlphaForCoverage) {
713 SkASSERT(SkScalarRoundToInt(255.0f * (tess.depth(i) + 0.5f)) <= 255);
714 unsigned scale = SkScalarRoundToInt(255.0f * (tess.depth(i) + 0.5f));
715 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
716 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
717 } else {
718 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
719 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
720 tess.depth(i) + 0.5f;
721 }
722 }
723
724 for (int i = 0; i < tess.numIndices(); ++i) {
725 idxs[i] = tess.index(i);
726 }
727}
728
729static const GrGeometryProcessor* create_fill_gp(bool tweakAlphaForCoverage,
joshualittb8c241a2015-05-19 08:23:30 -0700730 const SkMatrix& localMatrix,
731 bool usesLocalCoords,
732 bool coverageIgnored) {
robertphillipse16dfdb2015-05-08 04:46:51 -0700733 uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
734 if (!tweakAlphaForCoverage) {
735 flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
736 }
737
joshualittb8c241a2015-05-19 08:23:30 -0700738 return GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, usesLocalCoords, coverageIgnored,
739 SkMatrix::I(), localMatrix);
robertphillipse16dfdb2015-05-08 04:46:51 -0700740}
741
joshualitt27f398f2015-02-05 14:39:01 -0800742class AAConvexPathBatch : public GrBatch {
743public:
744 struct Geometry {
745 GrColor fColor;
746 SkMatrix fViewMatrix;
747 SkPath fPath;
joshualitt27f398f2015-02-05 14:39:01 -0800748 };
749
750 static GrBatch* Create(const Geometry& geometry) {
751 return SkNEW_ARGS(AAConvexPathBatch, (geometry));
752 }
753
mtklein36352bf2015-03-25 18:17:31 -0700754 const char* name() const override { return "AAConvexBatch"; }
joshualitt27f398f2015-02-05 14:39:01 -0800755
mtklein36352bf2015-03-25 18:17:31 -0700756 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt27f398f2015-02-05 14:39:01 -0800757 // When this is called on a batch, there is only one geometry bundle
758 out->setKnownFourComponents(fGeoData[0].fColor);
759 }
mtklein36352bf2015-03-25 18:17:31 -0700760 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt27f398f2015-02-05 14:39:01 -0800761 out->setUnknownSingleComponent();
762 }
763
mtklein36352bf2015-03-25 18:17:31 -0700764 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt27f398f2015-02-05 14:39:01 -0800765 // Handle any color overrides
766 if (init.fColorIgnored) {
767 fGeoData[0].fColor = GrColor_ILLEGAL;
768 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
769 fGeoData[0].fColor = init.fOverrideColor;
770 }
771
772 // setup batch properties
773 fBatch.fColorIgnored = init.fColorIgnored;
774 fBatch.fColor = fGeoData[0].fColor;
775 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
776 fBatch.fCoverageIgnored = init.fCoverageIgnored;
robertphillipse16dfdb2015-05-08 04:46:51 -0700777 fBatch.fLinesOnly = SkPath::kLine_SegmentMask == fGeoData[0].fPath.getSegmentMasks();
778 fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
779 }
780
781 void generateGeometryLinesOnly(GrBatchTarget* batchTarget, const GrPipeline* pipeline) {
782 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
783
784 SkMatrix invert;
785 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
786 SkDebugf("Could not invert viewmatrix\n");
787 return;
788 }
789
790 // Setup GrGeometryProcessor
791 SkAutoTUnref<const GrGeometryProcessor> gp(
joshualittb8c241a2015-05-19 08:23:30 -0700792 create_fill_gp(canTweakAlphaForCoverage, invert,
793 this->usesLocalCoords(),
794 this->coverageIgnored()));
robertphillipse16dfdb2015-05-08 04:46:51 -0700795
796 batchTarget->initDraw(gp, pipeline);
797
robertphillipse16dfdb2015-05-08 04:46:51 -0700798 size_t vertexStride = gp->getVertexStride();
799
800 SkASSERT(canTweakAlphaForCoverage ?
801 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
802 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
803
804 GrAAConvexTessellator tess;
805
806 int instanceCount = fGeoData.count();
807
808 for (int i = 0; i < instanceCount; i++) {
809 tess.rewind();
810
811 Geometry& args = fGeoData[i];
812
813 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
814 continue;
815 }
816
817 const GrVertexBuffer* vertexBuffer;
818 int firstVertex;
819
820 void* verts = batchTarget->makeVertSpace(vertexStride, tess.numPts(),
821 &vertexBuffer, &firstVertex);
822 if (!verts) {
823 SkDebugf("Could not allocate vertices\n");
824 return;
825 }
826
827 const GrIndexBuffer* indexBuffer;
828 int firstIndex;
829
830 uint16_t* idxs = batchTarget->makeIndexSpace(tess.numIndices(),
831 &indexBuffer, &firstIndex);
832 if (!idxs) {
833 SkDebugf("Could not allocate indices\n");
834 return;
835 }
836
837 extract_verts(tess, verts, vertexStride, args.fColor, idxs, canTweakAlphaForCoverage);
838
839 GrVertices info;
840 info.initIndexed(kTriangles_GrPrimitiveType,
841 vertexBuffer, indexBuffer,
842 firstVertex, firstIndex,
843 tess.numPts(), tess.numIndices());
844 batchTarget->draw(info);
845 }
joshualitt27f398f2015-02-05 14:39:01 -0800846 }
847
mtklein36352bf2015-03-25 18:17:31 -0700848 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
robertphillipse16dfdb2015-05-08 04:46:51 -0700849#ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
850 if (this->linesOnly()) {
851 this->generateGeometryLinesOnly(batchTarget, pipeline);
852 return;
853 }
854#endif
855
joshualitt27f398f2015-02-05 14:39:01 -0800856 int instanceCount = fGeoData.count();
857
858 SkMatrix invert;
joshualitt7abe15d2015-02-20 12:40:45 -0800859 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
joshualitt27f398f2015-02-05 14:39:01 -0800860 SkDebugf("Could not invert viewmatrix\n");
861 return;
862 }
863
864 // Setup GrGeometryProcessor
joshualittb8c241a2015-05-19 08:23:30 -0700865 SkAutoTUnref<GrGeometryProcessor> quadProcessor(
866 QuadEdgeEffect::Create(this->color(), invert, this->usesLocalCoords()));
joshualitt27f398f2015-02-05 14:39:01 -0800867
868 batchTarget->initDraw(quadProcessor, pipeline);
869
joshualitt27f398f2015-02-05 14:39:01 -0800870 // TODO generate all segments for all paths and use one vertex buffer
871 for (int i = 0; i < instanceCount; i++) {
872 Geometry& args = fGeoData[i];
873
874 // We use the fact that SkPath::transform path does subdivision based on
875 // perspective. Otherwise, we apply the view matrix when copying to the
876 // segment representation.
877 const SkMatrix* viewMatrix = &args.fViewMatrix;
878 if (viewMatrix->hasPerspective()) {
879 args.fPath.transform(*viewMatrix);
880 viewMatrix = &SkMatrix::I();
881 }
882
883 int vertexCount;
884 int indexCount;
885 enum {
886 kPreallocSegmentCnt = 512 / sizeof(Segment),
887 kPreallocDrawCnt = 4,
888 };
889 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
890 SkPoint fanPt;
891
892 if (!get_segments(args.fPath, *viewMatrix, &segments, &fanPt, &vertexCount,
893 &indexCount)) {
894 continue;
895 }
896
897 const GrVertexBuffer* vertexBuffer;
898 int firstVertex;
899
900 size_t vertexStride = quadProcessor->getVertexStride();
bsalomone64eb572015-05-07 11:35:55 -0700901 QuadVertex* verts = reinterpret_cast<QuadVertex*>(batchTarget->makeVertSpace(
902 vertexStride, vertexCount, &vertexBuffer, &firstVertex));
903
904 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800905 SkDebugf("Could not allocate vertices\n");
906 return;
907 }
908
joshualitt27f398f2015-02-05 14:39:01 -0800909 const GrIndexBuffer* indexBuffer;
910 int firstIndex;
911
robertphillipse40d3972015-05-07 09:51:43 -0700912 uint16_t *idxs = batchTarget->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
913 if (!idxs) {
joshualitt4b31de82015-03-05 14:33:41 -0800914 SkDebugf("Could not allocate indices\n");
915 return;
916 }
917
joshualitt27f398f2015-02-05 14:39:01 -0800918 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
919 create_vertices(segments, fanPt, &draws, verts, idxs);
920
bsalomone64eb572015-05-07 11:35:55 -0700921 GrVertices vertices;
joshualitt27f398f2015-02-05 14:39:01 -0800922
joshualitt27f398f2015-02-05 14:39:01 -0800923 for (int i = 0; i < draws.count(); ++i) {
924 const Draw& draw = draws[i];
bsalomone64eb572015-05-07 11:35:55 -0700925 vertices.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer,
926 firstVertex, firstIndex, draw.fVertexCnt, draw.fIndexCnt);
927 batchTarget->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -0700928 firstVertex += draw.fVertexCnt;
929 firstIndex += draw.fIndexCnt;
joshualitt27f398f2015-02-05 14:39:01 -0800930 }
931 }
932 }
933
934 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
935
936private:
937 AAConvexPathBatch(const Geometry& geometry) {
938 this->initClassID<AAConvexPathBatch>();
939 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700940
941 // compute bounds
942 fBounds = geometry.fPath.getBounds();
943 geometry.fViewMatrix.mapRect(&fBounds);
joshualitt27f398f2015-02-05 14:39:01 -0800944 }
945
mtklein36352bf2015-03-25 18:17:31 -0700946 bool onCombineIfPossible(GrBatch* t) override {
joshualitt27f398f2015-02-05 14:39:01 -0800947 AAConvexPathBatch* that = t->cast<AAConvexPathBatch>();
948
949 if (this->color() != that->color()) {
950 return false;
951 }
952
953 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
954 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
955 return false;
956 }
957
robertphillipse16dfdb2015-05-08 04:46:51 -0700958 if (this->linesOnly() != that->linesOnly()) {
959 return false;
960 }
961
962 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
963 // not tweaking
964 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
965 fBatch.fCanTweakAlphaForCoverage = false;
966 }
967
joshualitt27f398f2015-02-05 14:39:01 -0800968 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700969 this->joinBounds(that->bounds());
joshualitt27f398f2015-02-05 14:39:01 -0800970 return true;
971 }
972
973 GrColor color() const { return fBatch.fColor; }
robertphillipse16dfdb2015-05-08 04:46:51 -0700974 bool linesOnly() const { return fBatch.fLinesOnly; }
joshualitt27f398f2015-02-05 14:39:01 -0800975 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
robertphillipse16dfdb2015-05-08 04:46:51 -0700976 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
joshualitt27f398f2015-02-05 14:39:01 -0800977 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700978 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
joshualitt27f398f2015-02-05 14:39:01 -0800979
980 struct BatchTracker {
981 GrColor fColor;
982 bool fUsesLocalCoords;
983 bool fColorIgnored;
984 bool fCoverageIgnored;
robertphillipse16dfdb2015-05-08 04:46:51 -0700985 bool fLinesOnly;
986 bool fCanTweakAlphaForCoverage;
joshualitt27f398f2015-02-05 14:39:01 -0800987 };
988
joshualitt27f398f2015-02-05 14:39:01 -0800989 BatchTracker fBatch;
990 SkSTArray<1, Geometry, true> fGeoData;
991};
992
joshualitt9853cce2014-11-17 14:22:48 -0800993bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800994 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800995 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800996 const SkMatrix& vm,
joshualitt27f398f2015-02-05 14:39:01 -0800997 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700998 const GrStrokeInfo&,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000999 bool antiAlias) {
joshualitt27f398f2015-02-05 14:39:01 -08001000 if (path.isEmpty()) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +00001001 return true;
1002 }
bsalomon@google.com4647f902013-03-26 14:45:27 +00001003
joshualitt27f398f2015-02-05 14:39:01 -08001004 AAConvexPathBatch::Geometry geometry;
1005 geometry.fColor = color;
1006 geometry.fViewMatrix = vm;
1007 geometry.fPath = path;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +00001008
joshualitt27f398f2015-02-05 14:39:01 -08001009 SkAutoTUnref<GrBatch> batch(AAConvexPathBatch::Create(geometry));
joshualitt99c7c072015-05-01 13:43:30 -07001010 target->drawBatch(pipelineBuilder, batch);
bsalomon@google.coma8347462012-10-08 18:59:39 +00001011
bsalomon@google.comc2099d22012-03-02 21:26:50 +00001012 return true;
joshualitt27f398f2015-02-05 14:39:01 -08001013
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +00001014}
joshualitt8e5c1772015-05-11 08:58:52 -07001015
1016///////////////////////////////////////////////////////////////////////////////////////////////////
1017
1018#ifdef GR_TEST_UTILS
1019
joshualitt6c891102015-05-13 08:51:49 -07001020BATCH_TEST_DEFINE(AAConvexPathBatch) {
joshualitt8e5c1772015-05-11 08:58:52 -07001021 AAConvexPathBatch::Geometry geometry;
1022 geometry.fColor = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001023 geometry.fViewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt8e5c1772015-05-11 08:58:52 -07001024 geometry.fPath = GrTest::TestPathConvex(random);
1025
1026 return AAConvexPathBatch::Create(geometry);
1027}
1028
1029#endif