blob: 1e44b87e99f7dca99d0390ade504f43b18f7b696 [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"
bsalomoneb1cb5c2015-05-22 08:01:09 -07009#include "GrCaps.h"
Brian Salomon5ec9def2016-12-20 15:34:05 -050010#include "GrDrawOpTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080011#include "GrGeometryProcessor.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000012#include "GrPathUtils.h"
Brian Salomondad29232016-12-01 16:40:24 -050013#include "GrProcessor.h"
Brian Salomon653f42f2018-07-10 10:07:31 -040014#include "GrRenderTargetContext.h"
15#include "GrShape.h"
Brian Salomon10978a62017-06-15 16:21:49 -040016#include "GrSimpleMeshDrawOpHelper.h"
Brian Osmanf9aabff2018-11-13 16:11:38 -050017#include "GrVertexWriter.h"
egdanielaf18a092015-01-05 10:22:28 -080018#include "SkGeometry.h"
reed026beb52015-06-10 14:23:15 -070019#include "SkPathPriv.h"
Cary Clarkdf429f32017-11-08 11:44:31 -050020#include "SkPointPriv.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000021#include "SkString.h"
Greg Danield5b45932018-06-07 13:15:10 -040022#include "SkTypes.h"
egdaniel7ea439b2015-12-03 09:20:44 -080023#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080024#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070025#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080026#include "glsl/GrGLSLUniformHandler.h"
egdaniel0eafe792015-11-20 14:01:22 -080027#include "glsl/GrGLSLVarying.h"
Chris Daltonc17bf322017-10-24 10:59:03 -060028#include "glsl/GrGLSLVertexGeoBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050029#include "ops/GrMeshDrawOp.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000030
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000031GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
32}
33
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000034struct Segment {
35 enum {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000036 // These enum values are assumed in member functions below.
37 kLine = 0,
38 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000039 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000040
bsalomon@google.com9aed1142012-01-30 14:28:39 +000041 // line uses one pt, quad uses 2 pts
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000042 SkPoint fPts[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000043 // normal to edge ending at each pt
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000044 SkVector fNorms[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000045 // is the corner where the previous segment meets this segment
46 // sharp. If so, fMid is a normalized bisector facing outward.
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000047 SkVector fMid;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000048
49 int countPoints() {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000050 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
51 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000052 }
53 const SkPoint& endPt() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000054 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
55 return fPts[fType];
Mike Kleinfc6c37b2016-09-27 09:34:10 -040056 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000057 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000058 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
59 return fNorms[fType];
Mike Kleinfc6c37b2016-09-27 09:34:10 -040060 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000061};
62
63typedef SkTArray<Segment, true> SegmentArray;
64
Greg Daniel8b09b962018-04-03 14:53:45 -040065static bool center_of_mass(const SegmentArray& segments, SkPoint* c) {
bsalomon@google.com81712882012-11-01 17:12:34 +000066 SkScalar area = 0;
vandebo@chromium.org6390c722012-03-28 21:03:22 +000067 SkPoint center = {0, 0};
bsalomon@google.com9aed1142012-01-30 14:28:39 +000068 int count = segments.count();
vandebo@chromium.org6390c722012-03-28 21:03:22 +000069 SkPoint p0 = {0, 0};
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000070 if (count > 2) {
71 // We translate the polygon so that the first point is at the origin.
72 // This avoids some precision issues with small area polygons far away
73 // from the origin.
74 p0 = segments[0].endPt();
75 SkPoint pi;
76 SkPoint pj;
bsalomon@google.coma51ab842012-07-10 19:53:34 +000077 // the first and last iteration of the below loop would compute
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000078 // zeros since the starting / ending point is (0,0). So instead we start
79 // at i=1 and make the last iteration i=count-2.
80 pj = segments[1].endPt() - p0;
81 for (int i = 1; i < count - 1; ++i) {
82 pi = pj;
robertphillips44c31282015-09-03 12:58:48 -070083 pj = segments[i + 1].endPt() - p0;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000084
robertphillips44c31282015-09-03 12:58:48 -070085 SkScalar t = SkPoint::CrossProduct(pi, pj);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000086 area += t;
87 center.fX += (pi.fX + pj.fX) * t;
88 center.fY += (pi.fY + pj.fY) * t;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000089 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000090 }
robertphillips44c31282015-09-03 12:58:48 -070091
bsalomon@google.com278dc692012-02-15 16:52:51 +000092 // If the poly has no area then we instead return the average of
93 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000094 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +000095 SkPoint avg;
96 avg.set(0, 0);
97 for (int i = 0; i < count; ++i) {
98 const SkPoint& pt = segments[i].endPt();
99 avg.fX += pt.fX;
100 avg.fY += pt.fY;
101 }
102 SkScalar denom = SK_Scalar1 / count;
103 avg.scale(denom);
104 *c = avg;
105 } else {
106 area *= 3;
reed80ea19c2015-05-12 10:37:34 -0700107 area = SkScalarInvert(area);
robertphillips44c31282015-09-03 12:58:48 -0700108 center.scale(area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +0000109 // undo the translate of p0 to the origin.
110 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000111 }
Greg Daniel62473ad2018-04-03 15:44:18 -0400112 return !SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY) && c->isFinite();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000113}
114
Greg Daniel8b09b962018-04-03 14:53:45 -0400115static bool compute_vectors(SegmentArray* segments,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000116 SkPoint* fanPt,
reed026beb52015-06-10 14:23:15 -0700117 SkPathPriv::FirstDirection dir,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000118 int* vCount,
119 int* iCount) {
Greg Daniel8b09b962018-04-03 14:53:45 -0400120 if (!center_of_mass(*segments, fanPt)) {
121 return false;
122 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000123 int count = segments->count();
124
bsalomon@google.com278dc692012-02-15 16:52:51 +0000125 // Make the normals point towards the outside
Cary Clarkdf429f32017-11-08 11:44:31 -0500126 SkPointPriv::Side normSide;
reed026beb52015-06-10 14:23:15 -0700127 if (dir == SkPathPriv::kCCW_FirstDirection) {
Cary Clarkdf429f32017-11-08 11:44:31 -0500128 normSide = SkPointPriv::kRight_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000129 } else {
Cary Clarkdf429f32017-11-08 11:44:31 -0500130 normSide = SkPointPriv::kLeft_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000131 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000132
Greg Danield5b45932018-06-07 13:15:10 -0400133 int64_t vCount64 = 0;
134 int64_t iCount64 = 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000135 // compute normals at all points
136 for (int a = 0; a < count; ++a) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000137 Segment& sega = (*segments)[a];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000138 int b = (a + 1) % count;
139 Segment& segb = (*segments)[b];
140
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000141 const SkPoint* prevPt = &sega.endPt();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000142 int n = segb.countPoints();
143 for (int p = 0; p < n; ++p) {
144 segb.fNorms[p] = segb.fPts[p] - *prevPt;
145 segb.fNorms[p].normalize();
Brian Salomon0235c642018-08-31 12:04:18 -0400146 segb.fNorms[p] = SkPointPriv::MakeOrthog(segb.fNorms[p], normSide);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000147 prevPt = &segb.fPts[p];
148 }
149 if (Segment::kLine == segb.fType) {
Greg Danield5b45932018-06-07 13:15:10 -0400150 vCount64 += 5;
151 iCount64 += 9;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000152 } else {
Greg Danield5b45932018-06-07 13:15:10 -0400153 vCount64 += 6;
154 iCount64 += 12;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000155 }
156 }
157
158 // compute mid-vectors where segments meet. TODO: Detect shallow corners
159 // and leave out the wedges and close gaps by stitching segments together.
160 for (int a = 0; a < count; ++a) {
161 const Segment& sega = (*segments)[a];
162 int b = (a + 1) % count;
163 Segment& segb = (*segments)[b];
164 segb.fMid = segb.fNorms[0] + sega.endNorm();
165 segb.fMid.normalize();
166 // corner wedges
Greg Danield5b45932018-06-07 13:15:10 -0400167 vCount64 += 4;
168 iCount64 += 6;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000169 }
Greg Danield5b45932018-06-07 13:15:10 -0400170 if (vCount64 > SK_MaxS32 || iCount64 > SK_MaxS32) {
171 return false;
172 }
173 *vCount = vCount64;
174 *iCount = iCount64;
Greg Daniel8b09b962018-04-03 14:53:45 -0400175 return true;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000176}
177
bsalomon@google.com9732f622012-01-31 15:19:21 +0000178struct DegenerateTestData {
179 DegenerateTestData() { fStage = kInitial; }
180 bool isDegenerate() const { return kNonDegenerate != fStage; }
181 enum {
182 kInitial,
183 kPoint,
184 kLine,
185 kNonDegenerate
186 } fStage;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000187 SkPoint fFirstPoint;
188 SkVector fLineNormal;
bsalomon@google.com81712882012-11-01 17:12:34 +0000189 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000190};
191
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000192static const SkScalar kClose = (SK_Scalar1 / 16);
Mike Reed8be952a2017-02-13 20:44:33 -0500193static const SkScalar kCloseSqd = kClose * kClose;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000194
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000195static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000196 switch (data->fStage) {
197 case DegenerateTestData::kInitial:
198 data->fFirstPoint = pt;
199 data->fStage = DegenerateTestData::kPoint;
200 break;
201 case DegenerateTestData::kPoint:
Cary Clarkdf429f32017-11-08 11:44:31 -0500202 if (SkPointPriv::DistanceToSqd(pt, data->fFirstPoint) > kCloseSqd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000203 data->fLineNormal = pt - data->fFirstPoint;
204 data->fLineNormal.normalize();
Brian Salomon0235c642018-08-31 12:04:18 -0400205 data->fLineNormal = SkPointPriv::MakeOrthog(data->fLineNormal);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000206 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
207 data->fStage = DegenerateTestData::kLine;
208 }
209 break;
210 case DegenerateTestData::kLine:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000211 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000212 data->fStage = DegenerateTestData::kNonDegenerate;
213 }
214 case DegenerateTestData::kNonDegenerate:
215 break;
216 default:
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400217 SK_ABORT("Unexpected degenerate test stage.");
bsalomon@google.com9732f622012-01-31 15:19:21 +0000218 }
219}
220
reed026beb52015-06-10 14:23:15 -0700221static inline bool get_direction(const SkPath& path, const SkMatrix& m,
222 SkPathPriv::FirstDirection* dir) {
223 if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000224 return false;
225 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000226 // check whether m reverses the orientation
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000227 SkASSERT(!m.hasPerspective());
Mike Reed8be952a2017-02-13 20:44:33 -0500228 SkScalar det2x2 = m.get(SkMatrix::kMScaleX) * m.get(SkMatrix::kMScaleY) -
229 m.get(SkMatrix::kMSkewX) * m.get(SkMatrix::kMSkewY);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000230 if (det2x2 < 0) {
reed026beb52015-06-10 14:23:15 -0700231 *dir = SkPathPriv::OppositeFirstDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000232 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000233 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000234}
235
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000236static inline void add_line_to_segment(const SkPoint& pt,
joshualitt27f398f2015-02-05 14:39:01 -0800237 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000238 segments->push_back();
239 segments->back().fType = Segment::kLine;
240 segments->back().fPts[0] = pt;
241}
242
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000243static inline void add_quad_segment(const SkPoint pts[3],
joshualitt27f398f2015-02-05 14:39:01 -0800244 SegmentArray* segments) {
Cary Clarkdf429f32017-11-08 11:44:31 -0500245 if (SkPointPriv::DistanceToSqd(pts[0], pts[1]) < kCloseSqd ||
246 SkPointPriv::DistanceToSqd(pts[1], pts[2]) < kCloseSqd) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000247 if (pts[0] != pts[2]) {
joshualitt27f398f2015-02-05 14:39:01 -0800248 add_line_to_segment(pts[2], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000249 }
250 } else {
251 segments->push_back();
252 segments->back().fType = Segment::kQuad;
253 segments->back().fPts[0] = pts[1];
254 segments->back().fPts[1] = pts[2];
255 }
256}
257
258static inline void add_cubic_segments(const SkPoint pts[4],
reed026beb52015-06-10 14:23:15 -0700259 SkPathPriv::FirstDirection dir,
joshualitt27f398f2015-02-05 14:39:01 -0800260 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000261 SkSTArray<15, SkPoint, true> quads;
bsalomon18fab302016-02-16 08:00:05 -0800262 GrPathUtils::convertCubicToQuadsConstrainToTangents(pts, SK_Scalar1, dir, &quads);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000263 int count = quads.count();
264 for (int q = 0; q < count; q += 3) {
joshualitt27f398f2015-02-05 14:39:01 -0800265 add_quad_segment(&quads[q], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000266 }
267}
268
269static bool get_segments(const SkPath& path,
270 const SkMatrix& m,
271 SegmentArray* segments,
272 SkPoint* fanPt,
273 int* vCount,
joshualitt27f398f2015-02-05 14:39:01 -0800274 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000275 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000276 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000277 // to the path from the sample to compute coverage. Every pixel intersected
278 // 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 +0000279 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000280 // thus should be very light. This is particularly egregious for degenerate
281 // line paths. We detect paths that are very close to a line (zero area) and
282 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000283 DegenerateTestData degenerateData;
reed026beb52015-06-10 14:23:15 -0700284 SkPathPriv::FirstDirection dir;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000285 // get_direction can fail for some degenerate paths.
286 if (!get_direction(path, m, &dir)) {
287 return false;
288 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000289
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000290 for (;;) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000291 SkPoint pts[4];
Brian Salomon97042bf2017-02-28 11:21:28 -0500292 SkPath::Verb verb = iter.next(pts, true, true);
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000293 switch (verb) {
294 case SkPath::kMove_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000295 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000296 update_degenerate_test(&degenerateData, pts[0]);
297 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000298 case SkPath::kLine_Verb: {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000299 m.mapPoints(&pts[1], 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000300 update_degenerate_test(&degenerateData, pts[1]);
joshualitt27f398f2015-02-05 14:39:01 -0800301 add_line_to_segment(pts[1], segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000302 break;
303 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000304 case SkPath::kQuad_Verb:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000305 m.mapPoints(pts, 3);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000306 update_degenerate_test(&degenerateData, pts[1]);
307 update_degenerate_test(&degenerateData, pts[2]);
joshualitt27f398f2015-02-05 14:39:01 -0800308 add_quad_segment(pts, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000309 break;
egdanielaf18a092015-01-05 10:22:28 -0800310 case SkPath::kConic_Verb: {
311 m.mapPoints(pts, 3);
312 SkScalar weight = iter.conicWeight();
313 SkAutoConicToQuads converter;
Brian Osmane3deee12018-11-20 11:10:15 -0500314 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
egdanielaf18a092015-01-05 10:22:28 -0800315 for (int i = 0; i < converter.countQuads(); ++i) {
316 update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
317 update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
joshualitt27f398f2015-02-05 14:39:01 -0800318 add_quad_segment(quadPts + 2*i, segments);
egdanielaf18a092015-01-05 10:22:28 -0800319 }
320 break;
321 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000322 case SkPath::kCubic_Verb: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000323 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000324 update_degenerate_test(&degenerateData, pts[1]);
325 update_degenerate_test(&degenerateData, pts[2]);
326 update_degenerate_test(&degenerateData, pts[3]);
joshualitt27f398f2015-02-05 14:39:01 -0800327 add_cubic_segments(pts, dir, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000328 break;
Brian Salomon23356442018-11-30 15:33:19 -0500329 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000330 case SkPath::kDone_Verb:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000331 if (degenerateData.isDegenerate()) {
332 return false;
333 } else {
Greg Daniel8b09b962018-04-03 14:53:45 -0400334 return compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000335 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000336 default:
337 break;
338 }
339 }
340}
341
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000342struct Draw {
343 Draw() : fVertexCnt(0), fIndexCnt(0) {}
344 int fVertexCnt;
345 int fIndexCnt;
346};
347
348typedef SkTArray<Draw, true> DrawArray;
349
Brian Salomon60fb0b22017-06-15 17:09:36 -0400350static void create_vertices(const SegmentArray& segments,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000351 const SkPoint& fanPt,
Brian Osman7f4735b2019-01-02 13:55:10 +0000352 const GrVertexColor& color,
Brian Salomon60fb0b22017-06-15 17:09:36 -0400353 DrawArray* draws,
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500354 GrVertexWriter& verts,
355 uint16_t* idxs,
356 size_t vertexStride) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000357 Draw* draw = &draws->push_back();
358 // alias just to make vert/index assignments easier to read.
359 int* v = &draw->fVertexCnt;
360 int* i = &draw->fIndexCnt;
Brian Osman7f4735b2019-01-02 13:55:10 +0000361 const size_t uvOffset = sizeof(SkPoint) + color.size();
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000362
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000363 int count = segments.count();
364 for (int a = 0; a < count; ++a) {
365 const Segment& sega = segments[a];
366 int b = (a + 1) % count;
367 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000368
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000369 // Check whether adding the verts for this segment to the current draw would cause index
370 // values to overflow.
371 int vCount = 4;
372 if (Segment::kLine == segb.fType) {
373 vCount += 5;
374 } else {
375 vCount += 6;
376 }
377 if (draw->fVertexCnt + vCount > (1 << 16)) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000378 idxs += *i;
379 draw = &draws->push_back();
380 v = &draw->fVertexCnt;
381 i = &draw->fIndexCnt;
382 }
383
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500384 const SkScalar negOneDists[2] = { -SK_Scalar1, -SK_Scalar1 };
385
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000386 // FIXME: These tris are inset in the 1 unit arc around the corner
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500387 SkPoint p0 = sega.endPt();
388 // Position, Color, UV, D0, D1
389 verts.write(p0, color, SkPoint{0, 0}, negOneDists);
390 verts.write(p0 + sega.endNorm(), color, SkPoint{0, -SK_Scalar1}, negOneDists);
391 verts.write(p0 + segb.fMid, color, SkPoint{0, -SK_Scalar1}, negOneDists);
392 verts.write(p0 + segb.fNorms[0], color, SkPoint{0, -SK_Scalar1}, negOneDists);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000393
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000394 idxs[*i + 0] = *v + 0;
395 idxs[*i + 1] = *v + 2;
396 idxs[*i + 2] = *v + 1;
397 idxs[*i + 3] = *v + 0;
398 idxs[*i + 4] = *v + 3;
399 idxs[*i + 5] = *v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000400
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000401 *v += 4;
402 *i += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000403
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000404 if (Segment::kLine == segb.fType) {
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000405 // we draw the line edge as a degenerate quad (u is 0, v is the
406 // signed distance to the edge)
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500407 SkPoint v1Pos = sega.endPt();
408 SkPoint v2Pos = segb.fPts[0];
409 SkScalar dist = SkPointPriv::DistanceToLineBetween(fanPt, v1Pos, v2Pos);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000410
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500411 verts.write(fanPt, color, SkPoint{0, dist}, negOneDists);
412 verts.write(v1Pos, color, SkPoint{0, 0}, negOneDists);
413 verts.write(v2Pos, color, SkPoint{0, 0}, negOneDists);
414 verts.write(v1Pos + segb.fNorms[0], color, SkPoint{0, -SK_Scalar1}, negOneDists);
415 verts.write(v2Pos + segb.fNorms[0], color, SkPoint{0, -SK_Scalar1}, negOneDists);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000416
cdalton35964822015-04-29 10:14:03 -0700417 idxs[*i + 0] = *v + 3;
418 idxs[*i + 1] = *v + 1;
419 idxs[*i + 2] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000420
cdalton35964822015-04-29 10:14:03 -0700421 idxs[*i + 3] = *v + 4;
422 idxs[*i + 4] = *v + 3;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000423 idxs[*i + 5] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000424
cdalton35964822015-04-29 10:14:03 -0700425 *i += 6;
426
427 // Draw the interior fan if it exists.
428 // TODO: Detect and combine colinear segments. This will ensure we catch every case
429 // with no interior, and that the resulting shared edge uses the same endpoints.
430 if (count >= 3) {
431 idxs[*i + 0] = *v + 0;
432 idxs[*i + 1] = *v + 2;
433 idxs[*i + 2] = *v + 1;
434
435 *i += 3;
436 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000437
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000438 *v += 5;
bsalomon@google.com06809612012-01-21 15:03:39 +0000439 } else {
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500440 void* quadVertsBegin = verts.fPtr;
441
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000442 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000443
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500444 SkScalar c0 = segb.fNorms[0].dot(qpts[0]);
445 SkScalar c1 = segb.fNorms[1].dot(qpts[2]);
446 GrVertexWriter::Skip<SkPoint> skipUVs;
447
448 verts.write(fanPt,
449 color, skipUVs,
450 -segb.fNorms[0].dot(fanPt) + c0,
451 -segb.fNorms[1].dot(fanPt) + c1);
452
453 verts.write(qpts[0],
454 color, skipUVs,
455 0.0f,
456 -segb.fNorms[1].dot(qpts[0]) + c1);
457
458 verts.write(qpts[2],
459 color, skipUVs,
460 -segb.fNorms[0].dot(qpts[2]) + c0,
461 0.0f);
462
463 verts.write(qpts[0] + segb.fNorms[0],
464 color, skipUVs,
465 -SK_ScalarMax/100,
466 -SK_ScalarMax/100);
467
468 verts.write(qpts[2] + segb.fNorms[1],
469 color, skipUVs,
470 -SK_ScalarMax/100,
471 -SK_ScalarMax/100);
472
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000473 SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000474 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000475
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500476 verts.write(qpts[1] + midVec,
477 color, skipUVs,
478 -SK_ScalarMax/100,
479 -SK_ScalarMax/100);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000480
bsalomon@google.com19713172012-03-15 13:51:08 +0000481 GrPathUtils::QuadUVMatrix toUV(qpts);
Brian Osman7f4735b2019-01-02 13:55:10 +0000482 toUV.apply(quadVertsBegin, 6, vertexStride, uvOffset);
bsalomon@google.com06809612012-01-21 15:03:39 +0000483
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000484 idxs[*i + 0] = *v + 3;
485 idxs[*i + 1] = *v + 1;
486 idxs[*i + 2] = *v + 2;
487 idxs[*i + 3] = *v + 4;
488 idxs[*i + 4] = *v + 3;
489 idxs[*i + 5] = *v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000490
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000491 idxs[*i + 6] = *v + 5;
492 idxs[*i + 7] = *v + 3;
493 idxs[*i + 8] = *v + 4;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000494
cdalton35964822015-04-29 10:14:03 -0700495 *i += 9;
496
497 // Draw the interior fan if it exists.
498 // TODO: Detect and combine colinear segments. This will ensure we catch every case
499 // with no interior, and that the resulting shared edge uses the same endpoints.
500 if (count >= 3) {
501 idxs[*i + 0] = *v + 0;
502 idxs[*i + 1] = *v + 2;
503 idxs[*i + 2] = *v + 1;
504
505 *i += 3;
506 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000507
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000508 *v += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000509 }
510 }
511}
512
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000513///////////////////////////////////////////////////////////////////////////////
514
515/*
516 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
517 * two components of the vertex attribute. Coverage is based on signed
518 * distance with negative being inside, positive outside. The edge is specified in
519 * window space (y-down). If either the third or fourth component of the interpolated
520 * vertex coord is > 0 then the pixel is considered outside the edge. This is used to
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000521 * attempt to trim to a portion of the infinite quad.
522 * Requires shader derivative instruction support.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000523 */
524
joshualitt249af152014-09-15 11:41:13 -0700525class QuadEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000526public:
Brian Osman7f4735b2019-01-02 13:55:10 +0000527 static sk_sp<GrGeometryProcessor> Make(const SkMatrix& localMatrix, bool usesLocalCoords,
528 bool wideColor) {
529 return sk_sp<GrGeometryProcessor>(
530 new QuadEdgeEffect(localMatrix, usesLocalCoords, wideColor));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000531 }
532
Brian Salomond3b65972017-03-22 12:05:03 -0400533 ~QuadEdgeEffect() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000534
mtklein36352bf2015-03-25 18:17:31 -0700535 const char* name() const override { return "QuadEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000536
egdaniel57d3b032015-11-13 11:57:27 -0800537 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000538 public:
Brian Salomon60fb0b22017-06-15 17:09:36 -0400539 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000540
mtklein36352bf2015-03-25 18:17:31 -0700541 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800542 const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
egdaniel4ca2e602015-11-18 08:01:26 -0800543 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800544 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800545 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -0800546
joshualittabb52a12015-01-13 15:02:10 -0800547 // emit attributes
egdaniel0eafe792015-11-20 14:01:22 -0800548 varyingHandler->emitAttributes(qe);
joshualittabb52a12015-01-13 15:02:10 -0800549
Chris Dalton27372882017-12-08 13:34:21 -0700550 GrGLSLVarying v(kHalf4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800551 varyingHandler->addVarying("QuadEdge", &v);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500552 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.fInQuadEdge.name());
Brian Salomon60fb0b22017-06-15 17:09:36 -0400553
554 // Setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500555 varyingHandler->addPassThroughAttribute(qe.fInColor, args.fOutputColor);
joshualitt2dd1ae02014-12-03 06:24:10 -0800556
Chris Dalton60283612018-02-14 13:38:14 -0700557 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt9b989322014-12-15 14:16:27 -0800558
joshualittabb52a12015-01-13 15:02:10 -0800559 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500560 this->writeOutputPosition(vertBuilder, gpArgs, qe.fInPosition.name());
joshualittabb52a12015-01-13 15:02:10 -0800561
562 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800563 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800564 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800565 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500566 qe.fInPosition.asShaderVar(),
Brian Salomon60fb0b22017-06-15 17:09:36 -0400567 qe.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700568 args.fFPCoordTransformHandler);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000569
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400570 fragBuilder->codeAppendf("half edgeAlpha;");
joshualitt30ba4362014-08-21 20:18:45 -0700571
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000572 // keep the derivative instructions outside the conditional
Ethan Nicholase1f55022019-02-05 17:17:40 -0500573 fragBuilder->codeAppendf("half2 duvdx = half2(dFdx(%s.xy));", v.fsIn());
574 fragBuilder->codeAppendf("half2 duvdy = half2(dFdy(%s.xy));", v.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800575 fragBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000576 // today we know z and w are in device space. We could use derivatives
egdaniel4ca2e602015-11-18 08:01:26 -0800577 fragBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
578 v.fsIn());
579 fragBuilder->codeAppendf ("} else {");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400580 fragBuilder->codeAppendf("half2 gF = half2(2.0*%s.x*duvdx.x - duvdx.y,"
egdaniel4ca2e602015-11-18 08:01:26 -0800581 " 2.0*%s.x*duvdy.x - duvdy.y);",
582 v.fsIn(), v.fsIn());
583 fragBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
584 v.fsIn());
585 fragBuilder->codeAppendf("edgeAlpha = "
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400586 "saturate(0.5 - edgeAlpha / length(gF));}");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000587
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400588 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000589 }
590
joshualitt9b989322014-12-15 14:16:27 -0800591 static inline void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500592 const GrShaderCaps&,
joshualitt9b989322014-12-15 14:16:27 -0800593 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700594 const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
Brian Salomon60fb0b22017-06-15 17:09:36 -0400595 b->add32(SkToBool(qee.fUsesLocalCoords && qee.fLocalMatrix.hasPerspective()));
joshualitt9b989322014-12-15 14:16:27 -0800596 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000597
egdaniel018fb622015-10-28 07:26:40 -0700598 void setData(const GrGLSLProgramDataManager& pdman,
bsalomona624bf32016-09-20 09:12:47 -0700599 const GrPrimitiveProcessor& gp,
600 FPCoordTransformIter&& transformIter) override {
joshualittb8c241a2015-05-19 08:23:30 -0700601 const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
bsalomona624bf32016-09-20 09:12:47 -0700602 this->setTransformDataHelper(qe.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700603 }
604
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000605 private:
egdaniele659a582015-11-13 09:55:43 -0800606 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000607 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000608
Brian Salomon94efbf52016-11-29 13:43:05 -0500609 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800610 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800611 }
612
Brian Salomon94efbf52016-11-29 13:43:05 -0500613 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800614 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800615 }
616
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000617private:
Brian Osman7f4735b2019-01-02 13:55:10 +0000618 QuadEdgeEffect(const SkMatrix& localMatrix, bool usesLocalCoords, bool wideColor)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400619 : INHERITED(kQuadEdgeEffect_ClassID)
620 , fLocalMatrix(localMatrix)
621 , fUsesLocalCoords(usesLocalCoords) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500622 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osman7f4735b2019-01-02 13:55:10 +0000623 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500624 fInQuadEdge = {"inQuadEdge", kFloat4_GrVertexAttribType, kHalf4_GrSLType};
625 this->setVertexAttributes(&fInPosition, 3);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000626 }
627
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500628 Attribute fInPosition;
629 Attribute fInColor;
630 Attribute fInQuadEdge;
631
Brian Salomon92be2f72018-06-19 14:33:47 -0400632 SkMatrix fLocalMatrix;
633 bool fUsesLocalCoords;
joshualitt249af152014-09-15 11:41:13 -0700634
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400635 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000636
joshualitt2e3b3e32014-12-09 13:31:14 -0800637 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000638};
639
joshualittb0a8a372014-09-23 09:50:21 -0700640GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000641
Hal Canary6f6961e2017-01-31 13:50:44 -0500642#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700643sk_sp<GrGeometryProcessor> QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000644 // Doesn't work without derivative instructions.
Robert Phillips296b1cc2017-03-15 10:42:12 -0400645 return d->caps()->shaderCaps()->shaderDerivativeSupport()
Brian Osman7f4735b2019-01-02 13:55:10 +0000646 ? QuadEdgeEffect::Make(GrTest::TestMatrix(d->fRandom), d->fRandom->nextBool(),
647 d->fRandom->nextBool())
Brian Salomon9ae32a22017-01-25 14:58:24 -0500648 : nullptr;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000649}
Hal Canary6f6961e2017-01-31 13:50:44 -0500650#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000651
652///////////////////////////////////////////////////////////////////////////////
653
Chris Dalton5ed44232017-09-07 13:22:46 -0600654GrPathRenderer::CanDrawPath
655GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
656 if (args.fCaps->shaderCaps()->shaderDerivativeSupport() &&
657 (GrAAType::kCoverage == args.fAAType) && args.fShape->style().isSimpleFill() &&
658 !args.fShape->inverseFilled() && args.fShape->knownToBeConvex()) {
659 return CanDrawPath::kYes;
660 }
661 return CanDrawPath::kNo;
robertphillips@google.comfa662942012-05-17 12:20:22 +0000662}
663
Brian Salomon10978a62017-06-15 16:21:49 -0400664namespace {
665
666class AAConvexPathOp final : public GrMeshDrawOp {
667private:
668 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
669
joshualitt27f398f2015-02-05 14:39:01 -0800670public:
Brian Salomon25a88092016-12-01 09:36:50 -0500671 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400672
Robert Phillipsb97da532019-02-12 15:24:12 -0500673 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400674 GrPaint&& paint,
675 const SkMatrix& viewMatrix,
Brian Salomon10978a62017-06-15 16:21:49 -0400676 const SkPath& path,
677 const GrUserStencilSettings* stencilSettings) {
Robert Phillips7c525e62018-06-12 10:11:12 -0400678 return Helper::FactoryHelper<AAConvexPathOp>(context, std::move(paint), viewMatrix, path,
Brian Salomon10978a62017-06-15 16:21:49 -0400679 stencilSettings);
680 }
681
Brian Osmancf860852018-10-31 14:04:39 -0400682 AAConvexPathOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -0400683 const SkMatrix& viewMatrix, const SkPath& path,
684 const GrUserStencilSettings* stencilSettings)
Brian Salomon60fb0b22017-06-15 17:09:36 -0400685 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage, stencilSettings) {
686 fPaths.emplace_back(PathData{viewMatrix, path, color});
Brian Salomon10978a62017-06-15 16:21:49 -0400687 this->setTransformedBounds(path.getBounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
Brian Osman7f4735b2019-01-02 13:55:10 +0000688 fWideColor = !SkPMColor4fFitsInBytes(color);
bsalomonf1703092016-06-29 18:41:53 -0700689 }
joshualitt27f398f2015-02-05 14:39:01 -0800690
Brian Salomond0a0a652016-12-15 15:25:22 -0500691 const char* name() const override { return "AAConvexPathOp"; }
joshualitt27f398f2015-02-05 14:39:01 -0800692
Brian Salomon7d94bb52018-10-12 14:37:19 -0400693 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400694 fHelper.visitProxies(func);
695 }
696
Brian Osman9a390ac2018-11-12 09:47:48 -0500697#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -0500698 SkString dumpInfo() const override {
699 SkString string;
Brian Salomon60fb0b22017-06-15 17:09:36 -0400700 string.appendf("Count: %d\n", fPaths.count());
Brian Salomon10978a62017-06-15 16:21:49 -0400701 string += fHelper.dumpInfo();
702 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -0500703 return string;
704 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500705#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -0500706
Brian Salomon10978a62017-06-15 16:21:49 -0400707 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
708
Chris Dalton4b62aed2019-01-15 11:53:00 -0700709 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
710 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
711 &fPaths.back().fColor);
Brian Salomon10978a62017-06-15 16:21:49 -0400712 }
713
bsalomone46f9fe2015-08-18 06:05:14 -0700714private:
Brian Salomon91326c32017-08-09 16:02:19 -0400715 void onPrepareDraws(Target* target) override {
Brian Salomond0a0a652016-12-15 15:25:22 -0500716 int instanceCount = fPaths.count();
joshualitt27f398f2015-02-05 14:39:01 -0800717
718 SkMatrix invert;
Brian Salomon10978a62017-06-15 16:21:49 -0400719 if (fHelper.usesLocalCoords() && !fPaths.back().fViewMatrix.invert(&invert)) {
joshualitt27f398f2015-02-05 14:39:01 -0800720 return;
721 }
722
723 // Setup GrGeometryProcessor
bungeman06ca8ec2016-06-09 08:01:03 -0700724 sk_sp<GrGeometryProcessor> quadProcessor(
Brian Osman7f4735b2019-01-02 13:55:10 +0000725 QuadEdgeEffect::Make(invert, fHelper.usesLocalCoords(), fWideColor));
726 const size_t kVertexStride = quadProcessor->vertexStride();
joshualitt27f398f2015-02-05 14:39:01 -0800727
joshualitt27f398f2015-02-05 14:39:01 -0800728 // TODO generate all segments for all paths and use one vertex buffer
729 for (int i = 0; i < instanceCount; i++) {
Brian Salomond0a0a652016-12-15 15:25:22 -0500730 const PathData& args = fPaths[i];
joshualitt27f398f2015-02-05 14:39:01 -0800731
732 // We use the fact that SkPath::transform path does subdivision based on
733 // perspective. Otherwise, we apply the view matrix when copying to the
734 // segment representation.
735 const SkMatrix* viewMatrix = &args.fViewMatrix;
joshualitt144c3c82015-11-30 12:30:13 -0800736
737 // We avoid initializing the path unless we have to
738 const SkPath* pathPtr = &args.fPath;
739 SkTLazy<SkPath> tmpPath;
joshualitt27f398f2015-02-05 14:39:01 -0800740 if (viewMatrix->hasPerspective()) {
joshualitt144c3c82015-11-30 12:30:13 -0800741 SkPath* tmpPathPtr = tmpPath.init(*pathPtr);
742 tmpPathPtr->setIsVolatile(true);
743 tmpPathPtr->transform(*viewMatrix);
joshualitt27f398f2015-02-05 14:39:01 -0800744 viewMatrix = &SkMatrix::I();
joshualitt144c3c82015-11-30 12:30:13 -0800745 pathPtr = tmpPathPtr;
joshualitt27f398f2015-02-05 14:39:01 -0800746 }
747
748 int vertexCount;
749 int indexCount;
750 enum {
751 kPreallocSegmentCnt = 512 / sizeof(Segment),
752 kPreallocDrawCnt = 4,
753 };
754 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
755 SkPoint fanPt;
756
joshualitt144c3c82015-11-30 12:30:13 -0800757 if (!get_segments(*pathPtr, *viewMatrix, &segments, &fanPt, &vertexCount,
joshualitt27f398f2015-02-05 14:39:01 -0800758 &indexCount)) {
759 continue;
760 }
761
Brian Salomon12d22642019-01-29 14:38:50 -0500762 sk_sp<const GrBuffer> vertexBuffer;
Chris Daltonbca46e22017-05-15 11:03:26 -0600763 int firstVertex;
764
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500765 GrVertexWriter verts{target->makeVertexSpace(kVertexStride, vertexCount,
766 &vertexBuffer, &firstVertex)};
bsalomone64eb572015-05-07 11:35:55 -0700767
Brian Osmand2fa2eb2018-12-26 16:48:40 -0500768 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -0800769 SkDebugf("Could not allocate vertices\n");
770 return;
771 }
772
Brian Salomon12d22642019-01-29 14:38:50 -0500773 sk_sp<const GrBuffer> indexBuffer;
Chris Daltonbca46e22017-05-15 11:03:26 -0600774 int firstIndex;
775
776 uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
robertphillipse40d3972015-05-07 09:51:43 -0700777 if (!idxs) {
joshualitt4b31de82015-03-05 14:33:41 -0800778 SkDebugf("Could not allocate indices\n");
779 return;
780 }
781
joshualitt27f398f2015-02-05 14:39:01 -0800782 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
Brian Osman7f4735b2019-01-02 13:55:10 +0000783 GrVertexColor color(args.fColor, fWideColor);
784 create_vertices(segments, fanPt, color, &draws, verts, idxs, kVertexStride);
joshualitt27f398f2015-02-05 14:39:01 -0800785
Brian Salomon7eae3e02018-08-07 14:02:38 +0000786 GrMesh* meshes = target->allocMeshes(draws.count());
robertphillips44c31282015-09-03 12:58:48 -0700787 for (int j = 0; j < draws.count(); ++j) {
788 const Draw& draw = draws[j];
Brian Salomon7eae3e02018-08-07 14:02:38 +0000789 meshes[j].setPrimitiveType(GrPrimitiveType::kTriangles);
790 meshes[j].setIndexed(indexBuffer, draw.fIndexCnt, firstIndex, 0,
791 draw.fVertexCnt - 1, GrPrimitiveRestart::kNo);
792 meshes[j].setVertexData(vertexBuffer, firstVertex);
Chris Daltonbca46e22017-05-15 11:03:26 -0600793 firstIndex += draw.fIndexCnt;
794 firstVertex += draw.fVertexCnt;
joshualitt27f398f2015-02-05 14:39:01 -0800795 }
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700796 target->recordDraw(quadProcessor, meshes, draws.count());
joshualitt27f398f2015-02-05 14:39:01 -0800797 }
798 }
799
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700800 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
801 fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
802 }
803
Brian Salomon7eae3e02018-08-07 14:02:38 +0000804 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomond0a0a652016-12-15 15:25:22 -0500805 AAConvexPathOp* that = t->cast<AAConvexPathOp>();
Brian Salomon10978a62017-06-15 16:21:49 -0400806 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000807 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -0700808 }
Brian Salomon10978a62017-06-15 16:21:49 -0400809 if (fHelper.usesLocalCoords() &&
810 !fPaths[0].fViewMatrix.cheapEqualTo(that->fPaths[0].fViewMatrix)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000811 return CombineResult::kCannotCombine;
joshualitt27f398f2015-02-05 14:39:01 -0800812 }
813
Brian Salomond0a0a652016-12-15 15:25:22 -0500814 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
Brian Osman7f4735b2019-01-02 13:55:10 +0000815 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000816 return CombineResult::kMerged;
joshualitt27f398f2015-02-05 14:39:01 -0800817 }
818
Brian Salomond0a0a652016-12-15 15:25:22 -0500819 struct PathData {
bsalomonf1703092016-06-29 18:41:53 -0700820 SkMatrix fViewMatrix;
821 SkPath fPath;
Brian Osmancf860852018-10-31 14:04:39 -0400822 SkPMColor4f fColor;
bsalomonf1703092016-06-29 18:41:53 -0700823 };
824
Brian Salomon10978a62017-06-15 16:21:49 -0400825 Helper fHelper;
Brian Salomond0a0a652016-12-15 15:25:22 -0500826 SkSTArray<1, PathData, true> fPaths;
Brian Osman7f4735b2019-01-02 13:55:10 +0000827 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -0700828
Brian Salomon10978a62017-06-15 16:21:49 -0400829 typedef GrMeshDrawOp INHERITED;
joshualitt27f398f2015-02-05 14:39:01 -0800830};
831
Brian Salomon10978a62017-06-15 16:21:49 -0400832} // anonymous namespace
833
bsalomon0aff2fa2015-07-31 06:48:27 -0700834bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400835 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700836 "GrAAConvexPathRenderer::onDrawPath");
Brian Salomon7c8460e2017-05-12 11:36:10 -0400837 SkASSERT(GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType());
bsalomon8acedde2016-06-24 10:42:16 -0700838 SkASSERT(!args.fShape->isEmpty());
bsalomon@google.com4647f902013-03-26 14:45:27 +0000839
bsalomonf1703092016-06-29 18:41:53 -0700840 SkPath path;
841 args.fShape->asPath(&path);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000842
Robert Phillips7c525e62018-06-12 10:11:12 -0400843 std::unique_ptr<GrDrawOp> op = AAConvexPathOp::Make(args.fContext, std::move(args.fPaint),
844 *args.fViewMatrix,
Brian Salomon10978a62017-06-15 16:21:49 -0400845 path, args.fUserStencilSettings);
846 args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000847 return true;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000848}
joshualitt8e5c1772015-05-11 08:58:52 -0700849
850///////////////////////////////////////////////////////////////////////////////////////////////////
851
Hal Canary6f6961e2017-01-31 13:50:44 -0500852#if GR_TEST_UTILS
joshualitt8e5c1772015-05-11 08:58:52 -0700853
Brian Salomon10978a62017-06-15 16:21:49 -0400854GR_DRAW_OP_TEST_DEFINE(AAConvexPathOp) {
bsalomonf1703092016-06-29 18:41:53 -0700855 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
856 SkPath path = GrTest::TestPathConvex(random);
Brian Salomon10978a62017-06-15 16:21:49 -0400857 const GrUserStencilSettings* stencilSettings = GrGetRandomStencil(random, context);
Robert Phillips7c525e62018-06-12 10:11:12 -0400858 return AAConvexPathOp::Make(context, std::move(paint), viewMatrix, path, stencilSettings);
joshualitt8e5c1772015-05-11 08:58:52 -0700859}
860
861#endif