blob: 3d475b947a21deb0afe80dba166edc02d3c15aad [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"
robertphillipse16dfdb2015-05-08 04:46:51 -07009#include "GrAAConvexTessellator.h"
bsalomoneb1cb5c2015-05-22 08:01:09 -070010#include "GrCaps.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000011#include "GrContext.h"
robertphillipse16dfdb2015-05-08 04:46:51 -070012#include "GrDefaultGeoProcFactory.h"
Brian Salomon5ec9def2016-12-20 15:34:05 -050013#include "GrDrawOpTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080014#include "GrGeometryProcessor.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050015#include "GrOpFlushState.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000016#include "GrPathUtils.h"
Brian Salomondad29232016-12-01 16:40:24 -050017#include "GrProcessor.h"
Brian Salomon653f42f2018-07-10 10:07:31 -040018#include "GrRenderTargetContext.h"
19#include "GrShape.h"
Brian Salomon10978a62017-06-15 16:21:49 -040020#include "GrSimpleMeshDrawOpHelper.h"
egdanielaf18a092015-01-05 10:22:28 -080021#include "SkGeometry.h"
reed026beb52015-06-10 14:23:15 -070022#include "SkPathPriv.h"
Cary Clarkdf429f32017-11-08 11:44:31 -050023#include "SkPointPriv.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000024#include "SkString.h"
commit-bot@chromium.org933e65d2014-03-20 20:00:24 +000025#include "SkTraceEvent.h"
Greg Danield5b45932018-06-07 13:15:10 -040026#include "SkTypes.h"
egdaniel7ea439b2015-12-03 09:20:44 -080027#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080028#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070029#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080030#include "glsl/GrGLSLUniformHandler.h"
egdaniel0eafe792015-11-20 14:01:22 -080031#include "glsl/GrGLSLVarying.h"
Chris Daltonc17bf322017-10-24 10:59:03 -060032#include "glsl/GrGLSLVertexGeoBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050033#include "ops/GrMeshDrawOp.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000034
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000035GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
36}
37
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000038struct Segment {
39 enum {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000040 // These enum values are assumed in member functions below.
41 kLine = 0,
42 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000043 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000044
bsalomon@google.com9aed1142012-01-30 14:28:39 +000045 // line uses one pt, quad uses 2 pts
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000046 SkPoint fPts[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000047 // normal to edge ending at each pt
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000048 SkVector fNorms[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000049 // is the corner where the previous segment meets this segment
50 // sharp. If so, fMid is a normalized bisector facing outward.
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000051 SkVector fMid;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000052
53 int countPoints() {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000054 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
55 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000056 }
57 const SkPoint& endPt() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000058 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
59 return fPts[fType];
Mike Kleinfc6c37b2016-09-27 09:34:10 -040060 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000061 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000062 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
63 return fNorms[fType];
Mike Kleinfc6c37b2016-09-27 09:34:10 -040064 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000065};
66
67typedef SkTArray<Segment, true> SegmentArray;
68
Greg Daniel8b09b962018-04-03 14:53:45 -040069static bool center_of_mass(const SegmentArray& segments, SkPoint* c) {
bsalomon@google.com81712882012-11-01 17:12:34 +000070 SkScalar area = 0;
vandebo@chromium.org6390c722012-03-28 21:03:22 +000071 SkPoint center = {0, 0};
bsalomon@google.com9aed1142012-01-30 14:28:39 +000072 int count = segments.count();
vandebo@chromium.org6390c722012-03-28 21:03:22 +000073 SkPoint p0 = {0, 0};
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000074 if (count > 2) {
75 // We translate the polygon so that the first point is at the origin.
76 // This avoids some precision issues with small area polygons far away
77 // from the origin.
78 p0 = segments[0].endPt();
79 SkPoint pi;
80 SkPoint pj;
bsalomon@google.coma51ab842012-07-10 19:53:34 +000081 // the first and last iteration of the below loop would compute
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000082 // zeros since the starting / ending point is (0,0). So instead we start
83 // at i=1 and make the last iteration i=count-2.
84 pj = segments[1].endPt() - p0;
85 for (int i = 1; i < count - 1; ++i) {
86 pi = pj;
robertphillips44c31282015-09-03 12:58:48 -070087 pj = segments[i + 1].endPt() - p0;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000088
robertphillips44c31282015-09-03 12:58:48 -070089 SkScalar t = SkPoint::CrossProduct(pi, pj);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000090 area += t;
91 center.fX += (pi.fX + pj.fX) * t;
92 center.fY += (pi.fY + pj.fY) * t;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000093 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000094 }
robertphillips44c31282015-09-03 12:58:48 -070095
bsalomon@google.com278dc692012-02-15 16:52:51 +000096 // If the poly has no area then we instead return the average of
97 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000098 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +000099 SkPoint avg;
100 avg.set(0, 0);
101 for (int i = 0; i < count; ++i) {
102 const SkPoint& pt = segments[i].endPt();
103 avg.fX += pt.fX;
104 avg.fY += pt.fY;
105 }
106 SkScalar denom = SK_Scalar1 / count;
107 avg.scale(denom);
108 *c = avg;
109 } else {
110 area *= 3;
reed80ea19c2015-05-12 10:37:34 -0700111 area = SkScalarInvert(area);
robertphillips44c31282015-09-03 12:58:48 -0700112 center.scale(area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +0000113 // undo the translate of p0 to the origin.
114 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000115 }
Greg Daniel62473ad2018-04-03 15:44:18 -0400116 return !SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY) && c->isFinite();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000117}
118
Greg Daniel8b09b962018-04-03 14:53:45 -0400119static bool compute_vectors(SegmentArray* segments,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000120 SkPoint* fanPt,
reed026beb52015-06-10 14:23:15 -0700121 SkPathPriv::FirstDirection dir,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000122 int* vCount,
123 int* iCount) {
Greg Daniel8b09b962018-04-03 14:53:45 -0400124 if (!center_of_mass(*segments, fanPt)) {
125 return false;
126 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000127 int count = segments->count();
128
bsalomon@google.com278dc692012-02-15 16:52:51 +0000129 // Make the normals point towards the outside
Cary Clarkdf429f32017-11-08 11:44:31 -0500130 SkPointPriv::Side normSide;
reed026beb52015-06-10 14:23:15 -0700131 if (dir == SkPathPriv::kCCW_FirstDirection) {
Cary Clarkdf429f32017-11-08 11:44:31 -0500132 normSide = SkPointPriv::kRight_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000133 } else {
Cary Clarkdf429f32017-11-08 11:44:31 -0500134 normSide = SkPointPriv::kLeft_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000135 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000136
Greg Danield5b45932018-06-07 13:15:10 -0400137 int64_t vCount64 = 0;
138 int64_t iCount64 = 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000139 // compute normals at all points
140 for (int a = 0; a < count; ++a) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000141 Segment& sega = (*segments)[a];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000142 int b = (a + 1) % count;
143 Segment& segb = (*segments)[b];
144
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000145 const SkPoint* prevPt = &sega.endPt();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000146 int n = segb.countPoints();
147 for (int p = 0; p < n; ++p) {
148 segb.fNorms[p] = segb.fPts[p] - *prevPt;
149 segb.fNorms[p].normalize();
Cary Clarkdf429f32017-11-08 11:44:31 -0500150 SkPointPriv::SetOrthog(&segb.fNorms[p], segb.fNorms[p], normSide);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000151 prevPt = &segb.fPts[p];
152 }
153 if (Segment::kLine == segb.fType) {
Greg Danield5b45932018-06-07 13:15:10 -0400154 vCount64 += 5;
155 iCount64 += 9;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000156 } else {
Greg Danield5b45932018-06-07 13:15:10 -0400157 vCount64 += 6;
158 iCount64 += 12;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000159 }
160 }
161
162 // compute mid-vectors where segments meet. TODO: Detect shallow corners
163 // and leave out the wedges and close gaps by stitching segments together.
164 for (int a = 0; a < count; ++a) {
165 const Segment& sega = (*segments)[a];
166 int b = (a + 1) % count;
167 Segment& segb = (*segments)[b];
168 segb.fMid = segb.fNorms[0] + sega.endNorm();
169 segb.fMid.normalize();
170 // corner wedges
Greg Danield5b45932018-06-07 13:15:10 -0400171 vCount64 += 4;
172 iCount64 += 6;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000173 }
Greg Danield5b45932018-06-07 13:15:10 -0400174 if (vCount64 > SK_MaxS32 || iCount64 > SK_MaxS32) {
175 return false;
176 }
177 *vCount = vCount64;
178 *iCount = iCount64;
Greg Daniel8b09b962018-04-03 14:53:45 -0400179 return true;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000180}
181
bsalomon@google.com9732f622012-01-31 15:19:21 +0000182struct DegenerateTestData {
183 DegenerateTestData() { fStage = kInitial; }
184 bool isDegenerate() const { return kNonDegenerate != fStage; }
185 enum {
186 kInitial,
187 kPoint,
188 kLine,
189 kNonDegenerate
190 } fStage;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000191 SkPoint fFirstPoint;
192 SkVector fLineNormal;
bsalomon@google.com81712882012-11-01 17:12:34 +0000193 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000194};
195
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000196static const SkScalar kClose = (SK_Scalar1 / 16);
Mike Reed8be952a2017-02-13 20:44:33 -0500197static const SkScalar kCloseSqd = kClose * kClose;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000198
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000199static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000200 switch (data->fStage) {
201 case DegenerateTestData::kInitial:
202 data->fFirstPoint = pt;
203 data->fStage = DegenerateTestData::kPoint;
204 break;
205 case DegenerateTestData::kPoint:
Cary Clarkdf429f32017-11-08 11:44:31 -0500206 if (SkPointPriv::DistanceToSqd(pt, data->fFirstPoint) > kCloseSqd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000207 data->fLineNormal = pt - data->fFirstPoint;
208 data->fLineNormal.normalize();
Cary Clarkdf429f32017-11-08 11:44:31 -0500209 SkPointPriv::SetOrthog(&data->fLineNormal, data->fLineNormal);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000210 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
211 data->fStage = DegenerateTestData::kLine;
212 }
213 break;
214 case DegenerateTestData::kLine:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000215 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000216 data->fStage = DegenerateTestData::kNonDegenerate;
217 }
218 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
reed026beb52015-06-10 14:23:15 -0700225static inline bool get_direction(const SkPath& path, const SkMatrix& m,
226 SkPathPriv::FirstDirection* dir) {
227 if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000228 return false;
229 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000230 // check whether m reverses the orientation
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000231 SkASSERT(!m.hasPerspective());
Mike Reed8be952a2017-02-13 20:44:33 -0500232 SkScalar det2x2 = m.get(SkMatrix::kMScaleX) * m.get(SkMatrix::kMScaleY) -
233 m.get(SkMatrix::kMSkewX) * m.get(SkMatrix::kMSkewY);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000234 if (det2x2 < 0) {
reed026beb52015-06-10 14:23:15 -0700235 *dir = SkPathPriv::OppositeFirstDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000236 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000237 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000238}
239
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000240static inline void add_line_to_segment(const SkPoint& pt,
joshualitt27f398f2015-02-05 14:39:01 -0800241 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000242 segments->push_back();
243 segments->back().fType = Segment::kLine;
244 segments->back().fPts[0] = pt;
245}
246
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000247static inline void add_quad_segment(const SkPoint pts[3],
joshualitt27f398f2015-02-05 14:39:01 -0800248 SegmentArray* segments) {
Cary Clarkdf429f32017-11-08 11:44:31 -0500249 if (SkPointPriv::DistanceToSqd(pts[0], pts[1]) < kCloseSqd ||
250 SkPointPriv::DistanceToSqd(pts[1], pts[2]) < kCloseSqd) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000251 if (pts[0] != pts[2]) {
joshualitt27f398f2015-02-05 14:39:01 -0800252 add_line_to_segment(pts[2], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000253 }
254 } else {
255 segments->push_back();
256 segments->back().fType = Segment::kQuad;
257 segments->back().fPts[0] = pts[1];
258 segments->back().fPts[1] = pts[2];
259 }
260}
261
262static inline void add_cubic_segments(const SkPoint pts[4],
reed026beb52015-06-10 14:23:15 -0700263 SkPathPriv::FirstDirection dir,
joshualitt27f398f2015-02-05 14:39:01 -0800264 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000265 SkSTArray<15, SkPoint, true> quads;
bsalomon18fab302016-02-16 08:00:05 -0800266 GrPathUtils::convertCubicToQuadsConstrainToTangents(pts, SK_Scalar1, dir, &quads);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000267 int count = quads.count();
268 for (int q = 0; q < count; q += 3) {
joshualitt27f398f2015-02-05 14:39:01 -0800269 add_quad_segment(&quads[q], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000270 }
271}
272
273static bool get_segments(const SkPath& path,
274 const SkMatrix& m,
275 SegmentArray* segments,
276 SkPoint* fanPt,
277 int* vCount,
joshualitt27f398f2015-02-05 14:39:01 -0800278 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000279 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000280 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000281 // to the path from the sample to compute coverage. Every pixel intersected
282 // 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 +0000283 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000284 // thus should be very light. This is particularly egregious for degenerate
285 // line paths. We detect paths that are very close to a line (zero area) and
286 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000287 DegenerateTestData degenerateData;
reed026beb52015-06-10 14:23:15 -0700288 SkPathPriv::FirstDirection dir;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000289 // get_direction can fail for some degenerate paths.
290 if (!get_direction(path, m, &dir)) {
291 return false;
292 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000293
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000294 for (;;) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000295 SkPoint pts[4];
Brian Salomon97042bf2017-02-28 11:21:28 -0500296 SkPath::Verb verb = iter.next(pts, true, true);
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000297 switch (verb) {
298 case SkPath::kMove_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000299 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000300 update_degenerate_test(&degenerateData, pts[0]);
301 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000302 case SkPath::kLine_Verb: {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000303 m.mapPoints(&pts[1], 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000304 update_degenerate_test(&degenerateData, pts[1]);
joshualitt27f398f2015-02-05 14:39:01 -0800305 add_line_to_segment(pts[1], segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000306 break;
307 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000308 case SkPath::kQuad_Verb:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000309 m.mapPoints(pts, 3);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000310 update_degenerate_test(&degenerateData, pts[1]);
311 update_degenerate_test(&degenerateData, pts[2]);
joshualitt27f398f2015-02-05 14:39:01 -0800312 add_quad_segment(pts, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000313 break;
egdanielaf18a092015-01-05 10:22:28 -0800314 case SkPath::kConic_Verb: {
315 m.mapPoints(pts, 3);
316 SkScalar weight = iter.conicWeight();
317 SkAutoConicToQuads converter;
318 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5f);
319 for (int i = 0; i < converter.countQuads(); ++i) {
320 update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
321 update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
joshualitt27f398f2015-02-05 14:39:01 -0800322 add_quad_segment(quadPts + 2*i, segments);
egdanielaf18a092015-01-05 10:22:28 -0800323 }
324 break;
325 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000326 case SkPath::kCubic_Verb: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000327 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000328 update_degenerate_test(&degenerateData, pts[1]);
329 update_degenerate_test(&degenerateData, pts[2]);
330 update_degenerate_test(&degenerateData, pts[3]);
joshualitt27f398f2015-02-05 14:39:01 -0800331 add_cubic_segments(pts, dir, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000332 break;
333 };
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000334 case SkPath::kDone_Verb:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000335 if (degenerateData.isDegenerate()) {
336 return false;
337 } else {
Greg Daniel8b09b962018-04-03 14:53:45 -0400338 return compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000339 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000340 default:
341 break;
342 }
343 }
344}
345
346struct QuadVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000347 SkPoint fPos;
Brian Salomon60fb0b22017-06-15 17:09:36 -0400348 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000349 SkPoint fUV;
bsalomon@google.com81712882012-11-01 17:12:34 +0000350 SkScalar fD0;
351 SkScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000352};
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000353
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000354struct Draw {
355 Draw() : fVertexCnt(0), fIndexCnt(0) {}
356 int fVertexCnt;
357 int fIndexCnt;
358};
359
360typedef SkTArray<Draw, true> DrawArray;
361
Brian Salomon60fb0b22017-06-15 17:09:36 -0400362static void create_vertices(const SegmentArray& segments,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000363 const SkPoint& fanPt,
Brian Salomon60fb0b22017-06-15 17:09:36 -0400364 GrColor color,
365 DrawArray* draws,
366 QuadVertex* verts,
367 uint16_t* idxs) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000368 Draw* draw = &draws->push_back();
369 // alias just to make vert/index assignments easier to read.
370 int* v = &draw->fVertexCnt;
371 int* i = &draw->fIndexCnt;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000372
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000373 int count = segments.count();
374 for (int a = 0; a < count; ++a) {
375 const Segment& sega = segments[a];
376 int b = (a + 1) % count;
377 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000378
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000379 // Check whether adding the verts for this segment to the current draw would cause index
380 // values to overflow.
381 int vCount = 4;
382 if (Segment::kLine == segb.fType) {
383 vCount += 5;
384 } else {
385 vCount += 6;
386 }
387 if (draw->fVertexCnt + vCount > (1 << 16)) {
388 verts += *v;
389 idxs += *i;
390 draw = &draws->push_back();
391 v = &draw->fVertexCnt;
392 i = &draw->fIndexCnt;
393 }
394
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000395 // FIXME: These tris are inset in the 1 unit arc around the corner
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000396 verts[*v + 0].fPos = sega.endPt();
397 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
398 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
399 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
Brian Salomon60fb0b22017-06-15 17:09:36 -0400400 verts[*v + 0].fColor = color;
401 verts[*v + 1].fColor = color;
402 verts[*v + 2].fColor = color;
403 verts[*v + 3].fColor = color;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000404 verts[*v + 0].fUV.set(0,0);
405 verts[*v + 1].fUV.set(0,-SK_Scalar1);
406 verts[*v + 2].fUV.set(0,-SK_Scalar1);
407 verts[*v + 3].fUV.set(0,-SK_Scalar1);
408 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
409 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
410 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
411 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000412
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000413 idxs[*i + 0] = *v + 0;
414 idxs[*i + 1] = *v + 2;
415 idxs[*i + 2] = *v + 1;
416 idxs[*i + 3] = *v + 0;
417 idxs[*i + 4] = *v + 3;
418 idxs[*i + 5] = *v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000419
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000420 *v += 4;
421 *i += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000422
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000423 if (Segment::kLine == segb.fType) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000424 verts[*v + 0].fPos = fanPt;
425 verts[*v + 1].fPos = sega.endPt();
426 verts[*v + 2].fPos = segb.fPts[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000427
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000428 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
429 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000430
Brian Salomon60fb0b22017-06-15 17:09:36 -0400431 verts[*v + 0].fColor = color;
432 verts[*v + 1].fColor = color;
433 verts[*v + 2].fColor = color;
434 verts[*v + 3].fColor = color;
435 verts[*v + 4].fColor = color;
436
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000437 // we draw the line edge as a degenerate quad (u is 0, v is the
438 // signed distance to the edge)
Cary Clarkdf429f32017-11-08 11:44:31 -0500439 SkScalar dist = SkPointPriv::DistanceToLineBetween(fanPt, verts[*v + 1].fPos,
440 verts[*v + 2].fPos);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000441 verts[*v + 0].fUV.set(0, dist);
442 verts[*v + 1].fUV.set(0, 0);
443 verts[*v + 2].fUV.set(0, 0);
444 verts[*v + 3].fUV.set(0, -SK_Scalar1);
445 verts[*v + 4].fUV.set(0, -SK_Scalar1);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000446
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000447 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
448 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
449 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
450 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
451 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000452
cdalton35964822015-04-29 10:14:03 -0700453 idxs[*i + 0] = *v + 3;
454 idxs[*i + 1] = *v + 1;
455 idxs[*i + 2] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000456
cdalton35964822015-04-29 10:14:03 -0700457 idxs[*i + 3] = *v + 4;
458 idxs[*i + 4] = *v + 3;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000459 idxs[*i + 5] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000460
cdalton35964822015-04-29 10:14:03 -0700461 *i += 6;
462
463 // Draw the interior fan if it exists.
464 // TODO: Detect and combine colinear segments. This will ensure we catch every case
465 // with no interior, and that the resulting shared edge uses the same endpoints.
466 if (count >= 3) {
467 idxs[*i + 0] = *v + 0;
468 idxs[*i + 1] = *v + 2;
469 idxs[*i + 2] = *v + 1;
470
471 *i += 3;
472 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000473
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000474 *v += 5;
bsalomon@google.com06809612012-01-21 15:03:39 +0000475 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000476 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000477
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000478 SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000479 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000480
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000481 verts[*v + 0].fPos = fanPt;
482 verts[*v + 1].fPos = qpts[0];
483 verts[*v + 2].fPos = qpts[2];
484 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
485 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
486 verts[*v + 5].fPos = qpts[1] + midVec;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000487
Brian Salomon60fb0b22017-06-15 17:09:36 -0400488 verts[*v + 0].fColor = color;
489 verts[*v + 1].fColor = color;
490 verts[*v + 2].fColor = color;
491 verts[*v + 3].fColor = color;
492 verts[*v + 4].fColor = color;
493 verts[*v + 5].fColor = color;
494
bsalomon@google.com81712882012-11-01 17:12:34 +0000495 SkScalar c = segb.fNorms[0].dot(qpts[0]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000496 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
497 verts[*v + 1].fD0 = 0.f;
498 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
499 verts[*v + 3].fD0 = -SK_ScalarMax/100;
500 verts[*v + 4].fD0 = -SK_ScalarMax/100;
501 verts[*v + 5].fD0 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000502
503 c = segb.fNorms[1].dot(qpts[2]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000504 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
505 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
506 verts[*v + 2].fD1 = 0.f;
507 verts[*v + 3].fD1 = -SK_ScalarMax/100;
508 verts[*v + 4].fD1 = -SK_ScalarMax/100;
509 verts[*v + 5].fD1 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000510
bsalomon@google.com19713172012-03-15 13:51:08 +0000511 GrPathUtils::QuadUVMatrix toUV(qpts);
Brian Salomon60fb0b22017-06-15 17:09:36 -0400512 toUV.apply<6, sizeof(QuadVertex), offsetof(QuadVertex, fUV)>(verts + *v);
bsalomon@google.com06809612012-01-21 15:03:39 +0000513
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000514 idxs[*i + 0] = *v + 3;
515 idxs[*i + 1] = *v + 1;
516 idxs[*i + 2] = *v + 2;
517 idxs[*i + 3] = *v + 4;
518 idxs[*i + 4] = *v + 3;
519 idxs[*i + 5] = *v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000520
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000521 idxs[*i + 6] = *v + 5;
522 idxs[*i + 7] = *v + 3;
523 idxs[*i + 8] = *v + 4;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000524
cdalton35964822015-04-29 10:14:03 -0700525 *i += 9;
526
527 // Draw the interior fan if it exists.
528 // TODO: Detect and combine colinear segments. This will ensure we catch every case
529 // with no interior, and that the resulting shared edge uses the same endpoints.
530 if (count >= 3) {
531 idxs[*i + 0] = *v + 0;
532 idxs[*i + 1] = *v + 2;
533 idxs[*i + 2] = *v + 1;
534
535 *i += 3;
536 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000537
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000538 *v += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000539 }
540 }
541}
542
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000543///////////////////////////////////////////////////////////////////////////////
544
545/*
546 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
547 * two components of the vertex attribute. Coverage is based on signed
548 * distance with negative being inside, positive outside. The edge is specified in
549 * window space (y-down). If either the third or fourth component of the interpolated
550 * 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 +0000551 * attempt to trim to a portion of the infinite quad.
552 * Requires shader derivative instruction support.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000553 */
554
joshualitt249af152014-09-15 11:41:13 -0700555class QuadEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000556public:
Brian Salomon60fb0b22017-06-15 17:09:36 -0400557 static sk_sp<GrGeometryProcessor> Make(const SkMatrix& localMatrix, bool usesLocalCoords) {
558 return sk_sp<GrGeometryProcessor>(new QuadEdgeEffect(localMatrix, usesLocalCoords));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000559 }
560
Brian Salomond3b65972017-03-22 12:05:03 -0400561 ~QuadEdgeEffect() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000562
mtklein36352bf2015-03-25 18:17:31 -0700563 const char* name() const override { return "QuadEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000564
egdaniel57d3b032015-11-13 11:57:27 -0800565 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000566 public:
Brian Salomon60fb0b22017-06-15 17:09:36 -0400567 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000568
mtklein36352bf2015-03-25 18:17:31 -0700569 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800570 const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
egdaniel4ca2e602015-11-18 08:01:26 -0800571 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
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
Chris Dalton27372882017-12-08 13:34:21 -0700578 GrGLSLVarying v(kHalf4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800579 varyingHandler->addVarying("QuadEdge", &v);
Brian Salomon92be2f72018-06-19 14:33:47 -0400580 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.kInQuadEdge.name());
Brian Salomon60fb0b22017-06-15 17:09:36 -0400581
582 // Setup pass through color
Brian Salomon92be2f72018-06-19 14:33:47 -0400583 varyingHandler->addPassThroughAttribute(qe.kInColor, args.fOutputColor);
joshualitt2dd1ae02014-12-03 06:24:10 -0800584
Chris Dalton60283612018-02-14 13:38:14 -0700585 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt9b989322014-12-15 14:16:27 -0800586
joshualittabb52a12015-01-13 15:02:10 -0800587 // Setup position
Brian Salomon92be2f72018-06-19 14:33:47 -0400588 this->writeOutputPosition(vertBuilder, gpArgs, qe.kInPosition.name());
joshualittabb52a12015-01-13 15:02:10 -0800589
590 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800591 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800592 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800593 uniformHandler,
Brian Salomon92be2f72018-06-19 14:33:47 -0400594 qe.kInPosition.asShaderVar(),
Brian Salomon60fb0b22017-06-15 17:09:36 -0400595 qe.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700596 args.fFPCoordTransformHandler);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000597
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400598 fragBuilder->codeAppendf("half edgeAlpha;");
joshualitt30ba4362014-08-21 20:18:45 -0700599
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000600 // keep the derivative instructions outside the conditional
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400601 fragBuilder->codeAppendf("half2 duvdx = dFdx(%s.xy);", v.fsIn());
602 fragBuilder->codeAppendf("half2 duvdy = dFdy(%s.xy);", v.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800603 fragBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000604 // today we know z and w are in device space. We could use derivatives
egdaniel4ca2e602015-11-18 08:01:26 -0800605 fragBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
606 v.fsIn());
607 fragBuilder->codeAppendf ("} else {");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400608 fragBuilder->codeAppendf("half2 gF = half2(2.0*%s.x*duvdx.x - duvdx.y,"
egdaniel4ca2e602015-11-18 08:01:26 -0800609 " 2.0*%s.x*duvdy.x - duvdy.y);",
610 v.fsIn(), v.fsIn());
611 fragBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
612 v.fsIn());
613 fragBuilder->codeAppendf("edgeAlpha = "
614 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000615
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400616 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000617 }
618
joshualitt9b989322014-12-15 14:16:27 -0800619 static inline void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500620 const GrShaderCaps&,
joshualitt9b989322014-12-15 14:16:27 -0800621 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700622 const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
Brian Salomon60fb0b22017-06-15 17:09:36 -0400623 b->add32(SkToBool(qee.fUsesLocalCoords && qee.fLocalMatrix.hasPerspective()));
joshualitt9b989322014-12-15 14:16:27 -0800624 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000625
egdaniel018fb622015-10-28 07:26:40 -0700626 void setData(const GrGLSLProgramDataManager& pdman,
bsalomona624bf32016-09-20 09:12:47 -0700627 const GrPrimitiveProcessor& gp,
628 FPCoordTransformIter&& transformIter) override {
joshualittb8c241a2015-05-19 08:23:30 -0700629 const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
bsalomona624bf32016-09-20 09:12:47 -0700630 this->setTransformDataHelper(qe.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700631 }
632
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000633 private:
egdaniele659a582015-11-13 09:55:43 -0800634 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000635 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000636
Brian Salomon94efbf52016-11-29 13:43:05 -0500637 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800638 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800639 }
640
Brian Salomon94efbf52016-11-29 13:43:05 -0500641 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800642 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800643 }
644
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000645private:
Brian Salomon60fb0b22017-06-15 17:09:36 -0400646 QuadEdgeEffect(const SkMatrix& localMatrix, bool usesLocalCoords)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400647 : INHERITED(kQuadEdgeEffect_ClassID)
648 , fLocalMatrix(localMatrix)
649 , fUsesLocalCoords(usesLocalCoords) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400650 this->setVertexAttributeCnt(3);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000651 }
652
Brian Salomon92be2f72018-06-19 14:33:47 -0400653 const Attribute& onVertexAttribute(int i) const override {
654 return IthAttribute(i, kInPosition, kInColor, kInQuadEdge);
655 }
656 static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
657 static constexpr Attribute kInColor = {"inColor", kUByte4_norm_GrVertexAttribType};
658 static constexpr Attribute kInQuadEdge = {"inQuadEdge", kHalf4_GrVertexAttribType};
659 SkMatrix fLocalMatrix;
660 bool fUsesLocalCoords;
joshualitt249af152014-09-15 11:41:13 -0700661
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400662 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000663
joshualitt2e3b3e32014-12-09 13:31:14 -0800664 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000665};
Brian Salomon92be2f72018-06-19 14:33:47 -0400666constexpr GrPrimitiveProcessor::Attribute QuadEdgeEffect::kInPosition;
667constexpr GrPrimitiveProcessor::Attribute QuadEdgeEffect::kInColor;
668constexpr GrPrimitiveProcessor::Attribute QuadEdgeEffect::kInQuadEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000669
joshualittb0a8a372014-09-23 09:50:21 -0700670GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000671
Hal Canary6f6961e2017-01-31 13:50:44 -0500672#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700673sk_sp<GrGeometryProcessor> QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000674 // Doesn't work without derivative instructions.
Robert Phillips296b1cc2017-03-15 10:42:12 -0400675 return d->caps()->shaderCaps()->shaderDerivativeSupport()
Brian Salomon60fb0b22017-06-15 17:09:36 -0400676 ? QuadEdgeEffect::Make(GrTest::TestMatrix(d->fRandom), d->fRandom->nextBool())
Brian Salomon9ae32a22017-01-25 14:58:24 -0500677 : nullptr;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000678}
Hal Canary6f6961e2017-01-31 13:50:44 -0500679#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000680
681///////////////////////////////////////////////////////////////////////////////
682
Chris Dalton5ed44232017-09-07 13:22:46 -0600683GrPathRenderer::CanDrawPath
684GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
685 if (args.fCaps->shaderCaps()->shaderDerivativeSupport() &&
686 (GrAAType::kCoverage == args.fAAType) && args.fShape->style().isSimpleFill() &&
687 !args.fShape->inverseFilled() && args.fShape->knownToBeConvex()) {
688 return CanDrawPath::kYes;
689 }
690 return CanDrawPath::kNo;
robertphillips@google.comfa662942012-05-17 12:20:22 +0000691}
692
robertphillipse16dfdb2015-05-08 04:46:51 -0700693// extract the result vertices and indices from the GrAAConvexTessellator
Brian Salomon60fb0b22017-06-15 17:09:36 -0400694static void extract_lines_only_verts(const GrAAConvexTessellator& tess,
695 void* vertices,
696 size_t vertexStride,
697 GrColor color,
698 uint16_t* idxs,
699 bool tweakAlphaForCoverage) {
robertphillipse16dfdb2015-05-08 04:46:51 -0700700 intptr_t verts = reinterpret_cast<intptr_t>(vertices);
701
702 for (int i = 0; i < tess.numPts(); ++i) {
703 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
704 }
705
706 // Make 'verts' point to the colors
707 verts += sizeof(SkPoint);
708 for (int i = 0; i < tess.numPts(); ++i) {
robertphillipse16dfdb2015-05-08 04:46:51 -0700709 if (tweakAlphaForCoverage) {
fmalitabd5d7e72015-06-26 07:18:24 -0700710 SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
711 unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
robertphillipse16dfdb2015-05-08 04:46:51 -0700712 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
713 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
714 } else {
715 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
halcanary9d524f22016-03-29 09:03:52 -0700716 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
fmalitabd5d7e72015-06-26 07:18:24 -0700717 tess.coverage(i);
robertphillipse16dfdb2015-05-08 04:46:51 -0700718 }
719 }
720
721 for (int i = 0; i < tess.numIndices(); ++i) {
722 idxs[i] = tess.index(i);
723 }
724}
725
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400726static sk_sp<GrGeometryProcessor> make_lines_only_gp(const GrShaderCaps* shaderCaps,
727 bool tweakAlphaForCoverage,
Brian Salomon60fb0b22017-06-15 17:09:36 -0400728 const SkMatrix& viewMatrix,
729 bool usesLocalCoords) {
joshualittdf0c5572015-08-03 11:35:28 -0700730 using namespace GrDefaultGeoProcFactory;
joshualitte494a582015-08-03 09:32:36 -0700731
joshualittdf0c5572015-08-03 11:35:28 -0700732 Coverage::Type coverageType;
Brian Salomon8c852be2017-01-04 10:44:42 -0500733 if (tweakAlphaForCoverage) {
joshualittdf0c5572015-08-03 11:35:28 -0700734 coverageType = Coverage::kSolid_Type;
735 } else {
736 coverageType = Coverage::kAttribute_Type;
737 }
Brian Salomon8c852be2017-01-04 10:44:42 -0500738 LocalCoords::Type localCoordsType =
739 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400740 return MakeForDeviceSpace(shaderCaps,
741 Color::kPremulGrColorAttribute_Type,
742 coverageType,
743 localCoordsType,
Brian Salomon3de0aee2017-01-29 09:34:17 -0500744 viewMatrix);
robertphillipse16dfdb2015-05-08 04:46:51 -0700745}
746
Brian Salomon10978a62017-06-15 16:21:49 -0400747namespace {
748
749class AAConvexPathOp final : public GrMeshDrawOp {
750private:
751 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
752
joshualitt27f398f2015-02-05 14:39:01 -0800753public:
Brian Salomon25a88092016-12-01 09:36:50 -0500754 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400755
Robert Phillips7c525e62018-06-12 10:11:12 -0400756 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
757 GrPaint&& paint,
758 const SkMatrix& viewMatrix,
Brian Salomon10978a62017-06-15 16:21:49 -0400759 const SkPath& path,
760 const GrUserStencilSettings* stencilSettings) {
Robert Phillips7c525e62018-06-12 10:11:12 -0400761 return Helper::FactoryHelper<AAConvexPathOp>(context, std::move(paint), viewMatrix, path,
Brian Salomon10978a62017-06-15 16:21:49 -0400762 stencilSettings);
763 }
764
765 AAConvexPathOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
766 const SkPath& path, const GrUserStencilSettings* stencilSettings)
Brian Salomon60fb0b22017-06-15 17:09:36 -0400767 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage, stencilSettings) {
768 fPaths.emplace_back(PathData{viewMatrix, path, color});
Brian Salomon10978a62017-06-15 16:21:49 -0400769 this->setTransformedBounds(path.getBounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
770 fLinesOnly = SkPath::kLine_SegmentMask == path.getSegmentMasks();
bsalomonf1703092016-06-29 18:41:53 -0700771 }
joshualitt27f398f2015-02-05 14:39:01 -0800772
Brian Salomond0a0a652016-12-15 15:25:22 -0500773 const char* name() const override { return "AAConvexPathOp"; }
joshualitt27f398f2015-02-05 14:39:01 -0800774
Robert Phillipsf1748f52017-09-14 14:11:24 -0400775 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400776 fHelper.visitProxies(func);
777 }
778
Brian Salomon7c3e7182016-12-01 09:35:30 -0500779 SkString dumpInfo() const override {
780 SkString string;
Brian Salomon60fb0b22017-06-15 17:09:36 -0400781 string.appendf("Count: %d\n", fPaths.count());
Brian Salomon10978a62017-06-15 16:21:49 -0400782 string += fHelper.dumpInfo();
783 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -0500784 return string;
785 }
786
Brian Salomon10978a62017-06-15 16:21:49 -0400787 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
788
Brian Osman532b3f92018-07-11 10:02:07 -0400789 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
790 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
Brian Salomon60fb0b22017-06-15 17:09:36 -0400791 &fPaths.back().fColor);
Brian Salomon10978a62017-06-15 16:21:49 -0400792 }
793
bsalomone46f9fe2015-08-18 06:05:14 -0700794private:
Brian Salomon91326c32017-08-09 16:02:19 -0400795 void prepareLinesOnlyDraws(Target* target) {
Brian Salomon60fb0b22017-06-15 17:09:36 -0400796 // Setup GrGeometryProcessor
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400797 sk_sp<GrGeometryProcessor> gp(make_lines_only_gp(target->caps().shaderCaps(),
798 fHelper.compatibleWithAlphaAsCoverage(),
Brian Salomon60fb0b22017-06-15 17:09:36 -0400799 fPaths.back().fViewMatrix,
800 fHelper.usesLocalCoords()));
joshualittdf0c5572015-08-03 11:35:28 -0700801 if (!gp) {
802 SkDebugf("Could not create GrGeometryProcessor\n");
robertphillipse16dfdb2015-05-08 04:46:51 -0700803 return;
804 }
805
Brian Salomon92be2f72018-06-19 14:33:47 -0400806 size_t vertexStride = fHelper.compatibleWithAlphaAsCoverage()
807 ? sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
808 : sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr);
809 SkASSERT(vertexStride == gp->debugOnly_vertexStride());
robertphillipse16dfdb2015-05-08 04:46:51 -0700810
811 GrAAConvexTessellator tess;
812
Brian Salomond0a0a652016-12-15 15:25:22 -0500813 int instanceCount = fPaths.count();
Brian Salomon49348902018-06-26 09:12:38 -0400814 auto pipe = fHelper.makePipeline(target);
robertphillipse16dfdb2015-05-08 04:46:51 -0700815 for (int i = 0; i < instanceCount; i++) {
816 tess.rewind();
817
Brian Salomond0a0a652016-12-15 15:25:22 -0500818 const PathData& args = fPaths[i];
robertphillipse16dfdb2015-05-08 04:46:51 -0700819
820 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
821 continue;
822 }
823
cdalton397536c2016-03-25 12:15:03 -0700824 const GrBuffer* vertexBuffer;
robertphillipse16dfdb2015-05-08 04:46:51 -0700825 int firstVertex;
826
bsalomon75398562015-08-17 12:55:38 -0700827 void* verts = target->makeVertexSpace(vertexStride, tess.numPts(), &vertexBuffer,
828 &firstVertex);
robertphillipse16dfdb2015-05-08 04:46:51 -0700829 if (!verts) {
830 SkDebugf("Could not allocate vertices\n");
831 return;
832 }
833
cdalton397536c2016-03-25 12:15:03 -0700834 const GrBuffer* indexBuffer;
robertphillipse16dfdb2015-05-08 04:46:51 -0700835 int firstIndex;
836
bsalomon75398562015-08-17 12:55:38 -0700837 uint16_t* idxs = target->makeIndexSpace(tess.numIndices(), &indexBuffer, &firstIndex);
robertphillipse16dfdb2015-05-08 04:46:51 -0700838 if (!idxs) {
839 SkDebugf("Could not allocate indices\n");
840 return;
841 }
842
Brian Salomon60fb0b22017-06-15 17:09:36 -0400843 extract_lines_only_verts(tess, verts, vertexStride, args.fColor, idxs,
844 fHelper.compatibleWithAlphaAsCoverage());
robertphillipse16dfdb2015-05-08 04:46:51 -0700845
Chris Dalton3809bab2017-06-13 10:55:06 -0600846 GrMesh mesh(GrPrimitiveType::kTriangles);
Brian Salomon802cb312018-06-08 18:05:20 -0400847 mesh.setIndexed(indexBuffer, tess.numIndices(), firstIndex, 0, tess.numPts() - 1,
848 GrPrimitiveRestart::kNo);
Chris Dalton114a3c02017-05-26 15:17:19 -0600849 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -0400850 target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
robertphillipse16dfdb2015-05-08 04:46:51 -0700851 }
joshualitt27f398f2015-02-05 14:39:01 -0800852 }
853
Brian Salomon91326c32017-08-09 16:02:19 -0400854 void onPrepareDraws(Target* target) override {
robertphillipse16dfdb2015-05-08 04:46:51 -0700855#ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
Brian Salomon10978a62017-06-15 16:21:49 -0400856 if (fLinesOnly) {
bsalomon75398562015-08-17 12:55:38 -0700857 this->prepareLinesOnlyDraws(target);
robertphillipse16dfdb2015-05-08 04:46:51 -0700858 return;
859 }
860#endif
Brian Salomon49348902018-06-26 09:12:38 -0400861 auto pipe = fHelper.makePipeline(target);
Brian Salomond0a0a652016-12-15 15:25:22 -0500862 int instanceCount = fPaths.count();
joshualitt27f398f2015-02-05 14:39:01 -0800863
864 SkMatrix invert;
Brian Salomon10978a62017-06-15 16:21:49 -0400865 if (fHelper.usesLocalCoords() && !fPaths.back().fViewMatrix.invert(&invert)) {
joshualitt27f398f2015-02-05 14:39:01 -0800866 return;
867 }
868
869 // Setup GrGeometryProcessor
bungeman06ca8ec2016-06-09 08:01:03 -0700870 sk_sp<GrGeometryProcessor> quadProcessor(
Brian Salomon60fb0b22017-06-15 17:09:36 -0400871 QuadEdgeEffect::Make(invert, fHelper.usesLocalCoords()));
joshualitt27f398f2015-02-05 14:39:01 -0800872
joshualitt27f398f2015-02-05 14:39:01 -0800873 // TODO generate all segments for all paths and use one vertex buffer
874 for (int i = 0; i < instanceCount; i++) {
Brian Salomond0a0a652016-12-15 15:25:22 -0500875 const PathData& args = fPaths[i];
joshualitt27f398f2015-02-05 14:39:01 -0800876
877 // We use the fact that SkPath::transform path does subdivision based on
878 // perspective. Otherwise, we apply the view matrix when copying to the
879 // segment representation.
880 const SkMatrix* viewMatrix = &args.fViewMatrix;
joshualitt144c3c82015-11-30 12:30:13 -0800881
882 // We avoid initializing the path unless we have to
883 const SkPath* pathPtr = &args.fPath;
884 SkTLazy<SkPath> tmpPath;
joshualitt27f398f2015-02-05 14:39:01 -0800885 if (viewMatrix->hasPerspective()) {
joshualitt144c3c82015-11-30 12:30:13 -0800886 SkPath* tmpPathPtr = tmpPath.init(*pathPtr);
887 tmpPathPtr->setIsVolatile(true);
888 tmpPathPtr->transform(*viewMatrix);
joshualitt27f398f2015-02-05 14:39:01 -0800889 viewMatrix = &SkMatrix::I();
joshualitt144c3c82015-11-30 12:30:13 -0800890 pathPtr = tmpPathPtr;
joshualitt27f398f2015-02-05 14:39:01 -0800891 }
892
893 int vertexCount;
894 int indexCount;
895 enum {
896 kPreallocSegmentCnt = 512 / sizeof(Segment),
897 kPreallocDrawCnt = 4,
898 };
899 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
900 SkPoint fanPt;
901
joshualitt144c3c82015-11-30 12:30:13 -0800902 if (!get_segments(*pathPtr, *viewMatrix, &segments, &fanPt, &vertexCount,
joshualitt27f398f2015-02-05 14:39:01 -0800903 &indexCount)) {
904 continue;
905 }
906
Chris Daltonff926502017-05-03 14:36:54 -0400907 const GrBuffer* vertexBuffer;
Chris Daltonbca46e22017-05-15 11:03:26 -0600908 int firstVertex;
909
Brian Salomon92be2f72018-06-19 14:33:47 -0400910 SkASSERT(sizeof(QuadVertex) == quadProcessor->debugOnly_vertexStride());
bsalomon75398562015-08-17 12:55:38 -0700911 QuadVertex* verts = reinterpret_cast<QuadVertex*>(target->makeVertexSpace(
Brian Salomon92be2f72018-06-19 14:33:47 -0400912 sizeof(QuadVertex), vertexCount, &vertexBuffer, &firstVertex));
bsalomone64eb572015-05-07 11:35:55 -0700913
914 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800915 SkDebugf("Could not allocate vertices\n");
916 return;
917 }
918
cdalton397536c2016-03-25 12:15:03 -0700919 const GrBuffer* indexBuffer;
Chris Daltonbca46e22017-05-15 11:03:26 -0600920 int firstIndex;
921
922 uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
robertphillipse40d3972015-05-07 09:51:43 -0700923 if (!idxs) {
joshualitt4b31de82015-03-05 14:33:41 -0800924 SkDebugf("Could not allocate indices\n");
925 return;
926 }
927
joshualitt27f398f2015-02-05 14:39:01 -0800928 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
Brian Salomon60fb0b22017-06-15 17:09:36 -0400929 create_vertices(segments, fanPt, args.fColor, &draws, verts, idxs);
joshualitt27f398f2015-02-05 14:39:01 -0800930
Chris Dalton3809bab2017-06-13 10:55:06 -0600931 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Daltonbca46e22017-05-15 11:03:26 -0600932
robertphillips44c31282015-09-03 12:58:48 -0700933 for (int j = 0; j < draws.count(); ++j) {
934 const Draw& draw = draws[j];
Brian Salomon802cb312018-06-08 18:05:20 -0400935 mesh.setIndexed(indexBuffer, draw.fIndexCnt, firstIndex, 0, draw.fVertexCnt - 1,
936 GrPrimitiveRestart::kNo);
Chris Dalton114a3c02017-05-26 15:17:19 -0600937 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -0400938 target->draw(quadProcessor.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
Chris Daltonbca46e22017-05-15 11:03:26 -0600939 firstIndex += draw.fIndexCnt;
940 firstVertex += draw.fVertexCnt;
joshualitt27f398f2015-02-05 14:39:01 -0800941 }
942 }
943 }
944
Brian Salomon25a88092016-12-01 09:36:50 -0500945 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomond0a0a652016-12-15 15:25:22 -0500946 AAConvexPathOp* that = t->cast<AAConvexPathOp>();
Brian Salomon10978a62017-06-15 16:21:49 -0400947 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -0700948 return false;
949 }
Brian Salomon10978a62017-06-15 16:21:49 -0400950 if (fHelper.usesLocalCoords() &&
951 !fPaths[0].fViewMatrix.cheapEqualTo(that->fPaths[0].fViewMatrix)) {
joshualitt27f398f2015-02-05 14:39:01 -0800952 return false;
953 }
954
Brian Salomon10978a62017-06-15 16:21:49 -0400955 if (fLinesOnly != that->fLinesOnly) {
robertphillipse16dfdb2015-05-08 04:46:51 -0700956 return false;
957 }
958
Brian Salomond0a0a652016-12-15 15:25:22 -0500959 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700960 this->joinBounds(*that);
joshualitt27f398f2015-02-05 14:39:01 -0800961 return true;
962 }
963
Brian Salomond0a0a652016-12-15 15:25:22 -0500964 struct PathData {
bsalomonf1703092016-06-29 18:41:53 -0700965 SkMatrix fViewMatrix;
966 SkPath fPath;
Brian Salomon60fb0b22017-06-15 17:09:36 -0400967 GrColor fColor;
bsalomonf1703092016-06-29 18:41:53 -0700968 };
969
Brian Salomon10978a62017-06-15 16:21:49 -0400970 Helper fHelper;
Brian Salomond0a0a652016-12-15 15:25:22 -0500971 SkSTArray<1, PathData, true> fPaths;
Brian Salomon10978a62017-06-15 16:21:49 -0400972 bool fLinesOnly;
reed1b55a962015-09-17 20:16:13 -0700973
Brian Salomon10978a62017-06-15 16:21:49 -0400974 typedef GrMeshDrawOp INHERITED;
joshualitt27f398f2015-02-05 14:39:01 -0800975};
976
Brian Salomon10978a62017-06-15 16:21:49 -0400977} // anonymous namespace
978
bsalomon0aff2fa2015-07-31 06:48:27 -0700979bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400980 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700981 "GrAAConvexPathRenderer::onDrawPath");
Brian Salomon7c8460e2017-05-12 11:36:10 -0400982 SkASSERT(GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType());
bsalomon8acedde2016-06-24 10:42:16 -0700983 SkASSERT(!args.fShape->isEmpty());
bsalomon@google.com4647f902013-03-26 14:45:27 +0000984
bsalomonf1703092016-06-29 18:41:53 -0700985 SkPath path;
986 args.fShape->asPath(&path);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000987
Robert Phillips7c525e62018-06-12 10:11:12 -0400988 std::unique_ptr<GrDrawOp> op = AAConvexPathOp::Make(args.fContext, std::move(args.fPaint),
989 *args.fViewMatrix,
Brian Salomon10978a62017-06-15 16:21:49 -0400990 path, args.fUserStencilSettings);
991 args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000992 return true;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000993}
joshualitt8e5c1772015-05-11 08:58:52 -0700994
995///////////////////////////////////////////////////////////////////////////////////////////////////
996
Hal Canary6f6961e2017-01-31 13:50:44 -0500997#if GR_TEST_UTILS
joshualitt8e5c1772015-05-11 08:58:52 -0700998
Brian Salomon10978a62017-06-15 16:21:49 -0400999GR_DRAW_OP_TEST_DEFINE(AAConvexPathOp) {
bsalomonf1703092016-06-29 18:41:53 -07001000 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
1001 SkPath path = GrTest::TestPathConvex(random);
Brian Salomon10978a62017-06-15 16:21:49 -04001002 const GrUserStencilSettings* stencilSettings = GrGetRandomStencil(random, context);
Robert Phillips7c525e62018-06-12 10:11:12 -04001003 return AAConvexPathOp::Make(context, std::move(paint), viewMatrix, path, stencilSettings);
joshualitt8e5c1772015-05-11 08:58:52 -07001004}
1005
1006#endif