blob: 62781fd1068d24d1080046b1e10e882b236cdf98 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkString.h"
9#include "include/core/SkTypes.h"
10#include "src/core/SkGeometry.h"
Michael Ludwigfbe28592020-06-26 16:02:15 -040011#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "src/core/SkPathPriv.h"
13#include "src/core/SkPointPriv.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040014#include "src/gpu/GrAuditTrail.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "src/gpu/GrCaps.h"
16#include "src/gpu/GrDrawOpTest.h"
17#include "src/gpu/GrGeometryProcessor.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "src/gpu/GrProcessor.h"
Robert Phillips3eabf4a2020-03-10 14:49:20 -040019#include "src/gpu/GrProgramInfo.h"
Brian Salomoneebe7352020-12-09 16:37:04 -050020#include "src/gpu/GrSurfaceDrawContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "src/gpu/GrVertexWriter.h"
Michael Ludwig663afe52019-06-03 16:46:19 -040022#include "src/gpu/geometry/GrPathUtils.h"
Michael Ludwig2686d692020-04-17 20:21:37 +000023#include "src/gpu/geometry/GrStyledShape.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
25#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
26#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
27#include "src/gpu/glsl/GrGLSLUniformHandler.h"
28#include "src/gpu/glsl/GrGLSLVarying.h"
29#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
30#include "src/gpu/ops/GrAAConvexPathRenderer.h"
31#include "src/gpu/ops/GrMeshDrawOp.h"
Robert Phillips55f681f2020-02-28 08:58:15 -050032#include "src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000033
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000034GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
35}
36
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000037struct Segment {
38 enum {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000039 // These enum values are assumed in member functions below.
40 kLine = 0,
41 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000042 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000043
bsalomon@google.com9aed1142012-01-30 14:28:39 +000044 // line uses one pt, quad uses 2 pts
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000045 SkPoint fPts[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000046 // normal to edge ending at each pt
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000047 SkVector fNorms[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000048 // is the corner where the previous segment meets this segment
49 // sharp. If so, fMid is a normalized bisector facing outward.
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000050 SkVector fMid;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000051
52 int countPoints() {
Brian Salomon4dea72a2019-12-18 10:43:10 -050053 static_assert(0 == kLine && 1 == kQuad);
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000054 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000055 }
56 const SkPoint& endPt() const {
Brian Salomon4dea72a2019-12-18 10:43:10 -050057 static_assert(0 == kLine && 1 == kQuad);
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000058 return fPts[fType];
Mike Kleinfc6c37b2016-09-27 09:34:10 -040059 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000060 const SkPoint& endNorm() const {
Brian Salomon4dea72a2019-12-18 10:43:10 -050061 static_assert(0 == kLine && 1 == kQuad);
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000062 return fNorms[fType];
Mike Kleinfc6c37b2016-09-27 09:34:10 -040063 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000064};
65
66typedef SkTArray<Segment, true> SegmentArray;
67
Greg Daniel8b09b962018-04-03 14:53:45 -040068static bool center_of_mass(const SegmentArray& segments, SkPoint* c) {
bsalomon@google.com81712882012-11-01 17:12:34 +000069 SkScalar area = 0;
vandebo@chromium.org6390c722012-03-28 21:03:22 +000070 SkPoint center = {0, 0};
bsalomon@google.com9aed1142012-01-30 14:28:39 +000071 int count = segments.count();
vandebo@chromium.org6390c722012-03-28 21:03:22 +000072 SkPoint p0 = {0, 0};
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000073 if (count > 2) {
74 // We translate the polygon so that the first point is at the origin.
75 // This avoids some precision issues with small area polygons far away
76 // from the origin.
77 p0 = segments[0].endPt();
78 SkPoint pi;
79 SkPoint pj;
bsalomon@google.coma51ab842012-07-10 19:53:34 +000080 // the first and last iteration of the below loop would compute
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000081 // zeros since the starting / ending point is (0,0). So instead we start
82 // at i=1 and make the last iteration i=count-2.
83 pj = segments[1].endPt() - p0;
84 for (int i = 1; i < count - 1; ++i) {
85 pi = pj;
robertphillips44c31282015-09-03 12:58:48 -070086 pj = segments[i + 1].endPt() - p0;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000087
robertphillips44c31282015-09-03 12:58:48 -070088 SkScalar t = SkPoint::CrossProduct(pi, pj);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000089 area += t;
90 center.fX += (pi.fX + pj.fX) * t;
91 center.fY += (pi.fY + pj.fY) * t;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000092 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000093 }
robertphillips44c31282015-09-03 12:58:48 -070094
bsalomon@google.com278dc692012-02-15 16:52:51 +000095 // If the poly has no area then we instead return the average of
96 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000097 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +000098 SkPoint avg;
99 avg.set(0, 0);
100 for (int i = 0; i < count; ++i) {
101 const SkPoint& pt = segments[i].endPt();
102 avg.fX += pt.fX;
103 avg.fY += pt.fY;
104 }
105 SkScalar denom = SK_Scalar1 / count;
106 avg.scale(denom);
107 *c = avg;
108 } else {
109 area *= 3;
reed80ea19c2015-05-12 10:37:34 -0700110 area = SkScalarInvert(area);
robertphillips44c31282015-09-03 12:58:48 -0700111 center.scale(area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +0000112 // undo the translate of p0 to the origin.
113 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000114 }
Greg Daniel62473ad2018-04-03 15:44:18 -0400115 return !SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY) && c->isFinite();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000116}
117
Greg Daniel8b09b962018-04-03 14:53:45 -0400118static bool compute_vectors(SegmentArray* segments,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000119 SkPoint* fanPt,
Mike Reed3872c982020-08-29 17:46:51 -0400120 SkPathFirstDirection dir,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000121 int* vCount,
122 int* iCount) {
Greg Daniel8b09b962018-04-03 14:53:45 -0400123 if (!center_of_mass(*segments, fanPt)) {
124 return false;
125 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000126 int count = segments->count();
127
bsalomon@google.com278dc692012-02-15 16:52:51 +0000128 // Make the normals point towards the outside
Cary Clarkdf429f32017-11-08 11:44:31 -0500129 SkPointPriv::Side normSide;
Mike Reed3872c982020-08-29 17:46:51 -0400130 if (dir == SkPathFirstDirection::kCCW) {
Cary Clarkdf429f32017-11-08 11:44:31 -0500131 normSide = SkPointPriv::kRight_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000132 } else {
Cary Clarkdf429f32017-11-08 11:44:31 -0500133 normSide = SkPointPriv::kLeft_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000134 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000135
Greg Danield5b45932018-06-07 13:15:10 -0400136 int64_t vCount64 = 0;
137 int64_t iCount64 = 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000138 // compute normals at all points
139 for (int a = 0; a < count; ++a) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000140 Segment& sega = (*segments)[a];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000141 int b = (a + 1) % count;
142 Segment& segb = (*segments)[b];
143
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000144 const SkPoint* prevPt = &sega.endPt();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000145 int n = segb.countPoints();
146 for (int p = 0; p < n; ++p) {
147 segb.fNorms[p] = segb.fPts[p] - *prevPt;
148 segb.fNorms[p].normalize();
Brian Salomon0235c642018-08-31 12:04:18 -0400149 segb.fNorms[p] = SkPointPriv::MakeOrthog(segb.fNorms[p], normSide);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000150 prevPt = &segb.fPts[p];
151 }
152 if (Segment::kLine == segb.fType) {
Greg Danield5b45932018-06-07 13:15:10 -0400153 vCount64 += 5;
154 iCount64 += 9;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000155 } else {
Greg Danield5b45932018-06-07 13:15:10 -0400156 vCount64 += 6;
157 iCount64 += 12;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000158 }
159 }
160
161 // compute mid-vectors where segments meet. TODO: Detect shallow corners
162 // and leave out the wedges and close gaps by stitching segments together.
163 for (int a = 0; a < count; ++a) {
164 const Segment& sega = (*segments)[a];
165 int b = (a + 1) % count;
166 Segment& segb = (*segments)[b];
167 segb.fMid = segb.fNorms[0] + sega.endNorm();
168 segb.fMid.normalize();
169 // corner wedges
Greg Danield5b45932018-06-07 13:15:10 -0400170 vCount64 += 4;
171 iCount64 += 6;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000172 }
Greg Danield5b45932018-06-07 13:15:10 -0400173 if (vCount64 > SK_MaxS32 || iCount64 > SK_MaxS32) {
174 return false;
175 }
176 *vCount = vCount64;
177 *iCount = iCount64;
Greg Daniel8b09b962018-04-03 14:53:45 -0400178 return true;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000179}
180
bsalomon@google.com9732f622012-01-31 15:19:21 +0000181struct DegenerateTestData {
182 DegenerateTestData() { fStage = kInitial; }
183 bool isDegenerate() const { return kNonDegenerate != fStage; }
184 enum {
185 kInitial,
186 kPoint,
187 kLine,
188 kNonDegenerate
189 } fStage;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000190 SkPoint fFirstPoint;
191 SkVector fLineNormal;
bsalomon@google.com81712882012-11-01 17:12:34 +0000192 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000193};
194
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000195static const SkScalar kClose = (SK_Scalar1 / 16);
Mike Reed8be952a2017-02-13 20:44:33 -0500196static const SkScalar kCloseSqd = kClose * kClose;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000197
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000198static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000199 switch (data->fStage) {
200 case DegenerateTestData::kInitial:
201 data->fFirstPoint = pt;
202 data->fStage = DegenerateTestData::kPoint;
203 break;
204 case DegenerateTestData::kPoint:
Cary Clarkdf429f32017-11-08 11:44:31 -0500205 if (SkPointPriv::DistanceToSqd(pt, data->fFirstPoint) > kCloseSqd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000206 data->fLineNormal = pt - data->fFirstPoint;
207 data->fLineNormal.normalize();
Brian Salomon0235c642018-08-31 12:04:18 -0400208 data->fLineNormal = SkPointPriv::MakeOrthog(data->fLineNormal);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000209 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
210 data->fStage = DegenerateTestData::kLine;
211 }
212 break;
213 case DegenerateTestData::kLine:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000214 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000215 data->fStage = DegenerateTestData::kNonDegenerate;
216 }
John Stiles30212b72020-06-11 17:55:07 -0400217 break;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000218 case DegenerateTestData::kNonDegenerate:
219 break;
220 default:
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400221 SK_ABORT("Unexpected degenerate test stage.");
bsalomon@google.com9732f622012-01-31 15:19:21 +0000222 }
223}
224
Brian Osman2caecd22019-09-25 14:02:26 -0400225static inline bool get_direction(const SkPath& path, const SkMatrix& m,
Mike Reed3872c982020-08-29 17:46:51 -0400226 SkPathFirstDirection* dir) {
Michael Ludwig0a1e9ef2019-08-30 10:03:15 -0400227 // At this point, we've already returned true from canDraw(), which checked that the path's
228 // direction could be determined, so this should just be fetching the cached direction.
Brian Osman2caecd22019-09-25 14:02:26 -0400229 // However, if perspective is involved, we're operating on a transformed path, which may no
230 // longer have a computable direction.
Mike Reed85f51b22020-08-30 10:32:06 -0400231 *dir = SkPathPriv::ComputeFirstDirection(path);
232 if (*dir == SkPathFirstDirection::kUnknown) {
Brian Osman2caecd22019-09-25 14:02:26 -0400233 return false;
234 }
Michael Ludwig0a1e9ef2019-08-30 10:03:15 -0400235
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000236 // check whether m reverses the orientation
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000237 SkASSERT(!m.hasPerspective());
Mike Reed8be952a2017-02-13 20:44:33 -0500238 SkScalar det2x2 = m.get(SkMatrix::kMScaleX) * m.get(SkMatrix::kMScaleY) -
239 m.get(SkMatrix::kMSkewX) * m.get(SkMatrix::kMSkewY);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000240 if (det2x2 < 0) {
Brian Osman2caecd22019-09-25 14:02:26 -0400241 *dir = SkPathPriv::OppositeFirstDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000242 }
Michael Ludwig0a1e9ef2019-08-30 10:03:15 -0400243
Brian Osman2caecd22019-09-25 14:02:26 -0400244 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000245}
246
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000247static inline void add_line_to_segment(const SkPoint& pt,
joshualitt27f398f2015-02-05 14:39:01 -0800248 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000249 segments->push_back();
250 segments->back().fType = Segment::kLine;
251 segments->back().fPts[0] = pt;
252}
253
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000254static inline void add_quad_segment(const SkPoint pts[3],
joshualitt27f398f2015-02-05 14:39:01 -0800255 SegmentArray* segments) {
Brian Salomon5f8a62b2019-03-29 13:55:32 -0400256 if (SkPointPriv::DistanceToLineSegmentBetweenSqd(pts[1], pts[0], pts[2]) < kCloseSqd) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000257 if (pts[0] != pts[2]) {
joshualitt27f398f2015-02-05 14:39:01 -0800258 add_line_to_segment(pts[2], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000259 }
260 } else {
261 segments->push_back();
262 segments->back().fType = Segment::kQuad;
263 segments->back().fPts[0] = pts[1];
264 segments->back().fPts[1] = pts[2];
265 }
266}
267
268static inline void add_cubic_segments(const SkPoint pts[4],
Mike Reed3872c982020-08-29 17:46:51 -0400269 SkPathFirstDirection dir,
joshualitt27f398f2015-02-05 14:39:01 -0800270 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000271 SkSTArray<15, SkPoint, true> quads;
bsalomon18fab302016-02-16 08:00:05 -0800272 GrPathUtils::convertCubicToQuadsConstrainToTangents(pts, SK_Scalar1, dir, &quads);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000273 int count = quads.count();
274 for (int q = 0; q < count; q += 3) {
joshualitt27f398f2015-02-05 14:39:01 -0800275 add_quad_segment(&quads[q], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000276 }
277}
278
279static bool get_segments(const SkPath& path,
280 const SkMatrix& m,
281 SegmentArray* segments,
282 SkPoint* fanPt,
283 int* vCount,
joshualitt27f398f2015-02-05 14:39:01 -0800284 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000285 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000286 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000287 // to the path from the sample to compute coverage. Every pixel intersected
288 // 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 +0000289 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000290 // thus should be very light. This is particularly egregious for degenerate
291 // line paths. We detect paths that are very close to a line (zero area) and
292 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000293 DegenerateTestData degenerateData;
Mike Reed3872c982020-08-29 17:46:51 -0400294 SkPathFirstDirection dir;
Brian Osman2caecd22019-09-25 14:02:26 -0400295 if (!get_direction(path, m, &dir)) {
296 return false;
297 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000298
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000299 for (;;) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000300 SkPoint pts[4];
Mike Reedba7e9a62019-08-16 13:30:34 -0400301 SkPath::Verb verb = iter.next(pts);
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000302 switch (verb) {
303 case SkPath::kMove_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000304 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000305 update_degenerate_test(&degenerateData, pts[0]);
306 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000307 case SkPath::kLine_Verb: {
Mike Reedba7e9a62019-08-16 13:30:34 -0400308 if (!SkPathPriv::AllPointsEq(pts, 2)) {
309 m.mapPoints(&pts[1], 1);
310 update_degenerate_test(&degenerateData, pts[1]);
311 add_line_to_segment(pts[1], segments);
312 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000313 break;
314 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000315 case SkPath::kQuad_Verb:
Mike Reedba7e9a62019-08-16 13:30:34 -0400316 if (!SkPathPriv::AllPointsEq(pts, 3)) {
317 m.mapPoints(pts, 3);
318 update_degenerate_test(&degenerateData, pts[1]);
319 update_degenerate_test(&degenerateData, pts[2]);
320 add_quad_segment(pts, segments);
321 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000322 break;
egdanielaf18a092015-01-05 10:22:28 -0800323 case SkPath::kConic_Verb: {
Mike Reedba7e9a62019-08-16 13:30:34 -0400324 if (!SkPathPriv::AllPointsEq(pts, 3)) {
325 m.mapPoints(pts, 3);
326 SkScalar weight = iter.conicWeight();
327 SkAutoConicToQuads converter;
328 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
329 for (int i = 0; i < converter.countQuads(); ++i) {
330 update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
331 update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
332 add_quad_segment(quadPts + 2*i, segments);
333 }
egdanielaf18a092015-01-05 10:22:28 -0800334 }
335 break;
336 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000337 case SkPath::kCubic_Verb: {
Mike Reedba7e9a62019-08-16 13:30:34 -0400338 if (!SkPathPriv::AllPointsEq(pts, 4)) {
339 m.mapPoints(pts, 4);
340 update_degenerate_test(&degenerateData, pts[1]);
341 update_degenerate_test(&degenerateData, pts[2]);
342 update_degenerate_test(&degenerateData, pts[3]);
343 add_cubic_segments(pts, dir, segments);
344 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000345 break;
Brian Salomon23356442018-11-30 15:33:19 -0500346 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000347 case SkPath::kDone_Verb:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000348 if (degenerateData.isDegenerate()) {
349 return false;
350 } else {
Greg Daniel8b09b962018-04-03 14:53:45 -0400351 return compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000352 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000353 default:
354 break;
355 }
356 }
357}
358
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000359struct Draw {
360 Draw() : fVertexCnt(0), fIndexCnt(0) {}
361 int fVertexCnt;
362 int fIndexCnt;
363};
364
365typedef SkTArray<Draw, true> DrawArray;
366
Brian Salomon60fb0b22017-06-15 17:09:36 -0400367static void create_vertices(const SegmentArray& segments,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000368 const SkPoint& fanPt,
Brian Osman7f4735b2019-01-02 13:55:10 +0000369 const GrVertexColor& color,
Brian Salomon60fb0b22017-06-15 17:09:36 -0400370 DrawArray* draws,
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500371 GrVertexWriter& verts,
372 uint16_t* idxs,
373 size_t vertexStride) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000374 Draw* draw = &draws->push_back();
375 // alias just to make vert/index assignments easier to read.
376 int* v = &draw->fVertexCnt;
377 int* i = &draw->fIndexCnt;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000378
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000379 int count = segments.count();
380 for (int a = 0; a < count; ++a) {
381 const Segment& sega = segments[a];
382 int b = (a + 1) % count;
383 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000384
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000385 // Check whether adding the verts for this segment to the current draw would cause index
386 // values to overflow.
387 int vCount = 4;
388 if (Segment::kLine == segb.fType) {
389 vCount += 5;
390 } else {
391 vCount += 6;
392 }
393 if (draw->fVertexCnt + vCount > (1 << 16)) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000394 idxs += *i;
395 draw = &draws->push_back();
396 v = &draw->fVertexCnt;
397 i = &draw->fIndexCnt;
398 }
399
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500400 const SkScalar negOneDists[2] = { -SK_Scalar1, -SK_Scalar1 };
401
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000402 // FIXME: These tris are inset in the 1 unit arc around the corner
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500403 SkPoint p0 = sega.endPt();
404 // Position, Color, UV, D0, D1
405 verts.write(p0, color, SkPoint{0, 0}, negOneDists);
406 verts.write(p0 + sega.endNorm(), color, SkPoint{0, -SK_Scalar1}, negOneDists);
407 verts.write(p0 + segb.fMid, color, SkPoint{0, -SK_Scalar1}, negOneDists);
408 verts.write(p0 + segb.fNorms[0], color, SkPoint{0, -SK_Scalar1}, negOneDists);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000409
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000410 idxs[*i + 0] = *v + 0;
411 idxs[*i + 1] = *v + 2;
412 idxs[*i + 2] = *v + 1;
413 idxs[*i + 3] = *v + 0;
414 idxs[*i + 4] = *v + 3;
415 idxs[*i + 5] = *v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000416
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000417 *v += 4;
418 *i += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000419
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000420 if (Segment::kLine == segb.fType) {
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000421 // we draw the line edge as a degenerate quad (u is 0, v is the
422 // signed distance to the edge)
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500423 SkPoint v1Pos = sega.endPt();
424 SkPoint v2Pos = segb.fPts[0];
425 SkScalar dist = SkPointPriv::DistanceToLineBetween(fanPt, v1Pos, v2Pos);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000426
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500427 verts.write(fanPt, color, SkPoint{0, dist}, negOneDists);
428 verts.write(v1Pos, color, SkPoint{0, 0}, negOneDists);
429 verts.write(v2Pos, color, SkPoint{0, 0}, negOneDists);
430 verts.write(v1Pos + segb.fNorms[0], color, SkPoint{0, -SK_Scalar1}, negOneDists);
431 verts.write(v2Pos + segb.fNorms[0], color, SkPoint{0, -SK_Scalar1}, negOneDists);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000432
cdalton35964822015-04-29 10:14:03 -0700433 idxs[*i + 0] = *v + 3;
434 idxs[*i + 1] = *v + 1;
435 idxs[*i + 2] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000436
cdalton35964822015-04-29 10:14:03 -0700437 idxs[*i + 3] = *v + 4;
438 idxs[*i + 4] = *v + 3;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000439 idxs[*i + 5] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000440
cdalton35964822015-04-29 10:14:03 -0700441 *i += 6;
442
443 // Draw the interior fan if it exists.
444 // TODO: Detect and combine colinear segments. This will ensure we catch every case
445 // with no interior, and that the resulting shared edge uses the same endpoints.
446 if (count >= 3) {
447 idxs[*i + 0] = *v + 0;
448 idxs[*i + 1] = *v + 2;
449 idxs[*i + 2] = *v + 1;
450
451 *i += 3;
452 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000453
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000454 *v += 5;
bsalomon@google.com06809612012-01-21 15:03:39 +0000455 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000456 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000457
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500458 SkScalar c0 = segb.fNorms[0].dot(qpts[0]);
459 SkScalar c1 = segb.fNorms[1].dot(qpts[2]);
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500460
Greg Daniel0a0ad5b2021-01-29 22:49:30 -0500461 // We must transform the positions into UV in cpu memory and then copy them to the gpu
462 // buffer. If we write the position first into the gpu buffer then calculate the UVs, it
463 // will cause us to read from the GPU buffer which can be very slow.
464 struct PosAndUV {
465 SkPoint fPos;
466 SkPoint fUV;
467 };
468 PosAndUV posAndUVPoints[6];
469 posAndUVPoints[0].fPos = fanPt;
470 posAndUVPoints[1].fPos = qpts[0];
471 posAndUVPoints[2].fPos = qpts[2];
472 posAndUVPoints[3].fPos = qpts[0] + segb.fNorms[0];
473 posAndUVPoints[4].fPos = qpts[2] + segb.fNorms[1];
474 SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
475 midVec.normalize();
476 posAndUVPoints[5].fPos = qpts[1] + midVec;
477
478 GrPathUtils::QuadUVMatrix toUV(qpts);
479 toUV.apply(posAndUVPoints, 6, sizeof(PosAndUV), sizeof(SkPoint));
480
481 verts.write(posAndUVPoints[0].fPos, color, posAndUVPoints[0].fUV,
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500482 -segb.fNorms[0].dot(fanPt) + c0,
483 -segb.fNorms[1].dot(fanPt) + c1);
484
Greg Daniel0a0ad5b2021-01-29 22:49:30 -0500485 verts.write(posAndUVPoints[1].fPos, color, posAndUVPoints[1].fUV,
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500486 0.0f,
487 -segb.fNorms[1].dot(qpts[0]) + c1);
488
Greg Daniel0a0ad5b2021-01-29 22:49:30 -0500489 verts.write(posAndUVPoints[2].fPos, color, posAndUVPoints[2].fUV,
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500490 -segb.fNorms[0].dot(qpts[2]) + c0,
491 0.0f);
Greg Daniel1204c4b2020-04-22 14:10:12 -0400492 // We need a negative value that is very large that it won't effect results if it is
493 // interpolated with. However, the value can't be too large of a negative that it
494 // effects numerical precision on less powerful GPUs.
495 static const SkScalar kStableLargeNegativeValue = -SK_ScalarMax/1000000;
Greg Daniel0a0ad5b2021-01-29 22:49:30 -0500496 verts.write(posAndUVPoints[3].fPos, color, posAndUVPoints[3].fUV,
Greg Daniel1204c4b2020-04-22 14:10:12 -0400497 kStableLargeNegativeValue,
498 kStableLargeNegativeValue);
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500499
Greg Daniel0a0ad5b2021-01-29 22:49:30 -0500500 verts.write(posAndUVPoints[4].fPos, color, posAndUVPoints[4].fUV,
Greg Daniel1204c4b2020-04-22 14:10:12 -0400501 kStableLargeNegativeValue,
502 kStableLargeNegativeValue);
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500503
Greg Daniel0a0ad5b2021-01-29 22:49:30 -0500504 verts.write(posAndUVPoints[5].fPos, color, posAndUVPoints[5].fUV,
Greg Daniel1204c4b2020-04-22 14:10:12 -0400505 kStableLargeNegativeValue,
506 kStableLargeNegativeValue);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000507
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000508 idxs[*i + 0] = *v + 3;
509 idxs[*i + 1] = *v + 1;
510 idxs[*i + 2] = *v + 2;
511 idxs[*i + 3] = *v + 4;
512 idxs[*i + 4] = *v + 3;
513 idxs[*i + 5] = *v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000514
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000515 idxs[*i + 6] = *v + 5;
516 idxs[*i + 7] = *v + 3;
517 idxs[*i + 8] = *v + 4;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000518
cdalton35964822015-04-29 10:14:03 -0700519 *i += 9;
520
521 // Draw the interior fan if it exists.
522 // TODO: Detect and combine colinear segments. This will ensure we catch every case
523 // with no interior, and that the resulting shared edge uses the same endpoints.
524 if (count >= 3) {
525 idxs[*i + 0] = *v + 0;
526 idxs[*i + 1] = *v + 2;
527 idxs[*i + 2] = *v + 1;
528
529 *i += 3;
530 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000531
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000532 *v += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000533 }
534 }
535}
536
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000537///////////////////////////////////////////////////////////////////////////////
538
539/*
540 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
541 * two components of the vertex attribute. Coverage is based on signed
542 * distance with negative being inside, positive outside. The edge is specified in
543 * window space (y-down). If either the third or fourth component of the interpolated
544 * 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 +0000545 * attempt to trim to a portion of the infinite quad.
546 * Requires shader derivative instruction support.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000547 */
548
joshualitt249af152014-09-15 11:41:13 -0700549class QuadEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000550public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500551 static GrGeometryProcessor* Make(SkArenaAlloc* arena,
552 const SkMatrix& localMatrix,
553 bool usesLocalCoords,
554 bool wideColor) {
Mike Kleinf1241082020-12-14 15:59:09 -0600555 return arena->make([&](void* ptr) {
556 return new (ptr) QuadEdgeEffect(localMatrix, usesLocalCoords, wideColor);
557 });
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000558 }
559
Brian Salomond3b65972017-03-22 12:05:03 -0400560 ~QuadEdgeEffect() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000561
mtklein36352bf2015-03-25 18:17:31 -0700562 const char* name() const override { return "QuadEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000563
egdaniel57d3b032015-11-13 11:57:27 -0800564 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000565 public:
Brian Salomon60fb0b22017-06-15 17:09:36 -0400566 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000567
mtklein36352bf2015-03-25 18:17:31 -0700568 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400569 const QuadEdgeEffect& qe = args.fGeomProc.cast<QuadEdgeEffect>();
egdaniel4ca2e602015-11-18 08:01:26 -0800570 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
John Stiles4d7ac492021-03-09 20:16:43 -0500571 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800572 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800573 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -0800574
joshualittabb52a12015-01-13 15:02:10 -0800575 // emit attributes
egdaniel0eafe792015-11-20 14:01:22 -0800576 varyingHandler->emitAttributes(qe);
joshualittabb52a12015-01-13 15:02:10 -0800577
Jim Van Verth201bcee2020-12-09 13:32:38 -0500578 // GL on iOS 14 needs more precision for the quadedge attributes
Jim Van Verth4b6f54a2020-12-11 11:01:10 -0500579 // We might as well enable it everywhere
Jim Van Verth201bcee2020-12-09 13:32:38 -0500580 GrGLSLVarying v(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800581 varyingHandler->addVarying("QuadEdge", &v);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500582 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.fInQuadEdge.name());
Brian Salomon60fb0b22017-06-15 17:09:36 -0400583
584 // Setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500585 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500586 varyingHandler->addPassThroughAttribute(qe.fInColor, args.fOutputColor);
joshualitt2dd1ae02014-12-03 06:24:10 -0800587
joshualittabb52a12015-01-13 15:02:10 -0800588 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400589 WriteOutputPosition(vertBuilder, gpArgs, qe.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400590 if (qe.fUsesLocalCoords) {
Brian Salomon5a328282021-04-14 10:32:25 -0400591 WriteLocalCoord(vertBuilder,
592 uniformHandler,
593 *args.fShaderCaps,
594 gpArgs,
595 qe.fInPosition.asShaderVar(),
596 qe.fLocalMatrix,
597 &fLocalMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400598 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000599
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400600 fragBuilder->codeAppendf("half edgeAlpha;");
joshualitt30ba4362014-08-21 20:18:45 -0700601
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000602 // keep the derivative instructions outside the conditional
Ethan Nicholase1f55022019-02-05 17:17:40 -0500603 fragBuilder->codeAppendf("half2 duvdx = half2(dFdx(%s.xy));", v.fsIn());
604 fragBuilder->codeAppendf("half2 duvdy = half2(dFdy(%s.xy));", v.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800605 fragBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000606 // today we know z and w are in device space. We could use derivatives
Jim Van Verth201bcee2020-12-09 13:32:38 -0500607 fragBuilder->codeAppendf("edgeAlpha = half(min(min(%s.z, %s.w) + 0.5, 1.0));", v.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800608 v.fsIn());
609 fragBuilder->codeAppendf ("} else {");
Jim Van Verth201bcee2020-12-09 13:32:38 -0500610 fragBuilder->codeAppendf("half2 gF = half2(half(2.0*%s.x*duvdx.x - duvdx.y),"
611 " half(2.0*%s.x*duvdy.x - duvdy.y));",
egdaniel4ca2e602015-11-18 08:01:26 -0800612 v.fsIn(), v.fsIn());
Jim Van Verth201bcee2020-12-09 13:32:38 -0500613 fragBuilder->codeAppendf("edgeAlpha = half(%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800614 v.fsIn());
615 fragBuilder->codeAppendf("edgeAlpha = "
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400616 "saturate(0.5 - edgeAlpha / length(gF));}");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000617
John Stiles4d7ac492021-03-09 20:16:43 -0500618 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000619 }
620
joshualitt9b989322014-12-15 14:16:27 -0800621 static inline void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400622 const GrShaderCaps& shaderCaps,
joshualitt9b989322014-12-15 14:16:27 -0800623 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700624 const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500625 b->addBool(qee.fUsesLocalCoords, "usesLocalCoords");
Brian Salomon5a328282021-04-14 10:32:25 -0400626 b->addBits(kMatrixKeyBits,
627 ComputeMatrixKey(shaderCaps, qee.fLocalMatrix),
628 "localMatrixType");
joshualitt9b989322014-12-15 14:16:27 -0800629 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000630
egdaniel018fb622015-10-28 07:26:40 -0700631 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400632 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400633 const GrGeometryProcessor& geomProc) override {
634 const QuadEdgeEffect& qe = geomProc.cast<QuadEdgeEffect>();
Brian Salomon5a328282021-04-14 10:32:25 -0400635 SetTransform(pdman, shaderCaps, fLocalMatrixUniform, qe.fLocalMatrix, &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700636 }
637
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000638 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400639 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400640
641 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
642 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000643 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000644
Brian Salomon94efbf52016-11-29 13:43:05 -0500645 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800646 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800647 }
648
Robert Phillipsf10535f2021-03-23 09:30:45 -0400649 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800650 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800651 }
652
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000653private:
Brian Osman7f4735b2019-01-02 13:55:10 +0000654 QuadEdgeEffect(const SkMatrix& localMatrix, bool usesLocalCoords, bool wideColor)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400655 : INHERITED(kQuadEdgeEffect_ClassID)
656 , fLocalMatrix(localMatrix)
657 , fUsesLocalCoords(usesLocalCoords) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500658 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osman7f4735b2019-01-02 13:55:10 +0000659 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth201bcee2020-12-09 13:32:38 -0500660 // GL on iOS 14 needs more precision for the quadedge attributes
661 fInQuadEdge = {"inQuadEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500662 this->setVertexAttributes(&fInPosition, 3);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000663 }
664
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500665 Attribute fInPosition;
666 Attribute fInColor;
667 Attribute fInQuadEdge;
668
Brian Salomon92be2f72018-06-19 14:33:47 -0400669 SkMatrix fLocalMatrix;
670 bool fUsesLocalCoords;
joshualitt249af152014-09-15 11:41:13 -0700671
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400672 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000673
John Stiles7571f9e2020-09-02 22:42:33 -0400674 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000675};
676
joshualittb0a8a372014-09-23 09:50:21 -0700677GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000678
Hal Canary6f6961e2017-01-31 13:50:44 -0500679#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500680GrGeometryProcessor* QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400681 SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
682 bool usesLocalCoords = d->fRandom->nextBool();
683 bool wideColor = d->fRandom->nextBool();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000684 // Doesn't work without derivative instructions.
Robert Phillips296b1cc2017-03-15 10:42:12 -0400685 return d->caps()->shaderCaps()->shaderDerivativeSupport()
John Stiles391a9f62021-04-29 23:17:41 -0400686 ? QuadEdgeEffect::Make(d->allocator(), localMatrix, usesLocalCoords, wideColor)
Brian Salomon9ae32a22017-01-25 14:58:24 -0500687 : nullptr;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000688}
Hal Canary6f6961e2017-01-31 13:50:44 -0500689#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000690
691///////////////////////////////////////////////////////////////////////////////
692
Chris Dalton5ed44232017-09-07 13:22:46 -0600693GrPathRenderer::CanDrawPath
694GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
Michael Ludwig0a1e9ef2019-08-30 10:03:15 -0400695 // This check requires convexity and known direction, since the direction is used to build
696 // the geometry segments. Degenerate convex paths will fall through to some other path renderer.
Chris Dalton5ed44232017-09-07 13:22:46 -0600697 if (args.fCaps->shaderCaps()->shaderDerivativeSupport() &&
Chris Dalton6ce447a2019-06-23 18:07:38 -0600698 (GrAAType::kCoverage == args.fAAType) && args.fShape->style().isSimpleFill() &&
Michael Ludwig0a1e9ef2019-08-30 10:03:15 -0400699 !args.fShape->inverseFilled() && args.fShape->knownToBeConvex() &&
700 args.fShape->knownDirection()) {
Chris Dalton5ed44232017-09-07 13:22:46 -0600701 return CanDrawPath::kYes;
702 }
703 return CanDrawPath::kNo;
robertphillips@google.comfa662942012-05-17 12:20:22 +0000704}
705
Brian Salomon10978a62017-06-15 16:21:49 -0400706namespace {
707
708class AAConvexPathOp final : public GrMeshDrawOp {
709private:
710 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
711
joshualitt27f398f2015-02-05 14:39:01 -0800712public:
Brian Salomon25a88092016-12-01 09:36:50 -0500713 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400714
Herb Derbyc76d4092020-10-07 16:46:15 -0400715 static GrOp::Owner Make(GrRecordingContext* context,
716 GrPaint&& paint,
717 const SkMatrix& viewMatrix,
718 const SkPath& path,
719 const GrUserStencilSettings* stencilSettings) {
Robert Phillips7c525e62018-06-12 10:11:12 -0400720 return Helper::FactoryHelper<AAConvexPathOp>(context, std::move(paint), viewMatrix, path,
Brian Salomon10978a62017-06-15 16:21:49 -0400721 stencilSettings);
722 }
723
Herb Derbyc76d4092020-10-07 16:46:15 -0400724 AAConvexPathOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -0400725 const SkMatrix& viewMatrix, const SkPath& path,
726 const GrUserStencilSettings* stencilSettings)
Herb Derbyc76d4092020-10-07 16:46:15 -0400727 : INHERITED(ClassID()), fHelper(processorSet, GrAAType::kCoverage, stencilSettings) {
Brian Salomon60fb0b22017-06-15 17:09:36 -0400728 fPaths.emplace_back(PathData{viewMatrix, path, color});
Greg Daniel5faf4742019-10-01 15:14:44 -0400729 this->setTransformedBounds(path.getBounds(), viewMatrix, HasAABloat::kYes,
730 IsHairline::kNo);
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
Chris Dalton1706cbf2019-05-21 19:35:29 -0600735 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400736 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -0600737 fProgramInfo->visitFPProxies(func);
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400738 } else {
739 fHelper.visitProxies(func);
740 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400741 }
742
Brian Salomon10978a62017-06-15 16:21:49 -0400743 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
744
Chris Dalton57ab06c2021-04-22 12:57:28 -0600745 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
746 GrClampType clampType) override {
Chris Daltonb8fff0d2019-03-05 10:11:58 -0700747 return fHelper.finalizeProcessors(
Chris Dalton57ab06c2021-04-22 12:57:28 -0600748 caps, clip, clampType, GrProcessorAnalysisCoverage::kSingleChannel,
749 &fPaths.back().fColor, &fWideColor);
Brian Salomon10978a62017-06-15 16:21:49 -0400750 }
751
bsalomone46f9fe2015-08-18 06:05:14 -0700752private:
Robert Phillips2669a7b2020-03-12 12:07:19 -0400753 GrProgramInfo* programInfo() override { return fProgramInfo; }
754
Robert Phillips4133dc42020-03-11 15:55:55 -0400755 void onCreateProgramInfo(const GrCaps* caps,
756 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -0500757 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -0400758 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -0400759 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500760 GrXferBarrierFlags renderPassXferBarriers,
761 GrLoadOp colorLoadOp) override {
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400762 SkMatrix invert;
763 if (fHelper.usesLocalCoords() && !fPaths.back().fViewMatrix.invert(&invert)) {
Robert Phillips4133dc42020-03-11 15:55:55 -0400764 return;
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400765 }
766
767 GrGeometryProcessor* quadProcessor = QuadEdgeEffect::Make(arena, invert,
768 fHelper.usesLocalCoords(),
769 fWideColor);
770
Brian Salomon8afde5f2020-04-01 16:22:00 -0400771 fProgramInfo = fHelper.createProgramInfoWithStencil(caps, arena, writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -0400772 std::move(appliedClip),
773 dstProxyView, quadProcessor,
Greg Danield358cbe2020-09-11 09:33:54 -0400774 GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -0500775 renderPassXferBarriers, colorLoadOp);
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400776 }
777
Brian Salomon91326c32017-08-09 16:02:19 -0400778 void onPrepareDraws(Target* target) override {
Brian Salomond0a0a652016-12-15 15:25:22 -0500779 int instanceCount = fPaths.count();
joshualitt27f398f2015-02-05 14:39:01 -0800780
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400781 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -0400782 this->createProgramInfo(target);
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400783 if (!fProgramInfo) {
784 return;
785 }
joshualitt27f398f2015-02-05 14:39:01 -0800786 }
787
Robert Phillips787fd9d2021-03-22 14:48:09 -0400788 const size_t kVertexStride = fProgramInfo->geomProc().vertexStride();
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400789
790 fDraws.reserve(instanceCount);
joshualitt27f398f2015-02-05 14:39:01 -0800791
joshualitt27f398f2015-02-05 14:39:01 -0800792 // TODO generate all segments for all paths and use one vertex buffer
793 for (int i = 0; i < instanceCount; i++) {
Brian Salomond0a0a652016-12-15 15:25:22 -0500794 const PathData& args = fPaths[i];
joshualitt27f398f2015-02-05 14:39:01 -0800795
796 // We use the fact that SkPath::transform path does subdivision based on
797 // perspective. Otherwise, we apply the view matrix when copying to the
798 // segment representation.
799 const SkMatrix* viewMatrix = &args.fViewMatrix;
joshualitt144c3c82015-11-30 12:30:13 -0800800
801 // We avoid initializing the path unless we have to
802 const SkPath* pathPtr = &args.fPath;
803 SkTLazy<SkPath> tmpPath;
joshualitt27f398f2015-02-05 14:39:01 -0800804 if (viewMatrix->hasPerspective()) {
joshualitt144c3c82015-11-30 12:30:13 -0800805 SkPath* tmpPathPtr = tmpPath.init(*pathPtr);
806 tmpPathPtr->setIsVolatile(true);
807 tmpPathPtr->transform(*viewMatrix);
joshualitt27f398f2015-02-05 14:39:01 -0800808 viewMatrix = &SkMatrix::I();
joshualitt144c3c82015-11-30 12:30:13 -0800809 pathPtr = tmpPathPtr;
joshualitt27f398f2015-02-05 14:39:01 -0800810 }
811
812 int vertexCount;
813 int indexCount;
814 enum {
815 kPreallocSegmentCnt = 512 / sizeof(Segment),
816 kPreallocDrawCnt = 4,
817 };
818 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
819 SkPoint fanPt;
820
joshualitt144c3c82015-11-30 12:30:13 -0800821 if (!get_segments(*pathPtr, *viewMatrix, &segments, &fanPt, &vertexCount,
joshualitt27f398f2015-02-05 14:39:01 -0800822 &indexCount)) {
823 continue;
824 }
825
Brian Salomon12d22642019-01-29 14:38:50 -0500826 sk_sp<const GrBuffer> vertexBuffer;
Chris Daltonbca46e22017-05-15 11:03:26 -0600827 int firstVertex;
828
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500829 GrVertexWriter verts{target->makeVertexSpace(kVertexStride, vertexCount,
830 &vertexBuffer, &firstVertex)};
bsalomone64eb572015-05-07 11:35:55 -0700831
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500832 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -0800833 SkDebugf("Could not allocate vertices\n");
834 return;
835 }
836
Brian Salomon12d22642019-01-29 14:38:50 -0500837 sk_sp<const GrBuffer> indexBuffer;
Chris Daltonbca46e22017-05-15 11:03:26 -0600838 int firstIndex;
839
840 uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
robertphillipse40d3972015-05-07 09:51:43 -0700841 if (!idxs) {
joshualitt4b31de82015-03-05 14:33:41 -0800842 SkDebugf("Could not allocate indices\n");
843 return;
844 }
845
joshualitt27f398f2015-02-05 14:39:01 -0800846 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
Brian Osman7f4735b2019-01-02 13:55:10 +0000847 GrVertexColor color(args.fColor, fWideColor);
848 create_vertices(segments, fanPt, color, &draws, verts, idxs, kVertexStride);
joshualitt27f398f2015-02-05 14:39:01 -0800849
Chris Daltoneb694b72020-03-16 09:25:50 -0600850 GrSimpleMesh* meshes = target->allocMeshes(draws.count());
robertphillips44c31282015-09-03 12:58:48 -0700851 for (int j = 0; j < draws.count(); ++j) {
852 const Draw& draw = draws[j];
Brian Salomon7eae3e02018-08-07 14:02:38 +0000853 meshes[j].setIndexed(indexBuffer, draw.fIndexCnt, firstIndex, 0,
Chris Dalton37c7bdd2020-03-13 09:21:12 -0600854 draw.fVertexCnt - 1, GrPrimitiveRestart::kNo, vertexBuffer,
855 firstVertex);
Chris Daltonbca46e22017-05-15 11:03:26 -0600856 firstIndex += draw.fIndexCnt;
857 firstVertex += draw.fVertexCnt;
joshualitt27f398f2015-02-05 14:39:01 -0800858 }
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400859
860 fDraws.push_back({ meshes, draws.count() });
joshualitt27f398f2015-02-05 14:39:01 -0800861 }
862 }
863
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700864 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400865 if (!fProgramInfo || fDraws.isEmpty()) {
866 return;
867 }
Robert Phillips3968fcb2019-12-05 16:40:31 -0500868
Chris Dalton765ed362020-03-16 17:34:44 -0600869 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -0400870 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400871 for (int i = 0; i < fDraws.count(); ++i) {
Chris Dalton765ed362020-03-16 17:34:44 -0600872 for (int j = 0; j < fDraws[i].fMeshCount; ++j) {
873 flushState->drawMesh(fDraws[i].fMeshes[j]);
874 }
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400875 }
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700876 }
877
Herb Derbye25c3002020-10-27 15:57:27 -0400878 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomond0a0a652016-12-15 15:25:22 -0500879 AAConvexPathOp* that = t->cast<AAConvexPathOp>();
Brian Salomon10978a62017-06-15 16:21:49 -0400880 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000881 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -0700882 }
Brian Salomon10978a62017-06-15 16:21:49 -0400883 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -0500884 !SkMatrixPriv::CheapEqual(fPaths[0].fViewMatrix, that->fPaths[0].fViewMatrix)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000885 return CombineResult::kCannotCombine;
joshualitt27f398f2015-02-05 14:39:01 -0800886 }
887
Brian Salomond0a0a652016-12-15 15:25:22 -0500888 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
Brian Osman7f4735b2019-01-02 13:55:10 +0000889 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000890 return CombineResult::kMerged;
joshualitt27f398f2015-02-05 14:39:01 -0800891 }
892
John Stilesaf366522020-08-13 09:57:34 -0400893#if GR_TEST_UTILS
894 SkString onDumpInfo() const override {
895 return SkStringPrintf("Count: %d\n%s", fPaths.count(), fHelper.dumpInfo().c_str());
896 }
897#endif
898
Brian Salomond0a0a652016-12-15 15:25:22 -0500899 struct PathData {
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400900 SkMatrix fViewMatrix;
901 SkPath fPath;
Brian Osmancf860852018-10-31 14:04:39 -0400902 SkPMColor4f fColor;
bsalomonf1703092016-06-29 18:41:53 -0700903 };
904
Brian Salomon10978a62017-06-15 16:21:49 -0400905 Helper fHelper;
Brian Salomond0a0a652016-12-15 15:25:22 -0500906 SkSTArray<1, PathData, true> fPaths;
Brian Osman7f4735b2019-01-02 13:55:10 +0000907 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -0700908
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400909 struct MeshDraw {
Chris Daltoneb694b72020-03-16 09:25:50 -0600910 GrSimpleMesh* fMeshes;
911 int fMeshCount;
Robert Phillips3eabf4a2020-03-10 14:49:20 -0400912 };
913
914 SkTDArray<MeshDraw> fDraws;
915 GrProgramInfo* fProgramInfo = nullptr;
916
John Stiles7571f9e2020-09-02 22:42:33 -0400917 using INHERITED = GrMeshDrawOp;
joshualitt27f398f2015-02-05 14:39:01 -0800918};
919
Brian Salomon10978a62017-06-15 16:21:49 -0400920} // anonymous namespace
921
bsalomon0aff2fa2015-07-31 06:48:27 -0700922bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400923 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700924 "GrAAConvexPathRenderer::onDrawPath");
Chris Dalton6ce447a2019-06-23 18:07:38 -0600925 SkASSERT(args.fRenderTargetContext->numSamples() <= 1);
bsalomon8acedde2016-06-24 10:42:16 -0700926 SkASSERT(!args.fShape->isEmpty());
bsalomon@google.com4647f902013-03-26 14:45:27 +0000927
bsalomonf1703092016-06-29 18:41:53 -0700928 SkPath path;
929 args.fShape->asPath(&path);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000930
Herb Derbyc76d4092020-10-07 16:46:15 -0400931 GrOp::Owner op = AAConvexPathOp::Make(args.fContext, std::move(args.fPaint),
932 *args.fViewMatrix,
933 path, args.fUserStencilSettings);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400934 args.fRenderTargetContext->addDrawOp(args.fClip, std::move(op));
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000935 return true;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000936}
joshualitt8e5c1772015-05-11 08:58:52 -0700937
938///////////////////////////////////////////////////////////////////////////////////////////////////
939
Hal Canary6f6961e2017-01-31 13:50:44 -0500940#if GR_TEST_UTILS
joshualitt8e5c1772015-05-11 08:58:52 -0700941
Brian Salomon10978a62017-06-15 16:21:49 -0400942GR_DRAW_OP_TEST_DEFINE(AAConvexPathOp) {
bsalomonf1703092016-06-29 18:41:53 -0700943 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
John Stiles31954bf2020-08-07 17:35:54 -0400944 const SkPath& path = GrTest::TestPathConvex(random);
Brian Salomon10978a62017-06-15 16:21:49 -0400945 const GrUserStencilSettings* stencilSettings = GrGetRandomStencil(random, context);
Robert Phillips7c525e62018-06-12 10:11:12 -0400946 return AAConvexPathOp::Make(context, std::move(paint), viewMatrix, path, stencilSettings);
joshualitt8e5c1772015-05-11 08:58:52 -0700947}
948
949#endif