blob: a06de2789adc02ebaa6eef96e994049720f517f5 [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"
bsalomoneb1cb5c2015-05-22 08:01:09 -070011#include "GrCaps.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000012#include "GrContext.h"
robertphillipse16dfdb2015-05-08 04:46:51 -070013#include "GrDefaultGeoProcFactory.h"
Brian Salomon5ec9def2016-12-20 15:34:05 -050014#include "GrDrawOpTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080015#include "GrGeometryProcessor.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050016#include "GrOpFlushState.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000017#include "GrPathUtils.h"
bsalomonbb243832016-07-22 07:10:19 -070018#include "GrPipelineBuilder.h"
Brian Salomondad29232016-12-01 16:40:24 -050019#include "GrProcessor.h"
egdanielaf18a092015-01-05 10:22:28 -080020#include "SkGeometry.h"
reed026beb52015-06-10 14:23:15 -070021#include "SkPathPriv.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000022#include "SkString.h"
commit-bot@chromium.org933e65d2014-03-20 20:00:24 +000023#include "SkTraceEvent.h"
egdaniel7ea439b2015-12-03 09:20:44 -080024#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080025#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070026#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080027#include "glsl/GrGLSLUniformHandler.h"
egdaniel0eafe792015-11-20 14:01:22 -080028#include "glsl/GrGLSLVarying.h"
egdaniel7ea439b2015-12-03 09:20:44 -080029#include "glsl/GrGLSLVertexShaderBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050030#include "ops/GrMeshDrawOp.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000031
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000032GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
33}
34
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000035struct Segment {
36 enum {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000037 // These enum values are assumed in member functions below.
38 kLine = 0,
39 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000040 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000041
bsalomon@google.com9aed1142012-01-30 14:28:39 +000042 // line uses one pt, quad uses 2 pts
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000043 SkPoint fPts[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000044 // normal to edge ending at each pt
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000045 SkVector fNorms[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000046 // is the corner where the previous segment meets this segment
47 // sharp. If so, fMid is a normalized bisector facing outward.
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000048 SkVector fMid;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000049
50 int countPoints() {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000051 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
52 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000053 }
54 const SkPoint& endPt() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000055 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
56 return fPts[fType];
Mike Kleinfc6c37b2016-09-27 09:34:10 -040057 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000058 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000059 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
60 return fNorms[fType];
Mike Kleinfc6c37b2016-09-27 09:34:10 -040061 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000062};
63
64typedef SkTArray<Segment, true> SegmentArray;
65
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +000066static void center_of_mass(const SegmentArray& segments, SkPoint* c) {
bsalomon@google.com81712882012-11-01 17:12:34 +000067 SkScalar area = 0;
vandebo@chromium.org6390c722012-03-28 21:03:22 +000068 SkPoint center = {0, 0};
bsalomon@google.com9aed1142012-01-30 14:28:39 +000069 int count = segments.count();
vandebo@chromium.org6390c722012-03-28 21:03:22 +000070 SkPoint p0 = {0, 0};
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000071 if (count > 2) {
72 // We translate the polygon so that the first point is at the origin.
73 // This avoids some precision issues with small area polygons far away
74 // from the origin.
75 p0 = segments[0].endPt();
76 SkPoint pi;
77 SkPoint pj;
bsalomon@google.coma51ab842012-07-10 19:53:34 +000078 // the first and last iteration of the below loop would compute
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000079 // zeros since the starting / ending point is (0,0). So instead we start
80 // at i=1 and make the last iteration i=count-2.
81 pj = segments[1].endPt() - p0;
82 for (int i = 1; i < count - 1; ++i) {
83 pi = pj;
robertphillips44c31282015-09-03 12:58:48 -070084 pj = segments[i + 1].endPt() - p0;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000085
robertphillips44c31282015-09-03 12:58:48 -070086 SkScalar t = SkPoint::CrossProduct(pi, pj);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000087 area += t;
88 center.fX += (pi.fX + pj.fX) * t;
89 center.fY += (pi.fY + pj.fY) * t;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000090 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000091 }
robertphillips44c31282015-09-03 12:58:48 -070092
bsalomon@google.com278dc692012-02-15 16:52:51 +000093 // If the poly has no area then we instead return the average of
94 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000095 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +000096 SkPoint avg;
97 avg.set(0, 0);
98 for (int i = 0; i < count; ++i) {
99 const SkPoint& pt = segments[i].endPt();
100 avg.fX += pt.fX;
101 avg.fY += pt.fY;
102 }
103 SkScalar denom = SK_Scalar1 / count;
104 avg.scale(denom);
105 *c = avg;
106 } else {
107 area *= 3;
reed80ea19c2015-05-12 10:37:34 -0700108 area = SkScalarInvert(area);
robertphillips44c31282015-09-03 12:58:48 -0700109 center.scale(area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +0000110 // undo the translate of p0 to the origin.
111 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000112 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000113 SkASSERT(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000114}
115
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000116static void compute_vectors(SegmentArray* segments,
117 SkPoint* fanPt,
reed026beb52015-06-10 14:23:15 -0700118 SkPathPriv::FirstDirection dir,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000119 int* vCount,
120 int* iCount) {
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000121 center_of_mass(*segments, fanPt);
122 int count = segments->count();
123
bsalomon@google.com278dc692012-02-15 16:52:51 +0000124 // Make the normals point towards the outside
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000125 SkPoint::Side normSide;
reed026beb52015-06-10 14:23:15 -0700126 if (dir == SkPathPriv::kCCW_FirstDirection) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000127 normSide = SkPoint::kRight_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000128 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000129 normSide = SkPoint::kLeft_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000130 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000131
132 *vCount = 0;
133 *iCount = 0;
134 // compute normals at all points
135 for (int a = 0; a < count; ++a) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000136 Segment& sega = (*segments)[a];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000137 int b = (a + 1) % count;
138 Segment& segb = (*segments)[b];
139
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000140 const SkPoint* prevPt = &sega.endPt();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000141 int n = segb.countPoints();
142 for (int p = 0; p < n; ++p) {
143 segb.fNorms[p] = segb.fPts[p] - *prevPt;
144 segb.fNorms[p].normalize();
145 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
146 prevPt = &segb.fPts[p];
147 }
148 if (Segment::kLine == segb.fType) {
149 *vCount += 5;
150 *iCount += 9;
151 } else {
152 *vCount += 6;
153 *iCount += 12;
154 }
155 }
156
157 // compute mid-vectors where segments meet. TODO: Detect shallow corners
158 // and leave out the wedges and close gaps by stitching segments together.
159 for (int a = 0; a < count; ++a) {
160 const Segment& sega = (*segments)[a];
161 int b = (a + 1) % count;
162 Segment& segb = (*segments)[b];
163 segb.fMid = segb.fNorms[0] + sega.endNorm();
164 segb.fMid.normalize();
165 // corner wedges
166 *vCount += 4;
167 *iCount += 6;
168 }
169}
170
bsalomon@google.com9732f622012-01-31 15:19:21 +0000171struct DegenerateTestData {
172 DegenerateTestData() { fStage = kInitial; }
173 bool isDegenerate() const { return kNonDegenerate != fStage; }
174 enum {
175 kInitial,
176 kPoint,
177 kLine,
178 kNonDegenerate
179 } fStage;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000180 SkPoint fFirstPoint;
181 SkVector fLineNormal;
bsalomon@google.com81712882012-11-01 17:12:34 +0000182 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000183};
184
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000185static const SkScalar kClose = (SK_Scalar1 / 16);
Mike Reed8be952a2017-02-13 20:44:33 -0500186static const SkScalar kCloseSqd = kClose * kClose;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000187
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000188static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000189 switch (data->fStage) {
190 case DegenerateTestData::kInitial:
191 data->fFirstPoint = pt;
192 data->fStage = DegenerateTestData::kPoint;
193 break;
194 case DegenerateTestData::kPoint:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000195 if (pt.distanceToSqd(data->fFirstPoint) > kCloseSqd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000196 data->fLineNormal = pt - data->fFirstPoint;
197 data->fLineNormal.normalize();
198 data->fLineNormal.setOrthog(data->fLineNormal);
199 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
200 data->fStage = DegenerateTestData::kLine;
201 }
202 break;
203 case DegenerateTestData::kLine:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000204 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000205 data->fStage = DegenerateTestData::kNonDegenerate;
206 }
207 case DegenerateTestData::kNonDegenerate:
208 break;
209 default:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000210 SkFAIL("Unexpected degenerate test stage.");
bsalomon@google.com9732f622012-01-31 15:19:21 +0000211 }
212}
213
reed026beb52015-06-10 14:23:15 -0700214static inline bool get_direction(const SkPath& path, const SkMatrix& m,
215 SkPathPriv::FirstDirection* dir) {
216 if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000217 return false;
218 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000219 // check whether m reverses the orientation
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000220 SkASSERT(!m.hasPerspective());
Mike Reed8be952a2017-02-13 20:44:33 -0500221 SkScalar det2x2 = m.get(SkMatrix::kMScaleX) * m.get(SkMatrix::kMScaleY) -
222 m.get(SkMatrix::kMSkewX) * m.get(SkMatrix::kMSkewY);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000223 if (det2x2 < 0) {
reed026beb52015-06-10 14:23:15 -0700224 *dir = SkPathPriv::OppositeFirstDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000225 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000226 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000227}
228
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000229static inline void add_line_to_segment(const SkPoint& pt,
joshualitt27f398f2015-02-05 14:39:01 -0800230 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000231 segments->push_back();
232 segments->back().fType = Segment::kLine;
233 segments->back().fPts[0] = pt;
234}
235
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000236static inline void add_quad_segment(const SkPoint pts[3],
joshualitt27f398f2015-02-05 14:39:01 -0800237 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000238 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
239 if (pts[0] != pts[2]) {
joshualitt27f398f2015-02-05 14:39:01 -0800240 add_line_to_segment(pts[2], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000241 }
242 } else {
243 segments->push_back();
244 segments->back().fType = Segment::kQuad;
245 segments->back().fPts[0] = pts[1];
246 segments->back().fPts[1] = pts[2];
247 }
248}
249
250static inline void add_cubic_segments(const SkPoint pts[4],
reed026beb52015-06-10 14:23:15 -0700251 SkPathPriv::FirstDirection dir,
joshualitt27f398f2015-02-05 14:39:01 -0800252 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000253 SkSTArray<15, SkPoint, true> quads;
bsalomon18fab302016-02-16 08:00:05 -0800254 GrPathUtils::convertCubicToQuadsConstrainToTangents(pts, SK_Scalar1, dir, &quads);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000255 int count = quads.count();
256 for (int q = 0; q < count; q += 3) {
joshualitt27f398f2015-02-05 14:39:01 -0800257 add_quad_segment(&quads[q], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000258 }
259}
260
261static bool get_segments(const SkPath& path,
262 const SkMatrix& m,
263 SegmentArray* segments,
264 SkPoint* fanPt,
265 int* vCount,
joshualitt27f398f2015-02-05 14:39:01 -0800266 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000267 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000268 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000269 // to the path from the sample to compute coverage. Every pixel intersected
270 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000271 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000272 // thus should be very light. This is particularly egregious for degenerate
273 // line paths. We detect paths that are very close to a line (zero area) and
274 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000275 DegenerateTestData degenerateData;
reed026beb52015-06-10 14:23:15 -0700276 SkPathPriv::FirstDirection dir;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000277 // get_direction can fail for some degenerate paths.
278 if (!get_direction(path, m, &dir)) {
279 return false;
280 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000281
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000282 for (;;) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000283 SkPoint pts[4];
Brian Salomon97042bf2017-02-28 11:21:28 -0500284 SkPath::Verb verb = iter.next(pts, true, true);
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000285 switch (verb) {
286 case SkPath::kMove_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000287 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000288 update_degenerate_test(&degenerateData, pts[0]);
289 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000290 case SkPath::kLine_Verb: {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000291 m.mapPoints(&pts[1], 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000292 update_degenerate_test(&degenerateData, pts[1]);
joshualitt27f398f2015-02-05 14:39:01 -0800293 add_line_to_segment(pts[1], segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000294 break;
295 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000296 case SkPath::kQuad_Verb:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000297 m.mapPoints(pts, 3);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000298 update_degenerate_test(&degenerateData, pts[1]);
299 update_degenerate_test(&degenerateData, pts[2]);
joshualitt27f398f2015-02-05 14:39:01 -0800300 add_quad_segment(pts, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000301 break;
egdanielaf18a092015-01-05 10:22:28 -0800302 case SkPath::kConic_Verb: {
303 m.mapPoints(pts, 3);
304 SkScalar weight = iter.conicWeight();
305 SkAutoConicToQuads converter;
306 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5f);
307 for (int i = 0; i < converter.countQuads(); ++i) {
308 update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
309 update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
joshualitt27f398f2015-02-05 14:39:01 -0800310 add_quad_segment(quadPts + 2*i, segments);
egdanielaf18a092015-01-05 10:22:28 -0800311 }
312 break;
313 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000314 case SkPath::kCubic_Verb: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000315 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000316 update_degenerate_test(&degenerateData, pts[1]);
317 update_degenerate_test(&degenerateData, pts[2]);
318 update_degenerate_test(&degenerateData, pts[3]);
joshualitt27f398f2015-02-05 14:39:01 -0800319 add_cubic_segments(pts, dir, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000320 break;
321 };
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000322 case SkPath::kDone_Verb:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000323 if (degenerateData.isDegenerate()) {
324 return false;
325 } else {
bsalomon@google.com278dc692012-02-15 16:52:51 +0000326 compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000327 return true;
328 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000329 default:
330 break;
331 }
332 }
333}
334
335struct QuadVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000336 SkPoint fPos;
337 SkPoint fUV;
bsalomon@google.com81712882012-11-01 17:12:34 +0000338 SkScalar fD0;
339 SkScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000340};
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000341
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000342struct Draw {
343 Draw() : fVertexCnt(0), fIndexCnt(0) {}
344 int fVertexCnt;
345 int fIndexCnt;
346};
347
348typedef SkTArray<Draw, true> DrawArray;
349
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000350static void create_vertices(const SegmentArray& segments,
351 const SkPoint& fanPt,
352 DrawArray* draws,
353 QuadVertex* verts,
354 uint16_t* idxs) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000355 Draw* draw = &draws->push_back();
356 // alias just to make vert/index assignments easier to read.
357 int* v = &draw->fVertexCnt;
358 int* i = &draw->fIndexCnt;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000359
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000360 int count = segments.count();
361 for (int a = 0; a < count; ++a) {
362 const Segment& sega = segments[a];
363 int b = (a + 1) % count;
364 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000365
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000366 // Check whether adding the verts for this segment to the current draw would cause index
367 // values to overflow.
368 int vCount = 4;
369 if (Segment::kLine == segb.fType) {
370 vCount += 5;
371 } else {
372 vCount += 6;
373 }
374 if (draw->fVertexCnt + vCount > (1 << 16)) {
375 verts += *v;
376 idxs += *i;
377 draw = &draws->push_back();
378 v = &draw->fVertexCnt;
379 i = &draw->fIndexCnt;
380 }
381
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000382 // FIXME: These tris are inset in the 1 unit arc around the corner
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000383 verts[*v + 0].fPos = sega.endPt();
384 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
385 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
386 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
387 verts[*v + 0].fUV.set(0,0);
388 verts[*v + 1].fUV.set(0,-SK_Scalar1);
389 verts[*v + 2].fUV.set(0,-SK_Scalar1);
390 verts[*v + 3].fUV.set(0,-SK_Scalar1);
391 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
392 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
393 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
394 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000395
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000396 idxs[*i + 0] = *v + 0;
397 idxs[*i + 1] = *v + 2;
398 idxs[*i + 2] = *v + 1;
399 idxs[*i + 3] = *v + 0;
400 idxs[*i + 4] = *v + 3;
401 idxs[*i + 5] = *v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000402
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000403 *v += 4;
404 *i += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000405
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000406 if (Segment::kLine == segb.fType) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000407 verts[*v + 0].fPos = fanPt;
408 verts[*v + 1].fPos = sega.endPt();
409 verts[*v + 2].fPos = segb.fPts[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000410
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000411 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
412 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000413
414 // we draw the line edge as a degenerate quad (u is 0, v is the
415 // signed distance to the edge)
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000416 SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos,
417 verts[*v + 2].fPos);
418 verts[*v + 0].fUV.set(0, dist);
419 verts[*v + 1].fUV.set(0, 0);
420 verts[*v + 2].fUV.set(0, 0);
421 verts[*v + 3].fUV.set(0, -SK_Scalar1);
422 verts[*v + 4].fUV.set(0, -SK_Scalar1);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000423
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000424 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
425 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
426 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
427 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
428 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000429
cdalton35964822015-04-29 10:14:03 -0700430 idxs[*i + 0] = *v + 3;
431 idxs[*i + 1] = *v + 1;
432 idxs[*i + 2] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000433
cdalton35964822015-04-29 10:14:03 -0700434 idxs[*i + 3] = *v + 4;
435 idxs[*i + 4] = *v + 3;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000436 idxs[*i + 5] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000437
cdalton35964822015-04-29 10:14:03 -0700438 *i += 6;
439
440 // Draw the interior fan if it exists.
441 // TODO: Detect and combine colinear segments. This will ensure we catch every case
442 // with no interior, and that the resulting shared edge uses the same endpoints.
443 if (count >= 3) {
444 idxs[*i + 0] = *v + 0;
445 idxs[*i + 1] = *v + 2;
446 idxs[*i + 2] = *v + 1;
447
448 *i += 3;
449 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000450
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000451 *v += 5;
bsalomon@google.com06809612012-01-21 15:03:39 +0000452 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000453 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000454
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000455 SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000456 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000457
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000458 verts[*v + 0].fPos = fanPt;
459 verts[*v + 1].fPos = qpts[0];
460 verts[*v + 2].fPos = qpts[2];
461 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
462 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
463 verts[*v + 5].fPos = qpts[1] + midVec;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000464
bsalomon@google.com81712882012-11-01 17:12:34 +0000465 SkScalar c = segb.fNorms[0].dot(qpts[0]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000466 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
467 verts[*v + 1].fD0 = 0.f;
468 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
469 verts[*v + 3].fD0 = -SK_ScalarMax/100;
470 verts[*v + 4].fD0 = -SK_ScalarMax/100;
471 verts[*v + 5].fD0 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000472
473 c = segb.fNorms[1].dot(qpts[2]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000474 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
475 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
476 verts[*v + 2].fD1 = 0.f;
477 verts[*v + 3].fD1 = -SK_ScalarMax/100;
478 verts[*v + 4].fD1 = -SK_ScalarMax/100;
479 verts[*v + 5].fD1 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000480
bsalomon@google.com19713172012-03-15 13:51:08 +0000481 GrPathUtils::QuadUVMatrix toUV(qpts);
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000482 toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v);
bsalomon@google.com06809612012-01-21 15:03:39 +0000483
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000484 idxs[*i + 0] = *v + 3;
485 idxs[*i + 1] = *v + 1;
486 idxs[*i + 2] = *v + 2;
487 idxs[*i + 3] = *v + 4;
488 idxs[*i + 4] = *v + 3;
489 idxs[*i + 5] = *v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000490
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000491 idxs[*i + 6] = *v + 5;
492 idxs[*i + 7] = *v + 3;
493 idxs[*i + 8] = *v + 4;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000494
cdalton35964822015-04-29 10:14:03 -0700495 *i += 9;
496
497 // Draw the interior fan if it exists.
498 // TODO: Detect and combine colinear segments. This will ensure we catch every case
499 // with no interior, and that the resulting shared edge uses the same endpoints.
500 if (count >= 3) {
501 idxs[*i + 0] = *v + 0;
502 idxs[*i + 1] = *v + 2;
503 idxs[*i + 2] = *v + 1;
504
505 *i += 3;
506 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000507
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000508 *v += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000509 }
510 }
511}
512
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000513///////////////////////////////////////////////////////////////////////////////
514
515/*
516 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
517 * two components of the vertex attribute. Coverage is based on signed
518 * distance with negative being inside, positive outside. The edge is specified in
519 * window space (y-down). If either the third or fourth component of the interpolated
520 * vertex coord is > 0 then the pixel is considered outside the edge. This is used to
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000521 * attempt to trim to a portion of the infinite quad.
522 * Requires shader derivative instruction support.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000523 */
524
joshualitt249af152014-09-15 11:41:13 -0700525class QuadEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000526public:
527
bungeman06ca8ec2016-06-09 08:01:03 -0700528 static sk_sp<GrGeometryProcessor> Make(GrColor color, const SkMatrix& localMatrix,
529 bool usesLocalCoords) {
530 return sk_sp<GrGeometryProcessor>(new QuadEdgeEffect(color, localMatrix, usesLocalCoords));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000531 }
532
Brian Salomond3b65972017-03-22 12:05:03 -0400533 ~QuadEdgeEffect() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000534
mtklein36352bf2015-03-25 18:17:31 -0700535 const char* name() const override { return "QuadEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000536
joshualitt71c92602015-01-14 08:12:47 -0800537 const Attribute* inPosition() const { return fInPosition; }
538 const Attribute* inQuadEdge() const { return fInQuadEdge; }
joshualitt88c23fc2015-05-13 14:18:07 -0700539 GrColor color() const { return fColor; }
joshualitte3ababe2015-05-15 07:56:07 -0700540 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700541 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700542
egdaniel57d3b032015-11-13 11:57:27 -0800543 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000544 public:
Brian Salomonbfd51832017-01-04 13:22:08 -0500545 GLSLProcessor() : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000546
mtklein36352bf2015-03-25 18:17:31 -0700547 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800548 const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
egdaniel4ca2e602015-11-18 08:01:26 -0800549 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800550 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800551 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -0800552
joshualittabb52a12015-01-13 15:02:10 -0800553 // emit attributes
egdaniel0eafe792015-11-20 14:01:22 -0800554 varyingHandler->emitAttributes(qe);
joshualittabb52a12015-01-13 15:02:10 -0800555
egdaniel8dcdedc2015-11-11 06:27:20 -0800556 GrGLSLVertToFrag v(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800557 varyingHandler->addVarying("QuadEdge", &v);
egdaniel4ca2e602015-11-18 08:01:26 -0800558 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.inQuadEdge()->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800559
cdalton85285412016-02-18 12:37:07 -0800560 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt9b989322014-12-15 14:16:27 -0800561 // Setup pass through color
Brian Salomonbfd51832017-01-04 13:22:08 -0500562 this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
563 &fColorUniform);
joshualitt9b989322014-12-15 14:16:27 -0800564
joshualittabb52a12015-01-13 15:02:10 -0800565 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800566 this->setupPosition(vertBuilder, gpArgs, qe.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800567
568 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800569 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800570 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800571 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800572 gpArgs->fPositionVar,
573 qe.inPosition()->fName,
574 qe.localMatrix(),
bsalomona624bf32016-09-20 09:12:47 -0700575 args.fFPCoordTransformHandler);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000576
egdaniel4ca2e602015-11-18 08:01:26 -0800577 fragBuilder->codeAppendf("float edgeAlpha;");
joshualitt30ba4362014-08-21 20:18:45 -0700578
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000579 // keep the derivative instructions outside the conditional
egdaniel4ca2e602015-11-18 08:01:26 -0800580 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s.xy);", v.fsIn());
581 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s.xy);", v.fsIn());
582 fragBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000583 // today we know z and w are in device space. We could use derivatives
egdaniel4ca2e602015-11-18 08:01:26 -0800584 fragBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
585 v.fsIn());
586 fragBuilder->codeAppendf ("} else {");
587 fragBuilder->codeAppendf("vec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,"
588 " 2.0*%s.x*duvdy.x - duvdy.y);",
589 v.fsIn(), v.fsIn());
590 fragBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
591 v.fsIn());
592 fragBuilder->codeAppendf("edgeAlpha = "
593 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000594
egdaniel4ca2e602015-11-18 08:01:26 -0800595 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000596 }
597
joshualitt9b989322014-12-15 14:16:27 -0800598 static inline void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500599 const GrShaderCaps&,
joshualitt9b989322014-12-15 14:16:27 -0800600 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700601 const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
Brian Salomonbfd51832017-01-04 13:22:08 -0500602 b->add32(SkToBool(qee.usesLocalCoords() && qee.localMatrix().hasPerspective()));
joshualitt9b989322014-12-15 14:16:27 -0800603 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000604
egdaniel018fb622015-10-28 07:26:40 -0700605 void setData(const GrGLSLProgramDataManager& pdman,
bsalomona624bf32016-09-20 09:12:47 -0700606 const GrPrimitiveProcessor& gp,
607 FPCoordTransformIter&& transformIter) override {
joshualittb8c241a2015-05-19 08:23:30 -0700608 const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
609 if (qe.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700610 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700611 GrColorToRGBAFloat(qe.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800612 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700613 fColor = qe.color();
joshualitt9b989322014-12-15 14:16:27 -0800614 }
bsalomona624bf32016-09-20 09:12:47 -0700615 this->setTransformDataHelper(qe.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700616 }
617
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000618 private:
joshualitt9b989322014-12-15 14:16:27 -0800619 GrColor fColor;
620 UniformHandle fColorUniform;
621
egdaniele659a582015-11-13 09:55:43 -0800622 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000623 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000624
Brian Salomon94efbf52016-11-29 13:43:05 -0500625 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800626 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800627 }
628
Brian Salomon94efbf52016-11-29 13:43:05 -0500629 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800630 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800631 }
632
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000633private:
joshualittb8c241a2015-05-19 08:23:30 -0700634 QuadEdgeEffect(GrColor color, const SkMatrix& localMatrix, bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700635 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700636 , fLocalMatrix(localMatrix)
637 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800638 this->initClassID<QuadEdgeEffect>();
bsalomon6cb807b2016-08-17 11:33:39 -0700639 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
640 fInQuadEdge = &this->addVertexAttrib("inQuadEdge", kVec4f_GrVertexAttribType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000641 }
642
joshualitt71c92602015-01-14 08:12:47 -0800643 const Attribute* fInPosition;
644 const Attribute* fInQuadEdge;
joshualitt88c23fc2015-05-13 14:18:07 -0700645 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700646 SkMatrix fLocalMatrix;
joshualittb8c241a2015-05-19 08:23:30 -0700647 bool fUsesLocalCoords;
joshualitt249af152014-09-15 11:41:13 -0700648
joshualittb0a8a372014-09-23 09:50:21 -0700649 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000650
joshualitt2e3b3e32014-12-09 13:31:14 -0800651 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000652};
653
joshualittb0a8a372014-09-23 09:50:21 -0700654GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000655
Hal Canary6f6961e2017-01-31 13:50:44 -0500656#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700657sk_sp<GrGeometryProcessor> QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000658 // Doesn't work without derivative instructions.
Robert Phillips296b1cc2017-03-15 10:42:12 -0400659 return d->caps()->shaderCaps()->shaderDerivativeSupport()
Brian Salomon9ae32a22017-01-25 14:58:24 -0500660 ? QuadEdgeEffect::Make(GrRandomColor(d->fRandom),
661 GrTest::TestMatrix(d->fRandom),
662 d->fRandom->nextBool())
663 : nullptr;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000664}
Hal Canary6f6961e2017-01-31 13:50:44 -0500665#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000666
667///////////////////////////////////////////////////////////////////////////////
668
bsalomon0aff2fa2015-07-31 06:48:27 -0700669bool GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
Eric Karl5c779752017-05-08 12:02:07 -0700670 return (args.fCaps->shaderCaps()->shaderDerivativeSupport() &&
671 (GrAAType::kCoverage == args.fAAType) && args.fShape->style().isSimpleFill() &&
672 !args.fShape->inverseFilled() && args.fShape->knownToBeConvex());
robertphillips@google.comfa662942012-05-17 12:20:22 +0000673}
674
robertphillipse16dfdb2015-05-08 04:46:51 -0700675// extract the result vertices and indices from the GrAAConvexTessellator
676static void extract_verts(const GrAAConvexTessellator& tess,
677 void* vertices,
678 size_t vertexStride,
679 GrColor color,
680 uint16_t* idxs,
681 bool tweakAlphaForCoverage) {
682 intptr_t verts = reinterpret_cast<intptr_t>(vertices);
683
684 for (int i = 0; i < tess.numPts(); ++i) {
685 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
686 }
687
688 // Make 'verts' point to the colors
689 verts += sizeof(SkPoint);
690 for (int i = 0; i < tess.numPts(); ++i) {
robertphillipse16dfdb2015-05-08 04:46:51 -0700691 if (tweakAlphaForCoverage) {
fmalitabd5d7e72015-06-26 07:18:24 -0700692 SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
693 unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
robertphillipse16dfdb2015-05-08 04:46:51 -0700694 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
695 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
696 } else {
697 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
halcanary9d524f22016-03-29 09:03:52 -0700698 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
fmalitabd5d7e72015-06-26 07:18:24 -0700699 tess.coverage(i);
robertphillipse16dfdb2015-05-08 04:46:51 -0700700 }
701 }
702
703 for (int i = 0; i < tess.numIndices(); ++i) {
704 idxs[i] = tess.index(i);
705 }
706}
707
bungeman06ca8ec2016-06-09 08:01:03 -0700708static sk_sp<GrGeometryProcessor> create_fill_gp(bool tweakAlphaForCoverage,
joshualittdf0c5572015-08-03 11:35:28 -0700709 const SkMatrix& viewMatrix,
Brian Salomon8c852be2017-01-04 10:44:42 -0500710 bool usesLocalCoords) {
joshualittdf0c5572015-08-03 11:35:28 -0700711 using namespace GrDefaultGeoProcFactory;
joshualitte494a582015-08-03 09:32:36 -0700712
joshualittdf0c5572015-08-03 11:35:28 -0700713 Coverage::Type coverageType;
Brian Salomon8c852be2017-01-04 10:44:42 -0500714 if (tweakAlphaForCoverage) {
joshualittdf0c5572015-08-03 11:35:28 -0700715 coverageType = Coverage::kSolid_Type;
716 } else {
717 coverageType = Coverage::kAttribute_Type;
718 }
Brian Salomon8c852be2017-01-04 10:44:42 -0500719 LocalCoords::Type localCoordsType =
720 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
Brian Salomon3de0aee2017-01-29 09:34:17 -0500721 return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
722 viewMatrix);
robertphillipse16dfdb2015-05-08 04:46:51 -0700723}
724
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400725class AAConvexPathOp final : public GrLegacyMeshDrawOp {
joshualitt27f398f2015-02-05 14:39:01 -0800726public:
Brian Salomon25a88092016-12-01 09:36:50 -0500727 DEFINE_OP_CLASS_ID
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400728 static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
729 const SkPath& path) {
730 return std::unique_ptr<GrLegacyMeshDrawOp>(new AAConvexPathOp(color, viewMatrix, path));
bsalomonf1703092016-06-29 18:41:53 -0700731 }
joshualitt27f398f2015-02-05 14:39:01 -0800732
Brian Salomond0a0a652016-12-15 15:25:22 -0500733 const char* name() const override { return "AAConvexPathOp"; }
joshualitt27f398f2015-02-05 14:39:01 -0800734
Brian Salomon7c3e7182016-12-01 09:35:30 -0500735 SkString dumpInfo() const override {
736 SkString string;
Brian Salomond0a0a652016-12-15 15:25:22 -0500737 string.appendf("Color: 0x%08x, Count: %d\n", fColor, fPaths.count());
Brian Salomon7c3e7182016-12-01 09:35:30 -0500738 string.append(DumpPipelineInfo(*this->pipeline()));
739 string.append(INHERITED::dumpInfo());
740 return string;
741 }
742
bsalomone46f9fe2015-08-18 06:05:14 -0700743private:
Brian Salomond0a0a652016-12-15 15:25:22 -0500744 AAConvexPathOp(GrColor color, const SkMatrix& viewMatrix, const SkPath& path)
745 : INHERITED(ClassID()), fColor(color) {
746 fPaths.emplace_back(PathData{viewMatrix, path});
747 this->setTransformedBounds(path.getBounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
748 }
749
Brian Salomona811b122017-03-30 08:21:32 -0400750 void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
751 GrProcessorAnalysisCoverage* coverage) const override {
Brian Salomonc0b642c2017-03-27 13:09:36 -0400752 color->setToConstant(fColor);
Brian Salomona811b122017-03-30 08:21:32 -0400753 *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
Brian Salomon92aee3d2016-12-21 09:20:25 -0500754 }
755
Brian Salomone7d30482017-03-29 12:09:15 -0400756 void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
Brian Salomon92aee3d2016-12-21 09:20:25 -0500757 optimizations.getOverrideColorIfSet(&fColor);
joshualitt27f398f2015-02-05 14:39:01 -0800758
Brian Salomon92aee3d2016-12-21 09:20:25 -0500759 fUsesLocalCoords = optimizations.readsLocalCoords();
Brian Salomond0a0a652016-12-15 15:25:22 -0500760 fLinesOnly = SkPath::kLine_SegmentMask == fPaths[0].fPath.getSegmentMasks();
Brian Salomon92aee3d2016-12-21 09:20:25 -0500761 fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
robertphillipse16dfdb2015-05-08 04:46:51 -0700762 }
763
joshualitt144c3c82015-11-30 12:30:13 -0800764 void prepareLinesOnlyDraws(Target* target) const {
robertphillipse16dfdb2015-05-08 04:46:51 -0700765 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
766
joshualittdf0c5572015-08-03 11:35:28 -0700767 // Setup GrGeometryProcessor
Brian Salomon8c852be2017-01-04 10:44:42 -0500768 sk_sp<GrGeometryProcessor> gp(create_fill_gp(
769 canTweakAlphaForCoverage, this->viewMatrix(), this->usesLocalCoords()));
joshualittdf0c5572015-08-03 11:35:28 -0700770 if (!gp) {
771 SkDebugf("Could not create GrGeometryProcessor\n");
robertphillipse16dfdb2015-05-08 04:46:51 -0700772 return;
773 }
774
robertphillipse16dfdb2015-05-08 04:46:51 -0700775 size_t vertexStride = gp->getVertexStride();
776
777 SkASSERT(canTweakAlphaForCoverage ?
778 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
779 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
780
781 GrAAConvexTessellator tess;
782
Brian Salomond0a0a652016-12-15 15:25:22 -0500783 int instanceCount = fPaths.count();
robertphillipse16dfdb2015-05-08 04:46:51 -0700784
785 for (int i = 0; i < instanceCount; i++) {
786 tess.rewind();
787
Brian Salomond0a0a652016-12-15 15:25:22 -0500788 const PathData& args = fPaths[i];
robertphillipse16dfdb2015-05-08 04:46:51 -0700789
790 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
791 continue;
792 }
793
cdalton397536c2016-03-25 12:15:03 -0700794 const GrBuffer* vertexBuffer;
robertphillipse16dfdb2015-05-08 04:46:51 -0700795 int firstVertex;
796
bsalomon75398562015-08-17 12:55:38 -0700797 void* verts = target->makeVertexSpace(vertexStride, tess.numPts(), &vertexBuffer,
798 &firstVertex);
robertphillipse16dfdb2015-05-08 04:46:51 -0700799 if (!verts) {
800 SkDebugf("Could not allocate vertices\n");
801 return;
802 }
803
cdalton397536c2016-03-25 12:15:03 -0700804 const GrBuffer* indexBuffer;
robertphillipse16dfdb2015-05-08 04:46:51 -0700805 int firstIndex;
806
bsalomon75398562015-08-17 12:55:38 -0700807 uint16_t* idxs = target->makeIndexSpace(tess.numIndices(), &indexBuffer, &firstIndex);
robertphillipse16dfdb2015-05-08 04:46:51 -0700808 if (!idxs) {
809 SkDebugf("Could not allocate indices\n");
810 return;
811 }
812
Brian Salomond0a0a652016-12-15 15:25:22 -0500813 extract_verts(tess, verts, vertexStride, fColor, idxs, canTweakAlphaForCoverage);
robertphillipse16dfdb2015-05-08 04:46:51 -0700814
egdaniel0e1853c2016-03-17 11:35:45 -0700815 GrMesh mesh;
Chris Daltonff926502017-05-03 14:36:54 -0400816 mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
817 mesh.fIndexBuffer.reset(indexBuffer);
818 mesh.fIndexCount = tess.numIndices();
819 mesh.fBaseIndex = firstIndex;
820 mesh.fVertexBuffer.reset(vertexBuffer);
821 mesh.fVertexCount = tess.numPts();
822 mesh.fBaseVertex = firstVertex;
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400823 target->draw(gp.get(), this->pipeline(), mesh);
robertphillipse16dfdb2015-05-08 04:46:51 -0700824 }
joshualitt27f398f2015-02-05 14:39:01 -0800825 }
826
joshualitt144c3c82015-11-30 12:30:13 -0800827 void onPrepareDraws(Target* target) const override {
robertphillipse16dfdb2015-05-08 04:46:51 -0700828#ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
829 if (this->linesOnly()) {
bsalomon75398562015-08-17 12:55:38 -0700830 this->prepareLinesOnlyDraws(target);
robertphillipse16dfdb2015-05-08 04:46:51 -0700831 return;
832 }
833#endif
834
Brian Salomond0a0a652016-12-15 15:25:22 -0500835 int instanceCount = fPaths.count();
joshualitt27f398f2015-02-05 14:39:01 -0800836
837 SkMatrix invert;
joshualitt7abe15d2015-02-20 12:40:45 -0800838 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
joshualitt27f398f2015-02-05 14:39:01 -0800839 SkDebugf("Could not invert viewmatrix\n");
840 return;
841 }
842
843 // Setup GrGeometryProcessor
bungeman06ca8ec2016-06-09 08:01:03 -0700844 sk_sp<GrGeometryProcessor> quadProcessor(
845 QuadEdgeEffect::Make(this->color(), invert, this->usesLocalCoords()));
joshualitt27f398f2015-02-05 14:39:01 -0800846
joshualitt27f398f2015-02-05 14:39:01 -0800847 // TODO generate all segments for all paths and use one vertex buffer
848 for (int i = 0; i < instanceCount; i++) {
Brian Salomond0a0a652016-12-15 15:25:22 -0500849 const PathData& args = fPaths[i];
joshualitt27f398f2015-02-05 14:39:01 -0800850
851 // We use the fact that SkPath::transform path does subdivision based on
852 // perspective. Otherwise, we apply the view matrix when copying to the
853 // segment representation.
854 const SkMatrix* viewMatrix = &args.fViewMatrix;
joshualitt144c3c82015-11-30 12:30:13 -0800855
856 // We avoid initializing the path unless we have to
857 const SkPath* pathPtr = &args.fPath;
858 SkTLazy<SkPath> tmpPath;
joshualitt27f398f2015-02-05 14:39:01 -0800859 if (viewMatrix->hasPerspective()) {
joshualitt144c3c82015-11-30 12:30:13 -0800860 SkPath* tmpPathPtr = tmpPath.init(*pathPtr);
861 tmpPathPtr->setIsVolatile(true);
862 tmpPathPtr->transform(*viewMatrix);
joshualitt27f398f2015-02-05 14:39:01 -0800863 viewMatrix = &SkMatrix::I();
joshualitt144c3c82015-11-30 12:30:13 -0800864 pathPtr = tmpPathPtr;
joshualitt27f398f2015-02-05 14:39:01 -0800865 }
866
867 int vertexCount;
868 int indexCount;
869 enum {
870 kPreallocSegmentCnt = 512 / sizeof(Segment),
871 kPreallocDrawCnt = 4,
872 };
873 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
874 SkPoint fanPt;
875
joshualitt144c3c82015-11-30 12:30:13 -0800876 if (!get_segments(*pathPtr, *viewMatrix, &segments, &fanPt, &vertexCount,
joshualitt27f398f2015-02-05 14:39:01 -0800877 &indexCount)) {
878 continue;
879 }
880
Chris Daltonff926502017-05-03 14:36:54 -0400881 GrMesh mesh;
882 mesh.fPrimitiveType = kTriangles_GrPrimitiveType;
joshualitt27f398f2015-02-05 14:39:01 -0800883
Chris Daltonff926502017-05-03 14:36:54 -0400884 const GrBuffer* vertexBuffer;
joshualitt27f398f2015-02-05 14:39:01 -0800885 size_t vertexStride = quadProcessor->getVertexStride();
bsalomon75398562015-08-17 12:55:38 -0700886 QuadVertex* verts = reinterpret_cast<QuadVertex*>(target->makeVertexSpace(
Chris Daltonff926502017-05-03 14:36:54 -0400887 vertexStride, vertexCount, &vertexBuffer, &mesh.fBaseVertex));
888 mesh.fVertexBuffer.reset(vertexBuffer);
bsalomone64eb572015-05-07 11:35:55 -0700889
890 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800891 SkDebugf("Could not allocate vertices\n");
892 return;
893 }
894
cdalton397536c2016-03-25 12:15:03 -0700895 const GrBuffer* indexBuffer;
Chris Daltonff926502017-05-03 14:36:54 -0400896 uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &mesh.fBaseIndex);
robertphillipse40d3972015-05-07 09:51:43 -0700897 if (!idxs) {
joshualitt4b31de82015-03-05 14:33:41 -0800898 SkDebugf("Could not allocate indices\n");
899 return;
900 }
Chris Daltonff926502017-05-03 14:36:54 -0400901 mesh.fIndexBuffer.reset(indexBuffer);
joshualitt4b31de82015-03-05 14:33:41 -0800902
joshualitt27f398f2015-02-05 14:39:01 -0800903 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
904 create_vertices(segments, fanPt, &draws, verts, idxs);
905
robertphillips44c31282015-09-03 12:58:48 -0700906 for (int j = 0; j < draws.count(); ++j) {
907 const Draw& draw = draws[j];
Chris Daltonff926502017-05-03 14:36:54 -0400908 mesh.fIndexCount = draw.fIndexCnt;
909 mesh.fVertexCount = draw.fVertexCnt;
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400910 target->draw(quadProcessor.get(), this->pipeline(), mesh);
Chris Daltonff926502017-05-03 14:36:54 -0400911 mesh.fBaseIndex += draw.fIndexCnt;
912 mesh.fBaseVertex += draw.fVertexCnt;
joshualitt27f398f2015-02-05 14:39:01 -0800913 }
914 }
915 }
916
Brian Salomon25a88092016-12-01 09:36:50 -0500917 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomond0a0a652016-12-15 15:25:22 -0500918 AAConvexPathOp* that = t->cast<AAConvexPathOp>();
bsalomonabd30f52015-08-13 13:34:48 -0700919 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
920 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700921 return false;
922 }
923
joshualitt27f398f2015-02-05 14:39:01 -0800924 if (this->color() != that->color()) {
925 return false;
926 }
927
928 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
929 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
930 return false;
931 }
932
robertphillipse16dfdb2015-05-08 04:46:51 -0700933 if (this->linesOnly() != that->linesOnly()) {
934 return false;
935 }
936
Brian Salomond0a0a652016-12-15 15:25:22 -0500937 // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
938 // tweaking
robertphillipse16dfdb2015-05-08 04:46:51 -0700939 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
Brian Salomond0a0a652016-12-15 15:25:22 -0500940 fCanTweakAlphaForCoverage = false;
robertphillipse16dfdb2015-05-08 04:46:51 -0700941 }
942
Brian Salomond0a0a652016-12-15 15:25:22 -0500943 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700944 this->joinBounds(*that);
joshualitt27f398f2015-02-05 14:39:01 -0800945 return true;
946 }
947
Brian Salomond0a0a652016-12-15 15:25:22 -0500948 GrColor color() const { return fColor; }
949 bool linesOnly() const { return fLinesOnly; }
950 bool usesLocalCoords() const { return fUsesLocalCoords; }
951 bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; }
952 const SkMatrix& viewMatrix() const { return fPaths[0].fViewMatrix; }
joshualitt27f398f2015-02-05 14:39:01 -0800953
Brian Salomond0a0a652016-12-15 15:25:22 -0500954 GrColor fColor;
955 bool fUsesLocalCoords;
Brian Salomond0a0a652016-12-15 15:25:22 -0500956 bool fLinesOnly;
957 bool fCanTweakAlphaForCoverage;
joshualitt27f398f2015-02-05 14:39:01 -0800958
Brian Salomond0a0a652016-12-15 15:25:22 -0500959 struct PathData {
bsalomonf1703092016-06-29 18:41:53 -0700960 SkMatrix fViewMatrix;
961 SkPath fPath;
962 };
963
Brian Salomond0a0a652016-12-15 15:25:22 -0500964 SkSTArray<1, PathData, true> fPaths;
reed1b55a962015-09-17 20:16:13 -0700965
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400966 typedef GrLegacyMeshDrawOp INHERITED;
joshualitt27f398f2015-02-05 14:39:01 -0800967};
968
bsalomon0aff2fa2015-07-31 06:48:27 -0700969bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400970 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700971 "GrAAConvexPathRenderer::onDrawPath");
Brian Salomon7c8460e2017-05-12 11:36:10 -0400972 SkASSERT(GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType());
bsalomon8acedde2016-06-24 10:42:16 -0700973 SkASSERT(!args.fShape->isEmpty());
bsalomon@google.com4647f902013-03-26 14:45:27 +0000974
bsalomonf1703092016-06-29 18:41:53 -0700975 SkPath path;
976 args.fShape->asPath(&path);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000977
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400978 std::unique_ptr<GrLegacyMeshDrawOp> op =
Brian Salomon82f44312017-01-11 13:42:54 -0500979 AAConvexPathOp::Make(args.fPaint.getColor(), *args.fViewMatrix, path);
robertphillips976f5f02016-06-03 10:59:20 -0700980
Brian Salomon82f44312017-01-11 13:42:54 -0500981 GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
bsalomonbb243832016-07-22 07:10:19 -0700982 pipelineBuilder.setUserStencil(args.fUserStencilSettings);
983
Brian Salomone14bd802017-04-04 15:13:25 -0400984 args.fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), *args.fClip,
985 std::move(op));
bsalomon@google.coma8347462012-10-08 18:59:39 +0000986
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000987 return true;
joshualitt27f398f2015-02-05 14:39:01 -0800988
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000989}
joshualitt8e5c1772015-05-11 08:58:52 -0700990
991///////////////////////////////////////////////////////////////////////////////////////////////////
992
Hal Canary6f6961e2017-01-31 13:50:44 -0500993#if GR_TEST_UTILS
joshualitt8e5c1772015-05-11 08:58:52 -0700994
Brian Salomon17726632017-05-12 14:09:46 -0400995GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(AAConvexPathOp) {
bsalomonf1703092016-06-29 18:41:53 -0700996 GrColor color = GrRandomColor(random);
997 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
998 SkPath path = GrTest::TestPathConvex(random);
joshualitt8e5c1772015-05-11 08:58:52 -0700999
Brian Salomon5ec9def2016-12-20 15:34:05 -05001000 return AAConvexPathOp::Make(color, viewMatrix, path);
joshualitt8e5c1772015-05-11 08:58:52 -07001001}
1002
1003#endif