blob: a1d7392de003185be3690232f1452846810dd157 [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"
Brian Salomondad29232016-12-01 16:40:24 -050018#include "GrProcessor.h"
Brian Salomon10978a62017-06-15 16:21:49 -040019#include "GrSimpleMeshDrawOpHelper.h"
egdanielaf18a092015-01-05 10:22:28 -080020#include "SkGeometry.h"
reed026beb52015-06-10 14:23:15 -070021#include "SkPathPriv.h"
Cary Clarkdf429f32017-11-08 11:44:31 -050022#include "SkPointPriv.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"
egdaniel7ea439b2015-12-03 09:20:44 -080025#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080026#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070027#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080028#include "glsl/GrGLSLUniformHandler.h"
egdaniel0eafe792015-11-20 14:01:22 -080029#include "glsl/GrGLSLVarying.h"
Chris Daltonc17bf322017-10-24 10:59:03 -060030#include "glsl/GrGLSLVertexGeoBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050031#include "ops/GrMeshDrawOp.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
Greg Daniel8b09b962018-04-03 14:53:45 -040067static bool 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 }
Greg Daniel62473ad2018-04-03 15:44:18 -0400114 return !SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY) && c->isFinite();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000115}
116
Greg Daniel8b09b962018-04-03 14:53:45 -0400117static bool compute_vectors(SegmentArray* segments,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000118 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) {
Greg Daniel8b09b962018-04-03 14:53:45 -0400122 if (!center_of_mass(*segments, fanPt)) {
123 return false;
124 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000125 int count = segments->count();
126
bsalomon@google.com278dc692012-02-15 16:52:51 +0000127 // Make the normals point towards the outside
Cary Clarkdf429f32017-11-08 11:44:31 -0500128 SkPointPriv::Side normSide;
reed026beb52015-06-10 14:23:15 -0700129 if (dir == SkPathPriv::kCCW_FirstDirection) {
Cary Clarkdf429f32017-11-08 11:44:31 -0500130 normSide = SkPointPriv::kRight_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000131 } else {
Cary Clarkdf429f32017-11-08 11:44:31 -0500132 normSide = SkPointPriv::kLeft_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000133 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000134
135 *vCount = 0;
136 *iCount = 0;
137 // compute normals at all points
138 for (int a = 0; a < count; ++a) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000139 Segment& sega = (*segments)[a];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000140 int b = (a + 1) % count;
141 Segment& segb = (*segments)[b];
142
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000143 const SkPoint* prevPt = &sega.endPt();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000144 int n = segb.countPoints();
145 for (int p = 0; p < n; ++p) {
146 segb.fNorms[p] = segb.fPts[p] - *prevPt;
147 segb.fNorms[p].normalize();
Cary Clarkdf429f32017-11-08 11:44:31 -0500148 SkPointPriv::SetOrthog(&segb.fNorms[p], segb.fNorms[p], normSide);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000149 prevPt = &segb.fPts[p];
150 }
151 if (Segment::kLine == segb.fType) {
152 *vCount += 5;
153 *iCount += 9;
154 } else {
155 *vCount += 6;
156 *iCount += 12;
157 }
158 }
159
160 // compute mid-vectors where segments meet. TODO: Detect shallow corners
161 // and leave out the wedges and close gaps by stitching segments together.
162 for (int a = 0; a < count; ++a) {
163 const Segment& sega = (*segments)[a];
164 int b = (a + 1) % count;
165 Segment& segb = (*segments)[b];
166 segb.fMid = segb.fNorms[0] + sega.endNorm();
167 segb.fMid.normalize();
168 // corner wedges
169 *vCount += 4;
170 *iCount += 6;
171 }
Greg Daniel8b09b962018-04-03 14:53:45 -0400172 return true;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000173}
174
bsalomon@google.com9732f622012-01-31 15:19:21 +0000175struct DegenerateTestData {
176 DegenerateTestData() { fStage = kInitial; }
177 bool isDegenerate() const { return kNonDegenerate != fStage; }
178 enum {
179 kInitial,
180 kPoint,
181 kLine,
182 kNonDegenerate
183 } fStage;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000184 SkPoint fFirstPoint;
185 SkVector fLineNormal;
bsalomon@google.com81712882012-11-01 17:12:34 +0000186 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000187};
188
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000189static const SkScalar kClose = (SK_Scalar1 / 16);
Mike Reed8be952a2017-02-13 20:44:33 -0500190static const SkScalar kCloseSqd = kClose * kClose;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000191
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000192static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000193 switch (data->fStage) {
194 case DegenerateTestData::kInitial:
195 data->fFirstPoint = pt;
196 data->fStage = DegenerateTestData::kPoint;
197 break;
198 case DegenerateTestData::kPoint:
Cary Clarkdf429f32017-11-08 11:44:31 -0500199 if (SkPointPriv::DistanceToSqd(pt, data->fFirstPoint) > kCloseSqd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000200 data->fLineNormal = pt - data->fFirstPoint;
201 data->fLineNormal.normalize();
Cary Clarkdf429f32017-11-08 11:44:31 -0500202 SkPointPriv::SetOrthog(&data->fLineNormal, data->fLineNormal);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000203 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
204 data->fStage = DegenerateTestData::kLine;
205 }
206 break;
207 case DegenerateTestData::kLine:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000208 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000209 data->fStage = DegenerateTestData::kNonDegenerate;
210 }
211 case DegenerateTestData::kNonDegenerate:
212 break;
213 default:
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400214 SK_ABORT("Unexpected degenerate test stage.");
bsalomon@google.com9732f622012-01-31 15:19:21 +0000215 }
216}
217
reed026beb52015-06-10 14:23:15 -0700218static inline bool get_direction(const SkPath& path, const SkMatrix& m,
219 SkPathPriv::FirstDirection* dir) {
220 if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000221 return false;
222 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000223 // check whether m reverses the orientation
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000224 SkASSERT(!m.hasPerspective());
Mike Reed8be952a2017-02-13 20:44:33 -0500225 SkScalar det2x2 = m.get(SkMatrix::kMScaleX) * m.get(SkMatrix::kMScaleY) -
226 m.get(SkMatrix::kMSkewX) * m.get(SkMatrix::kMSkewY);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000227 if (det2x2 < 0) {
reed026beb52015-06-10 14:23:15 -0700228 *dir = SkPathPriv::OppositeFirstDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000229 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000230 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000231}
232
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000233static inline void add_line_to_segment(const SkPoint& pt,
joshualitt27f398f2015-02-05 14:39:01 -0800234 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000235 segments->push_back();
236 segments->back().fType = Segment::kLine;
237 segments->back().fPts[0] = pt;
238}
239
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000240static inline void add_quad_segment(const SkPoint pts[3],
joshualitt27f398f2015-02-05 14:39:01 -0800241 SegmentArray* segments) {
Cary Clarkdf429f32017-11-08 11:44:31 -0500242 if (SkPointPriv::DistanceToSqd(pts[0], pts[1]) < kCloseSqd ||
243 SkPointPriv::DistanceToSqd(pts[1], pts[2]) < kCloseSqd) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000244 if (pts[0] != pts[2]) {
joshualitt27f398f2015-02-05 14:39:01 -0800245 add_line_to_segment(pts[2], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000246 }
247 } else {
248 segments->push_back();
249 segments->back().fType = Segment::kQuad;
250 segments->back().fPts[0] = pts[1];
251 segments->back().fPts[1] = pts[2];
252 }
253}
254
255static inline void add_cubic_segments(const SkPoint pts[4],
reed026beb52015-06-10 14:23:15 -0700256 SkPathPriv::FirstDirection dir,
joshualitt27f398f2015-02-05 14:39:01 -0800257 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000258 SkSTArray<15, SkPoint, true> quads;
bsalomon18fab302016-02-16 08:00:05 -0800259 GrPathUtils::convertCubicToQuadsConstrainToTangents(pts, SK_Scalar1, dir, &quads);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000260 int count = quads.count();
261 for (int q = 0; q < count; q += 3) {
joshualitt27f398f2015-02-05 14:39:01 -0800262 add_quad_segment(&quads[q], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000263 }
264}
265
266static bool get_segments(const SkPath& path,
267 const SkMatrix& m,
268 SegmentArray* segments,
269 SkPoint* fanPt,
270 int* vCount,
joshualitt27f398f2015-02-05 14:39:01 -0800271 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000272 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000273 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000274 // to the path from the sample to compute coverage. Every pixel intersected
275 // 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 +0000276 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000277 // thus should be very light. This is particularly egregious for degenerate
278 // line paths. We detect paths that are very close to a line (zero area) and
279 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000280 DegenerateTestData degenerateData;
reed026beb52015-06-10 14:23:15 -0700281 SkPathPriv::FirstDirection dir;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000282 // get_direction can fail for some degenerate paths.
283 if (!get_direction(path, m, &dir)) {
284 return false;
285 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000286
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000287 for (;;) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000288 SkPoint pts[4];
Brian Salomon97042bf2017-02-28 11:21:28 -0500289 SkPath::Verb verb = iter.next(pts, true, true);
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000290 switch (verb) {
291 case SkPath::kMove_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000292 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000293 update_degenerate_test(&degenerateData, pts[0]);
294 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000295 case SkPath::kLine_Verb: {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000296 m.mapPoints(&pts[1], 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000297 update_degenerate_test(&degenerateData, pts[1]);
joshualitt27f398f2015-02-05 14:39:01 -0800298 add_line_to_segment(pts[1], segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000299 break;
300 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000301 case SkPath::kQuad_Verb:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000302 m.mapPoints(pts, 3);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000303 update_degenerate_test(&degenerateData, pts[1]);
304 update_degenerate_test(&degenerateData, pts[2]);
joshualitt27f398f2015-02-05 14:39:01 -0800305 add_quad_segment(pts, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000306 break;
egdanielaf18a092015-01-05 10:22:28 -0800307 case SkPath::kConic_Verb: {
308 m.mapPoints(pts, 3);
309 SkScalar weight = iter.conicWeight();
310 SkAutoConicToQuads converter;
311 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5f);
312 for (int i = 0; i < converter.countQuads(); ++i) {
313 update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
314 update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
joshualitt27f398f2015-02-05 14:39:01 -0800315 add_quad_segment(quadPts + 2*i, segments);
egdanielaf18a092015-01-05 10:22:28 -0800316 }
317 break;
318 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000319 case SkPath::kCubic_Verb: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000320 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000321 update_degenerate_test(&degenerateData, pts[1]);
322 update_degenerate_test(&degenerateData, pts[2]);
323 update_degenerate_test(&degenerateData, pts[3]);
joshualitt27f398f2015-02-05 14:39:01 -0800324 add_cubic_segments(pts, dir, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000325 break;
326 };
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000327 case SkPath::kDone_Verb:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000328 if (degenerateData.isDegenerate()) {
329 return false;
330 } else {
Greg Daniel8b09b962018-04-03 14:53:45 -0400331 return compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000332 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000333 default:
334 break;
335 }
336 }
337}
338
339struct QuadVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000340 SkPoint fPos;
Brian Salomon60fb0b22017-06-15 17:09:36 -0400341 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000342 SkPoint fUV;
bsalomon@google.com81712882012-11-01 17:12:34 +0000343 SkScalar fD0;
344 SkScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000345};
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000346
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000347struct Draw {
348 Draw() : fVertexCnt(0), fIndexCnt(0) {}
349 int fVertexCnt;
350 int fIndexCnt;
351};
352
353typedef SkTArray<Draw, true> DrawArray;
354
Brian Salomon60fb0b22017-06-15 17:09:36 -0400355static void create_vertices(const SegmentArray& segments,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000356 const SkPoint& fanPt,
Brian Salomon60fb0b22017-06-15 17:09:36 -0400357 GrColor color,
358 DrawArray* draws,
359 QuadVertex* verts,
360 uint16_t* idxs) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000361 Draw* draw = &draws->push_back();
362 // alias just to make vert/index assignments easier to read.
363 int* v = &draw->fVertexCnt;
364 int* i = &draw->fIndexCnt;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000365
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000366 int count = segments.count();
367 for (int a = 0; a < count; ++a) {
368 const Segment& sega = segments[a];
369 int b = (a + 1) % count;
370 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000371
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000372 // Check whether adding the verts for this segment to the current draw would cause index
373 // values to overflow.
374 int vCount = 4;
375 if (Segment::kLine == segb.fType) {
376 vCount += 5;
377 } else {
378 vCount += 6;
379 }
380 if (draw->fVertexCnt + vCount > (1 << 16)) {
381 verts += *v;
382 idxs += *i;
383 draw = &draws->push_back();
384 v = &draw->fVertexCnt;
385 i = &draw->fIndexCnt;
386 }
387
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000388 // FIXME: These tris are inset in the 1 unit arc around the corner
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000389 verts[*v + 0].fPos = sega.endPt();
390 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
391 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
392 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
Brian Salomon60fb0b22017-06-15 17:09:36 -0400393 verts[*v + 0].fColor = color;
394 verts[*v + 1].fColor = color;
395 verts[*v + 2].fColor = color;
396 verts[*v + 3].fColor = color;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000397 verts[*v + 0].fUV.set(0,0);
398 verts[*v + 1].fUV.set(0,-SK_Scalar1);
399 verts[*v + 2].fUV.set(0,-SK_Scalar1);
400 verts[*v + 3].fUV.set(0,-SK_Scalar1);
401 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
402 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
403 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
404 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000405
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000406 idxs[*i + 0] = *v + 0;
407 idxs[*i + 1] = *v + 2;
408 idxs[*i + 2] = *v + 1;
409 idxs[*i + 3] = *v + 0;
410 idxs[*i + 4] = *v + 3;
411 idxs[*i + 5] = *v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000412
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000413 *v += 4;
414 *i += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000415
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000416 if (Segment::kLine == segb.fType) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000417 verts[*v + 0].fPos = fanPt;
418 verts[*v + 1].fPos = sega.endPt();
419 verts[*v + 2].fPos = segb.fPts[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000420
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000421 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
422 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000423
Brian Salomon60fb0b22017-06-15 17:09:36 -0400424 verts[*v + 0].fColor = color;
425 verts[*v + 1].fColor = color;
426 verts[*v + 2].fColor = color;
427 verts[*v + 3].fColor = color;
428 verts[*v + 4].fColor = color;
429
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000430 // we draw the line edge as a degenerate quad (u is 0, v is the
431 // signed distance to the edge)
Cary Clarkdf429f32017-11-08 11:44:31 -0500432 SkScalar dist = SkPointPriv::DistanceToLineBetween(fanPt, verts[*v + 1].fPos,
433 verts[*v + 2].fPos);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000434 verts[*v + 0].fUV.set(0, dist);
435 verts[*v + 1].fUV.set(0, 0);
436 verts[*v + 2].fUV.set(0, 0);
437 verts[*v + 3].fUV.set(0, -SK_Scalar1);
438 verts[*v + 4].fUV.set(0, -SK_Scalar1);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000439
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000440 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
441 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
442 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
443 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
444 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000445
cdalton35964822015-04-29 10:14:03 -0700446 idxs[*i + 0] = *v + 3;
447 idxs[*i + 1] = *v + 1;
448 idxs[*i + 2] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000449
cdalton35964822015-04-29 10:14:03 -0700450 idxs[*i + 3] = *v + 4;
451 idxs[*i + 4] = *v + 3;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000452 idxs[*i + 5] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000453
cdalton35964822015-04-29 10:14:03 -0700454 *i += 6;
455
456 // Draw the interior fan if it exists.
457 // TODO: Detect and combine colinear segments. This will ensure we catch every case
458 // with no interior, and that the resulting shared edge uses the same endpoints.
459 if (count >= 3) {
460 idxs[*i + 0] = *v + 0;
461 idxs[*i + 1] = *v + 2;
462 idxs[*i + 2] = *v + 1;
463
464 *i += 3;
465 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000466
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000467 *v += 5;
bsalomon@google.com06809612012-01-21 15:03:39 +0000468 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000469 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000470
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000471 SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000472 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000473
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000474 verts[*v + 0].fPos = fanPt;
475 verts[*v + 1].fPos = qpts[0];
476 verts[*v + 2].fPos = qpts[2];
477 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
478 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
479 verts[*v + 5].fPos = qpts[1] + midVec;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000480
Brian Salomon60fb0b22017-06-15 17:09:36 -0400481 verts[*v + 0].fColor = color;
482 verts[*v + 1].fColor = color;
483 verts[*v + 2].fColor = color;
484 verts[*v + 3].fColor = color;
485 verts[*v + 4].fColor = color;
486 verts[*v + 5].fColor = color;
487
bsalomon@google.com81712882012-11-01 17:12:34 +0000488 SkScalar c = segb.fNorms[0].dot(qpts[0]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000489 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
490 verts[*v + 1].fD0 = 0.f;
491 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
492 verts[*v + 3].fD0 = -SK_ScalarMax/100;
493 verts[*v + 4].fD0 = -SK_ScalarMax/100;
494 verts[*v + 5].fD0 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000495
496 c = segb.fNorms[1].dot(qpts[2]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000497 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
498 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
499 verts[*v + 2].fD1 = 0.f;
500 verts[*v + 3].fD1 = -SK_ScalarMax/100;
501 verts[*v + 4].fD1 = -SK_ScalarMax/100;
502 verts[*v + 5].fD1 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000503
bsalomon@google.com19713172012-03-15 13:51:08 +0000504 GrPathUtils::QuadUVMatrix toUV(qpts);
Brian Salomon60fb0b22017-06-15 17:09:36 -0400505 toUV.apply<6, sizeof(QuadVertex), offsetof(QuadVertex, fUV)>(verts + *v);
bsalomon@google.com06809612012-01-21 15:03:39 +0000506
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000507 idxs[*i + 0] = *v + 3;
508 idxs[*i + 1] = *v + 1;
509 idxs[*i + 2] = *v + 2;
510 idxs[*i + 3] = *v + 4;
511 idxs[*i + 4] = *v + 3;
512 idxs[*i + 5] = *v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000513
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000514 idxs[*i + 6] = *v + 5;
515 idxs[*i + 7] = *v + 3;
516 idxs[*i + 8] = *v + 4;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000517
cdalton35964822015-04-29 10:14:03 -0700518 *i += 9;
519
520 // Draw the interior fan if it exists.
521 // TODO: Detect and combine colinear segments. This will ensure we catch every case
522 // with no interior, and that the resulting shared edge uses the same endpoints.
523 if (count >= 3) {
524 idxs[*i + 0] = *v + 0;
525 idxs[*i + 1] = *v + 2;
526 idxs[*i + 2] = *v + 1;
527
528 *i += 3;
529 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000530
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000531 *v += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000532 }
533 }
534}
535
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000536///////////////////////////////////////////////////////////////////////////////
537
538/*
539 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
540 * two components of the vertex attribute. Coverage is based on signed
541 * distance with negative being inside, positive outside. The edge is specified in
542 * window space (y-down). If either the third or fourth component of the interpolated
543 * 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 +0000544 * attempt to trim to a portion of the infinite quad.
545 * Requires shader derivative instruction support.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000546 */
547
joshualitt249af152014-09-15 11:41:13 -0700548class QuadEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000549public:
Brian Salomon60fb0b22017-06-15 17:09:36 -0400550 static sk_sp<GrGeometryProcessor> Make(const SkMatrix& localMatrix, bool usesLocalCoords) {
551 return sk_sp<GrGeometryProcessor>(new QuadEdgeEffect(localMatrix, usesLocalCoords));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000552 }
553
Brian Salomond3b65972017-03-22 12:05:03 -0400554 ~QuadEdgeEffect() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000555
mtklein36352bf2015-03-25 18:17:31 -0700556 const char* name() const override { return "QuadEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000557
egdaniel57d3b032015-11-13 11:57:27 -0800558 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000559 public:
Brian Salomon60fb0b22017-06-15 17:09:36 -0400560 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000561
mtklein36352bf2015-03-25 18:17:31 -0700562 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800563 const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
egdaniel4ca2e602015-11-18 08:01:26 -0800564 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800565 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800566 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -0800567
joshualittabb52a12015-01-13 15:02:10 -0800568 // emit attributes
egdaniel0eafe792015-11-20 14:01:22 -0800569 varyingHandler->emitAttributes(qe);
joshualittabb52a12015-01-13 15:02:10 -0800570
Chris Dalton27372882017-12-08 13:34:21 -0700571 GrGLSLVarying v(kHalf4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800572 varyingHandler->addVarying("QuadEdge", &v);
Brian Salomon70132d02018-05-29 15:33:06 -0400573 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.fInQuadEdge->name());
Brian Salomon60fb0b22017-06-15 17:09:36 -0400574
575 // Setup pass through color
576 varyingHandler->addPassThroughAttribute(qe.fInColor, args.fOutputColor);
joshualitt2dd1ae02014-12-03 06:24:10 -0800577
Chris Dalton60283612018-02-14 13:38:14 -0700578 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt9b989322014-12-15 14:16:27 -0800579
joshualittabb52a12015-01-13 15:02:10 -0800580 // Setup position
Brian Salomon70132d02018-05-29 15:33:06 -0400581 this->writeOutputPosition(vertBuilder, gpArgs, qe.fInPosition->name());
joshualittabb52a12015-01-13 15:02:10 -0800582
583 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800584 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800585 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800586 uniformHandler,
Brian Salomon04460cc2017-12-06 14:47:42 -0500587 qe.fInPosition->asShaderVar(),
Brian Salomon60fb0b22017-06-15 17:09:36 -0400588 qe.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700589 args.fFPCoordTransformHandler);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000590
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400591 fragBuilder->codeAppendf("half edgeAlpha;");
joshualitt30ba4362014-08-21 20:18:45 -0700592
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000593 // keep the derivative instructions outside the conditional
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400594 fragBuilder->codeAppendf("half2 duvdx = dFdx(%s.xy);", v.fsIn());
595 fragBuilder->codeAppendf("half2 duvdy = dFdy(%s.xy);", v.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800596 fragBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000597 // today we know z and w are in device space. We could use derivatives
egdaniel4ca2e602015-11-18 08:01:26 -0800598 fragBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
599 v.fsIn());
600 fragBuilder->codeAppendf ("} else {");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400601 fragBuilder->codeAppendf("half2 gF = half2(2.0*%s.x*duvdx.x - duvdx.y,"
egdaniel4ca2e602015-11-18 08:01:26 -0800602 " 2.0*%s.x*duvdy.x - duvdy.y);",
603 v.fsIn(), v.fsIn());
604 fragBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
605 v.fsIn());
606 fragBuilder->codeAppendf("edgeAlpha = "
607 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000608
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400609 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000610 }
611
joshualitt9b989322014-12-15 14:16:27 -0800612 static inline void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500613 const GrShaderCaps&,
joshualitt9b989322014-12-15 14:16:27 -0800614 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700615 const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
Brian Salomon60fb0b22017-06-15 17:09:36 -0400616 b->add32(SkToBool(qee.fUsesLocalCoords && qee.fLocalMatrix.hasPerspective()));
joshualitt9b989322014-12-15 14:16:27 -0800617 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000618
egdaniel018fb622015-10-28 07:26:40 -0700619 void setData(const GrGLSLProgramDataManager& pdman,
bsalomona624bf32016-09-20 09:12:47 -0700620 const GrPrimitiveProcessor& gp,
621 FPCoordTransformIter&& transformIter) override {
joshualittb8c241a2015-05-19 08:23:30 -0700622 const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
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:
egdaniele659a582015-11-13 09:55:43 -0800627 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000628 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000629
Brian Salomon94efbf52016-11-29 13:43:05 -0500630 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800631 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800632 }
633
Brian Salomon94efbf52016-11-29 13:43:05 -0500634 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800635 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800636 }
637
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000638private:
Brian Salomon60fb0b22017-06-15 17:09:36 -0400639 QuadEdgeEffect(const SkMatrix& localMatrix, bool usesLocalCoords)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400640 : INHERITED(kQuadEdgeEffect_ClassID)
641 , fLocalMatrix(localMatrix)
642 , fUsesLocalCoords(usesLocalCoords) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -0400643 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
644 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
645 fInQuadEdge = &this->addVertexAttrib("inQuadEdge", kHalf4_GrVertexAttribType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000646 }
647
joshualitt71c92602015-01-14 08:12:47 -0800648 const Attribute* fInPosition;
649 const Attribute* fInQuadEdge;
Brian Salomon60fb0b22017-06-15 17:09:36 -0400650 const Attribute* fInColor;
joshualitte3ababe2015-05-15 07:56:07 -0700651 SkMatrix fLocalMatrix;
joshualittb8c241a2015-05-19 08:23:30 -0700652 bool fUsesLocalCoords;
joshualitt249af152014-09-15 11:41:13 -0700653
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400654 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000655
joshualitt2e3b3e32014-12-09 13:31:14 -0800656 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000657};
658
joshualittb0a8a372014-09-23 09:50:21 -0700659GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000660
Hal Canary6f6961e2017-01-31 13:50:44 -0500661#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700662sk_sp<GrGeometryProcessor> QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000663 // Doesn't work without derivative instructions.
Robert Phillips296b1cc2017-03-15 10:42:12 -0400664 return d->caps()->shaderCaps()->shaderDerivativeSupport()
Brian Salomon60fb0b22017-06-15 17:09:36 -0400665 ? QuadEdgeEffect::Make(GrTest::TestMatrix(d->fRandom), d->fRandom->nextBool())
Brian Salomon9ae32a22017-01-25 14:58:24 -0500666 : nullptr;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000667}
Hal Canary6f6961e2017-01-31 13:50:44 -0500668#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000669
670///////////////////////////////////////////////////////////////////////////////
671
Chris Dalton5ed44232017-09-07 13:22:46 -0600672GrPathRenderer::CanDrawPath
673GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
674 if (args.fCaps->shaderCaps()->shaderDerivativeSupport() &&
675 (GrAAType::kCoverage == args.fAAType) && args.fShape->style().isSimpleFill() &&
676 !args.fShape->inverseFilled() && args.fShape->knownToBeConvex()) {
677 return CanDrawPath::kYes;
678 }
679 return CanDrawPath::kNo;
robertphillips@google.comfa662942012-05-17 12:20:22 +0000680}
681
robertphillipse16dfdb2015-05-08 04:46:51 -0700682// extract the result vertices and indices from the GrAAConvexTessellator
Brian Salomon60fb0b22017-06-15 17:09:36 -0400683static void extract_lines_only_verts(const GrAAConvexTessellator& tess,
684 void* vertices,
685 size_t vertexStride,
686 GrColor color,
687 uint16_t* idxs,
688 bool tweakAlphaForCoverage) {
robertphillipse16dfdb2015-05-08 04:46:51 -0700689 intptr_t verts = reinterpret_cast<intptr_t>(vertices);
690
691 for (int i = 0; i < tess.numPts(); ++i) {
692 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
693 }
694
695 // Make 'verts' point to the colors
696 verts += sizeof(SkPoint);
697 for (int i = 0; i < tess.numPts(); ++i) {
robertphillipse16dfdb2015-05-08 04:46:51 -0700698 if (tweakAlphaForCoverage) {
fmalitabd5d7e72015-06-26 07:18:24 -0700699 SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
700 unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
robertphillipse16dfdb2015-05-08 04:46:51 -0700701 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
702 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
703 } else {
704 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
halcanary9d524f22016-03-29 09:03:52 -0700705 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
fmalitabd5d7e72015-06-26 07:18:24 -0700706 tess.coverage(i);
robertphillipse16dfdb2015-05-08 04:46:51 -0700707 }
708 }
709
710 for (int i = 0; i < tess.numIndices(); ++i) {
711 idxs[i] = tess.index(i);
712 }
713}
714
Brian Salomon60fb0b22017-06-15 17:09:36 -0400715static sk_sp<GrGeometryProcessor> make_lines_only_gp(bool tweakAlphaForCoverage,
716 const SkMatrix& viewMatrix,
717 bool usesLocalCoords) {
joshualittdf0c5572015-08-03 11:35:28 -0700718 using namespace GrDefaultGeoProcFactory;
joshualitte494a582015-08-03 09:32:36 -0700719
joshualittdf0c5572015-08-03 11:35:28 -0700720 Coverage::Type coverageType;
Brian Salomon8c852be2017-01-04 10:44:42 -0500721 if (tweakAlphaForCoverage) {
joshualittdf0c5572015-08-03 11:35:28 -0700722 coverageType = Coverage::kSolid_Type;
723 } else {
724 coverageType = Coverage::kAttribute_Type;
725 }
Brian Salomon8c852be2017-01-04 10:44:42 -0500726 LocalCoords::Type localCoordsType =
727 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
Brian Salomon3de0aee2017-01-29 09:34:17 -0500728 return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
729 viewMatrix);
robertphillipse16dfdb2015-05-08 04:46:51 -0700730}
731
Brian Salomon10978a62017-06-15 16:21:49 -0400732namespace {
733
734class AAConvexPathOp final : public GrMeshDrawOp {
735private:
736 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
737
joshualitt27f398f2015-02-05 14:39:01 -0800738public:
Brian Salomon25a88092016-12-01 09:36:50 -0500739 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400740
Brian Salomon10978a62017-06-15 16:21:49 -0400741 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
742 const SkPath& path,
743 const GrUserStencilSettings* stencilSettings) {
744 return Helper::FactoryHelper<AAConvexPathOp>(std::move(paint), viewMatrix, path,
745 stencilSettings);
746 }
747
748 AAConvexPathOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
749 const SkPath& path, const GrUserStencilSettings* stencilSettings)
Brian Salomon60fb0b22017-06-15 17:09:36 -0400750 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage, stencilSettings) {
751 fPaths.emplace_back(PathData{viewMatrix, path, color});
Brian Salomon10978a62017-06-15 16:21:49 -0400752 this->setTransformedBounds(path.getBounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
753 fLinesOnly = SkPath::kLine_SegmentMask == path.getSegmentMasks();
bsalomonf1703092016-06-29 18:41:53 -0700754 }
joshualitt27f398f2015-02-05 14:39:01 -0800755
Brian Salomond0a0a652016-12-15 15:25:22 -0500756 const char* name() const override { return "AAConvexPathOp"; }
joshualitt27f398f2015-02-05 14:39:01 -0800757
Robert Phillipsf1748f52017-09-14 14:11:24 -0400758 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400759 fHelper.visitProxies(func);
760 }
761
Brian Salomon7c3e7182016-12-01 09:35:30 -0500762 SkString dumpInfo() const override {
763 SkString string;
Brian Salomon60fb0b22017-06-15 17:09:36 -0400764 string.appendf("Count: %d\n", fPaths.count());
Brian Salomon10978a62017-06-15 16:21:49 -0400765 string += fHelper.dumpInfo();
766 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -0500767 return string;
768 }
769
Brian Salomon10978a62017-06-15 16:21:49 -0400770 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
771
Brian Osman9a725dd2017-09-20 09:53:22 -0400772 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
773 GrPixelConfigIsClamped dstIsClamped) override {
774 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
775 GrProcessorAnalysisCoverage::kSingleChannel,
Brian Salomon60fb0b22017-06-15 17:09:36 -0400776 &fPaths.back().fColor);
Brian Salomon10978a62017-06-15 16:21:49 -0400777 }
778
bsalomone46f9fe2015-08-18 06:05:14 -0700779private:
Brian Salomon91326c32017-08-09 16:02:19 -0400780 void prepareLinesOnlyDraws(Target* target) {
Brian Salomon60fb0b22017-06-15 17:09:36 -0400781 // Setup GrGeometryProcessor
782 sk_sp<GrGeometryProcessor> gp(make_lines_only_gp(fHelper.compatibleWithAlphaAsCoverage(),
783 fPaths.back().fViewMatrix,
784 fHelper.usesLocalCoords()));
joshualittdf0c5572015-08-03 11:35:28 -0700785 if (!gp) {
786 SkDebugf("Could not create GrGeometryProcessor\n");
robertphillipse16dfdb2015-05-08 04:46:51 -0700787 return;
788 }
789
robertphillipse16dfdb2015-05-08 04:46:51 -0700790 size_t vertexStride = gp->getVertexStride();
791
Brian Salomon60fb0b22017-06-15 17:09:36 -0400792 SkASSERT(fHelper.compatibleWithAlphaAsCoverage()
793 ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
794 : vertexStride ==
795 sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
robertphillipse16dfdb2015-05-08 04:46:51 -0700796
797 GrAAConvexTessellator tess;
798
Brian Salomond0a0a652016-12-15 15:25:22 -0500799 int instanceCount = fPaths.count();
Brian Salomon10978a62017-06-15 16:21:49 -0400800 const GrPipeline* pipeline = fHelper.makePipeline(target);
robertphillipse16dfdb2015-05-08 04:46:51 -0700801 for (int i = 0; i < instanceCount; i++) {
802 tess.rewind();
803
Brian Salomond0a0a652016-12-15 15:25:22 -0500804 const PathData& args = fPaths[i];
robertphillipse16dfdb2015-05-08 04:46:51 -0700805
806 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
807 continue;
808 }
809
cdalton397536c2016-03-25 12:15:03 -0700810 const GrBuffer* vertexBuffer;
robertphillipse16dfdb2015-05-08 04:46:51 -0700811 int firstVertex;
812
bsalomon75398562015-08-17 12:55:38 -0700813 void* verts = target->makeVertexSpace(vertexStride, tess.numPts(), &vertexBuffer,
814 &firstVertex);
robertphillipse16dfdb2015-05-08 04:46:51 -0700815 if (!verts) {
816 SkDebugf("Could not allocate vertices\n");
817 return;
818 }
819
cdalton397536c2016-03-25 12:15:03 -0700820 const GrBuffer* indexBuffer;
robertphillipse16dfdb2015-05-08 04:46:51 -0700821 int firstIndex;
822
bsalomon75398562015-08-17 12:55:38 -0700823 uint16_t* idxs = target->makeIndexSpace(tess.numIndices(), &indexBuffer, &firstIndex);
robertphillipse16dfdb2015-05-08 04:46:51 -0700824 if (!idxs) {
825 SkDebugf("Could not allocate indices\n");
826 return;
827 }
828
Brian Salomon60fb0b22017-06-15 17:09:36 -0400829 extract_lines_only_verts(tess, verts, vertexStride, args.fColor, idxs,
830 fHelper.compatibleWithAlphaAsCoverage());
robertphillipse16dfdb2015-05-08 04:46:51 -0700831
Chris Dalton3809bab2017-06-13 10:55:06 -0600832 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -0600833 mesh.setIndexed(indexBuffer, tess.numIndices(), firstIndex, 0, tess.numPts() - 1);
834 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon10978a62017-06-15 16:21:49 -0400835 target->draw(gp.get(), pipeline, mesh);
robertphillipse16dfdb2015-05-08 04:46:51 -0700836 }
joshualitt27f398f2015-02-05 14:39:01 -0800837 }
838
Brian Salomon91326c32017-08-09 16:02:19 -0400839 void onPrepareDraws(Target* target) override {
robertphillipse16dfdb2015-05-08 04:46:51 -0700840#ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
Brian Salomon10978a62017-06-15 16:21:49 -0400841 if (fLinesOnly) {
bsalomon75398562015-08-17 12:55:38 -0700842 this->prepareLinesOnlyDraws(target);
robertphillipse16dfdb2015-05-08 04:46:51 -0700843 return;
844 }
845#endif
Brian Salomon10978a62017-06-15 16:21:49 -0400846 const GrPipeline* pipeline = fHelper.makePipeline(target);
Brian Salomond0a0a652016-12-15 15:25:22 -0500847 int instanceCount = fPaths.count();
joshualitt27f398f2015-02-05 14:39:01 -0800848
849 SkMatrix invert;
Brian Salomon10978a62017-06-15 16:21:49 -0400850 if (fHelper.usesLocalCoords() && !fPaths.back().fViewMatrix.invert(&invert)) {
joshualitt27f398f2015-02-05 14:39:01 -0800851 return;
852 }
853
854 // Setup GrGeometryProcessor
bungeman06ca8ec2016-06-09 08:01:03 -0700855 sk_sp<GrGeometryProcessor> quadProcessor(
Brian Salomon60fb0b22017-06-15 17:09:36 -0400856 QuadEdgeEffect::Make(invert, fHelper.usesLocalCoords()));
joshualitt27f398f2015-02-05 14:39:01 -0800857
joshualitt27f398f2015-02-05 14:39:01 -0800858 // TODO generate all segments for all paths and use one vertex buffer
859 for (int i = 0; i < instanceCount; i++) {
Brian Salomond0a0a652016-12-15 15:25:22 -0500860 const PathData& args = fPaths[i];
joshualitt27f398f2015-02-05 14:39:01 -0800861
862 // We use the fact that SkPath::transform path does subdivision based on
863 // perspective. Otherwise, we apply the view matrix when copying to the
864 // segment representation.
865 const SkMatrix* viewMatrix = &args.fViewMatrix;
joshualitt144c3c82015-11-30 12:30:13 -0800866
867 // We avoid initializing the path unless we have to
868 const SkPath* pathPtr = &args.fPath;
869 SkTLazy<SkPath> tmpPath;
joshualitt27f398f2015-02-05 14:39:01 -0800870 if (viewMatrix->hasPerspective()) {
joshualitt144c3c82015-11-30 12:30:13 -0800871 SkPath* tmpPathPtr = tmpPath.init(*pathPtr);
872 tmpPathPtr->setIsVolatile(true);
873 tmpPathPtr->transform(*viewMatrix);
joshualitt27f398f2015-02-05 14:39:01 -0800874 viewMatrix = &SkMatrix::I();
joshualitt144c3c82015-11-30 12:30:13 -0800875 pathPtr = tmpPathPtr;
joshualitt27f398f2015-02-05 14:39:01 -0800876 }
877
878 int vertexCount;
879 int indexCount;
880 enum {
881 kPreallocSegmentCnt = 512 / sizeof(Segment),
882 kPreallocDrawCnt = 4,
883 };
884 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
885 SkPoint fanPt;
886
joshualitt144c3c82015-11-30 12:30:13 -0800887 if (!get_segments(*pathPtr, *viewMatrix, &segments, &fanPt, &vertexCount,
joshualitt27f398f2015-02-05 14:39:01 -0800888 &indexCount)) {
889 continue;
890 }
891
Chris Daltonff926502017-05-03 14:36:54 -0400892 const GrBuffer* vertexBuffer;
Chris Daltonbca46e22017-05-15 11:03:26 -0600893 int firstVertex;
894
joshualitt27f398f2015-02-05 14:39:01 -0800895 size_t vertexStride = quadProcessor->getVertexStride();
bsalomon75398562015-08-17 12:55:38 -0700896 QuadVertex* verts = reinterpret_cast<QuadVertex*>(target->makeVertexSpace(
Chris Daltonbca46e22017-05-15 11:03:26 -0600897 vertexStride, vertexCount, &vertexBuffer, &firstVertex));
bsalomone64eb572015-05-07 11:35:55 -0700898
899 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800900 SkDebugf("Could not allocate vertices\n");
901 return;
902 }
903
cdalton397536c2016-03-25 12:15:03 -0700904 const GrBuffer* indexBuffer;
Chris Daltonbca46e22017-05-15 11:03:26 -0600905 int firstIndex;
906
907 uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
robertphillipse40d3972015-05-07 09:51:43 -0700908 if (!idxs) {
joshualitt4b31de82015-03-05 14:33:41 -0800909 SkDebugf("Could not allocate indices\n");
910 return;
911 }
912
joshualitt27f398f2015-02-05 14:39:01 -0800913 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
Brian Salomon60fb0b22017-06-15 17:09:36 -0400914 create_vertices(segments, fanPt, args.fColor, &draws, verts, idxs);
joshualitt27f398f2015-02-05 14:39:01 -0800915
Chris Dalton3809bab2017-06-13 10:55:06 -0600916 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Daltonbca46e22017-05-15 11:03:26 -0600917
robertphillips44c31282015-09-03 12:58:48 -0700918 for (int j = 0; j < draws.count(); ++j) {
919 const Draw& draw = draws[j];
Chris Dalton114a3c02017-05-26 15:17:19 -0600920 mesh.setIndexed(indexBuffer, draw.fIndexCnt, firstIndex, 0, draw.fVertexCnt - 1);
921 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon10978a62017-06-15 16:21:49 -0400922 target->draw(quadProcessor.get(), pipeline, mesh);
Chris Daltonbca46e22017-05-15 11:03:26 -0600923 firstIndex += draw.fIndexCnt;
924 firstVertex += draw.fVertexCnt;
joshualitt27f398f2015-02-05 14:39:01 -0800925 }
926 }
927 }
928
Brian Salomon25a88092016-12-01 09:36:50 -0500929 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomond0a0a652016-12-15 15:25:22 -0500930 AAConvexPathOp* that = t->cast<AAConvexPathOp>();
Brian Salomon10978a62017-06-15 16:21:49 -0400931 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -0700932 return false;
933 }
Brian Salomon10978a62017-06-15 16:21:49 -0400934 if (fHelper.usesLocalCoords() &&
935 !fPaths[0].fViewMatrix.cheapEqualTo(that->fPaths[0].fViewMatrix)) {
joshualitt27f398f2015-02-05 14:39:01 -0800936 return false;
937 }
938
Brian Salomon10978a62017-06-15 16:21:49 -0400939 if (fLinesOnly != that->fLinesOnly) {
robertphillipse16dfdb2015-05-08 04:46:51 -0700940 return false;
941 }
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 struct PathData {
bsalomonf1703092016-06-29 18:41:53 -0700949 SkMatrix fViewMatrix;
950 SkPath fPath;
Brian Salomon60fb0b22017-06-15 17:09:36 -0400951 GrColor fColor;
bsalomonf1703092016-06-29 18:41:53 -0700952 };
953
Brian Salomon10978a62017-06-15 16:21:49 -0400954 Helper fHelper;
Brian Salomond0a0a652016-12-15 15:25:22 -0500955 SkSTArray<1, PathData, true> fPaths;
Brian Salomon10978a62017-06-15 16:21:49 -0400956 bool fLinesOnly;
reed1b55a962015-09-17 20:16:13 -0700957
Brian Salomon10978a62017-06-15 16:21:49 -0400958 typedef GrMeshDrawOp INHERITED;
joshualitt27f398f2015-02-05 14:39:01 -0800959};
960
Brian Salomon10978a62017-06-15 16:21:49 -0400961} // anonymous namespace
962
bsalomon0aff2fa2015-07-31 06:48:27 -0700963bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400964 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700965 "GrAAConvexPathRenderer::onDrawPath");
Brian Salomon7c8460e2017-05-12 11:36:10 -0400966 SkASSERT(GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType());
bsalomon8acedde2016-06-24 10:42:16 -0700967 SkASSERT(!args.fShape->isEmpty());
bsalomon@google.com4647f902013-03-26 14:45:27 +0000968
bsalomonf1703092016-06-29 18:41:53 -0700969 SkPath path;
970 args.fShape->asPath(&path);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000971
Brian Salomon10978a62017-06-15 16:21:49 -0400972 std::unique_ptr<GrDrawOp> op = AAConvexPathOp::Make(std::move(args.fPaint), *args.fViewMatrix,
973 path, args.fUserStencilSettings);
974 args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000975 return true;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000976}
joshualitt8e5c1772015-05-11 08:58:52 -0700977
978///////////////////////////////////////////////////////////////////////////////////////////////////
979
Hal Canary6f6961e2017-01-31 13:50:44 -0500980#if GR_TEST_UTILS
joshualitt8e5c1772015-05-11 08:58:52 -0700981
Brian Salomon10978a62017-06-15 16:21:49 -0400982GR_DRAW_OP_TEST_DEFINE(AAConvexPathOp) {
bsalomonf1703092016-06-29 18:41:53 -0700983 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
984 SkPath path = GrTest::TestPathConvex(random);
Brian Salomon10978a62017-06-15 16:21:49 -0400985 const GrUserStencilSettings* stencilSettings = GrGetRandomStencil(random, context);
986 return AAConvexPathOp::Make(std::move(paint), viewMatrix, path, stencilSettings);
joshualitt8e5c1772015-05-11 08:58:52 -0700987}
988
989#endif