blob: 2921b3ee5484551894695eba64f542993a5df9c6 [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"
Brian Osmanf9aabff2018-11-13 16:11:38 -050021#include "GrVertexWriter.h"
egdanielaf18a092015-01-05 10:22:28 -080022#include "SkGeometry.h"
reed026beb52015-06-10 14:23:15 -070023#include "SkPathPriv.h"
Cary Clarkdf429f32017-11-08 11:44:31 -050024#include "SkPointPriv.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000025#include "SkString.h"
commit-bot@chromium.org933e65d2014-03-20 20:00:24 +000026#include "SkTraceEvent.h"
Greg Danield5b45932018-06-07 13:15:10 -040027#include "SkTypes.h"
egdaniel7ea439b2015-12-03 09:20:44 -080028#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080029#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070030#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080031#include "glsl/GrGLSLUniformHandler.h"
egdaniel0eafe792015-11-20 14:01:22 -080032#include "glsl/GrGLSLVarying.h"
Chris Daltonc17bf322017-10-24 10:59:03 -060033#include "glsl/GrGLSLVertexGeoBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050034#include "ops/GrMeshDrawOp.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000035
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000036GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
37}
38
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000039struct Segment {
40 enum {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000041 // These enum values are assumed in member functions below.
42 kLine = 0,
43 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000044 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000045
bsalomon@google.com9aed1142012-01-30 14:28:39 +000046 // line uses one pt, quad uses 2 pts
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000047 SkPoint fPts[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000048 // normal to edge ending at each pt
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000049 SkVector fNorms[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000050 // is the corner where the previous segment meets this segment
51 // sharp. If so, fMid is a normalized bisector facing outward.
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000052 SkVector fMid;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000053
54 int countPoints() {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000055 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
56 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000057 }
58 const SkPoint& endPt() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000059 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
60 return fPts[fType];
Mike Kleinfc6c37b2016-09-27 09:34:10 -040061 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000062 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000063 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
64 return fNorms[fType];
Mike Kleinfc6c37b2016-09-27 09:34:10 -040065 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000066};
67
68typedef SkTArray<Segment, true> SegmentArray;
69
Greg Daniel8b09b962018-04-03 14:53:45 -040070static bool center_of_mass(const SegmentArray& segments, SkPoint* c) {
bsalomon@google.com81712882012-11-01 17:12:34 +000071 SkScalar area = 0;
vandebo@chromium.org6390c722012-03-28 21:03:22 +000072 SkPoint center = {0, 0};
bsalomon@google.com9aed1142012-01-30 14:28:39 +000073 int count = segments.count();
vandebo@chromium.org6390c722012-03-28 21:03:22 +000074 SkPoint p0 = {0, 0};
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000075 if (count > 2) {
76 // We translate the polygon so that the first point is at the origin.
77 // This avoids some precision issues with small area polygons far away
78 // from the origin.
79 p0 = segments[0].endPt();
80 SkPoint pi;
81 SkPoint pj;
bsalomon@google.coma51ab842012-07-10 19:53:34 +000082 // the first and last iteration of the below loop would compute
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000083 // zeros since the starting / ending point is (0,0). So instead we start
84 // at i=1 and make the last iteration i=count-2.
85 pj = segments[1].endPt() - p0;
86 for (int i = 1; i < count - 1; ++i) {
87 pi = pj;
robertphillips44c31282015-09-03 12:58:48 -070088 pj = segments[i + 1].endPt() - p0;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000089
robertphillips44c31282015-09-03 12:58:48 -070090 SkScalar t = SkPoint::CrossProduct(pi, pj);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000091 area += t;
92 center.fX += (pi.fX + pj.fX) * t;
93 center.fY += (pi.fY + pj.fY) * t;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000094 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000095 }
robertphillips44c31282015-09-03 12:58:48 -070096
bsalomon@google.com278dc692012-02-15 16:52:51 +000097 // If the poly has no area then we instead return the average of
98 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000099 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +0000100 SkPoint avg;
101 avg.set(0, 0);
102 for (int i = 0; i < count; ++i) {
103 const SkPoint& pt = segments[i].endPt();
104 avg.fX += pt.fX;
105 avg.fY += pt.fY;
106 }
107 SkScalar denom = SK_Scalar1 / count;
108 avg.scale(denom);
109 *c = avg;
110 } else {
111 area *= 3;
reed80ea19c2015-05-12 10:37:34 -0700112 area = SkScalarInvert(area);
robertphillips44c31282015-09-03 12:58:48 -0700113 center.scale(area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +0000114 // undo the translate of p0 to the origin.
115 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000116 }
Greg Daniel62473ad2018-04-03 15:44:18 -0400117 return !SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY) && c->isFinite();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000118}
119
Greg Daniel8b09b962018-04-03 14:53:45 -0400120static bool compute_vectors(SegmentArray* segments,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000121 SkPoint* fanPt,
reed026beb52015-06-10 14:23:15 -0700122 SkPathPriv::FirstDirection dir,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000123 int* vCount,
124 int* iCount) {
Greg Daniel8b09b962018-04-03 14:53:45 -0400125 if (!center_of_mass(*segments, fanPt)) {
126 return false;
127 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000128 int count = segments->count();
129
bsalomon@google.com278dc692012-02-15 16:52:51 +0000130 // Make the normals point towards the outside
Cary Clarkdf429f32017-11-08 11:44:31 -0500131 SkPointPriv::Side normSide;
reed026beb52015-06-10 14:23:15 -0700132 if (dir == SkPathPriv::kCCW_FirstDirection) {
Cary Clarkdf429f32017-11-08 11:44:31 -0500133 normSide = SkPointPriv::kRight_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000134 } else {
Cary Clarkdf429f32017-11-08 11:44:31 -0500135 normSide = SkPointPriv::kLeft_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000136 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000137
Greg Danield5b45932018-06-07 13:15:10 -0400138 int64_t vCount64 = 0;
139 int64_t iCount64 = 0;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000140 // compute normals at all points
141 for (int a = 0; a < count; ++a) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000142 Segment& sega = (*segments)[a];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000143 int b = (a + 1) % count;
144 Segment& segb = (*segments)[b];
145
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000146 const SkPoint* prevPt = &sega.endPt();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000147 int n = segb.countPoints();
148 for (int p = 0; p < n; ++p) {
149 segb.fNorms[p] = segb.fPts[p] - *prevPt;
150 segb.fNorms[p].normalize();
Brian Salomon0235c642018-08-31 12:04:18 -0400151 segb.fNorms[p] = SkPointPriv::MakeOrthog(segb.fNorms[p], normSide);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000152 prevPt = &segb.fPts[p];
153 }
154 if (Segment::kLine == segb.fType) {
Greg Danield5b45932018-06-07 13:15:10 -0400155 vCount64 += 5;
156 iCount64 += 9;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000157 } else {
Greg Danield5b45932018-06-07 13:15:10 -0400158 vCount64 += 6;
159 iCount64 += 12;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000160 }
161 }
162
163 // compute mid-vectors where segments meet. TODO: Detect shallow corners
164 // and leave out the wedges and close gaps by stitching segments together.
165 for (int a = 0; a < count; ++a) {
166 const Segment& sega = (*segments)[a];
167 int b = (a + 1) % count;
168 Segment& segb = (*segments)[b];
169 segb.fMid = segb.fNorms[0] + sega.endNorm();
170 segb.fMid.normalize();
171 // corner wedges
Greg Danield5b45932018-06-07 13:15:10 -0400172 vCount64 += 4;
173 iCount64 += 6;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000174 }
Greg Danield5b45932018-06-07 13:15:10 -0400175 if (vCount64 > SK_MaxS32 || iCount64 > SK_MaxS32) {
176 return false;
177 }
178 *vCount = vCount64;
179 *iCount = iCount64;
Greg Daniel8b09b962018-04-03 14:53:45 -0400180 return true;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000181}
182
bsalomon@google.com9732f622012-01-31 15:19:21 +0000183struct DegenerateTestData {
184 DegenerateTestData() { fStage = kInitial; }
185 bool isDegenerate() const { return kNonDegenerate != fStage; }
186 enum {
187 kInitial,
188 kPoint,
189 kLine,
190 kNonDegenerate
191 } fStage;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000192 SkPoint fFirstPoint;
193 SkVector fLineNormal;
bsalomon@google.com81712882012-11-01 17:12:34 +0000194 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000195};
196
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000197static const SkScalar kClose = (SK_Scalar1 / 16);
Mike Reed8be952a2017-02-13 20:44:33 -0500198static const SkScalar kCloseSqd = kClose * kClose;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000199
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000200static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000201 switch (data->fStage) {
202 case DegenerateTestData::kInitial:
203 data->fFirstPoint = pt;
204 data->fStage = DegenerateTestData::kPoint;
205 break;
206 case DegenerateTestData::kPoint:
Cary Clarkdf429f32017-11-08 11:44:31 -0500207 if (SkPointPriv::DistanceToSqd(pt, data->fFirstPoint) > kCloseSqd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000208 data->fLineNormal = pt - data->fFirstPoint;
209 data->fLineNormal.normalize();
Brian Salomon0235c642018-08-31 12:04:18 -0400210 data->fLineNormal = SkPointPriv::MakeOrthog(data->fLineNormal);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000211 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
212 data->fStage = DegenerateTestData::kLine;
213 }
214 break;
215 case DegenerateTestData::kLine:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000216 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000217 data->fStage = DegenerateTestData::kNonDegenerate;
218 }
219 case DegenerateTestData::kNonDegenerate:
220 break;
221 default:
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400222 SK_ABORT("Unexpected degenerate test stage.");
bsalomon@google.com9732f622012-01-31 15:19:21 +0000223 }
224}
225
reed026beb52015-06-10 14:23:15 -0700226static inline bool get_direction(const SkPath& path, const SkMatrix& m,
227 SkPathPriv::FirstDirection* dir) {
228 if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000229 return false;
230 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000231 // check whether m reverses the orientation
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000232 SkASSERT(!m.hasPerspective());
Mike Reed8be952a2017-02-13 20:44:33 -0500233 SkScalar det2x2 = m.get(SkMatrix::kMScaleX) * m.get(SkMatrix::kMScaleY) -
234 m.get(SkMatrix::kMSkewX) * m.get(SkMatrix::kMSkewY);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000235 if (det2x2 < 0) {
reed026beb52015-06-10 14:23:15 -0700236 *dir = SkPathPriv::OppositeFirstDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000237 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000238 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000239}
240
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000241static inline void add_line_to_segment(const SkPoint& pt,
joshualitt27f398f2015-02-05 14:39:01 -0800242 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000243 segments->push_back();
244 segments->back().fType = Segment::kLine;
245 segments->back().fPts[0] = pt;
246}
247
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000248static inline void add_quad_segment(const SkPoint pts[3],
joshualitt27f398f2015-02-05 14:39:01 -0800249 SegmentArray* segments) {
Cary Clarkdf429f32017-11-08 11:44:31 -0500250 if (SkPointPriv::DistanceToSqd(pts[0], pts[1]) < kCloseSqd ||
251 SkPointPriv::DistanceToSqd(pts[1], pts[2]) < kCloseSqd) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000252 if (pts[0] != pts[2]) {
joshualitt27f398f2015-02-05 14:39:01 -0800253 add_line_to_segment(pts[2], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000254 }
255 } else {
256 segments->push_back();
257 segments->back().fType = Segment::kQuad;
258 segments->back().fPts[0] = pts[1];
259 segments->back().fPts[1] = pts[2];
260 }
261}
262
263static inline void add_cubic_segments(const SkPoint pts[4],
reed026beb52015-06-10 14:23:15 -0700264 SkPathPriv::FirstDirection dir,
joshualitt27f398f2015-02-05 14:39:01 -0800265 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000266 SkSTArray<15, SkPoint, true> quads;
bsalomon18fab302016-02-16 08:00:05 -0800267 GrPathUtils::convertCubicToQuadsConstrainToTangents(pts, SK_Scalar1, dir, &quads);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000268 int count = quads.count();
269 for (int q = 0; q < count; q += 3) {
joshualitt27f398f2015-02-05 14:39:01 -0800270 add_quad_segment(&quads[q], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000271 }
272}
273
274static bool get_segments(const SkPath& path,
275 const SkMatrix& m,
276 SegmentArray* segments,
277 SkPoint* fanPt,
278 int* vCount,
joshualitt27f398f2015-02-05 14:39:01 -0800279 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000280 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000281 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000282 // to the path from the sample to compute coverage. Every pixel intersected
283 // 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 +0000284 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000285 // thus should be very light. This is particularly egregious for degenerate
286 // line paths. We detect paths that are very close to a line (zero area) and
287 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000288 DegenerateTestData degenerateData;
reed026beb52015-06-10 14:23:15 -0700289 SkPathPriv::FirstDirection dir;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000290 // get_direction can fail for some degenerate paths.
291 if (!get_direction(path, m, &dir)) {
292 return false;
293 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000294
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000295 for (;;) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000296 SkPoint pts[4];
Brian Salomon97042bf2017-02-28 11:21:28 -0500297 SkPath::Verb verb = iter.next(pts, true, true);
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000298 switch (verb) {
299 case SkPath::kMove_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000300 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000301 update_degenerate_test(&degenerateData, pts[0]);
302 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000303 case SkPath::kLine_Verb: {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000304 m.mapPoints(&pts[1], 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000305 update_degenerate_test(&degenerateData, pts[1]);
joshualitt27f398f2015-02-05 14:39:01 -0800306 add_line_to_segment(pts[1], segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000307 break;
308 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000309 case SkPath::kQuad_Verb:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000310 m.mapPoints(pts, 3);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000311 update_degenerate_test(&degenerateData, pts[1]);
312 update_degenerate_test(&degenerateData, pts[2]);
joshualitt27f398f2015-02-05 14:39:01 -0800313 add_quad_segment(pts, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000314 break;
egdanielaf18a092015-01-05 10:22:28 -0800315 case SkPath::kConic_Verb: {
316 m.mapPoints(pts, 3);
317 SkScalar weight = iter.conicWeight();
318 SkAutoConicToQuads converter;
Brian Osmane3deee12018-11-20 11:10:15 -0500319 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
egdanielaf18a092015-01-05 10:22:28 -0800320 for (int i = 0; i < converter.countQuads(); ++i) {
321 update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
322 update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
joshualitt27f398f2015-02-05 14:39:01 -0800323 add_quad_segment(quadPts + 2*i, segments);
egdanielaf18a092015-01-05 10:22:28 -0800324 }
325 break;
326 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000327 case SkPath::kCubic_Verb: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000328 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000329 update_degenerate_test(&degenerateData, pts[1]);
330 update_degenerate_test(&degenerateData, pts[2]);
331 update_degenerate_test(&degenerateData, pts[3]);
joshualitt27f398f2015-02-05 14:39:01 -0800332 add_cubic_segments(pts, dir, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000333 break;
334 };
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000335 case SkPath::kDone_Verb:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000336 if (degenerateData.isDegenerate()) {
337 return false;
338 } else {
Greg Daniel8b09b962018-04-03 14:53:45 -0400339 return compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000340 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000341 default:
342 break;
343 }
344 }
345}
346
347struct QuadVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000348 SkPoint fPos;
Brian Salomon60fb0b22017-06-15 17:09:36 -0400349 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000350 SkPoint fUV;
bsalomon@google.com81712882012-11-01 17:12:34 +0000351 SkScalar fD0;
352 SkScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000353};
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000354
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000355struct Draw {
356 Draw() : fVertexCnt(0), fIndexCnt(0) {}
357 int fVertexCnt;
358 int fIndexCnt;
359};
360
361typedef SkTArray<Draw, true> DrawArray;
362
Brian Salomon60fb0b22017-06-15 17:09:36 -0400363static void create_vertices(const SegmentArray& segments,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000364 const SkPoint& fanPt,
Brian Salomon60fb0b22017-06-15 17:09:36 -0400365 GrColor color,
366 DrawArray* draws,
367 QuadVertex* verts,
368 uint16_t* idxs) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000369 Draw* draw = &draws->push_back();
370 // alias just to make vert/index assignments easier to read.
371 int* v = &draw->fVertexCnt;
372 int* i = &draw->fIndexCnt;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000373
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000374 int count = segments.count();
375 for (int a = 0; a < count; ++a) {
376 const Segment& sega = segments[a];
377 int b = (a + 1) % count;
378 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000379
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000380 // Check whether adding the verts for this segment to the current draw would cause index
381 // values to overflow.
382 int vCount = 4;
383 if (Segment::kLine == segb.fType) {
384 vCount += 5;
385 } else {
386 vCount += 6;
387 }
388 if (draw->fVertexCnt + vCount > (1 << 16)) {
389 verts += *v;
390 idxs += *i;
391 draw = &draws->push_back();
392 v = &draw->fVertexCnt;
393 i = &draw->fIndexCnt;
394 }
395
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000396 // FIXME: These tris are inset in the 1 unit arc around the corner
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000397 verts[*v + 0].fPos = sega.endPt();
398 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
399 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
400 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
Brian Salomon60fb0b22017-06-15 17:09:36 -0400401 verts[*v + 0].fColor = color;
402 verts[*v + 1].fColor = color;
403 verts[*v + 2].fColor = color;
404 verts[*v + 3].fColor = color;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000405 verts[*v + 0].fUV.set(0,0);
406 verts[*v + 1].fUV.set(0,-SK_Scalar1);
407 verts[*v + 2].fUV.set(0,-SK_Scalar1);
408 verts[*v + 3].fUV.set(0,-SK_Scalar1);
409 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
410 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
411 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
412 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000413
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000414 idxs[*i + 0] = *v + 0;
415 idxs[*i + 1] = *v + 2;
416 idxs[*i + 2] = *v + 1;
417 idxs[*i + 3] = *v + 0;
418 idxs[*i + 4] = *v + 3;
419 idxs[*i + 5] = *v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000420
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000421 *v += 4;
422 *i += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000423
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000424 if (Segment::kLine == segb.fType) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000425 verts[*v + 0].fPos = fanPt;
426 verts[*v + 1].fPos = sega.endPt();
427 verts[*v + 2].fPos = segb.fPts[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000428
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000429 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
430 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000431
Brian Salomon60fb0b22017-06-15 17:09:36 -0400432 verts[*v + 0].fColor = color;
433 verts[*v + 1].fColor = color;
434 verts[*v + 2].fColor = color;
435 verts[*v + 3].fColor = color;
436 verts[*v + 4].fColor = color;
437
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000438 // we draw the line edge as a degenerate quad (u is 0, v is the
439 // signed distance to the edge)
Cary Clarkdf429f32017-11-08 11:44:31 -0500440 SkScalar dist = SkPointPriv::DistanceToLineBetween(fanPt, verts[*v + 1].fPos,
441 verts[*v + 2].fPos);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000442 verts[*v + 0].fUV.set(0, dist);
443 verts[*v + 1].fUV.set(0, 0);
444 verts[*v + 2].fUV.set(0, 0);
445 verts[*v + 3].fUV.set(0, -SK_Scalar1);
446 verts[*v + 4].fUV.set(0, -SK_Scalar1);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000447
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000448 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
449 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
450 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
451 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
452 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000453
cdalton35964822015-04-29 10:14:03 -0700454 idxs[*i + 0] = *v + 3;
455 idxs[*i + 1] = *v + 1;
456 idxs[*i + 2] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000457
cdalton35964822015-04-29 10:14:03 -0700458 idxs[*i + 3] = *v + 4;
459 idxs[*i + 4] = *v + 3;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000460 idxs[*i + 5] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000461
cdalton35964822015-04-29 10:14:03 -0700462 *i += 6;
463
464 // Draw the interior fan if it exists.
465 // TODO: Detect and combine colinear segments. This will ensure we catch every case
466 // with no interior, and that the resulting shared edge uses the same endpoints.
467 if (count >= 3) {
468 idxs[*i + 0] = *v + 0;
469 idxs[*i + 1] = *v + 2;
470 idxs[*i + 2] = *v + 1;
471
472 *i += 3;
473 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000474
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000475 *v += 5;
bsalomon@google.com06809612012-01-21 15:03:39 +0000476 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000477 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000478
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000479 SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000480 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000481
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000482 verts[*v + 0].fPos = fanPt;
483 verts[*v + 1].fPos = qpts[0];
484 verts[*v + 2].fPos = qpts[2];
485 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
486 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
487 verts[*v + 5].fPos = qpts[1] + midVec;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000488
Brian Salomon60fb0b22017-06-15 17:09:36 -0400489 verts[*v + 0].fColor = color;
490 verts[*v + 1].fColor = color;
491 verts[*v + 2].fColor = color;
492 verts[*v + 3].fColor = color;
493 verts[*v + 4].fColor = color;
494 verts[*v + 5].fColor = color;
495
bsalomon@google.com81712882012-11-01 17:12:34 +0000496 SkScalar c = segb.fNorms[0].dot(qpts[0]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000497 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
498 verts[*v + 1].fD0 = 0.f;
499 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
500 verts[*v + 3].fD0 = -SK_ScalarMax/100;
501 verts[*v + 4].fD0 = -SK_ScalarMax/100;
502 verts[*v + 5].fD0 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000503
504 c = segb.fNorms[1].dot(qpts[2]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000505 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
506 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
507 verts[*v + 2].fD1 = 0.f;
508 verts[*v + 3].fD1 = -SK_ScalarMax/100;
509 verts[*v + 4].fD1 = -SK_ScalarMax/100;
510 verts[*v + 5].fD1 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000511
bsalomon@google.com19713172012-03-15 13:51:08 +0000512 GrPathUtils::QuadUVMatrix toUV(qpts);
Brian Salomon60fb0b22017-06-15 17:09:36 -0400513 toUV.apply<6, sizeof(QuadVertex), offsetof(QuadVertex, fUV)>(verts + *v);
bsalomon@google.com06809612012-01-21 15:03:39 +0000514
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000515 idxs[*i + 0] = *v + 3;
516 idxs[*i + 1] = *v + 1;
517 idxs[*i + 2] = *v + 2;
518 idxs[*i + 3] = *v + 4;
519 idxs[*i + 4] = *v + 3;
520 idxs[*i + 5] = *v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000521
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000522 idxs[*i + 6] = *v + 5;
523 idxs[*i + 7] = *v + 3;
524 idxs[*i + 8] = *v + 4;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000525
cdalton35964822015-04-29 10:14:03 -0700526 *i += 9;
527
528 // Draw the interior fan if it exists.
529 // TODO: Detect and combine colinear segments. This will ensure we catch every case
530 // with no interior, and that the resulting shared edge uses the same endpoints.
531 if (count >= 3) {
532 idxs[*i + 0] = *v + 0;
533 idxs[*i + 1] = *v + 2;
534 idxs[*i + 2] = *v + 1;
535
536 *i += 3;
537 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000538
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000539 *v += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000540 }
541 }
542}
543
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000544///////////////////////////////////////////////////////////////////////////////
545
546/*
547 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
548 * two components of the vertex attribute. Coverage is based on signed
549 * distance with negative being inside, positive outside. The edge is specified in
550 * window space (y-down). If either the third or fourth component of the interpolated
551 * 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 +0000552 * attempt to trim to a portion of the infinite quad.
553 * Requires shader derivative instruction support.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000554 */
555
joshualitt249af152014-09-15 11:41:13 -0700556class QuadEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000557public:
Brian Salomon60fb0b22017-06-15 17:09:36 -0400558 static sk_sp<GrGeometryProcessor> Make(const SkMatrix& localMatrix, bool usesLocalCoords) {
559 return sk_sp<GrGeometryProcessor>(new QuadEdgeEffect(localMatrix, usesLocalCoords));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000560 }
561
Brian Salomond3b65972017-03-22 12:05:03 -0400562 ~QuadEdgeEffect() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000563
mtklein36352bf2015-03-25 18:17:31 -0700564 const char* name() const override { return "QuadEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000565
egdaniel57d3b032015-11-13 11:57:27 -0800566 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000567 public:
Brian Salomon60fb0b22017-06-15 17:09:36 -0400568 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000569
mtklein36352bf2015-03-25 18:17:31 -0700570 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800571 const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
egdaniel4ca2e602015-11-18 08:01:26 -0800572 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800573 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800574 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -0800575
joshualittabb52a12015-01-13 15:02:10 -0800576 // emit attributes
egdaniel0eafe792015-11-20 14:01:22 -0800577 varyingHandler->emitAttributes(qe);
joshualittabb52a12015-01-13 15:02:10 -0800578
Chris Dalton27372882017-12-08 13:34:21 -0700579 GrGLSLVarying v(kHalf4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800580 varyingHandler->addVarying("QuadEdge", &v);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500581 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.fInQuadEdge.name());
Brian Salomon60fb0b22017-06-15 17:09:36 -0400582
583 // Setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500584 varyingHandler->addPassThroughAttribute(qe.fInColor, args.fOutputColor);
joshualitt2dd1ae02014-12-03 06:24:10 -0800585
Chris Dalton60283612018-02-14 13:38:14 -0700586 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt9b989322014-12-15 14:16:27 -0800587
joshualittabb52a12015-01-13 15:02:10 -0800588 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500589 this->writeOutputPosition(vertBuilder, gpArgs, qe.fInPosition.name());
joshualittabb52a12015-01-13 15:02:10 -0800590
591 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800592 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800593 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800594 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500595 qe.fInPosition.asShaderVar(),
Brian Salomon60fb0b22017-06-15 17:09:36 -0400596 qe.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700597 args.fFPCoordTransformHandler);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000598
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400599 fragBuilder->codeAppendf("half edgeAlpha;");
joshualitt30ba4362014-08-21 20:18:45 -0700600
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000601 // keep the derivative instructions outside the conditional
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400602 fragBuilder->codeAppendf("half2 duvdx = dFdx(%s.xy);", v.fsIn());
603 fragBuilder->codeAppendf("half2 duvdy = dFdy(%s.xy);", v.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800604 fragBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000605 // today we know z and w are in device space. We could use derivatives
egdaniel4ca2e602015-11-18 08:01:26 -0800606 fragBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
607 v.fsIn());
608 fragBuilder->codeAppendf ("} else {");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400609 fragBuilder->codeAppendf("half2 gF = half2(2.0*%s.x*duvdx.x - duvdx.y,"
egdaniel4ca2e602015-11-18 08:01:26 -0800610 " 2.0*%s.x*duvdy.x - duvdy.y);",
611 v.fsIn(), v.fsIn());
612 fragBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
613 v.fsIn());
614 fragBuilder->codeAppendf("edgeAlpha = "
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400615 "saturate(0.5 - edgeAlpha / length(gF));}");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000616
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400617 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000618 }
619
joshualitt9b989322014-12-15 14:16:27 -0800620 static inline void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500621 const GrShaderCaps&,
joshualitt9b989322014-12-15 14:16:27 -0800622 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700623 const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
Brian Salomon60fb0b22017-06-15 17:09:36 -0400624 b->add32(SkToBool(qee.fUsesLocalCoords && qee.fLocalMatrix.hasPerspective()));
joshualitt9b989322014-12-15 14:16:27 -0800625 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000626
egdaniel018fb622015-10-28 07:26:40 -0700627 void setData(const GrGLSLProgramDataManager& pdman,
bsalomona624bf32016-09-20 09:12:47 -0700628 const GrPrimitiveProcessor& gp,
629 FPCoordTransformIter&& transformIter) override {
joshualittb8c241a2015-05-19 08:23:30 -0700630 const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
bsalomona624bf32016-09-20 09:12:47 -0700631 this->setTransformDataHelper(qe.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700632 }
633
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000634 private:
egdaniele659a582015-11-13 09:55:43 -0800635 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000636 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000637
Brian Salomon94efbf52016-11-29 13:43:05 -0500638 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800639 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800640 }
641
Brian Salomon94efbf52016-11-29 13:43:05 -0500642 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
egdaniel57d3b032015-11-13 11:57:27 -0800643 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800644 }
645
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000646private:
Brian Salomon60fb0b22017-06-15 17:09:36 -0400647 QuadEdgeEffect(const SkMatrix& localMatrix, bool usesLocalCoords)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400648 : INHERITED(kQuadEdgeEffect_ClassID)
649 , fLocalMatrix(localMatrix)
650 , fUsesLocalCoords(usesLocalCoords) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500651 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
652 fInColor = {"inColor", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
653 fInQuadEdge = {"inQuadEdge", kFloat4_GrVertexAttribType, kHalf4_GrSLType};
654 this->setVertexAttributes(&fInPosition, 3);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000655 }
656
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500657 Attribute fInPosition;
658 Attribute fInColor;
659 Attribute fInQuadEdge;
660
Brian Salomon92be2f72018-06-19 14:33:47 -0400661 SkMatrix fLocalMatrix;
662 bool fUsesLocalCoords;
joshualitt249af152014-09-15 11:41:13 -0700663
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400664 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000665
joshualitt2e3b3e32014-12-09 13:31:14 -0800666 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000667};
668
joshualittb0a8a372014-09-23 09:50:21 -0700669GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000670
Hal Canary6f6961e2017-01-31 13:50:44 -0500671#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700672sk_sp<GrGeometryProcessor> QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000673 // Doesn't work without derivative instructions.
Robert Phillips296b1cc2017-03-15 10:42:12 -0400674 return d->caps()->shaderCaps()->shaderDerivativeSupport()
Brian Salomon60fb0b22017-06-15 17:09:36 -0400675 ? QuadEdgeEffect::Make(GrTest::TestMatrix(d->fRandom), d->fRandom->nextBool())
Brian Salomon9ae32a22017-01-25 14:58:24 -0500676 : nullptr;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000677}
Hal Canary6f6961e2017-01-31 13:50:44 -0500678#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000679
680///////////////////////////////////////////////////////////////////////////////
681
Chris Dalton5ed44232017-09-07 13:22:46 -0600682GrPathRenderer::CanDrawPath
683GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
684 if (args.fCaps->shaderCaps()->shaderDerivativeSupport() &&
685 (GrAAType::kCoverage == args.fAAType) && args.fShape->style().isSimpleFill() &&
686 !args.fShape->inverseFilled() && args.fShape->knownToBeConvex()) {
687 return CanDrawPath::kYes;
688 }
689 return CanDrawPath::kNo;
robertphillips@google.comfa662942012-05-17 12:20:22 +0000690}
691
robertphillipse16dfdb2015-05-08 04:46:51 -0700692// extract the result vertices and indices from the GrAAConvexTessellator
Brian Salomon60fb0b22017-06-15 17:09:36 -0400693static void extract_lines_only_verts(const GrAAConvexTessellator& tess,
Brian Osmanf9aabff2018-11-13 16:11:38 -0500694 void* vertData,
Brian Salomon60fb0b22017-06-15 17:09:36 -0400695 GrColor color,
696 uint16_t* idxs,
697 bool tweakAlphaForCoverage) {
Brian Osmanf9aabff2018-11-13 16:11:38 -0500698 GrVertexWriter verts{vertData};
robertphillipse16dfdb2015-05-08 04:46:51 -0700699 for (int i = 0; i < tess.numPts(); ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -0500700 verts.write(tess.point(i));
robertphillipse16dfdb2015-05-08 04:46:51 -0700701 if (tweakAlphaForCoverage) {
fmalitabd5d7e72015-06-26 07:18:24 -0700702 SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
703 unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
robertphillipse16dfdb2015-05-08 04:46:51 -0700704 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
Brian Osmanf9aabff2018-11-13 16:11:38 -0500705 verts.write(scaledColor);
robertphillipse16dfdb2015-05-08 04:46:51 -0700706 } else {
Brian Osmanf9aabff2018-11-13 16:11:38 -0500707 verts.write(color, tess.coverage(i));
robertphillipse16dfdb2015-05-08 04:46:51 -0700708 }
709 }
710
711 for (int i = 0; i < tess.numIndices(); ++i) {
712 idxs[i] = tess.index(i);
713 }
714}
715
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400716static sk_sp<GrGeometryProcessor> make_lines_only_gp(const GrShaderCaps* shaderCaps,
717 bool tweakAlphaForCoverage,
Brian Salomon60fb0b22017-06-15 17:09:36 -0400718 const SkMatrix& viewMatrix,
719 bool usesLocalCoords) {
joshualittdf0c5572015-08-03 11:35:28 -0700720 using namespace GrDefaultGeoProcFactory;
joshualitte494a582015-08-03 09:32:36 -0700721
joshualittdf0c5572015-08-03 11:35:28 -0700722 Coverage::Type coverageType;
Brian Salomon8c852be2017-01-04 10:44:42 -0500723 if (tweakAlphaForCoverage) {
joshualittdf0c5572015-08-03 11:35:28 -0700724 coverageType = Coverage::kSolid_Type;
725 } else {
726 coverageType = Coverage::kAttribute_Type;
727 }
Brian Salomon8c852be2017-01-04 10:44:42 -0500728 LocalCoords::Type localCoordsType =
729 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400730 return MakeForDeviceSpace(shaderCaps,
731 Color::kPremulGrColorAttribute_Type,
732 coverageType,
733 localCoordsType,
Brian Salomon3de0aee2017-01-29 09:34:17 -0500734 viewMatrix);
robertphillipse16dfdb2015-05-08 04:46:51 -0700735}
736
Brian Salomon10978a62017-06-15 16:21:49 -0400737namespace {
738
739class AAConvexPathOp final : public GrMeshDrawOp {
740private:
741 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
742
joshualitt27f398f2015-02-05 14:39:01 -0800743public:
Brian Salomon25a88092016-12-01 09:36:50 -0500744 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400745
Robert Phillips7c525e62018-06-12 10:11:12 -0400746 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
747 GrPaint&& paint,
748 const SkMatrix& viewMatrix,
Brian Salomon10978a62017-06-15 16:21:49 -0400749 const SkPath& path,
750 const GrUserStencilSettings* stencilSettings) {
Robert Phillips7c525e62018-06-12 10:11:12 -0400751 return Helper::FactoryHelper<AAConvexPathOp>(context, std::move(paint), viewMatrix, path,
Brian Salomon10978a62017-06-15 16:21:49 -0400752 stencilSettings);
753 }
754
Brian Osmancf860852018-10-31 14:04:39 -0400755 AAConvexPathOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -0400756 const SkMatrix& viewMatrix, const SkPath& path,
757 const GrUserStencilSettings* stencilSettings)
Brian Salomon60fb0b22017-06-15 17:09:36 -0400758 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage, stencilSettings) {
759 fPaths.emplace_back(PathData{viewMatrix, path, color});
Brian Salomon10978a62017-06-15 16:21:49 -0400760 this->setTransformedBounds(path.getBounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
761 fLinesOnly = SkPath::kLine_SegmentMask == path.getSegmentMasks();
bsalomonf1703092016-06-29 18:41:53 -0700762 }
joshualitt27f398f2015-02-05 14:39:01 -0800763
Brian Salomond0a0a652016-12-15 15:25:22 -0500764 const char* name() const override { return "AAConvexPathOp"; }
joshualitt27f398f2015-02-05 14:39:01 -0800765
Brian Salomon7d94bb52018-10-12 14:37:19 -0400766 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400767 fHelper.visitProxies(func);
768 }
769
Brian Osman9a390ac2018-11-12 09:47:48 -0500770#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -0500771 SkString dumpInfo() const override {
772 SkString string;
Brian Salomon60fb0b22017-06-15 17:09:36 -0400773 string.appendf("Count: %d\n", fPaths.count());
Brian Salomon10978a62017-06-15 16:21:49 -0400774 string += fHelper.dumpInfo();
775 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -0500776 return string;
777 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500778#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -0500779
Brian Salomon10978a62017-06-15 16:21:49 -0400780 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
781
Brian Osman532b3f92018-07-11 10:02:07 -0400782 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
783 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
Brian Salomon60fb0b22017-06-15 17:09:36 -0400784 &fPaths.back().fColor);
Brian Salomon10978a62017-06-15 16:21:49 -0400785 }
786
bsalomone46f9fe2015-08-18 06:05:14 -0700787private:
Brian Salomon91326c32017-08-09 16:02:19 -0400788 void prepareLinesOnlyDraws(Target* target) {
Brian Salomon60fb0b22017-06-15 17:09:36 -0400789 // Setup GrGeometryProcessor
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400790 sk_sp<GrGeometryProcessor> gp(make_lines_only_gp(target->caps().shaderCaps(),
791 fHelper.compatibleWithAlphaAsCoverage(),
Brian Salomon60fb0b22017-06-15 17:09:36 -0400792 fPaths.back().fViewMatrix,
793 fHelper.usesLocalCoords()));
joshualittdf0c5572015-08-03 11:35:28 -0700794 if (!gp) {
795 SkDebugf("Could not create GrGeometryProcessor\n");
robertphillipse16dfdb2015-05-08 04:46:51 -0700796 return;
797 }
798
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500799 size_t vertexStride = gp->vertexStride();
robertphillipse16dfdb2015-05-08 04:46:51 -0700800 GrAAConvexTessellator tess;
801
Brian Salomond0a0a652016-12-15 15:25:22 -0500802 int instanceCount = fPaths.count();
Brian Salomon49348902018-06-26 09:12:38 -0400803 auto pipe = fHelper.makePipeline(target);
robertphillipse16dfdb2015-05-08 04:46:51 -0700804 for (int i = 0; i < instanceCount; i++) {
805 tess.rewind();
806
Brian Salomond0a0a652016-12-15 15:25:22 -0500807 const PathData& args = fPaths[i];
robertphillipse16dfdb2015-05-08 04:46:51 -0700808
809 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
810 continue;
811 }
812
cdalton397536c2016-03-25 12:15:03 -0700813 const GrBuffer* vertexBuffer;
robertphillipse16dfdb2015-05-08 04:46:51 -0700814 int firstVertex;
815
bsalomon75398562015-08-17 12:55:38 -0700816 void* verts = target->makeVertexSpace(vertexStride, tess.numPts(), &vertexBuffer,
817 &firstVertex);
robertphillipse16dfdb2015-05-08 04:46:51 -0700818 if (!verts) {
819 SkDebugf("Could not allocate vertices\n");
820 return;
821 }
822
cdalton397536c2016-03-25 12:15:03 -0700823 const GrBuffer* indexBuffer;
robertphillipse16dfdb2015-05-08 04:46:51 -0700824 int firstIndex;
825
bsalomon75398562015-08-17 12:55:38 -0700826 uint16_t* idxs = target->makeIndexSpace(tess.numIndices(), &indexBuffer, &firstIndex);
robertphillipse16dfdb2015-05-08 04:46:51 -0700827 if (!idxs) {
828 SkDebugf("Could not allocate indices\n");
829 return;
830 }
831
Brian Osman1be2b7c2018-10-29 16:07:15 -0400832 // TODO4F: Preserve float colors
Brian Osman7d8f82b2018-11-08 10:24:09 -0500833 extract_lines_only_verts(tess, verts, args.fColor.toBytes_RGBA(), idxs,
Brian Salomon60fb0b22017-06-15 17:09:36 -0400834 fHelper.compatibleWithAlphaAsCoverage());
robertphillipse16dfdb2015-05-08 04:46:51 -0700835
Brian Salomon7eae3e02018-08-07 14:02:38 +0000836 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
837 mesh->setIndexed(indexBuffer, tess.numIndices(), firstIndex, 0, tess.numPts() - 1,
838 GrPrimitiveRestart::kNo);
839 mesh->setVertexData(vertexBuffer, firstVertex);
840 target->draw(gp, pipe.fPipeline, pipe.fFixedDynamicState, mesh);
robertphillipse16dfdb2015-05-08 04:46:51 -0700841 }
joshualitt27f398f2015-02-05 14:39:01 -0800842 }
843
Brian Salomon91326c32017-08-09 16:02:19 -0400844 void onPrepareDraws(Target* target) override {
robertphillipse16dfdb2015-05-08 04:46:51 -0700845#ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
Brian Salomon10978a62017-06-15 16:21:49 -0400846 if (fLinesOnly) {
bsalomon75398562015-08-17 12:55:38 -0700847 this->prepareLinesOnlyDraws(target);
robertphillipse16dfdb2015-05-08 04:46:51 -0700848 return;
849 }
850#endif
Brian Salomon49348902018-06-26 09:12:38 -0400851 auto pipe = fHelper.makePipeline(target);
Brian Salomond0a0a652016-12-15 15:25:22 -0500852 int instanceCount = fPaths.count();
joshualitt27f398f2015-02-05 14:39:01 -0800853
854 SkMatrix invert;
Brian Salomon10978a62017-06-15 16:21:49 -0400855 if (fHelper.usesLocalCoords() && !fPaths.back().fViewMatrix.invert(&invert)) {
joshualitt27f398f2015-02-05 14:39:01 -0800856 return;
857 }
858
859 // Setup GrGeometryProcessor
bungeman06ca8ec2016-06-09 08:01:03 -0700860 sk_sp<GrGeometryProcessor> quadProcessor(
Brian Salomon60fb0b22017-06-15 17:09:36 -0400861 QuadEdgeEffect::Make(invert, fHelper.usesLocalCoords()));
joshualitt27f398f2015-02-05 14:39:01 -0800862
joshualitt27f398f2015-02-05 14:39:01 -0800863 // TODO generate all segments for all paths and use one vertex buffer
864 for (int i = 0; i < instanceCount; i++) {
Brian Salomond0a0a652016-12-15 15:25:22 -0500865 const PathData& args = fPaths[i];
joshualitt27f398f2015-02-05 14:39:01 -0800866
867 // We use the fact that SkPath::transform path does subdivision based on
868 // perspective. Otherwise, we apply the view matrix when copying to the
869 // segment representation.
870 const SkMatrix* viewMatrix = &args.fViewMatrix;
joshualitt144c3c82015-11-30 12:30:13 -0800871
872 // We avoid initializing the path unless we have to
873 const SkPath* pathPtr = &args.fPath;
874 SkTLazy<SkPath> tmpPath;
joshualitt27f398f2015-02-05 14:39:01 -0800875 if (viewMatrix->hasPerspective()) {
joshualitt144c3c82015-11-30 12:30:13 -0800876 SkPath* tmpPathPtr = tmpPath.init(*pathPtr);
877 tmpPathPtr->setIsVolatile(true);
878 tmpPathPtr->transform(*viewMatrix);
joshualitt27f398f2015-02-05 14:39:01 -0800879 viewMatrix = &SkMatrix::I();
joshualitt144c3c82015-11-30 12:30:13 -0800880 pathPtr = tmpPathPtr;
joshualitt27f398f2015-02-05 14:39:01 -0800881 }
882
883 int vertexCount;
884 int indexCount;
885 enum {
886 kPreallocSegmentCnt = 512 / sizeof(Segment),
887 kPreallocDrawCnt = 4,
888 };
889 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
890 SkPoint fanPt;
891
joshualitt144c3c82015-11-30 12:30:13 -0800892 if (!get_segments(*pathPtr, *viewMatrix, &segments, &fanPt, &vertexCount,
joshualitt27f398f2015-02-05 14:39:01 -0800893 &indexCount)) {
894 continue;
895 }
896
Chris Daltonff926502017-05-03 14:36:54 -0400897 const GrBuffer* vertexBuffer;
Chris Daltonbca46e22017-05-15 11:03:26 -0600898 int firstVertex;
899
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500900 SkASSERT(sizeof(QuadVertex) == quadProcessor->vertexStride());
bsalomon75398562015-08-17 12:55:38 -0700901 QuadVertex* verts = reinterpret_cast<QuadVertex*>(target->makeVertexSpace(
Brian Salomon92be2f72018-06-19 14:33:47 -0400902 sizeof(QuadVertex), vertexCount, &vertexBuffer, &firstVertex));
bsalomone64eb572015-05-07 11:35:55 -0700903
904 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800905 SkDebugf("Could not allocate vertices\n");
906 return;
907 }
908
cdalton397536c2016-03-25 12:15:03 -0700909 const GrBuffer* indexBuffer;
Chris Daltonbca46e22017-05-15 11:03:26 -0600910 int firstIndex;
911
912 uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
robertphillipse40d3972015-05-07 09:51:43 -0700913 if (!idxs) {
joshualitt4b31de82015-03-05 14:33:41 -0800914 SkDebugf("Could not allocate indices\n");
915 return;
916 }
917
joshualitt27f398f2015-02-05 14:39:01 -0800918 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
Brian Osman1be2b7c2018-10-29 16:07:15 -0400919 // TODO4F: Preserve float colors
Brian Osmancf860852018-10-31 14:04:39 -0400920 create_vertices(segments, fanPt, args.fColor.toBytes_RGBA(), &draws, verts, idxs);
joshualitt27f398f2015-02-05 14:39:01 -0800921
Brian Salomon7eae3e02018-08-07 14:02:38 +0000922 GrMesh* meshes = target->allocMeshes(draws.count());
robertphillips44c31282015-09-03 12:58:48 -0700923 for (int j = 0; j < draws.count(); ++j) {
924 const Draw& draw = draws[j];
Brian Salomon7eae3e02018-08-07 14:02:38 +0000925 meshes[j].setPrimitiveType(GrPrimitiveType::kTriangles);
926 meshes[j].setIndexed(indexBuffer, draw.fIndexCnt, firstIndex, 0,
927 draw.fVertexCnt - 1, GrPrimitiveRestart::kNo);
928 meshes[j].setVertexData(vertexBuffer, firstVertex);
Chris Daltonbca46e22017-05-15 11:03:26 -0600929 firstIndex += draw.fIndexCnt;
930 firstVertex += draw.fVertexCnt;
joshualitt27f398f2015-02-05 14:39:01 -0800931 }
Brian Salomonf7232642018-09-19 08:58:08 -0400932 target->draw(quadProcessor, pipe.fPipeline, pipe.fFixedDynamicState, nullptr, meshes,
Brian Salomon7eae3e02018-08-07 14:02:38 +0000933 draws.count());
joshualitt27f398f2015-02-05 14:39:01 -0800934 }
935 }
936
Brian Salomon7eae3e02018-08-07 14:02:38 +0000937 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomond0a0a652016-12-15 15:25:22 -0500938 AAConvexPathOp* that = t->cast<AAConvexPathOp>();
Brian Salomon10978a62017-06-15 16:21:49 -0400939 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000940 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -0700941 }
Brian Salomon10978a62017-06-15 16:21:49 -0400942 if (fHelper.usesLocalCoords() &&
943 !fPaths[0].fViewMatrix.cheapEqualTo(that->fPaths[0].fViewMatrix)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000944 return CombineResult::kCannotCombine;
joshualitt27f398f2015-02-05 14:39:01 -0800945 }
946
Brian Salomon10978a62017-06-15 16:21:49 -0400947 if (fLinesOnly != that->fLinesOnly) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000948 return CombineResult::kCannotCombine;
robertphillipse16dfdb2015-05-08 04:46:51 -0700949 }
950
Brian Salomond0a0a652016-12-15 15:25:22 -0500951 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
Brian Salomon7eae3e02018-08-07 14:02:38 +0000952 return CombineResult::kMerged;
joshualitt27f398f2015-02-05 14:39:01 -0800953 }
954
Brian Salomond0a0a652016-12-15 15:25:22 -0500955 struct PathData {
bsalomonf1703092016-06-29 18:41:53 -0700956 SkMatrix fViewMatrix;
957 SkPath fPath;
Brian Osmancf860852018-10-31 14:04:39 -0400958 SkPMColor4f fColor;
bsalomonf1703092016-06-29 18:41:53 -0700959 };
960
Brian Salomon10978a62017-06-15 16:21:49 -0400961 Helper fHelper;
Brian Salomond0a0a652016-12-15 15:25:22 -0500962 SkSTArray<1, PathData, true> fPaths;
Brian Salomon10978a62017-06-15 16:21:49 -0400963 bool fLinesOnly;
reed1b55a962015-09-17 20:16:13 -0700964
Brian Salomon10978a62017-06-15 16:21:49 -0400965 typedef GrMeshDrawOp INHERITED;
joshualitt27f398f2015-02-05 14:39:01 -0800966};
967
Brian Salomon10978a62017-06-15 16:21:49 -0400968} // anonymous namespace
969
bsalomon0aff2fa2015-07-31 06:48:27 -0700970bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400971 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700972 "GrAAConvexPathRenderer::onDrawPath");
Brian Salomon7c8460e2017-05-12 11:36:10 -0400973 SkASSERT(GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType());
bsalomon8acedde2016-06-24 10:42:16 -0700974 SkASSERT(!args.fShape->isEmpty());
bsalomon@google.com4647f902013-03-26 14:45:27 +0000975
bsalomonf1703092016-06-29 18:41:53 -0700976 SkPath path;
977 args.fShape->asPath(&path);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000978
Robert Phillips7c525e62018-06-12 10:11:12 -0400979 std::unique_ptr<GrDrawOp> op = AAConvexPathOp::Make(args.fContext, std::move(args.fPaint),
980 *args.fViewMatrix,
Brian Salomon10978a62017-06-15 16:21:49 -0400981 path, args.fUserStencilSettings);
982 args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000983 return true;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000984}
joshualitt8e5c1772015-05-11 08:58:52 -0700985
986///////////////////////////////////////////////////////////////////////////////////////////////////
987
Hal Canary6f6961e2017-01-31 13:50:44 -0500988#if GR_TEST_UTILS
joshualitt8e5c1772015-05-11 08:58:52 -0700989
Brian Salomon10978a62017-06-15 16:21:49 -0400990GR_DRAW_OP_TEST_DEFINE(AAConvexPathOp) {
bsalomonf1703092016-06-29 18:41:53 -0700991 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
992 SkPath path = GrTest::TestPathConvex(random);
Brian Salomon10978a62017-06-15 16:21:49 -0400993 const GrUserStencilSettings* stencilSettings = GrGetRandomStencil(random, context);
Robert Phillips7c525e62018-06-12 10:11:12 -0400994 return AAConvexPathOp::Make(context, std::move(paint), viewMatrix, path, stencilSettings);
joshualitt8e5c1772015-05-11 08:58:52 -0700995}
996
997#endif