blob: 56a5e7ae1431b5eaa7b7790cf322ccafba097c38 [file] [log] [blame]
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrAAConvexPathRenderer.h"
9
robertphillipse16dfdb2015-05-08 04:46:51 -070010#include "GrAAConvexTessellator.h"
joshualitt8e5c1772015-05-11 08:58:52 -070011#include "GrBatchTest.h"
bsalomoneb1cb5c2015-05-22 08:01:09 -070012#include "GrCaps.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000013#include "GrContext.h"
robertphillipse16dfdb2015-05-08 04:46:51 -070014#include "GrDefaultGeoProcFactory.h"
joshualitteb2a6762014-12-04 11:35:33 -080015#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080016#include "GrInvariantOutput.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050017#include "GrOpFlushState.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000018#include "GrPathUtils.h"
bsalomonbb243832016-07-22 07:10:19 -070019#include "GrPipelineBuilder.h"
Brian Salomondad29232016-12-01 16:40:24 -050020#include "GrProcessor.h"
egdanielaf18a092015-01-05 10:22:28 -080021#include "SkGeometry.h"
reed026beb52015-06-10 14:23:15 -070022#include "SkPathPriv.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000023#include "SkString.h"
commit-bot@chromium.org933e65d2014-03-20 20:00:24 +000024#include "SkTraceEvent.h"
Brian Salomondad29232016-12-01 16:40:24 -050025#include "batches/GrMeshDrawOp.h"
egdaniel7ea439b2015-12-03 09:20:44 -080026#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080027#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070028#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080029#include "glsl/GrGLSLUniformHandler.h"
egdaniel0eafe792015-11-20 14:01:22 -080030#include "glsl/GrGLSLVarying.h"
egdaniel7ea439b2015-12-03 09:20:44 -080031#include "glsl/GrGLSLVertexShaderBuilder.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];
Mike Kleinfc6c37b2016-09-27 09:34:10 -040058 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000059 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000060 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
61 return fNorms[fType];
Mike Kleinfc6c37b2016-09-27 09:34:10 -040062 }
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;
robertphillips44c31282015-09-03 12:58:48 -070085 pj = segments[i + 1].endPt() - p0;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000086
robertphillips44c31282015-09-03 12:58:48 -070087 SkScalar t = SkPoint::CrossProduct(pi, pj);
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;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000091 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000092 }
robertphillips44c31282015-09-03 12:58:48 -070093
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);
robertphillips44c31282015-09-03 12:58:48 -0700110 center.scale(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,
reed026beb52015-06-10 14:23:15 -0700119 SkPathPriv::FirstDirection dir,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000120 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;
reed026beb52015-06-10 14:23:15 -0700127 if (dir == SkPathPriv::kCCW_FirstDirection) {
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
reed026beb52015-06-10 14:23:15 -0700215static inline bool get_direction(const SkPath& path, const SkMatrix& m,
216 SkPathPriv::FirstDirection* dir) {
217 if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000218 return false;
219 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000220 // check whether m reverses the orientation
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000221 SkASSERT(!m.hasPerspective());
bsalomon@google.com81712882012-11-01 17:12:34 +0000222 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
223 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000224 if (det2x2 < 0) {
reed026beb52015-06-10 14:23:15 -0700225 *dir = SkPathPriv::OppositeFirstDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000226 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000227 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000228}
229
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000230static inline void add_line_to_segment(const SkPoint& pt,
joshualitt27f398f2015-02-05 14:39:01 -0800231 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000232 segments->push_back();
233 segments->back().fType = Segment::kLine;
234 segments->back().fPts[0] = pt;
235}
236
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000237static inline void add_quad_segment(const SkPoint pts[3],
joshualitt27f398f2015-02-05 14:39:01 -0800238 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000239 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
240 if (pts[0] != pts[2]) {
joshualitt27f398f2015-02-05 14:39:01 -0800241 add_line_to_segment(pts[2], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000242 }
243 } else {
244 segments->push_back();
245 segments->back().fType = Segment::kQuad;
246 segments->back().fPts[0] = pts[1];
247 segments->back().fPts[1] = pts[2];
248 }
249}
250
251static inline void add_cubic_segments(const SkPoint pts[4],
reed026beb52015-06-10 14:23:15 -0700252 SkPathPriv::FirstDirection dir,
joshualitt27f398f2015-02-05 14:39:01 -0800253 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000254 SkSTArray<15, SkPoint, true> quads;
bsalomon18fab302016-02-16 08:00:05 -0800255 GrPathUtils::convertCubicToQuadsConstrainToTangents(pts, SK_Scalar1, dir, &quads);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000256 int count = quads.count();
257 for (int q = 0; q < count; q += 3) {
joshualitt27f398f2015-02-05 14:39:01 -0800258 add_quad_segment(&quads[q], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000259 }
260}
261
262static bool get_segments(const SkPath& path,
263 const SkMatrix& m,
264 SegmentArray* segments,
265 SkPoint* fanPt,
266 int* vCount,
joshualitt27f398f2015-02-05 14:39:01 -0800267 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000268 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000269 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000270 // to the path from the sample to compute coverage. Every pixel intersected
271 // 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 +0000272 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000273 // thus should be very light. This is particularly egregious for degenerate
274 // line paths. We detect paths that are very close to a line (zero area) and
275 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000276 DegenerateTestData degenerateData;
reed026beb52015-06-10 14:23:15 -0700277 SkPathPriv::FirstDirection dir;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000278 // get_direction can fail for some degenerate paths.
279 if (!get_direction(path, m, &dir)) {
280 return false;
281 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000282
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000283 for (;;) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000284 SkPoint pts[4];
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000285 SkPath::Verb verb = iter.next(pts);
286 switch (verb) {
287 case SkPath::kMove_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000288 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000289 update_degenerate_test(&degenerateData, pts[0]);
290 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000291 case SkPath::kLine_Verb: {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000292 m.mapPoints(&pts[1], 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000293 update_degenerate_test(&degenerateData, pts[1]);
joshualitt27f398f2015-02-05 14:39:01 -0800294 add_line_to_segment(pts[1], segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000295 break;
296 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000297 case SkPath::kQuad_Verb:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000298 m.mapPoints(pts, 3);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000299 update_degenerate_test(&degenerateData, pts[1]);
300 update_degenerate_test(&degenerateData, pts[2]);
joshualitt27f398f2015-02-05 14:39:01 -0800301 add_quad_segment(pts, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000302 break;
egdanielaf18a092015-01-05 10:22:28 -0800303 case SkPath::kConic_Verb: {
304 m.mapPoints(pts, 3);
305 SkScalar weight = iter.conicWeight();
306 SkAutoConicToQuads converter;
307 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5f);
308 for (int i = 0; i < converter.countQuads(); ++i) {
309 update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
310 update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
joshualitt27f398f2015-02-05 14:39:01 -0800311 add_quad_segment(quadPts + 2*i, segments);
egdanielaf18a092015-01-05 10:22:28 -0800312 }
313 break;
314 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000315 case SkPath::kCubic_Verb: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000316 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000317 update_degenerate_test(&degenerateData, pts[1]);
318 update_degenerate_test(&degenerateData, pts[2]);
319 update_degenerate_test(&degenerateData, pts[3]);
joshualitt27f398f2015-02-05 14:39:01 -0800320 add_cubic_segments(pts, dir, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000321 break;
322 };
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000323 case SkPath::kDone_Verb:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000324 if (degenerateData.isDegenerate()) {
325 return false;
326 } else {
bsalomon@google.com278dc692012-02-15 16:52:51 +0000327 compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000328 return true;
329 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000330 default:
331 break;
332 }
333 }
334}
335
336struct QuadVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000337 SkPoint fPos;
338 SkPoint fUV;
bsalomon@google.com81712882012-11-01 17:12:34 +0000339 SkScalar fD0;
340 SkScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000341};
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000342
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000343struct Draw {
344 Draw() : fVertexCnt(0), fIndexCnt(0) {}
345 int fVertexCnt;
346 int fIndexCnt;
347};
348
349typedef SkTArray<Draw, true> DrawArray;
350
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000351static void create_vertices(const SegmentArray& segments,
352 const SkPoint& fanPt,
353 DrawArray* draws,
354 QuadVertex* verts,
355 uint16_t* idxs) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000356 Draw* draw = &draws->push_back();
357 // alias just to make vert/index assignments easier to read.
358 int* v = &draw->fVertexCnt;
359 int* i = &draw->fIndexCnt;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000360
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000361 int count = segments.count();
362 for (int a = 0; a < count; ++a) {
363 const Segment& sega = segments[a];
364 int b = (a + 1) % count;
365 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000366
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000367 // Check whether adding the verts for this segment to the current draw would cause index
368 // values to overflow.
369 int vCount = 4;
370 if (Segment::kLine == segb.fType) {
371 vCount += 5;
372 } else {
373 vCount += 6;
374 }
375 if (draw->fVertexCnt + vCount > (1 << 16)) {
376 verts += *v;
377 idxs += *i;
378 draw = &draws->push_back();
379 v = &draw->fVertexCnt;
380 i = &draw->fIndexCnt;
381 }
382
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000383 // FIXME: These tris are inset in the 1 unit arc around the corner
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000384 verts[*v + 0].fPos = sega.endPt();
385 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
386 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
387 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
388 verts[*v + 0].fUV.set(0,0);
389 verts[*v + 1].fUV.set(0,-SK_Scalar1);
390 verts[*v + 2].fUV.set(0,-SK_Scalar1);
391 verts[*v + 3].fUV.set(0,-SK_Scalar1);
392 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
393 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
394 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
395 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000396
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000397 idxs[*i + 0] = *v + 0;
398 idxs[*i + 1] = *v + 2;
399 idxs[*i + 2] = *v + 1;
400 idxs[*i + 3] = *v + 0;
401 idxs[*i + 4] = *v + 3;
402 idxs[*i + 5] = *v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000403
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000404 *v += 4;
405 *i += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000406
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000407 if (Segment::kLine == segb.fType) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000408 verts[*v + 0].fPos = fanPt;
409 verts[*v + 1].fPos = sega.endPt();
410 verts[*v + 2].fPos = segb.fPts[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000411
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000412 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
413 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000414
415 // we draw the line edge as a degenerate quad (u is 0, v is the
416 // signed distance to the edge)
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000417 SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos,
418 verts[*v + 2].fPos);
419 verts[*v + 0].fUV.set(0, dist);
420 verts[*v + 1].fUV.set(0, 0);
421 verts[*v + 2].fUV.set(0, 0);
422 verts[*v + 3].fUV.set(0, -SK_Scalar1);
423 verts[*v + 4].fUV.set(0, -SK_Scalar1);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000424
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000425 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
426 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
427 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
428 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
429 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000430
cdalton35964822015-04-29 10:14:03 -0700431 idxs[*i + 0] = *v + 3;
432 idxs[*i + 1] = *v + 1;
433 idxs[*i + 2] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000434
cdalton35964822015-04-29 10:14:03 -0700435 idxs[*i + 3] = *v + 4;
436 idxs[*i + 4] = *v + 3;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000437 idxs[*i + 5] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000438
cdalton35964822015-04-29 10:14:03 -0700439 *i += 6;
440
441 // Draw the interior fan if it exists.
442 // TODO: Detect and combine colinear segments. This will ensure we catch every case
443 // with no interior, and that the resulting shared edge uses the same endpoints.
444 if (count >= 3) {
445 idxs[*i + 0] = *v + 0;
446 idxs[*i + 1] = *v + 2;
447 idxs[*i + 2] = *v + 1;
448
449 *i += 3;
450 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000451
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000452 *v += 5;
bsalomon@google.com06809612012-01-21 15:03:39 +0000453 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000454 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000455
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000456 SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000457 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000458
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000459 verts[*v + 0].fPos = fanPt;
460 verts[*v + 1].fPos = qpts[0];
461 verts[*v + 2].fPos = qpts[2];
462 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
463 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
464 verts[*v + 5].fPos = qpts[1] + midVec;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000465
bsalomon@google.com81712882012-11-01 17:12:34 +0000466 SkScalar c = segb.fNorms[0].dot(qpts[0]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000467 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
468 verts[*v + 1].fD0 = 0.f;
469 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
470 verts[*v + 3].fD0 = -SK_ScalarMax/100;
471 verts[*v + 4].fD0 = -SK_ScalarMax/100;
472 verts[*v + 5].fD0 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000473
474 c = segb.fNorms[1].dot(qpts[2]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000475 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
476 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
477 verts[*v + 2].fD1 = 0.f;
478 verts[*v + 3].fD1 = -SK_ScalarMax/100;
479 verts[*v + 4].fD1 = -SK_ScalarMax/100;
480 verts[*v + 5].fD1 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000481
bsalomon@google.com19713172012-03-15 13:51:08 +0000482 GrPathUtils::QuadUVMatrix toUV(qpts);
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000483 toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v);
bsalomon@google.com06809612012-01-21 15:03:39 +0000484
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000485 idxs[*i + 0] = *v + 3;
486 idxs[*i + 1] = *v + 1;
487 idxs[*i + 2] = *v + 2;
488 idxs[*i + 3] = *v + 4;
489 idxs[*i + 4] = *v + 3;
490 idxs[*i + 5] = *v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000491
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000492 idxs[*i + 6] = *v + 5;
493 idxs[*i + 7] = *v + 3;
494 idxs[*i + 8] = *v + 4;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000495
cdalton35964822015-04-29 10:14:03 -0700496 *i += 9;
497
498 // Draw the interior fan if it exists.
499 // TODO: Detect and combine colinear segments. This will ensure we catch every case
500 // with no interior, and that the resulting shared edge uses the same endpoints.
501 if (count >= 3) {
502 idxs[*i + 0] = *v + 0;
503 idxs[*i + 1] = *v + 2;
504 idxs[*i + 2] = *v + 1;
505
506 *i += 3;
507 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000508
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000509 *v += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000510 }
511 }
512}
513
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000514///////////////////////////////////////////////////////////////////////////////
515
516/*
517 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
518 * two components of the vertex attribute. Coverage is based on signed
519 * distance with negative being inside, positive outside. The edge is specified in
520 * window space (y-down). If either the third or fourth component of the interpolated
521 * 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 +0000522 * attempt to trim to a portion of the infinite quad.
523 * Requires shader derivative instruction support.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000524 */
525
joshualitt249af152014-09-15 11:41:13 -0700526class QuadEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000527public:
528
bungeman06ca8ec2016-06-09 08:01:03 -0700529 static sk_sp<GrGeometryProcessor> Make(GrColor color, const SkMatrix& localMatrix,
530 bool usesLocalCoords) {
531 return sk_sp<GrGeometryProcessor>(new QuadEdgeEffect(color, localMatrix, usesLocalCoords));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000532 }
533
534 virtual ~QuadEdgeEffect() {}
535
mtklein36352bf2015-03-25 18:17:31 -0700536 const char* name() const override { return "QuadEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000537
joshualitt71c92602015-01-14 08:12:47 -0800538 const Attribute* inPosition() const { return fInPosition; }
539 const Attribute* inQuadEdge() const { return fInQuadEdge; }
joshualitt88c23fc2015-05-13 14:18:07 -0700540 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700541 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -0700542 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700543 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700544
egdaniel57d3b032015-11-13 11:57:27 -0800545 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000546 public:
egdaniel57d3b032015-11-13 11:57:27 -0800547 GLSLProcessor()
joshualitt9b989322014-12-15 14:16:27 -0800548 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000549
mtklein36352bf2015-03-25 18:17:31 -0700550 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800551 const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
egdaniel4ca2e602015-11-18 08:01:26 -0800552 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800553 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800554 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -0800555
joshualittabb52a12015-01-13 15:02:10 -0800556 // emit attributes
egdaniel0eafe792015-11-20 14:01:22 -0800557 varyingHandler->emitAttributes(qe);
joshualittabb52a12015-01-13 15:02:10 -0800558
egdaniel8dcdedc2015-11-11 06:27:20 -0800559 GrGLSLVertToFrag v(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800560 varyingHandler->addVarying("QuadEdge", &v);
egdaniel4ca2e602015-11-18 08:01:26 -0800561 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.inQuadEdge()->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800562
cdalton85285412016-02-18 12:37:07 -0800563 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt9b989322014-12-15 14:16:27 -0800564 // Setup pass through color
joshualittb8c241a2015-05-19 08:23:30 -0700565 if (!qe.colorIgnored()) {
egdaniel7ea439b2015-12-03 09:20:44 -0800566 this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
567 &fColorUniform);
joshualittb8c241a2015-05-19 08:23:30 -0700568 }
joshualitt9b989322014-12-15 14:16:27 -0800569
joshualittabb52a12015-01-13 15:02:10 -0800570 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800571 this->setupPosition(vertBuilder, gpArgs, qe.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800572
573 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800574 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800575 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800576 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800577 gpArgs->fPositionVar,
578 qe.inPosition()->fName,
579 qe.localMatrix(),
bsalomona624bf32016-09-20 09:12:47 -0700580 args.fFPCoordTransformHandler);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000581
egdaniel4ca2e602015-11-18 08:01:26 -0800582 fragBuilder->codeAppendf("float edgeAlpha;");
joshualitt30ba4362014-08-21 20:18:45 -0700583
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000584 // keep the derivative instructions outside the conditional
egdaniel4ca2e602015-11-18 08:01:26 -0800585 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s.xy);", v.fsIn());
586 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s.xy);", v.fsIn());
587 fragBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000588 // today we know z and w are in device space. We could use derivatives
egdaniel4ca2e602015-11-18 08:01:26 -0800589 fragBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
590 v.fsIn());
591 fragBuilder->codeAppendf ("} else {");
592 fragBuilder->codeAppendf("vec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,"
593 " 2.0*%s.x*duvdy.x - duvdy.y);",
594 v.fsIn(), v.fsIn());
595 fragBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
596 v.fsIn());
597 fragBuilder->codeAppendf("edgeAlpha = "
598 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000599
egdaniel4ca2e602015-11-18 08:01:26 -0800600 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000601 }
602
joshualitt9b989322014-12-15 14:16:27 -0800603 static inline void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500604 const GrShaderCaps&,
joshualitt9b989322014-12-15 14:16:27 -0800605 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700606 const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
joshualittb8c241a2015-05-19 08:23:30 -0700607 uint32_t key = 0;
608 key |= qee.usesLocalCoords() && qee.localMatrix().hasPerspective() ? 0x1 : 0x0;
609 key |= qee.colorIgnored() ? 0x2 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800610 b->add32(key);
joshualitt9b989322014-12-15 14:16:27 -0800611 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000612
egdaniel018fb622015-10-28 07:26:40 -0700613 void setData(const GrGLSLProgramDataManager& pdman,
bsalomona624bf32016-09-20 09:12:47 -0700614 const GrPrimitiveProcessor& gp,
615 FPCoordTransformIter&& transformIter) override {
joshualittb8c241a2015-05-19 08:23:30 -0700616 const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
617 if (qe.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700618 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700619 GrColorToRGBAFloat(qe.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800620 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700621 fColor = qe.color();
joshualitt9b989322014-12-15 14:16:27 -0800622 }
bsalomona624bf32016-09-20 09:12:47 -0700623 this->setTransformDataHelper(qe.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700624 }
625
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000626 private:
joshualitt9b989322014-12-15 14:16:27 -0800627 GrColor fColor;
628 UniformHandle fColorUniform;
629
egdaniele659a582015-11-13 09:55:43 -0800630 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000631 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000632
Brian Salomon94efbf52016-11-29 13:43:05 -0500633 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800634 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800635 }
636
Brian Salomon94efbf52016-11-29 13:43:05 -0500637 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800638 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800639 }
640
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000641private:
joshualittb8c241a2015-05-19 08:23:30 -0700642 QuadEdgeEffect(GrColor color, const SkMatrix& localMatrix, bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700643 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700644 , fLocalMatrix(localMatrix)
645 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800646 this->initClassID<QuadEdgeEffect>();
bsalomon6cb807b2016-08-17 11:33:39 -0700647 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
648 fInQuadEdge = &this->addVertexAttrib("inQuadEdge", kVec4f_GrVertexAttribType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000649 }
650
joshualitt71c92602015-01-14 08:12:47 -0800651 const Attribute* fInPosition;
652 const Attribute* fInQuadEdge;
joshualitt88c23fc2015-05-13 14:18:07 -0700653 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700654 SkMatrix fLocalMatrix;
joshualittb8c241a2015-05-19 08:23:30 -0700655 bool fUsesLocalCoords;
joshualitt249af152014-09-15 11:41:13 -0700656
joshualittb0a8a372014-09-23 09:50:21 -0700657 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000658
joshualitt2e3b3e32014-12-09 13:31:14 -0800659 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000660};
661
joshualittb0a8a372014-09-23 09:50:21 -0700662GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000663
bungeman06ca8ec2016-06-09 08:01:03 -0700664sk_sp<GrGeometryProcessor> QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000665 // Doesn't work without derivative instructions.
joshualitt0067ff52015-07-08 14:26:19 -0700666 return d->fCaps->shaderCaps()->shaderDerivativeSupport() ?
bungeman06ca8ec2016-06-09 08:01:03 -0700667 QuadEdgeEffect::Make(GrRandomColor(d->fRandom),
668 GrTest::TestMatrix(d->fRandom),
669 d->fRandom->nextBool()) : nullptr;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000670}
671
672///////////////////////////////////////////////////////////////////////////////
673
bsalomon0aff2fa2015-07-31 06:48:27 -0700674bool GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
robertphillipse7d4b2f2015-08-13 07:57:10 -0700675 return (args.fShaderCaps->shaderDerivativeSupport() && args.fAntiAlias &&
bsalomon8acedde2016-06-24 10:42:16 -0700676 args.fShape->style().isSimpleFill() && !args.fShape->inverseFilled() &&
677 args.fShape->knownToBeConvex());
robertphillips@google.comfa662942012-05-17 12:20:22 +0000678}
679
robertphillipse16dfdb2015-05-08 04:46:51 -0700680// extract the result vertices and indices from the GrAAConvexTessellator
681static void extract_verts(const GrAAConvexTessellator& tess,
682 void* vertices,
683 size_t vertexStride,
684 GrColor color,
685 uint16_t* idxs,
686 bool tweakAlphaForCoverage) {
687 intptr_t verts = reinterpret_cast<intptr_t>(vertices);
688
689 for (int i = 0; i < tess.numPts(); ++i) {
690 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
691 }
692
693 // Make 'verts' point to the colors
694 verts += sizeof(SkPoint);
695 for (int i = 0; i < tess.numPts(); ++i) {
robertphillipse16dfdb2015-05-08 04:46:51 -0700696 if (tweakAlphaForCoverage) {
fmalitabd5d7e72015-06-26 07:18:24 -0700697 SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
698 unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
robertphillipse16dfdb2015-05-08 04:46:51 -0700699 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
700 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
701 } else {
702 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
halcanary9d524f22016-03-29 09:03:52 -0700703 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
fmalitabd5d7e72015-06-26 07:18:24 -0700704 tess.coverage(i);
robertphillipse16dfdb2015-05-08 04:46:51 -0700705 }
706 }
707
708 for (int i = 0; i < tess.numIndices(); ++i) {
709 idxs[i] = tess.index(i);
710 }
711}
712
bungeman06ca8ec2016-06-09 08:01:03 -0700713static sk_sp<GrGeometryProcessor> create_fill_gp(bool tweakAlphaForCoverage,
joshualittdf0c5572015-08-03 11:35:28 -0700714 const SkMatrix& viewMatrix,
joshualittb8c241a2015-05-19 08:23:30 -0700715 bool usesLocalCoords,
716 bool coverageIgnored) {
joshualittdf0c5572015-08-03 11:35:28 -0700717 using namespace GrDefaultGeoProcFactory;
joshualitte494a582015-08-03 09:32:36 -0700718
joshualittdf0c5572015-08-03 11:35:28 -0700719 Color color(Color::kAttribute_Type);
720 Coverage::Type coverageType;
721 // TODO remove coverage if coverage is ignored
722 /*if (coverageIgnored) {
723 coverageType = Coverage::kNone_Type;
724 } else*/ if (tweakAlphaForCoverage) {
725 coverageType = Coverage::kSolid_Type;
726 } else {
727 coverageType = Coverage::kAttribute_Type;
728 }
729 Coverage coverage(coverageType);
730 LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type :
731 LocalCoords::kUnused_Type);
bungeman06ca8ec2016-06-09 08:01:03 -0700732 return MakeForDeviceSpace(color, coverage, localCoords, viewMatrix);
robertphillipse16dfdb2015-05-08 04:46:51 -0700733}
734
Brian Salomondad29232016-12-01 16:40:24 -0500735class AAConvexPathBatch : public GrMeshDrawOp {
joshualitt27f398f2015-02-05 14:39:01 -0800736public:
Brian Salomon25a88092016-12-01 09:36:50 -0500737 DEFINE_OP_CLASS_ID
bsalomonf1703092016-06-29 18:41:53 -0700738 AAConvexPathBatch(GrColor color, const SkMatrix& viewMatrix, const SkPath& path)
739 : INHERITED(ClassID()) {
740 fGeoData.emplace_back(Geometry{color, viewMatrix, path});
bsalomon88cf17d2016-07-08 06:40:56 -0700741 this->setTransformedBounds(path.getBounds(), viewMatrix, HasAABloat::kYes,
742 IsZeroArea::kNo);
bsalomonf1703092016-06-29 18:41:53 -0700743 }
joshualitt27f398f2015-02-05 14:39:01 -0800744
mtklein36352bf2015-03-25 18:17:31 -0700745 const char* name() const override { return "AAConvexBatch"; }
joshualitt27f398f2015-02-05 14:39:01 -0800746
Brian Salomon7c3e7182016-12-01 09:35:30 -0500747 SkString dumpInfo() const override {
748 SkString string;
749 for (const auto& geo : fGeoData) {
750 string.appendf("Color: 0x%08x\n", geo.fColor);
751 }
752 string.append(DumpPipelineInfo(*this->pipeline()));
753 string.append(INHERITED::dumpInfo());
754 return string;
755 }
756
halcanary9d524f22016-03-29 09:03:52 -0700757 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800758 GrInitInvariantOutput* coverage,
759 GrBatchToXPOverrides* overrides) const override {
joshualitt27f398f2015-02-05 14:39:01 -0800760 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800761 color->setKnownFourComponents(fGeoData[0].fColor);
762 coverage->setUnknownSingleComponent();
joshualitt27f398f2015-02-05 14:39:01 -0800763 }
764
bsalomone46f9fe2015-08-18 06:05:14 -0700765private:
ethannicholasff210322015-11-24 12:10:10 -0800766 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
joshualitt27f398f2015-02-05 14:39:01 -0800767 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800768 if (!overrides.readsColor()) {
joshualitt27f398f2015-02-05 14:39:01 -0800769 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt27f398f2015-02-05 14:39:01 -0800770 }
ethannicholasff210322015-11-24 12:10:10 -0800771 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt27f398f2015-02-05 14:39:01 -0800772
773 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800774 fBatch.fColorIgnored = !overrides.readsColor();
joshualitt27f398f2015-02-05 14:39:01 -0800775 fBatch.fColor = fGeoData[0].fColor;
ethannicholasff210322015-11-24 12:10:10 -0800776 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
777 fBatch.fCoverageIgnored = !overrides.readsCoverage();
robertphillipse16dfdb2015-05-08 04:46:51 -0700778 fBatch.fLinesOnly = SkPath::kLine_SegmentMask == fGeoData[0].fPath.getSegmentMasks();
ethannicholasff210322015-11-24 12:10:10 -0800779 fBatch.fCanTweakAlphaForCoverage = overrides.canTweakAlphaForCoverage();
robertphillipse16dfdb2015-05-08 04:46:51 -0700780 }
781
joshualitt144c3c82015-11-30 12:30:13 -0800782 void prepareLinesOnlyDraws(Target* target) const {
robertphillipse16dfdb2015-05-08 04:46:51 -0700783 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
784
joshualittdf0c5572015-08-03 11:35:28 -0700785 // Setup GrGeometryProcessor
bungeman06ca8ec2016-06-09 08:01:03 -0700786 sk_sp<GrGeometryProcessor> gp(create_fill_gp(canTweakAlphaForCoverage,
787 this->viewMatrix(),
788 this->usesLocalCoords(),
789 this->coverageIgnored()));
joshualittdf0c5572015-08-03 11:35:28 -0700790 if (!gp) {
791 SkDebugf("Could not create GrGeometryProcessor\n");
robertphillipse16dfdb2015-05-08 04:46:51 -0700792 return;
793 }
794
robertphillipse16dfdb2015-05-08 04:46:51 -0700795 size_t vertexStride = gp->getVertexStride();
796
797 SkASSERT(canTweakAlphaForCoverage ?
798 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
799 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
800
801 GrAAConvexTessellator tess;
802
803 int instanceCount = fGeoData.count();
804
805 for (int i = 0; i < instanceCount; i++) {
806 tess.rewind();
807
joshualitt144c3c82015-11-30 12:30:13 -0800808 const Geometry& args = fGeoData[i];
robertphillipse16dfdb2015-05-08 04:46:51 -0700809
810 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
811 continue;
812 }
813
cdalton397536c2016-03-25 12:15:03 -0700814 const GrBuffer* vertexBuffer;
robertphillipse16dfdb2015-05-08 04:46:51 -0700815 int firstVertex;
816
bsalomon75398562015-08-17 12:55:38 -0700817 void* verts = target->makeVertexSpace(vertexStride, tess.numPts(), &vertexBuffer,
818 &firstVertex);
robertphillipse16dfdb2015-05-08 04:46:51 -0700819 if (!verts) {
820 SkDebugf("Could not allocate vertices\n");
821 return;
822 }
823
cdalton397536c2016-03-25 12:15:03 -0700824 const GrBuffer* indexBuffer;
robertphillipse16dfdb2015-05-08 04:46:51 -0700825 int firstIndex;
826
bsalomon75398562015-08-17 12:55:38 -0700827 uint16_t* idxs = target->makeIndexSpace(tess.numIndices(), &indexBuffer, &firstIndex);
robertphillipse16dfdb2015-05-08 04:46:51 -0700828 if (!idxs) {
829 SkDebugf("Could not allocate indices\n");
830 return;
831 }
832
833 extract_verts(tess, verts, vertexStride, args.fColor, idxs, canTweakAlphaForCoverage);
834
egdaniel0e1853c2016-03-17 11:35:45 -0700835 GrMesh mesh;
836 mesh.initIndexed(kTriangles_GrPrimitiveType,
robertphillipse16dfdb2015-05-08 04:46:51 -0700837 vertexBuffer, indexBuffer,
838 firstVertex, firstIndex,
839 tess.numPts(), tess.numIndices());
bungeman06ca8ec2016-06-09 08:01:03 -0700840 target->draw(gp.get(), mesh);
robertphillipse16dfdb2015-05-08 04:46:51 -0700841 }
joshualitt27f398f2015-02-05 14:39:01 -0800842 }
843
joshualitt144c3c82015-11-30 12:30:13 -0800844 void onPrepareDraws(Target* target) const override {
robertphillipse16dfdb2015-05-08 04:46:51 -0700845#ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
846 if (this->linesOnly()) {
bsalomon75398562015-08-17 12:55:38 -0700847 this->prepareLinesOnlyDraws(target);
robertphillipse16dfdb2015-05-08 04:46:51 -0700848 return;
849 }
850#endif
851
joshualitt27f398f2015-02-05 14:39:01 -0800852 int instanceCount = fGeoData.count();
853
854 SkMatrix invert;
joshualitt7abe15d2015-02-20 12:40:45 -0800855 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
joshualitt27f398f2015-02-05 14:39:01 -0800856 SkDebugf("Could not invert viewmatrix\n");
857 return;
858 }
859
860 // Setup GrGeometryProcessor
bungeman06ca8ec2016-06-09 08:01:03 -0700861 sk_sp<GrGeometryProcessor> quadProcessor(
862 QuadEdgeEffect::Make(this->color(), invert, this->usesLocalCoords()));
joshualitt27f398f2015-02-05 14:39:01 -0800863
joshualitt27f398f2015-02-05 14:39:01 -0800864 // TODO generate all segments for all paths and use one vertex buffer
865 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800866 const Geometry& args = fGeoData[i];
joshualitt27f398f2015-02-05 14:39:01 -0800867
868 // We use the fact that SkPath::transform path does subdivision based on
869 // perspective. Otherwise, we apply the view matrix when copying to the
870 // segment representation.
871 const SkMatrix* viewMatrix = &args.fViewMatrix;
joshualitt144c3c82015-11-30 12:30:13 -0800872
873 // We avoid initializing the path unless we have to
874 const SkPath* pathPtr = &args.fPath;
875 SkTLazy<SkPath> tmpPath;
joshualitt27f398f2015-02-05 14:39:01 -0800876 if (viewMatrix->hasPerspective()) {
joshualitt144c3c82015-11-30 12:30:13 -0800877 SkPath* tmpPathPtr = tmpPath.init(*pathPtr);
878 tmpPathPtr->setIsVolatile(true);
879 tmpPathPtr->transform(*viewMatrix);
joshualitt27f398f2015-02-05 14:39:01 -0800880 viewMatrix = &SkMatrix::I();
joshualitt144c3c82015-11-30 12:30:13 -0800881 pathPtr = tmpPathPtr;
joshualitt27f398f2015-02-05 14:39:01 -0800882 }
883
884 int vertexCount;
885 int indexCount;
886 enum {
887 kPreallocSegmentCnt = 512 / sizeof(Segment),
888 kPreallocDrawCnt = 4,
889 };
890 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
891 SkPoint fanPt;
892
joshualitt144c3c82015-11-30 12:30:13 -0800893 if (!get_segments(*pathPtr, *viewMatrix, &segments, &fanPt, &vertexCount,
joshualitt27f398f2015-02-05 14:39:01 -0800894 &indexCount)) {
895 continue;
896 }
897
cdalton397536c2016-03-25 12:15:03 -0700898 const GrBuffer* vertexBuffer;
joshualitt27f398f2015-02-05 14:39:01 -0800899 int firstVertex;
900
901 size_t vertexStride = quadProcessor->getVertexStride();
bsalomon75398562015-08-17 12:55:38 -0700902 QuadVertex* verts = reinterpret_cast<QuadVertex*>(target->makeVertexSpace(
bsalomone64eb572015-05-07 11:35:55 -0700903 vertexStride, vertexCount, &vertexBuffer, &firstVertex));
904
905 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800906 SkDebugf("Could not allocate vertices\n");
907 return;
908 }
909
cdalton397536c2016-03-25 12:15:03 -0700910 const GrBuffer* indexBuffer;
joshualitt27f398f2015-02-05 14:39:01 -0800911 int firstIndex;
912
bsalomon75398562015-08-17 12:55:38 -0700913 uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
robertphillipse40d3972015-05-07 09:51:43 -0700914 if (!idxs) {
joshualitt4b31de82015-03-05 14:33:41 -0800915 SkDebugf("Could not allocate indices\n");
916 return;
917 }
918
joshualitt27f398f2015-02-05 14:39:01 -0800919 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
920 create_vertices(segments, fanPt, &draws, verts, idxs);
921
egdaniel0e1853c2016-03-17 11:35:45 -0700922 GrMesh mesh;
joshualitt27f398f2015-02-05 14:39:01 -0800923
robertphillips44c31282015-09-03 12:58:48 -0700924 for (int j = 0; j < draws.count(); ++j) {
925 const Draw& draw = draws[j];
egdaniel0e1853c2016-03-17 11:35:45 -0700926 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer,
927 firstVertex, firstIndex, draw.fVertexCnt, draw.fIndexCnt);
bungeman06ca8ec2016-06-09 08:01:03 -0700928 target->draw(quadProcessor.get(), mesh);
bsalomonb5238a72015-05-05 07:49:49 -0700929 firstVertex += draw.fVertexCnt;
930 firstIndex += draw.fIndexCnt;
joshualitt27f398f2015-02-05 14:39:01 -0800931 }
932 }
933 }
934
Brian Salomon25a88092016-12-01 09:36:50 -0500935 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700936 AAConvexPathBatch* that = t->cast<AAConvexPathBatch>();
937 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
938 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700939 return false;
940 }
941
joshualitt27f398f2015-02-05 14:39:01 -0800942 if (this->color() != that->color()) {
943 return false;
944 }
945
946 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
947 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
948 return false;
949 }
950
robertphillipse16dfdb2015-05-08 04:46:51 -0700951 if (this->linesOnly() != that->linesOnly()) {
952 return false;
953 }
954
955 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
956 // not tweaking
957 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
958 fBatch.fCanTweakAlphaForCoverage = false;
959 }
960
bsalomonf1703092016-06-29 18:41:53 -0700961 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700962 this->joinBounds(*that);
joshualitt27f398f2015-02-05 14:39:01 -0800963 return true;
964 }
965
966 GrColor color() const { return fBatch.fColor; }
robertphillipse16dfdb2015-05-08 04:46:51 -0700967 bool linesOnly() const { return fBatch.fLinesOnly; }
joshualitt27f398f2015-02-05 14:39:01 -0800968 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
robertphillipse16dfdb2015-05-08 04:46:51 -0700969 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
joshualitt27f398f2015-02-05 14:39:01 -0800970 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700971 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
joshualitt27f398f2015-02-05 14:39:01 -0800972
973 struct BatchTracker {
974 GrColor fColor;
975 bool fUsesLocalCoords;
976 bool fColorIgnored;
977 bool fCoverageIgnored;
robertphillipse16dfdb2015-05-08 04:46:51 -0700978 bool fLinesOnly;
979 bool fCanTweakAlphaForCoverage;
joshualitt27f398f2015-02-05 14:39:01 -0800980 };
981
bsalomonf1703092016-06-29 18:41:53 -0700982 struct Geometry {
983 GrColor fColor;
984 SkMatrix fViewMatrix;
985 SkPath fPath;
986 };
987
joshualitt27f398f2015-02-05 14:39:01 -0800988 BatchTracker fBatch;
989 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700990
Brian Salomondad29232016-12-01 16:40:24 -0500991 typedef GrMeshDrawOp INHERITED;
joshualitt27f398f2015-02-05 14:39:01 -0800992};
993
bsalomon0aff2fa2015-07-31 06:48:27 -0700994bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400995 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700996 "GrAAConvexPathRenderer::onDrawPath");
Brian Osman11052242016-10-27 14:47:55 -0400997 SkASSERT(!args.fRenderTargetContext->isUnifiedMultisampled());
bsalomon8acedde2016-06-24 10:42:16 -0700998 SkASSERT(!args.fShape->isEmpty());
bsalomon@google.com4647f902013-03-26 14:45:27 +0000999
bsalomonf1703092016-06-29 18:41:53 -07001000 SkPath path;
1001 args.fShape->asPath(&path);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +00001002
Brian Salomon9afd3712016-12-01 10:59:09 -05001003 sk_sp<GrDrawOp> batch(new AAConvexPathBatch(args.fPaint->getColor(), *args.fViewMatrix, path));
robertphillips976f5f02016-06-03 10:59:20 -07001004
bsalomonbb243832016-07-22 07:10:19 -07001005 GrPipelineBuilder pipelineBuilder(*args.fPaint);
1006 pipelineBuilder.setUserStencil(args.fUserStencilSettings);
1007
Brian Salomon42521e82016-12-07 16:44:58 -05001008 args.fRenderTargetContext->addDrawOp(pipelineBuilder, *args.fClip, batch.get());
bsalomon@google.coma8347462012-10-08 18:59:39 +00001009
bsalomon@google.comc2099d22012-03-02 21:26:50 +00001010 return true;
joshualitt27f398f2015-02-05 14:39:01 -08001011
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +00001012}
joshualitt8e5c1772015-05-11 08:58:52 -07001013
1014///////////////////////////////////////////////////////////////////////////////////////////////////
1015
1016#ifdef GR_TEST_UTILS
1017
bsalomonabd30f52015-08-13 13:34:48 -07001018DRAW_BATCH_TEST_DEFINE(AAConvexPathBatch) {
bsalomonf1703092016-06-29 18:41:53 -07001019 GrColor color = GrRandomColor(random);
1020 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
1021 SkPath path = GrTest::TestPathConvex(random);
joshualitt8e5c1772015-05-11 08:58:52 -07001022
bsalomonf1703092016-06-29 18:41:53 -07001023 return new AAConvexPathBatch(color, viewMatrix, path);
joshualitt8e5c1772015-05-11 08:58:52 -07001024}
1025
1026#endif