blob: 8c55de7e79cc79d202992a03355c0cecb4a49e78 [file] [log] [blame]
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrAAConvexPathRenderer.h"
9
robertphillipse16dfdb2015-05-08 04:46:51 -070010#include "GrAAConvexTessellator.h"
bsalomon75398562015-08-17 12:55:38 -070011#include "GrBatchFlushState.h"
joshualitt8e5c1772015-05-11 08:58:52 -070012#include "GrBatchTest.h"
bsalomoneb1cb5c2015-05-22 08:01:09 -070013#include "GrCaps.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000014#include "GrContext.h"
robertphillipse16dfdb2015-05-08 04:46:51 -070015#include "GrDefaultGeoProcFactory.h"
joshualitteb2a6762014-12-04 11:35:33 -080016#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080017#include "GrInvariantOutput.h"
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000018#include "GrPathUtils.h"
egdaniel8dd688b2015-01-22 10:16:09 -080019#include "GrProcessor.h"
20#include "GrPipelineBuilder.h"
kkinnunen18996512015-04-26 23:18:49 -070021#include "GrStrokeInfo.h"
egdanielaf18a092015-01-05 10:22:28 -080022#include "SkGeometry.h"
reed026beb52015-06-10 14:23:15 -070023#include "SkPathPriv.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"
bsalomon16b99132015-08-13 14:55:50 -070026#include "batches/GrVertexBatch.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"
egdaniel7ea439b2015-12-03 09:20:44 -080032#include "glsl/GrGLSLVertexShaderBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000033
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000034GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
35}
36
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000037struct Segment {
38 enum {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000039 // These enum values are assumed in member functions below.
40 kLine = 0,
41 kQuad = 1,
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000042 } fType;
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000043
bsalomon@google.com9aed1142012-01-30 14:28:39 +000044 // line uses one pt, quad uses 2 pts
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000045 SkPoint fPts[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000046 // normal to edge ending at each pt
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000047 SkVector fNorms[2];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000048 // is the corner where the previous segment meets this segment
49 // sharp. If so, fMid is a normalized bisector facing outward.
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000050 SkVector fMid;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000051
52 int countPoints() {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000053 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
54 return fType + 1;
bsalomon@google.com9aed1142012-01-30 14:28:39 +000055 }
56 const SkPoint& endPt() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000057 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
58 return fPts[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000059 };
60 const SkPoint& endNorm() const {
bsalomon@google.com9b1517e2012-03-05 17:58:34 +000061 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
62 return fNorms[fType];
bsalomon@google.com9aed1142012-01-30 14:28:39 +000063 };
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000064};
65
66typedef SkTArray<Segment, true> SegmentArray;
67
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +000068static void center_of_mass(const SegmentArray& segments, SkPoint* c) {
bsalomon@google.com81712882012-11-01 17:12:34 +000069 SkScalar area = 0;
vandebo@chromium.org6390c722012-03-28 21:03:22 +000070 SkPoint center = {0, 0};
bsalomon@google.com9aed1142012-01-30 14:28:39 +000071 int count = segments.count();
vandebo@chromium.org6390c722012-03-28 21:03:22 +000072 SkPoint p0 = {0, 0};
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000073 if (count > 2) {
74 // We translate the polygon so that the first point is at the origin.
75 // This avoids some precision issues with small area polygons far away
76 // from the origin.
77 p0 = segments[0].endPt();
78 SkPoint pi;
79 SkPoint pj;
bsalomon@google.coma51ab842012-07-10 19:53:34 +000080 // the first and last iteration of the below loop would compute
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000081 // zeros since the starting / ending point is (0,0). So instead we start
82 // at i=1 and make the last iteration i=count-2.
83 pj = segments[1].endPt() - p0;
84 for (int i = 1; i < count - 1; ++i) {
85 pi = pj;
robertphillips44c31282015-09-03 12:58:48 -070086 pj = segments[i + 1].endPt() - p0;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000087
robertphillips44c31282015-09-03 12:58:48 -070088 SkScalar t = SkPoint::CrossProduct(pi, pj);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000089 area += t;
90 center.fX += (pi.fX + pj.fX) * t;
91 center.fY += (pi.fY + pj.fY) * t;
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000092 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +000093 }
robertphillips44c31282015-09-03 12:58:48 -070094
bsalomon@google.com278dc692012-02-15 16:52:51 +000095 // If the poly has no area then we instead return the average of
96 // its points.
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +000097 if (SkScalarNearlyZero(area)) {
bsalomon@google.com278dc692012-02-15 16:52:51 +000098 SkPoint avg;
99 avg.set(0, 0);
100 for (int i = 0; i < count; ++i) {
101 const SkPoint& pt = segments[i].endPt();
102 avg.fX += pt.fX;
103 avg.fY += pt.fY;
104 }
105 SkScalar denom = SK_Scalar1 / count;
106 avg.scale(denom);
107 *c = avg;
108 } else {
109 area *= 3;
reed80ea19c2015-05-12 10:37:34 -0700110 area = SkScalarInvert(area);
robertphillips44c31282015-09-03 12:58:48 -0700111 center.scale(area);
bsalomon@google.com5b56d9e2012-02-23 19:18:37 +0000112 // undo the translate of p0 to the origin.
113 *c = center + p0;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000114 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000115 SkASSERT(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000116}
117
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000118static void compute_vectors(SegmentArray* segments,
119 SkPoint* fanPt,
reed026beb52015-06-10 14:23:15 -0700120 SkPathPriv::FirstDirection dir,
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000121 int* vCount,
122 int* iCount) {
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000123 center_of_mass(*segments, fanPt);
124 int count = segments->count();
125
bsalomon@google.com278dc692012-02-15 16:52:51 +0000126 // Make the normals point towards the outside
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000127 SkPoint::Side normSide;
reed026beb52015-06-10 14:23:15 -0700128 if (dir == SkPathPriv::kCCW_FirstDirection) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000129 normSide = SkPoint::kRight_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000130 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000131 normSide = SkPoint::kLeft_Side;
bsalomon@google.com278dc692012-02-15 16:52:51 +0000132 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000133
134 *vCount = 0;
135 *iCount = 0;
136 // compute normals at all points
137 for (int a = 0; a < count; ++a) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000138 Segment& sega = (*segments)[a];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000139 int b = (a + 1) % count;
140 Segment& segb = (*segments)[b];
141
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000142 const SkPoint* prevPt = &sega.endPt();
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000143 int n = segb.countPoints();
144 for (int p = 0; p < n; ++p) {
145 segb.fNorms[p] = segb.fPts[p] - *prevPt;
146 segb.fNorms[p].normalize();
147 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
148 prevPt = &segb.fPts[p];
149 }
150 if (Segment::kLine == segb.fType) {
151 *vCount += 5;
152 *iCount += 9;
153 } else {
154 *vCount += 6;
155 *iCount += 12;
156 }
157 }
158
159 // compute mid-vectors where segments meet. TODO: Detect shallow corners
160 // and leave out the wedges and close gaps by stitching segments together.
161 for (int a = 0; a < count; ++a) {
162 const Segment& sega = (*segments)[a];
163 int b = (a + 1) % count;
164 Segment& segb = (*segments)[b];
165 segb.fMid = segb.fNorms[0] + sega.endNorm();
166 segb.fMid.normalize();
167 // corner wedges
168 *vCount += 4;
169 *iCount += 6;
170 }
171}
172
bsalomon@google.com9732f622012-01-31 15:19:21 +0000173struct DegenerateTestData {
174 DegenerateTestData() { fStage = kInitial; }
175 bool isDegenerate() const { return kNonDegenerate != fStage; }
176 enum {
177 kInitial,
178 kPoint,
179 kLine,
180 kNonDegenerate
181 } fStage;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000182 SkPoint fFirstPoint;
183 SkVector fLineNormal;
bsalomon@google.com81712882012-11-01 17:12:34 +0000184 SkScalar fLineC;
bsalomon@google.com9732f622012-01-31 15:19:21 +0000185};
186
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000187static const SkScalar kClose = (SK_Scalar1 / 16);
188static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000189
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000190static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000191 switch (data->fStage) {
192 case DegenerateTestData::kInitial:
193 data->fFirstPoint = pt;
194 data->fStage = DegenerateTestData::kPoint;
195 break;
196 case DegenerateTestData::kPoint:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000197 if (pt.distanceToSqd(data->fFirstPoint) > kCloseSqd) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000198 data->fLineNormal = pt - data->fFirstPoint;
199 data->fLineNormal.normalize();
200 data->fLineNormal.setOrthog(data->fLineNormal);
201 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
202 data->fStage = DegenerateTestData::kLine;
203 }
204 break;
205 case DegenerateTestData::kLine:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000206 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
bsalomon@google.com9732f622012-01-31 15:19:21 +0000207 data->fStage = DegenerateTestData::kNonDegenerate;
208 }
209 case DegenerateTestData::kNonDegenerate:
210 break;
211 default:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000212 SkFAIL("Unexpected degenerate test stage.");
bsalomon@google.com9732f622012-01-31 15:19:21 +0000213 }
214}
215
reed026beb52015-06-10 14:23:15 -0700216static inline bool get_direction(const SkPath& path, const SkMatrix& m,
217 SkPathPriv::FirstDirection* dir) {
218 if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000219 return false;
220 }
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000221 // check whether m reverses the orientation
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000222 SkASSERT(!m.hasPerspective());
bsalomon@google.com81712882012-11-01 17:12:34 +0000223 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
224 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000225 if (det2x2 < 0) {
reed026beb52015-06-10 14:23:15 -0700226 *dir = SkPathPriv::OppositeFirstDirection(*dir);
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000227 }
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000228 return true;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +0000229}
230
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000231static inline void add_line_to_segment(const SkPoint& pt,
joshualitt27f398f2015-02-05 14:39:01 -0800232 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000233 segments->push_back();
234 segments->back().fType = Segment::kLine;
235 segments->back().fPts[0] = pt;
236}
237
commit-bot@chromium.org106655e2013-09-03 21:28:55 +0000238static inline void add_quad_segment(const SkPoint pts[3],
joshualitt27f398f2015-02-05 14:39:01 -0800239 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000240 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
241 if (pts[0] != pts[2]) {
joshualitt27f398f2015-02-05 14:39:01 -0800242 add_line_to_segment(pts[2], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000243 }
244 } else {
245 segments->push_back();
246 segments->back().fType = Segment::kQuad;
247 segments->back().fPts[0] = pts[1];
248 segments->back().fPts[1] = pts[2];
249 }
250}
251
252static inline void add_cubic_segments(const SkPoint pts[4],
reed026beb52015-06-10 14:23:15 -0700253 SkPathPriv::FirstDirection dir,
joshualitt27f398f2015-02-05 14:39:01 -0800254 SegmentArray* segments) {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000255 SkSTArray<15, SkPoint, true> quads;
bsalomon18fab302016-02-16 08:00:05 -0800256 GrPathUtils::convertCubicToQuadsConstrainToTangents(pts, SK_Scalar1, dir, &quads);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000257 int count = quads.count();
258 for (int q = 0; q < count; q += 3) {
joshualitt27f398f2015-02-05 14:39:01 -0800259 add_quad_segment(&quads[q], segments);
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000260 }
261}
262
263static bool get_segments(const SkPath& path,
264 const SkMatrix& m,
265 SegmentArray* segments,
266 SkPoint* fanPt,
267 int* vCount,
joshualitt27f398f2015-02-05 14:39:01 -0800268 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000269 SkPath::Iter iter(path, true);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000270 // This renderer over-emphasizes very thin path regions. We use the distance
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000271 // to the path from the sample to compute coverage. Every pixel intersected
272 // 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 +0000273 // notice that the sample may be close to a very thin area of the path and
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000274 // thus should be very light. This is particularly egregious for degenerate
275 // line paths. We detect paths that are very close to a line (zero area) and
276 // draw nothing.
bsalomon@google.com9732f622012-01-31 15:19:21 +0000277 DegenerateTestData degenerateData;
reed026beb52015-06-10 14:23:15 -0700278 SkPathPriv::FirstDirection dir;
bsalomon@google.coma51ab842012-07-10 19:53:34 +0000279 // get_direction can fail for some degenerate paths.
280 if (!get_direction(path, m, &dir)) {
281 return false;
282 }
bsalomon@google.com9732f622012-01-31 15:19:21 +0000283
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000284 for (;;) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000285 SkPoint pts[4];
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000286 SkPath::Verb verb = iter.next(pts);
287 switch (verb) {
288 case SkPath::kMove_Verb:
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000289 m.mapPoints(pts, 1);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000290 update_degenerate_test(&degenerateData, pts[0]);
291 break;
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000292 case SkPath::kLine_Verb: {
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000293 m.mapPoints(&pts[1], 1);
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000294 update_degenerate_test(&degenerateData, pts[1]);
joshualitt27f398f2015-02-05 14:39:01 -0800295 add_line_to_segment(pts[1], segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000296 break;
297 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000298 case SkPath::kQuad_Verb:
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000299 m.mapPoints(pts, 3);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000300 update_degenerate_test(&degenerateData, pts[1]);
301 update_degenerate_test(&degenerateData, pts[2]);
joshualitt27f398f2015-02-05 14:39:01 -0800302 add_quad_segment(pts, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000303 break;
egdanielaf18a092015-01-05 10:22:28 -0800304 case SkPath::kConic_Verb: {
305 m.mapPoints(pts, 3);
306 SkScalar weight = iter.conicWeight();
307 SkAutoConicToQuads converter;
308 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5f);
309 for (int i = 0; i < converter.countQuads(); ++i) {
310 update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
311 update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
joshualitt27f398f2015-02-05 14:39:01 -0800312 add_quad_segment(quadPts + 2*i, segments);
egdanielaf18a092015-01-05 10:22:28 -0800313 }
314 break;
315 }
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000316 case SkPath::kCubic_Verb: {
bsalomon@google.com1a38d552012-03-15 14:40:46 +0000317 m.mapPoints(pts, 4);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000318 update_degenerate_test(&degenerateData, pts[1]);
319 update_degenerate_test(&degenerateData, pts[2]);
320 update_degenerate_test(&degenerateData, pts[3]);
joshualitt27f398f2015-02-05 14:39:01 -0800321 add_cubic_segments(pts, dir, segments);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000322 break;
323 };
bsalomon@google.com94b284d2013-05-10 17:14:06 +0000324 case SkPath::kDone_Verb:
bsalomon@google.com9732f622012-01-31 15:19:21 +0000325 if (degenerateData.isDegenerate()) {
326 return false;
327 } else {
bsalomon@google.com278dc692012-02-15 16:52:51 +0000328 compute_vectors(segments, fanPt, dir, vCount, iCount);
bsalomon@google.com9732f622012-01-31 15:19:21 +0000329 return true;
330 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000331 default:
332 break;
333 }
334 }
335}
336
337struct QuadVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000338 SkPoint fPos;
339 SkPoint fUV;
bsalomon@google.com81712882012-11-01 17:12:34 +0000340 SkScalar fD0;
341 SkScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000342};
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000343
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000344struct Draw {
345 Draw() : fVertexCnt(0), fIndexCnt(0) {}
346 int fVertexCnt;
347 int fIndexCnt;
348};
349
350typedef SkTArray<Draw, true> DrawArray;
351
commit-bot@chromium.orgfdfbb9d2013-08-15 18:16:27 +0000352static void create_vertices(const SegmentArray& segments,
353 const SkPoint& fanPt,
354 DrawArray* draws,
355 QuadVertex* verts,
356 uint16_t* idxs) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000357 Draw* draw = &draws->push_back();
358 // alias just to make vert/index assignments easier to read.
359 int* v = &draw->fVertexCnt;
360 int* i = &draw->fIndexCnt;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000361
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000362 int count = segments.count();
363 for (int a = 0; a < count; ++a) {
364 const Segment& sega = segments[a];
365 int b = (a + 1) % count;
366 const Segment& segb = segments[b];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000367
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000368 // Check whether adding the verts for this segment to the current draw would cause index
369 // values to overflow.
370 int vCount = 4;
371 if (Segment::kLine == segb.fType) {
372 vCount += 5;
373 } else {
374 vCount += 6;
375 }
376 if (draw->fVertexCnt + vCount > (1 << 16)) {
377 verts += *v;
378 idxs += *i;
379 draw = &draws->push_back();
380 v = &draw->fVertexCnt;
381 i = &draw->fIndexCnt;
382 }
383
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000384 // FIXME: These tris are inset in the 1 unit arc around the corner
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000385 verts[*v + 0].fPos = sega.endPt();
386 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
387 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
388 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
389 verts[*v + 0].fUV.set(0,0);
390 verts[*v + 1].fUV.set(0,-SK_Scalar1);
391 verts[*v + 2].fUV.set(0,-SK_Scalar1);
392 verts[*v + 3].fUV.set(0,-SK_Scalar1);
393 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
394 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
395 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
396 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000397
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000398 idxs[*i + 0] = *v + 0;
399 idxs[*i + 1] = *v + 2;
400 idxs[*i + 2] = *v + 1;
401 idxs[*i + 3] = *v + 0;
402 idxs[*i + 4] = *v + 3;
403 idxs[*i + 5] = *v + 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000404
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000405 *v += 4;
406 *i += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000407
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000408 if (Segment::kLine == segb.fType) {
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000409 verts[*v + 0].fPos = fanPt;
410 verts[*v + 1].fPos = sega.endPt();
411 verts[*v + 2].fPos = segb.fPts[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000412
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000413 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
414 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000415
416 // we draw the line edge as a degenerate quad (u is 0, v is the
417 // signed distance to the edge)
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000418 SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos,
419 verts[*v + 2].fPos);
420 verts[*v + 0].fUV.set(0, dist);
421 verts[*v + 1].fUV.set(0, 0);
422 verts[*v + 2].fUV.set(0, 0);
423 verts[*v + 3].fUV.set(0, -SK_Scalar1);
424 verts[*v + 4].fUV.set(0, -SK_Scalar1);
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000425
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000426 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
427 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
428 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
429 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
430 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000431
cdalton35964822015-04-29 10:14:03 -0700432 idxs[*i + 0] = *v + 3;
433 idxs[*i + 1] = *v + 1;
434 idxs[*i + 2] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000435
cdalton35964822015-04-29 10:14:03 -0700436 idxs[*i + 3] = *v + 4;
437 idxs[*i + 4] = *v + 3;
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000438 idxs[*i + 5] = *v + 2;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000439
cdalton35964822015-04-29 10:14:03 -0700440 *i += 6;
441
442 // Draw the interior fan if it exists.
443 // TODO: Detect and combine colinear segments. This will ensure we catch every case
444 // with no interior, and that the resulting shared edge uses the same endpoints.
445 if (count >= 3) {
446 idxs[*i + 0] = *v + 0;
447 idxs[*i + 1] = *v + 2;
448 idxs[*i + 2] = *v + 1;
449
450 *i += 3;
451 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000452
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000453 *v += 5;
bsalomon@google.com06809612012-01-21 15:03:39 +0000454 } else {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000455 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000456
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000457 SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000458 midVec.normalize();
bsalomon@google.com06809612012-01-21 15:03:39 +0000459
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000460 verts[*v + 0].fPos = fanPt;
461 verts[*v + 1].fPos = qpts[0];
462 verts[*v + 2].fPos = qpts[2];
463 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
464 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
465 verts[*v + 5].fPos = qpts[1] + midVec;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000466
bsalomon@google.com81712882012-11-01 17:12:34 +0000467 SkScalar c = segb.fNorms[0].dot(qpts[0]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000468 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
469 verts[*v + 1].fD0 = 0.f;
470 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
471 verts[*v + 3].fD0 = -SK_ScalarMax/100;
472 verts[*v + 4].fD0 = -SK_ScalarMax/100;
473 verts[*v + 5].fD0 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000474
475 c = segb.fNorms[1].dot(qpts[2]);
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000476 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
477 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
478 verts[*v + 2].fD1 = 0.f;
479 verts[*v + 3].fD1 = -SK_ScalarMax/100;
480 verts[*v + 4].fD1 = -SK_ScalarMax/100;
481 verts[*v + 5].fD1 = -SK_ScalarMax/100;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000482
bsalomon@google.com19713172012-03-15 13:51:08 +0000483 GrPathUtils::QuadUVMatrix toUV(qpts);
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000484 toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v);
bsalomon@google.com06809612012-01-21 15:03:39 +0000485
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000486 idxs[*i + 0] = *v + 3;
487 idxs[*i + 1] = *v + 1;
488 idxs[*i + 2] = *v + 2;
489 idxs[*i + 3] = *v + 4;
490 idxs[*i + 4] = *v + 3;
491 idxs[*i + 5] = *v + 2;
bsalomon@google.com06809612012-01-21 15:03:39 +0000492
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000493 idxs[*i + 6] = *v + 5;
494 idxs[*i + 7] = *v + 3;
495 idxs[*i + 8] = *v + 4;
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000496
cdalton35964822015-04-29 10:14:03 -0700497 *i += 9;
498
499 // Draw the interior fan if it exists.
500 // TODO: Detect and combine colinear segments. This will ensure we catch every case
501 // with no interior, and that the resulting shared edge uses the same endpoints.
502 if (count >= 3) {
503 idxs[*i + 0] = *v + 0;
504 idxs[*i + 1] = *v + 2;
505 idxs[*i + 2] = *v + 1;
506
507 *i += 3;
508 }
bsalomon@google.com9aed1142012-01-30 14:28:39 +0000509
bsalomon@google.com7d9ffc82013-05-14 14:20:28 +0000510 *v += 6;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000511 }
512 }
513}
514
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000515///////////////////////////////////////////////////////////////////////////////
516
517/*
518 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
519 * two components of the vertex attribute. Coverage is based on signed
520 * distance with negative being inside, positive outside. The edge is specified in
521 * window space (y-down). If either the third or fourth component of the interpolated
522 * 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 +0000523 * attempt to trim to a portion of the infinite quad.
524 * Requires shader derivative instruction support.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000525 */
526
joshualitt249af152014-09-15 11:41:13 -0700527class QuadEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000528public:
529
joshualittb8c241a2015-05-19 08:23:30 -0700530 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& localMatrix,
531 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -0700532 return new QuadEdgeEffect(color, localMatrix, usesLocalCoords);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000533 }
534
535 virtual ~QuadEdgeEffect() {}
536
mtklein36352bf2015-03-25 18:17:31 -0700537 const char* name() const override { return "QuadEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000538
joshualitt71c92602015-01-14 08:12:47 -0800539 const Attribute* inPosition() const { return fInPosition; }
540 const Attribute* inQuadEdge() const { return fInQuadEdge; }
joshualitt88c23fc2015-05-13 14:18:07 -0700541 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700542 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -0700543 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700544 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700545
egdaniel57d3b032015-11-13 11:57:27 -0800546 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000547 public:
egdaniel57d3b032015-11-13 11:57:27 -0800548 GLSLProcessor()
joshualitt9b989322014-12-15 14:16:27 -0800549 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000550
mtklein36352bf2015-03-25 18:17:31 -0700551 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800552 const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
egdaniel4ca2e602015-11-18 08:01:26 -0800553 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800554 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800555 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -0800556
joshualittabb52a12015-01-13 15:02:10 -0800557 // emit attributes
egdaniel0eafe792015-11-20 14:01:22 -0800558 varyingHandler->emitAttributes(qe);
joshualittabb52a12015-01-13 15:02:10 -0800559
egdaniel8dcdedc2015-11-11 06:27:20 -0800560 GrGLSLVertToFrag v(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800561 varyingHandler->addVarying("QuadEdge", &v);
egdaniel4ca2e602015-11-18 08:01:26 -0800562 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.inQuadEdge()->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800563
cdalton85285412016-02-18 12:37:07 -0800564 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt9b989322014-12-15 14:16:27 -0800565 // Setup pass through color
joshualittb8c241a2015-05-19 08:23:30 -0700566 if (!qe.colorIgnored()) {
egdaniel7ea439b2015-12-03 09:20:44 -0800567 this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
568 &fColorUniform);
joshualittb8c241a2015-05-19 08:23:30 -0700569 }
joshualitt9b989322014-12-15 14:16:27 -0800570
joshualittabb52a12015-01-13 15:02:10 -0800571 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800572 this->setupPosition(vertBuilder, gpArgs, qe.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800573
574 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800575 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800576 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800577 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800578 gpArgs->fPositionVar,
579 qe.inPosition()->fName,
580 qe.localMatrix(),
581 args.fTransformsIn,
582 args.fTransformsOut);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000583
egdaniel4ca2e602015-11-18 08:01:26 -0800584 SkAssertResult(fragBuilder->enableFeature(
egdaniel2d721d32015-11-11 13:06:05 -0800585 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
egdaniel4ca2e602015-11-18 08:01:26 -0800586 fragBuilder->codeAppendf("float edgeAlpha;");
joshualitt30ba4362014-08-21 20:18:45 -0700587
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000588 // keep the derivative instructions outside the conditional
egdaniel4ca2e602015-11-18 08:01:26 -0800589 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s.xy);", v.fsIn());
590 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s.xy);", v.fsIn());
591 fragBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000592 // today we know z and w are in device space. We could use derivatives
egdaniel4ca2e602015-11-18 08:01:26 -0800593 fragBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
594 v.fsIn());
595 fragBuilder->codeAppendf ("} else {");
596 fragBuilder->codeAppendf("vec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,"
597 " 2.0*%s.x*duvdy.x - duvdy.y);",
598 v.fsIn(), v.fsIn());
599 fragBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
600 v.fsIn());
601 fragBuilder->codeAppendf("edgeAlpha = "
602 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000603
egdaniel4ca2e602015-11-18 08:01:26 -0800604 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000605 }
606
joshualitt9b989322014-12-15 14:16:27 -0800607 static inline void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700608 const GrGLSLCaps&,
joshualitt9b989322014-12-15 14:16:27 -0800609 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700610 const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
joshualittb8c241a2015-05-19 08:23:30 -0700611 uint32_t key = 0;
612 key |= qee.usesLocalCoords() && qee.localMatrix().hasPerspective() ? 0x1 : 0x0;
613 key |= qee.colorIgnored() ? 0x2 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800614 b->add32(key);
joshualitt9b989322014-12-15 14:16:27 -0800615 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000616
egdaniel018fb622015-10-28 07:26:40 -0700617 void setData(const GrGLSLProgramDataManager& pdman,
618 const GrPrimitiveProcessor& gp) override {
joshualittb8c241a2015-05-19 08:23:30 -0700619 const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
620 if (qe.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700621 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700622 GrColorToRGBAFloat(qe.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800623 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700624 fColor = qe.color();
joshualitt9b989322014-12-15 14:16:27 -0800625 }
626 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000627
joshualitte3ababe2015-05-15 07:56:07 -0700628 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700629 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700630 int index,
631 const SkTArray<const GrCoordTransform*, true>& transforms) override {
632 this->setTransformDataHelper<QuadEdgeEffect>(primProc, pdman, index, transforms);
633 }
634
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000635 private:
joshualitt9b989322014-12-15 14:16:27 -0800636 GrColor fColor;
637 UniformHandle fColorUniform;
638
egdaniele659a582015-11-13 09:55:43 -0800639 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000640 };
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000641
egdaniel57d3b032015-11-13 11:57:27 -0800642 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
643 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800644 }
645
egdaniel57d3b032015-11-13 11:57:27 -0800646 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
647 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800648 }
649
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000650private:
joshualittb8c241a2015-05-19 08:23:30 -0700651 QuadEdgeEffect(GrColor color, const SkMatrix& localMatrix, bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700652 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700653 , fLocalMatrix(localMatrix)
654 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800655 this->initClassID<QuadEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800656 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
657 fInQuadEdge = &this->addVertexAttrib(Attribute("inQuadEdge", kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000658 }
659
joshualitt71c92602015-01-14 08:12:47 -0800660 const Attribute* fInPosition;
661 const Attribute* fInQuadEdge;
joshualitt88c23fc2015-05-13 14:18:07 -0700662 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700663 SkMatrix fLocalMatrix;
joshualittb8c241a2015-05-19 08:23:30 -0700664 bool fUsesLocalCoords;
joshualitt249af152014-09-15 11:41:13 -0700665
joshualittb0a8a372014-09-23 09:50:21 -0700666 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000667
joshualitt2e3b3e32014-12-09 13:31:14 -0800668 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000669};
670
joshualittb0a8a372014-09-23 09:50:21 -0700671GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000672
bsalomonc21b09e2015-08-28 18:46:56 -0700673const GrGeometryProcessor* QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000674 // Doesn't work without derivative instructions.
joshualitt0067ff52015-07-08 14:26:19 -0700675 return d->fCaps->shaderCaps()->shaderDerivativeSupport() ?
676 QuadEdgeEffect::Create(GrRandomColor(d->fRandom),
677 GrTest::TestMatrix(d->fRandom),
halcanary96fcdcc2015-08-27 07:41:13 -0700678 d->fRandom->nextBool()) : nullptr;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000679}
680
681///////////////////////////////////////////////////////////////////////////////
682
bsalomon0aff2fa2015-07-31 06:48:27 -0700683bool GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
robertphillipse7d4b2f2015-08-13 07:57:10 -0700684 return (args.fShaderCaps->shaderDerivativeSupport() && args.fAntiAlias &&
bsalomon0aff2fa2015-07-31 06:48:27 -0700685 args.fStroke->isFillStyle() && !args.fPath->isInverseFillType() &&
686 args.fPath->isConvex());
robertphillips@google.comfa662942012-05-17 12:20:22 +0000687}
688
robertphillipse16dfdb2015-05-08 04:46:51 -0700689// extract the result vertices and indices from the GrAAConvexTessellator
690static void extract_verts(const GrAAConvexTessellator& tess,
691 void* vertices,
692 size_t vertexStride,
693 GrColor color,
694 uint16_t* idxs,
695 bool tweakAlphaForCoverage) {
696 intptr_t verts = reinterpret_cast<intptr_t>(vertices);
697
698 for (int i = 0; i < tess.numPts(); ++i) {
699 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
700 }
701
702 // Make 'verts' point to the colors
703 verts += sizeof(SkPoint);
704 for (int i = 0; i < tess.numPts(); ++i) {
robertphillipse16dfdb2015-05-08 04:46:51 -0700705 if (tweakAlphaForCoverage) {
fmalitabd5d7e72015-06-26 07:18:24 -0700706 SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
707 unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
robertphillipse16dfdb2015-05-08 04:46:51 -0700708 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
709 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
710 } else {
711 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
halcanary9d524f22016-03-29 09:03:52 -0700712 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
fmalitabd5d7e72015-06-26 07:18:24 -0700713 tess.coverage(i);
robertphillipse16dfdb2015-05-08 04:46:51 -0700714 }
715 }
716
717 for (int i = 0; i < tess.numIndices(); ++i) {
718 idxs[i] = tess.index(i);
719 }
720}
721
722static const GrGeometryProcessor* create_fill_gp(bool tweakAlphaForCoverage,
joshualittdf0c5572015-08-03 11:35:28 -0700723 const SkMatrix& viewMatrix,
joshualittb8c241a2015-05-19 08:23:30 -0700724 bool usesLocalCoords,
725 bool coverageIgnored) {
joshualittdf0c5572015-08-03 11:35:28 -0700726 using namespace GrDefaultGeoProcFactory;
joshualitte494a582015-08-03 09:32:36 -0700727
joshualittdf0c5572015-08-03 11:35:28 -0700728 Color color(Color::kAttribute_Type);
729 Coverage::Type coverageType;
730 // TODO remove coverage if coverage is ignored
731 /*if (coverageIgnored) {
732 coverageType = Coverage::kNone_Type;
733 } else*/ if (tweakAlphaForCoverage) {
734 coverageType = Coverage::kSolid_Type;
735 } else {
736 coverageType = Coverage::kAttribute_Type;
737 }
738 Coverage coverage(coverageType);
739 LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type :
740 LocalCoords::kUnused_Type);
741 return CreateForDeviceSpace(color, coverage, localCoords, viewMatrix);
robertphillipse16dfdb2015-05-08 04:46:51 -0700742}
743
bsalomonabd30f52015-08-13 13:34:48 -0700744class AAConvexPathBatch : public GrVertexBatch {
joshualitt27f398f2015-02-05 14:39:01 -0800745public:
reed1b55a962015-09-17 20:16:13 -0700746 DEFINE_BATCH_CLASS_ID
joshualitt27f398f2015-02-05 14:39:01 -0800747 struct Geometry {
748 GrColor fColor;
749 SkMatrix fViewMatrix;
750 SkPath fPath;
joshualitt27f398f2015-02-05 14:39:01 -0800751 };
752
halcanary385fe4d2015-08-26 13:07:48 -0700753 static GrDrawBatch* Create(const Geometry& geometry) { return new AAConvexPathBatch(geometry); }
joshualitt27f398f2015-02-05 14:39:01 -0800754
mtklein36352bf2015-03-25 18:17:31 -0700755 const char* name() const override { return "AAConvexBatch"; }
joshualitt27f398f2015-02-05 14:39:01 -0800756
halcanary9d524f22016-03-29 09:03:52 -0700757 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800758 GrInitInvariantOutput* coverage,
759 GrBatchToXPOverrides* overrides) const override {
joshualitt27f398f2015-02-05 14:39:01 -0800760 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800761 color->setKnownFourComponents(fGeoData[0].fColor);
762 coverage->setUnknownSingleComponent();
joshualitt27f398f2015-02-05 14:39:01 -0800763 }
764
bsalomone46f9fe2015-08-18 06:05:14 -0700765private:
ethannicholasff210322015-11-24 12:10:10 -0800766 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
joshualitt27f398f2015-02-05 14:39:01 -0800767 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800768 if (!overrides.readsColor()) {
joshualitt27f398f2015-02-05 14:39:01 -0800769 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt27f398f2015-02-05 14:39:01 -0800770 }
ethannicholasff210322015-11-24 12:10:10 -0800771 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt27f398f2015-02-05 14:39:01 -0800772
773 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800774 fBatch.fColorIgnored = !overrides.readsColor();
joshualitt27f398f2015-02-05 14:39:01 -0800775 fBatch.fColor = fGeoData[0].fColor;
ethannicholasff210322015-11-24 12:10:10 -0800776 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
777 fBatch.fCoverageIgnored = !overrides.readsCoverage();
robertphillipse16dfdb2015-05-08 04:46:51 -0700778 fBatch.fLinesOnly = SkPath::kLine_SegmentMask == fGeoData[0].fPath.getSegmentMasks();
ethannicholasff210322015-11-24 12:10:10 -0800779 fBatch.fCanTweakAlphaForCoverage = overrides.canTweakAlphaForCoverage();
robertphillipse16dfdb2015-05-08 04:46:51 -0700780 }
781
joshualitt144c3c82015-11-30 12:30:13 -0800782 void prepareLinesOnlyDraws(Target* target) const {
robertphillipse16dfdb2015-05-08 04:46:51 -0700783 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
784
joshualittdf0c5572015-08-03 11:35:28 -0700785 // Setup GrGeometryProcessor
786 SkAutoTUnref<const GrGeometryProcessor> gp(create_fill_gp(canTweakAlphaForCoverage,
787 this->viewMatrix(),
788 this->usesLocalCoords(),
789 this->coverageIgnored()));
790 if (!gp) {
791 SkDebugf("Could not create GrGeometryProcessor\n");
robertphillipse16dfdb2015-05-08 04:46:51 -0700792 return;
793 }
794
robertphillipse16dfdb2015-05-08 04:46:51 -0700795 size_t vertexStride = gp->getVertexStride();
796
797 SkASSERT(canTweakAlphaForCoverage ?
798 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
799 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
800
801 GrAAConvexTessellator tess;
802
803 int instanceCount = fGeoData.count();
804
805 for (int i = 0; i < instanceCount; i++) {
806 tess.rewind();
807
joshualitt144c3c82015-11-30 12:30:13 -0800808 const Geometry& args = fGeoData[i];
robertphillipse16dfdb2015-05-08 04:46:51 -0700809
810 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
811 continue;
812 }
813
cdalton397536c2016-03-25 12:15:03 -0700814 const GrBuffer* vertexBuffer;
robertphillipse16dfdb2015-05-08 04:46:51 -0700815 int firstVertex;
816
bsalomon75398562015-08-17 12:55:38 -0700817 void* verts = target->makeVertexSpace(vertexStride, tess.numPts(), &vertexBuffer,
818 &firstVertex);
robertphillipse16dfdb2015-05-08 04:46:51 -0700819 if (!verts) {
820 SkDebugf("Could not allocate vertices\n");
821 return;
822 }
823
cdalton397536c2016-03-25 12:15:03 -0700824 const GrBuffer* indexBuffer;
robertphillipse16dfdb2015-05-08 04:46:51 -0700825 int firstIndex;
826
bsalomon75398562015-08-17 12:55:38 -0700827 uint16_t* idxs = target->makeIndexSpace(tess.numIndices(), &indexBuffer, &firstIndex);
robertphillipse16dfdb2015-05-08 04:46:51 -0700828 if (!idxs) {
829 SkDebugf("Could not allocate indices\n");
830 return;
831 }
832
833 extract_verts(tess, verts, vertexStride, args.fColor, idxs, canTweakAlphaForCoverage);
834
egdaniel0e1853c2016-03-17 11:35:45 -0700835 GrMesh mesh;
836 mesh.initIndexed(kTriangles_GrPrimitiveType,
robertphillipse16dfdb2015-05-08 04:46:51 -0700837 vertexBuffer, indexBuffer,
838 firstVertex, firstIndex,
839 tess.numPts(), tess.numIndices());
bsalomon342bfc22016-04-01 06:06:20 -0700840 target->draw(gp, mesh);
robertphillipse16dfdb2015-05-08 04:46:51 -0700841 }
joshualitt27f398f2015-02-05 14:39:01 -0800842 }
843
joshualitt144c3c82015-11-30 12:30:13 -0800844 void onPrepareDraws(Target* target) const override {
robertphillipse16dfdb2015-05-08 04:46:51 -0700845#ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
846 if (this->linesOnly()) {
bsalomon75398562015-08-17 12:55:38 -0700847 this->prepareLinesOnlyDraws(target);
robertphillipse16dfdb2015-05-08 04:46:51 -0700848 return;
849 }
850#endif
851
joshualitt27f398f2015-02-05 14:39:01 -0800852 int instanceCount = fGeoData.count();
853
854 SkMatrix invert;
joshualitt7abe15d2015-02-20 12:40:45 -0800855 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
joshualitt27f398f2015-02-05 14:39:01 -0800856 SkDebugf("Could not invert viewmatrix\n");
857 return;
858 }
859
860 // Setup GrGeometryProcessor
joshualittb8c241a2015-05-19 08:23:30 -0700861 SkAutoTUnref<GrGeometryProcessor> quadProcessor(
862 QuadEdgeEffect::Create(this->color(), invert, this->usesLocalCoords()));
joshualitt27f398f2015-02-05 14:39:01 -0800863
joshualitt27f398f2015-02-05 14:39:01 -0800864 // TODO generate all segments for all paths and use one vertex buffer
865 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800866 const Geometry& args = fGeoData[i];
joshualitt27f398f2015-02-05 14:39:01 -0800867
868 // We use the fact that SkPath::transform path does subdivision based on
869 // perspective. Otherwise, we apply the view matrix when copying to the
870 // segment representation.
871 const SkMatrix* viewMatrix = &args.fViewMatrix;
joshualitt144c3c82015-11-30 12:30:13 -0800872
873 // We avoid initializing the path unless we have to
874 const SkPath* pathPtr = &args.fPath;
875 SkTLazy<SkPath> tmpPath;
joshualitt27f398f2015-02-05 14:39:01 -0800876 if (viewMatrix->hasPerspective()) {
joshualitt144c3c82015-11-30 12:30:13 -0800877 SkPath* tmpPathPtr = tmpPath.init(*pathPtr);
878 tmpPathPtr->setIsVolatile(true);
879 tmpPathPtr->transform(*viewMatrix);
joshualitt27f398f2015-02-05 14:39:01 -0800880 viewMatrix = &SkMatrix::I();
joshualitt144c3c82015-11-30 12:30:13 -0800881 pathPtr = tmpPathPtr;
joshualitt27f398f2015-02-05 14:39:01 -0800882 }
883
884 int vertexCount;
885 int indexCount;
886 enum {
887 kPreallocSegmentCnt = 512 / sizeof(Segment),
888 kPreallocDrawCnt = 4,
889 };
890 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
891 SkPoint fanPt;
892
joshualitt144c3c82015-11-30 12:30:13 -0800893 if (!get_segments(*pathPtr, *viewMatrix, &segments, &fanPt, &vertexCount,
joshualitt27f398f2015-02-05 14:39:01 -0800894 &indexCount)) {
895 continue;
896 }
897
cdalton397536c2016-03-25 12:15:03 -0700898 const GrBuffer* vertexBuffer;
joshualitt27f398f2015-02-05 14:39:01 -0800899 int firstVertex;
900
901 size_t vertexStride = quadProcessor->getVertexStride();
bsalomon75398562015-08-17 12:55:38 -0700902 QuadVertex* verts = reinterpret_cast<QuadVertex*>(target->makeVertexSpace(
bsalomone64eb572015-05-07 11:35:55 -0700903 vertexStride, vertexCount, &vertexBuffer, &firstVertex));
904
905 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800906 SkDebugf("Could not allocate vertices\n");
907 return;
908 }
909
cdalton397536c2016-03-25 12:15:03 -0700910 const GrBuffer* indexBuffer;
joshualitt27f398f2015-02-05 14:39:01 -0800911 int firstIndex;
912
bsalomon75398562015-08-17 12:55:38 -0700913 uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
robertphillipse40d3972015-05-07 09:51:43 -0700914 if (!idxs) {
joshualitt4b31de82015-03-05 14:33:41 -0800915 SkDebugf("Could not allocate indices\n");
916 return;
917 }
918
joshualitt27f398f2015-02-05 14:39:01 -0800919 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
920 create_vertices(segments, fanPt, &draws, verts, idxs);
921
egdaniel0e1853c2016-03-17 11:35:45 -0700922 GrMesh mesh;
joshualitt27f398f2015-02-05 14:39:01 -0800923
robertphillips44c31282015-09-03 12:58:48 -0700924 for (int j = 0; j < draws.count(); ++j) {
925 const Draw& draw = draws[j];
egdaniel0e1853c2016-03-17 11:35:45 -0700926 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer,
927 firstVertex, firstIndex, draw.fVertexCnt, draw.fIndexCnt);
bsalomon342bfc22016-04-01 06:06:20 -0700928 target->draw(quadProcessor, mesh);
bsalomonb5238a72015-05-05 07:49:49 -0700929 firstVertex += draw.fVertexCnt;
930 firstIndex += draw.fIndexCnt;
joshualitt27f398f2015-02-05 14:39:01 -0800931 }
932 }
933 }
934
935 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
936
reed1b55a962015-09-17 20:16:13 -0700937 AAConvexPathBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt27f398f2015-02-05 14:39:01 -0800938 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700939
940 // compute bounds
941 fBounds = geometry.fPath.getBounds();
942 geometry.fViewMatrix.mapRect(&fBounds);
joshualitt27f398f2015-02-05 14:39:01 -0800943 }
944
bsalomoncb02b382015-08-12 11:14:50 -0700945 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700946 AAConvexPathBatch* that = t->cast<AAConvexPathBatch>();
947 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
948 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700949 return false;
950 }
951
joshualitt27f398f2015-02-05 14:39:01 -0800952 if (this->color() != that->color()) {
953 return false;
954 }
955
956 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
957 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
958 return false;
959 }
960
robertphillipse16dfdb2015-05-08 04:46:51 -0700961 if (this->linesOnly() != that->linesOnly()) {
962 return false;
963 }
964
965 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
966 // not tweaking
967 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
968 fBatch.fCanTweakAlphaForCoverage = false;
969 }
970
joshualitt27f398f2015-02-05 14:39:01 -0800971 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700972 this->joinBounds(that->bounds());
joshualitt27f398f2015-02-05 14:39:01 -0800973 return true;
974 }
975
976 GrColor color() const { return fBatch.fColor; }
robertphillipse16dfdb2015-05-08 04:46:51 -0700977 bool linesOnly() const { return fBatch.fLinesOnly; }
joshualitt27f398f2015-02-05 14:39:01 -0800978 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
robertphillipse16dfdb2015-05-08 04:46:51 -0700979 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
joshualitt27f398f2015-02-05 14:39:01 -0800980 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700981 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
joshualitt27f398f2015-02-05 14:39:01 -0800982
983 struct BatchTracker {
984 GrColor fColor;
985 bool fUsesLocalCoords;
986 bool fColorIgnored;
987 bool fCoverageIgnored;
robertphillipse16dfdb2015-05-08 04:46:51 -0700988 bool fLinesOnly;
989 bool fCanTweakAlphaForCoverage;
joshualitt27f398f2015-02-05 14:39:01 -0800990 };
991
joshualitt27f398f2015-02-05 14:39:01 -0800992 BatchTracker fBatch;
993 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700994
995 typedef GrVertexBatch INHERITED;
joshualitt27f398f2015-02-05 14:39:01 -0800996};
997
bsalomon0aff2fa2015-07-31 06:48:27 -0700998bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
joshualittde83b412016-01-14 09:58:36 -0800999 GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), "GrAAConvexPathRenderer::onDrawPath");
bsalomon0aff2fa2015-07-31 06:48:27 -07001000 if (args.fPath->isEmpty()) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +00001001 return true;
1002 }
bsalomon@google.com4647f902013-03-26 14:45:27 +00001003
joshualitt27f398f2015-02-05 14:39:01 -08001004 AAConvexPathBatch::Geometry geometry;
bsalomon0aff2fa2015-07-31 06:48:27 -07001005 geometry.fColor = args.fColor;
1006 geometry.fViewMatrix = *args.fViewMatrix;
1007 geometry.fPath = *args.fPath;
bsalomon@google.comaf90f7f2012-03-05 20:50:10 +00001008
bsalomonabd30f52015-08-13 13:34:48 -07001009 SkAutoTUnref<GrDrawBatch> batch(AAConvexPathBatch::Create(geometry));
bsalomon0aff2fa2015-07-31 06:48:27 -07001010 args.fTarget->drawBatch(*args.fPipelineBuilder, batch);
bsalomon@google.coma8347462012-10-08 18:59:39 +00001011
bsalomon@google.comc2099d22012-03-02 21:26:50 +00001012 return true;
joshualitt27f398f2015-02-05 14:39:01 -08001013
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +00001014}
joshualitt8e5c1772015-05-11 08:58:52 -07001015
1016///////////////////////////////////////////////////////////////////////////////////////////////////
1017
1018#ifdef GR_TEST_UTILS
1019
bsalomonabd30f52015-08-13 13:34:48 -07001020DRAW_BATCH_TEST_DEFINE(AAConvexPathBatch) {
joshualitt8e5c1772015-05-11 08:58:52 -07001021 AAConvexPathBatch::Geometry geometry;
1022 geometry.fColor = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001023 geometry.fViewMatrix = GrTest::TestMatrixInvertible(random);
joshualitt8e5c1772015-05-11 08:58:52 -07001024 geometry.fPath = GrTest::TestPathConvex(random);
1025
1026 return AAConvexPathBatch::Create(geometry);
1027}
1028
1029#endif