blob: 94508b130b4fcc05198bab77955c7e878f816bb4 [file] [log] [blame]
bsalomon@google.com30085192011-08-19 15:42:31 +00001/*
2 * Copyright 2011 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 "GrDefaultPathRenderer.h"
9
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt622d3ad2015-05-07 08:13:11 -070011#include "GrBatchTest.h"
bsalomon@google.com30085192011-08-19 15:42:31 +000012#include "GrContext.h"
joshualitt5478d422014-11-14 16:00:38 -080013#include "GrDefaultGeoProcFactory.h"
egdaniel0e1853c2016-03-17 11:35:45 -070014#include "GrMesh.h"
bsalomon@google.com30085192011-08-19 15:42:31 +000015#include "GrPathUtils.h"
egdaniel8dd688b2015-01-22 10:16:09 -080016#include "GrPipelineBuilder.h"
egdanielaf18a092015-01-05 10:22:28 -080017#include "SkGeometry.h"
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000018#include "SkString.h"
sugoi@google.com5f74cf82012-12-17 21:16:45 +000019#include "SkStrokeRec.h"
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +000020#include "SkTLazy.h"
commit-bot@chromium.org933e65d2014-03-20 20:00:24 +000021#include "SkTraceEvent.h"
bsalomon@google.com30085192011-08-19 15:42:31 +000022
joshualitt04194f32016-01-13 10:08:27 -080023#include "batches/GrRectBatchFactory.h"
bsalomon16b99132015-08-13 14:55:50 -070024#include "batches/GrVertexBatch.h"
joshualitt74417822015-08-07 11:42:16 -070025
bsalomon@google.com30085192011-08-19 15:42:31 +000026GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
27 bool stencilWrapOpsSupport)
28 : fSeparateStencil(separateStencilSupport)
bsalomon@google.comc2099d22012-03-02 21:26:50 +000029 , fStencilWrapOps(stencilWrapOpsSupport) {
bsalomon@google.com289533a2011-10-27 12:34:25 +000030}
31
32
bsalomon@google.com30085192011-08-19 15:42:31 +000033////////////////////////////////////////////////////////////////////////////////
34// Stencil rules for paths
35
36////// Even/Odd
37
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000038GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass,
39 kInvert_StencilOp,
40 kKeep_StencilOp,
41 kAlwaysIfInClip_StencilFunc,
42 0xffff,
43 0xffff,
44 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000045
46// ok not to check clip b/c stencil pass only wrote inside clip
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000047GR_STATIC_CONST_SAME_STENCIL(gEOColorPass,
48 kZero_StencilOp,
49 kZero_StencilOp,
50 kNotEqual_StencilFunc,
51 0xffff,
52 0x0000,
53 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000054
55// have to check clip b/c outside clip will always be zero.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000056GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass,
57 kZero_StencilOp,
58 kZero_StencilOp,
59 kEqualIfInClip_StencilFunc,
60 0xffff,
61 0x0000,
62 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000063
64////// Winding
65
66// when we have separate stencil we increment front faces / decrement back faces
67// when we don't have wrap incr and decr we use the stencil test to simulate
68// them.
69
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000070GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap,
bsalomon@google.com30085192011-08-19 15:42:31 +000071 kIncWrap_StencilOp, kDecWrap_StencilOp,
72 kKeep_StencilOp, kKeep_StencilOp,
73 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
tomhudson@google.com62b09682011-11-09 16:39:17 +000074 0xffff, 0xffff,
75 0xffff, 0xffff,
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000076 0xffff, 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000077
78// if inc'ing the max value, invert to make 0
79// if dec'ing zero invert to make all ones.
80// we can't avoid touching the stencil on both passing and
81// failing, so we can't resctrict ourselves to the clip.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000082GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap,
bsalomon@google.com30085192011-08-19 15:42:31 +000083 kInvert_StencilOp, kInvert_StencilOp,
84 kIncClamp_StencilOp, kDecClamp_StencilOp,
85 kEqual_StencilFunc, kEqual_StencilFunc,
tomhudson@google.com62b09682011-11-09 16:39:17 +000086 0xffff, 0xffff,
87 0xffff, 0x0000,
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000088 0xffff, 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000089
90// When there are no separate faces we do two passes to setup the winding rule
91// stencil. First we draw the front faces and inc, then we draw the back faces
92// and dec. These are same as the above two split into the incrementing and
93// decrementing passes.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000094GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
95 kIncWrap_StencilOp,
96 kKeep_StencilOp,
97 kAlwaysIfInClip_StencilFunc,
98 0xffff,
99 0xffff,
100 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000101
bsalomon@google.com6b2445e2011-12-15 19:47:46 +0000102GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
103 kDecWrap_StencilOp,
104 kKeep_StencilOp,
105 kAlwaysIfInClip_StencilFunc,
106 0xffff,
107 0xffff,
108 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000109
bsalomon@google.com6b2445e2011-12-15 19:47:46 +0000110GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc,
111 kInvert_StencilOp,
112 kIncClamp_StencilOp,
113 kEqual_StencilFunc,
114 0xffff,
115 0xffff,
116 0xffff);
117
118GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec,
119 kInvert_StencilOp,
120 kDecClamp_StencilOp,
121 kEqual_StencilFunc,
122 0xffff,
123 0x0000,
124 0xffff);
125
126// Color passes are the same whether we use the two-sided stencil or two passes
127
128GR_STATIC_CONST_SAME_STENCIL(gWindColorPass,
129 kZero_StencilOp,
130 kZero_StencilOp,
131 kNonZeroIfInClip_StencilFunc,
132 0xffff,
133 0x0000,
134 0xffff);
135
136GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass,
137 kZero_StencilOp,
138 kZero_StencilOp,
139 kEqualIfInClip_StencilFunc,
140 0xffff,
141 0x0000,
142 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000143
144////// Normal render to stencil
145
146// Sometimes the default path renderer can draw a path directly to the stencil
147// buffer without having to first resolve the interior / exterior.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +0000148GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil,
149 kZero_StencilOp,
150 kIncClamp_StencilOp,
151 kAlwaysIfInClip_StencilFunc,
152 0xffff,
153 0x0000,
154 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000155
156////////////////////////////////////////////////////////////////////////////////
157// Helpers for drawPath
158
bsalomon@google.com30085192011-08-19 15:42:31 +0000159#define STENCIL_OFF 0 // Always disable stencil (even when needed)
160
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000161static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000162#if STENCIL_OFF
163 return true;
164#else
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000165 if (!stroke.isHairlineStyle() && !path.isInverseFillType()) {
bsalomon@google.com7d72c452012-01-30 14:02:44 +0000166 return path.isConvex();
bsalomon@google.com30085192011-08-19 15:42:31 +0000167 }
168 return false;
169#endif
170}
171
joshualitt9853cce2014-11-17 14:22:48 -0800172GrPathRenderer::StencilSupport
robertphillipse7d4b2f2015-08-13 07:57:10 -0700173GrDefaultPathRenderer::onGetStencilSupport(const SkPath& path, const GrStrokeInfo& stroke) const {
kkinnunend156d362015-05-18 22:23:54 -0700174 if (single_pass_path(path, stroke)) {
bsalomon@google.com45a15f52012-12-10 19:10:17 +0000175 return GrPathRenderer::kNoRestriction_StencilSupport;
176 } else {
177 return GrPathRenderer::kStencilOnly_StencilSupport;
178 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000179}
180
sugoi@google.com12b4e272012-12-06 20:13:11 +0000181static inline void append_countour_edge_indices(bool hairLine,
bsalomon@google.com30085192011-08-19 15:42:31 +0000182 uint16_t fanCenterIdx,
183 uint16_t edgeV0Idx,
184 uint16_t** indices) {
185 // when drawing lines we're appending line segments along
186 // the contour. When applying the other fill rules we're
187 // drawing triangle fans around fanCenterIdx.
sugoi@google.com12b4e272012-12-06 20:13:11 +0000188 if (!hairLine) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000189 *((*indices)++) = fanCenterIdx;
190 }
191 *((*indices)++) = edgeV0Idx;
192 *((*indices)++) = edgeV0Idx + 1;
193}
194
egdanielaf18a092015-01-05 10:22:28 -0800195static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[],
196 SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed,
joshualitt332c7292015-02-23 08:44:31 -0800197 bool isHairline, uint16_t subpathIdxStart, int offset, uint16_t** idx) {
egdanielaf18a092015-01-05 10:22:28 -0800198 // first pt of quad is the pt we ended on in previous step
joshualitt332c7292015-02-23 08:44:31 -0800199 uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1 + offset;
egdanielaf18a092015-01-05 10:22:28 -0800200 uint16_t numPts = (uint16_t)
201 GrPathUtils::generateQuadraticPoints(
202 pts[0], pts[1], pts[2],
203 srcSpaceTolSqd, vert,
204 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
205 if (indexed) {
206 for (uint16_t i = 0; i < numPts; ++i) {
207 append_countour_edge_indices(isHairline, subpathIdxStart,
208 firstQPtIdx + i, idx);
209 }
210 }
211}
212
bsalomonabd30f52015-08-13 13:34:48 -0700213class DefaultPathBatch : public GrVertexBatch {
joshualitt332c7292015-02-23 08:44:31 -0800214public:
reed1b55a962015-09-17 20:16:13 -0700215 DEFINE_BATCH_CLASS_ID
216
joshualitt332c7292015-02-23 08:44:31 -0800217 struct Geometry {
218 GrColor fColor;
219 SkPath fPath;
220 SkScalar fTolerance;
joshualitt332c7292015-02-23 08:44:31 -0800221 };
bsalomon@google.com30085192011-08-19 15:42:31 +0000222
bsalomonabd30f52015-08-13 13:34:48 -0700223 static GrDrawBatch* Create(const Geometry& geometry, uint8_t coverage,
224 const SkMatrix& viewMatrix, bool isHairline,
225 const SkRect& devBounds) {
halcanary385fe4d2015-08-26 13:07:48 -0700226 return new DefaultPathBatch(geometry, coverage, viewMatrix, isHairline, devBounds);
bsalomon@google.com30085192011-08-19 15:42:31 +0000227 }
228
mtklein36352bf2015-03-25 18:17:31 -0700229 const char* name() const override { return "DefaultPathBatch"; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000230
halcanary9d524f22016-03-29 09:03:52 -0700231 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800232 GrInitInvariantOutput* coverage,
233 GrBatchToXPOverrides* overrides) const override {
joshualitt332c7292015-02-23 08:44:31 -0800234 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800235 color->setKnownFourComponents(fGeoData[0].fColor);
236 coverage->setKnownSingleComponent(this->coverage());
joshualitt332c7292015-02-23 08:44:31 -0800237 }
sugoi@google.com12b4e272012-12-06 20:13:11 +0000238
bsalomone46f9fe2015-08-18 06:05:14 -0700239private:
ethannicholasff210322015-11-24 12:10:10 -0800240 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
joshualitt332c7292015-02-23 08:44:31 -0800241 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800242 if (!overrides.readsColor()) {
joshualitt332c7292015-02-23 08:44:31 -0800243 fGeoData[0].fColor = GrColor_ILLEGAL;
bsalomon@google.com30085192011-08-19 15:42:31 +0000244 }
ethannicholasff210322015-11-24 12:10:10 -0800245 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt332c7292015-02-23 08:44:31 -0800246
247 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800248 fBatch.fColorIgnored = !overrides.readsColor();
joshualitt332c7292015-02-23 08:44:31 -0800249 fBatch.fColor = fGeoData[0].fColor;
ethannicholasff210322015-11-24 12:10:10 -0800250 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
251 fBatch.fCoverageIgnored = !overrides.readsCoverage();
joshualitt332c7292015-02-23 08:44:31 -0800252 }
253
joshualitt144c3c82015-11-30 12:30:13 -0800254 void onPrepareDraws(Target* target) const override {
joshualittdf0c5572015-08-03 11:35:28 -0700255 SkAutoTUnref<const GrGeometryProcessor> gp;
256 {
257 using namespace GrDefaultGeoProcFactory;
258 Color color(this->color());
259 Coverage coverage(this->coverage());
260 if (this->coverageIgnored()) {
261 coverage.fType = Coverage::kNone_Type;
262 }
263 LocalCoords localCoords(this->usesLocalCoords() ? LocalCoords::kUsePosition_Type :
264 LocalCoords::kUnused_Type);
265 gp.reset(GrDefaultGeoProcFactory::Create(color, coverage, localCoords,
266 this->viewMatrix()));
267 }
joshualitt332c7292015-02-23 08:44:31 -0800268
269 size_t vertexStride = gp->getVertexStride();
270 SkASSERT(vertexStride == sizeof(SkPoint));
271
joshualitt332c7292015-02-23 08:44:31 -0800272 int instanceCount = fGeoData.count();
273
274 // compute number of vertices
275 int maxVertices = 0;
276
277 // We will use index buffers if we have multiple paths or one path with multiple contours
278 bool isIndexed = instanceCount > 1;
279 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800280 const Geometry& args = fGeoData[i];
joshualitt332c7292015-02-23 08:44:31 -0800281
282 int contourCount;
283 maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount,
284 args.fTolerance);
285
286 isIndexed = isIndexed || contourCount > 1;
bsalomon@google.com30085192011-08-19 15:42:31 +0000287 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000288
joshualitt5b27b142015-02-24 12:58:46 -0800289 if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) {
joshualitt8985aea2015-12-08 18:58:48 -0800290 //SkDebugf("Cannot render path (%d)\n", maxVertices);
joshualitt5b27b142015-02-24 12:58:46 -0800291 return;
292 }
293
joshualitt332c7292015-02-23 08:44:31 -0800294 // determine primitiveType
295 int maxIndices = 0;
296 GrPrimitiveType primitiveType;
297 if (this->isHairline()) {
298 if (isIndexed) {
299 maxIndices = 2 * maxVertices;
300 primitiveType = kLines_GrPrimitiveType;
301 } else {
302 primitiveType = kLineStrip_GrPrimitiveType;
bsalomon@google.com30085192011-08-19 15:42:31 +0000303 }
joshualitt332c7292015-02-23 08:44:31 -0800304 } else {
305 if (isIndexed) {
306 maxIndices = 3 * maxVertices;
307 primitiveType = kTriangles_GrPrimitiveType;
308 } else {
309 primitiveType = kTriangleFan_GrPrimitiveType;
310 }
311 }
312
313 // allocate vertex / index buffers
cdalton397536c2016-03-25 12:15:03 -0700314 const GrBuffer* vertexBuffer;
joshualitt332c7292015-02-23 08:44:31 -0800315 int firstVertex;
316
bsalomon75398562015-08-17 12:55:38 -0700317 void* verts = target->makeVertexSpace(vertexStride, maxVertices,
318 &vertexBuffer, &firstVertex);
joshualitt332c7292015-02-23 08:44:31 -0800319
bsalomoncb8979d2015-05-05 09:51:38 -0700320 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800321 SkDebugf("Could not allocate vertices\n");
322 return;
323 }
324
cdalton397536c2016-03-25 12:15:03 -0700325 const GrBuffer* indexBuffer = nullptr;
bsalomonb5238a72015-05-05 07:49:49 -0700326 int firstIndex = 0;
joshualitt332c7292015-02-23 08:44:31 -0800327
halcanary96fcdcc2015-08-27 07:41:13 -0700328 void* indices = nullptr;
joshualitt332c7292015-02-23 08:44:31 -0800329 if (isIndexed) {
bsalomon75398562015-08-17 12:55:38 -0700330 indices = target->makeIndexSpace(maxIndices, &indexBuffer, &firstIndex);
joshualitt4b31de82015-03-05 14:33:41 -0800331
332 if (!indices) {
333 SkDebugf("Could not allocate indices\n");
334 return;
335 }
joshualitt332c7292015-02-23 08:44:31 -0800336 }
337
338 // fill buffers
339 int vertexOffset = 0;
340 int indexOffset = 0;
341 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800342 const Geometry& args = fGeoData[i];
joshualitt332c7292015-02-23 08:44:31 -0800343
344 int vertexCnt = 0;
345 int indexCnt = 0;
bsalomoncb8979d2015-05-05 09:51:38 -0700346 if (!this->createGeom(verts,
joshualitt332c7292015-02-23 08:44:31 -0800347 vertexOffset,
348 indices,
349 indexOffset,
350 &vertexCnt,
351 &indexCnt,
352 args.fPath,
353 args.fTolerance,
354 isIndexed)) {
355 return;
356 }
357
358 vertexOffset += vertexCnt;
359 indexOffset += indexCnt;
360 SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
361 }
362
egdaniel0e1853c2016-03-17 11:35:45 -0700363 GrMesh mesh;
joshualitt332c7292015-02-23 08:44:31 -0800364 if (isIndexed) {
egdaniel0e1853c2016-03-17 11:35:45 -0700365 mesh.initIndexed(primitiveType, vertexBuffer, indexBuffer, firstVertex, firstIndex,
366 vertexOffset, indexOffset);
joshualitt332c7292015-02-23 08:44:31 -0800367 } else {
egdaniel0e1853c2016-03-17 11:35:45 -0700368 mesh.init(primitiveType, vertexBuffer, firstVertex, vertexOffset);
joshualitt332c7292015-02-23 08:44:31 -0800369 }
bsalomon342bfc22016-04-01 06:06:20 -0700370 target->draw(gp, mesh);
joshualitt6065b882015-02-24 13:20:59 -0800371
372 // put back reserves
bsalomon75398562015-08-17 12:55:38 -0700373 target->putBackIndices((size_t)(maxIndices - indexOffset));
374 target->putBackVertices((size_t)(maxVertices - vertexOffset), (size_t)vertexStride);
joshualitt332c7292015-02-23 08:44:31 -0800375 }
376
377 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
378
joshualitt332c7292015-02-23 08:44:31 -0800379 DefaultPathBatch(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
reed1b55a962015-09-17 20:16:13 -0700380 bool isHairline, const SkRect& devBounds)
381 : INHERITED(ClassID()) {
joshualitt332c7292015-02-23 08:44:31 -0800382 fBatch.fCoverage = coverage;
383 fBatch.fIsHairline = isHairline;
384 fBatch.fViewMatrix = viewMatrix;
385 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700386
387 this->setBounds(devBounds);
joshualitte2bcec32015-09-30 06:22:22 -0700388
389 // This is b.c. hairlines are notionally infinitely thin so without expansion
390 // two overlapping lines could be reordered even though they hit the same pixels.
391 if (isHairline) {
392 fBounds.outset(0.5f, 0.5f);
393 }
joshualitt332c7292015-02-23 08:44:31 -0800394 }
395
bsalomoncb02b382015-08-12 11:14:50 -0700396 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700397 DefaultPathBatch* that = t->cast<DefaultPathBatch>();
398 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
399 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700400 return false;
401 }
402
joshualitt332c7292015-02-23 08:44:31 -0800403 if (this->color() != that->color()) {
404 return false;
405 }
406
407 if (this->coverage() != that->coverage()) {
408 return false;
409 }
410
411 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
412 return false;
413 }
414
415 if (this->isHairline() != that->isHairline()) {
416 return false;
417 }
418
419 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700420 this->joinBounds(that->bounds());
joshualitt332c7292015-02-23 08:44:31 -0800421 return true;
422 }
423
424 bool createGeom(void* vertices,
425 size_t vertexOffset,
426 void* indices,
427 size_t indexOffset,
428 int* vertexCnt,
429 int* indexCnt,
430 const SkPath& path,
431 SkScalar srcSpaceTol,
joshualitt144c3c82015-11-30 12:30:13 -0800432 bool isIndexed) const {
joshualitt332c7292015-02-23 08:44:31 -0800433 {
434 SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol);
435
436 uint16_t indexOffsetU16 = (uint16_t)indexOffset;
437 uint16_t vertexOffsetU16 = (uint16_t)vertexOffset;
438
439 uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16;
440 uint16_t* idx = idxBase;
441 uint16_t subpathIdxStart = vertexOffsetU16;
442
443 SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset;
444 SkPoint* vert = base;
445
446 SkPoint pts[4];
447
448 bool first = true;
449 int subpath = 0;
450
451 SkPath::Iter iter(path, false);
452
453 bool done = false;
454 while (!done) {
455 SkPath::Verb verb = iter.next(pts);
456 switch (verb) {
457 case SkPath::kMove_Verb:
458 if (!first) {
459 uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16;
460 subpathIdxStart = currIdx;
461 ++subpath;
462 }
463 *vert = pts[0];
464 vert++;
465 break;
466 case SkPath::kLine_Verb:
467 if (isIndexed) {
468 uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
469 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
470 prevIdx, &idx);
471 }
472 *(vert++) = pts[1];
473 break;
474 case SkPath::kConic_Verb: {
475 SkScalar weight = iter.conicWeight();
476 SkAutoConicToQuads converter;
477 // Converting in src-space, hance the finer tolerance (0.25)
478 // TODO: find a way to do this in dev-space so the tolerance means something
479 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
480 for (int i = 0; i < converter.countQuads(); ++i) {
481 add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol,
482 isIndexed, this->isHairline(), subpathIdxStart,
483 (int)vertexOffset, &idx);
484 }
485 break;
bsalomon@google.com30085192011-08-19 15:42:31 +0000486 }
joshualitt332c7292015-02-23 08:44:31 -0800487 case SkPath::kQuad_Verb:
488 add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed,
489 this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx);
490 break;
491 case SkPath::kCubic_Verb: {
492 // first pt of cubic is the pt we ended on in previous step
493 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
494 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
495 pts[0], pts[1], pts[2], pts[3],
496 srcSpaceTolSqd, &vert,
497 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
498 if (isIndexed) {
499 for (uint16_t i = 0; i < numPts; ++i) {
500 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
501 firstCPtIdx + i, &idx);
502 }
503 }
504 break;
505 }
506 case SkPath::kClose_Verb:
507 break;
508 case SkPath::kDone_Verb:
509 done = true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000510 }
joshualitt332c7292015-02-23 08:44:31 -0800511 first = false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000512 }
joshualitt332c7292015-02-23 08:44:31 -0800513
514 *vertexCnt = static_cast<int>(vert - base);
515 *indexCnt = static_cast<int>(idx - idxBase);
516
bsalomon@google.com30085192011-08-19 15:42:31 +0000517 }
joshualitt332c7292015-02-23 08:44:31 -0800518 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000519 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000520
joshualitt332c7292015-02-23 08:44:31 -0800521 GrColor color() const { return fBatch.fColor; }
522 uint8_t coverage() const { return fBatch.fCoverage; }
523 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
524 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
525 bool isHairline() const { return fBatch.fIsHairline; }
joshualittb8c241a2015-05-19 08:23:30 -0700526 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000527
joshualitt332c7292015-02-23 08:44:31 -0800528 struct BatchTracker {
529 GrColor fColor;
530 uint8_t fCoverage;
531 SkMatrix fViewMatrix;
532 bool fUsesLocalCoords;
533 bool fColorIgnored;
534 bool fCoverageIgnored;
535 bool fIsHairline;
536 };
537
538 BatchTracker fBatch;
539 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700540
541 typedef GrVertexBatch INHERITED;
joshualitt332c7292015-02-23 08:44:31 -0800542};
bsalomon@google.com30085192011-08-19 15:42:31 +0000543
joshualitt9853cce2014-11-17 14:22:48 -0800544bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800545 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800546 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800547 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800548 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700549 const GrStrokeInfo& origStroke,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000550 bool stencilOnly) {
kkinnunen18996512015-04-26 23:18:49 -0700551 SkTCopyOnFirstWrite<GrStrokeInfo> stroke(origStroke);
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000552
553 SkScalar hairlineCoverage;
joshualitt2e3b3e32014-12-09 13:31:14 -0800554 uint8_t newCoverage = 0xff;
joshualitt8059eb92014-12-29 15:10:07 -0800555 if (IsStrokeHairlineOrEquivalent(*stroke, viewMatrix, &hairlineCoverage)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800556 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000557
kkinnunend156d362015-05-18 22:23:54 -0700558 if (!stroke->isHairlineStyle()) {
559 stroke.writable()->setHairlineStyle();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000560 }
561 }
562
kkinnunend156d362015-05-18 22:23:54 -0700563 const bool isHairline = stroke->isHairlineStyle();
bsalomon@google.com30085192011-08-19 15:42:31 +0000564
egdaniel080e6732014-12-22 07:35:52 -0800565 // Save the current xp on the draw state so we can reset it if needed
egdanielc4b72722015-11-23 13:20:41 -0800566 const GrXPFactory* xpFactory = pipelineBuilder->getXPFactory();
567 SkAutoTUnref<const GrXPFactory> backupXPFactory(SkSafeRef(xpFactory));
bsalomon@google.com30085192011-08-19 15:42:31 +0000568 // face culling doesn't make sense here
egdaniel8dd688b2015-01-22 10:16:09 -0800569 SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace());
bsalomon@google.com30085192011-08-19 15:42:31 +0000570
571 int passCount = 0;
572 const GrStencilSettings* passes[3];
joshualitt332c7292015-02-23 08:44:31 -0800573 GrPipelineBuilder::DrawFace drawFace[3];
bsalomon@google.com30085192011-08-19 15:42:31 +0000574 bool reverse = false;
575 bool lastPassIsBounds;
576
joshualitt332c7292015-02-23 08:44:31 -0800577 if (isHairline) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000578 passCount = 1;
579 if (stencilOnly) {
580 passes[0] = &gDirectToStencil;
581 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700582 passes[0] = nullptr;
bsalomon@google.com30085192011-08-19 15:42:31 +0000583 }
584 lastPassIsBounds = false;
egdaniel8dd688b2015-01-22 10:16:09 -0800585 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000586 } else {
kkinnunend156d362015-05-18 22:23:54 -0700587 if (single_pass_path(path, *stroke)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000588 passCount = 1;
589 if (stencilOnly) {
590 passes[0] = &gDirectToStencil;
591 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700592 passes[0] = nullptr;
bsalomon@google.com30085192011-08-19 15:42:31 +0000593 }
egdaniel8dd688b2015-01-22 10:16:09 -0800594 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000595 lastPassIsBounds = false;
596 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000597 switch (path.getFillType()) {
sugoi@google.com12b4e272012-12-06 20:13:11 +0000598 case SkPath::kInverseEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000599 reverse = true;
600 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000601 case SkPath::kEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000602 passes[0] = &gEOStencilPass;
603 if (stencilOnly) {
604 passCount = 1;
605 lastPassIsBounds = false;
606 } else {
607 passCount = 2;
608 lastPassIsBounds = true;
609 if (reverse) {
610 passes[1] = &gInvEOColorPass;
611 } else {
612 passes[1] = &gEOColorPass;
613 }
614 }
egdaniel8dd688b2015-01-22 10:16:09 -0800615 drawFace[0] = drawFace[1] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000616 break;
617
sugoi@google.com12b4e272012-12-06 20:13:11 +0000618 case SkPath::kInverseWinding_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000619 reverse = true;
620 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000621 case SkPath::kWinding_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000622 if (fSeparateStencil) {
623 if (fStencilWrapOps) {
624 passes[0] = &gWindStencilSeparateWithWrap;
625 } else {
626 passes[0] = &gWindStencilSeparateNoWrap;
627 }
628 passCount = 2;
egdaniel8dd688b2015-01-22 10:16:09 -0800629 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000630 } else {
631 if (fStencilWrapOps) {
632 passes[0] = &gWindSingleStencilWithWrapInc;
633 passes[1] = &gWindSingleStencilWithWrapDec;
634 } else {
635 passes[0] = &gWindSingleStencilNoWrapInc;
636 passes[1] = &gWindSingleStencilNoWrapDec;
637 }
638 // which is cw and which is ccw is arbitrary.
egdaniel8dd688b2015-01-22 10:16:09 -0800639 drawFace[0] = GrPipelineBuilder::kCW_DrawFace;
640 drawFace[1] = GrPipelineBuilder::kCCW_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000641 passCount = 3;
642 }
643 if (stencilOnly) {
644 lastPassIsBounds = false;
645 --passCount;
646 } else {
647 lastPassIsBounds = true;
egdaniel8dd688b2015-01-22 10:16:09 -0800648 drawFace[passCount-1] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000649 if (reverse) {
650 passes[passCount-1] = &gInvWindColorPass;
651 } else {
652 passes[passCount-1] = &gWindColorPass;
653 }
654 }
655 break;
656 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000657 SkDEBUGFAIL("Unknown path fFill!");
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000658 return false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000659 }
660 }
661 }
662
senorblanco2b4bb072015-04-22 13:45:18 -0700663 SkScalar tol = GrPathUtils::kDefaultTolerance;
joshualitt332c7292015-02-23 08:44:31 -0800664 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
665
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000666 SkRect devBounds;
egdaniel8dd688b2015-01-22 10:16:09 -0800667 GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds);
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000668
bsalomon@google.com30085192011-08-19 15:42:31 +0000669 for (int p = 0; p < passCount; ++p) {
egdaniel8dd688b2015-01-22 10:16:09 -0800670 pipelineBuilder->setDrawFace(drawFace[p]);
bsalomon49f085d2014-09-05 13:34:00 -0700671 if (passes[p]) {
egdaniel8dd688b2015-01-22 10:16:09 -0800672 *pipelineBuilder->stencil() = *passes[p];
bsalomon@google.com30085192011-08-19 15:42:31 +0000673 }
674
675 if (lastPassIsBounds && (p == passCount-1)) {
egdaniel8dd688b2015-01-22 10:16:09 -0800676 // Reset the XP Factory on pipelineBuilder
677 pipelineBuilder->setXPFactory(backupXPFactory);
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000678 SkRect bounds;
joshualittd27f73e2014-12-29 07:43:36 -0800679 SkMatrix localMatrix = SkMatrix::I();
bsalomon@google.com30085192011-08-19 15:42:31 +0000680 if (reverse) {
egdaniel8dd688b2015-01-22 10:16:09 -0800681 SkASSERT(pipelineBuilder->getRenderTarget());
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000682 // draw over the dev bounds (which will be the whole dst surface for inv fill).
683 bounds = devBounds;
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000684 SkMatrix vmi;
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000685 // mapRect through persp matrix may not be correct
joshualitt8059eb92014-12-29 15:10:07 -0800686 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000687 vmi.mapRect(&bounds);
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000688 } else {
joshualittd27f73e2014-12-29 07:43:36 -0800689 if (!viewMatrix.invert(&localMatrix)) {
690 return false;
691 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000692 }
693 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000694 bounds = path.getBounds();
bsalomon@google.com30085192011-08-19 15:42:31 +0000695 }
joshualitt8059eb92014-12-29 15:10:07 -0800696 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
697 viewMatrix;
joshualitt04194f32016-01-13 10:08:27 -0800698 SkAutoTUnref<GrDrawBatch> batch(
699 GrRectBatchFactory::CreateNonAAFill(color, viewM, bounds, nullptr,
700 &localMatrix));
701 target->drawBatch(*pipelineBuilder, batch);
bsalomon@google.com30085192011-08-19 15:42:31 +0000702 } else {
703 if (passCount > 1) {
egdaniel8dd688b2015-01-22 10:16:09 -0800704 pipelineBuilder->setDisableColorXPFactory();
bsalomon@google.com30085192011-08-19 15:42:31 +0000705 }
joshualitt332c7292015-02-23 08:44:31 -0800706
707 DefaultPathBatch::Geometry geometry;
708 geometry.fColor = color;
709 geometry.fPath = path;
710 geometry.fTolerance = srcSpaceTol;
joshualitt332c7292015-02-23 08:44:31 -0800711
bsalomonabd30f52015-08-13 13:34:48 -0700712 SkAutoTUnref<GrDrawBatch> batch(DefaultPathBatch::Create(geometry, newCoverage,
713 viewMatrix, isHairline,
714 devBounds));
joshualitt332c7292015-02-23 08:44:31 -0800715
joshualitt1c735482015-07-13 08:08:25 -0700716 target->drawBatch(*pipelineBuilder, batch);
bsalomon@google.com30085192011-08-19 15:42:31 +0000717 }
718 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000719 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000720}
721
bsalomon0aff2fa2015-07-31 06:48:27 -0700722bool GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000723 // this class can draw any path with any fill but doesn't do any anti-aliasing.
bsalomon0aff2fa2015-07-31 06:48:27 -0700724 return !args.fAntiAlias && (args.fStroke->isFillStyle() ||
725 IsStrokeHairlineOrEquivalent(*args.fStroke, *args.fViewMatrix,
halcanary96fcdcc2015-08-27 07:41:13 -0700726 nullptr));
bsalomon@google.com30085192011-08-19 15:42:31 +0000727}
728
bsalomon0aff2fa2015-07-31 06:48:27 -0700729bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
joshualittde83b412016-01-14 09:58:36 -0800730 GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), "GrDefaultPathRenderer::onDrawPath");
bsalomon0aff2fa2015-07-31 06:48:27 -0700731 return this->internalDrawPath(args.fTarget,
732 args.fPipelineBuilder,
733 args.fColor,
734 *args.fViewMatrix,
735 *args.fPath,
736 *args.fStroke,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000737 false);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000738}
739
bsalomon0aff2fa2015-07-31 06:48:27 -0700740void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
joshualittde83b412016-01-14 09:58:36 -0800741 GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(),"GrDefaultPathRenderer::onStencilPath");
bsalomon0aff2fa2015-07-31 06:48:27 -0700742 SkASSERT(SkPath::kInverseEvenOdd_FillType != args.fPath->getFillType());
743 SkASSERT(SkPath::kInverseWinding_FillType != args.fPath->getFillType());
744 this->internalDrawPath(args.fTarget, args.fPipelineBuilder, GrColor_WHITE, *args.fViewMatrix,
745 *args.fPath, *args.fStroke, true);
bsalomon@google.com30085192011-08-19 15:42:31 +0000746}
joshualitt622d3ad2015-05-07 08:13:11 -0700747
748///////////////////////////////////////////////////////////////////////////////////////////////////
749
750#ifdef GR_TEST_UTILS
751
bsalomonabd30f52015-08-13 13:34:48 -0700752DRAW_BATCH_TEST_DEFINE(DefaultPathBatch) {
joshualitt622d3ad2015-05-07 08:13:11 -0700753 GrColor color = GrRandomColor(random);
754 SkMatrix viewMatrix = GrTest::TestMatrix(random);
755
756 // For now just hairlines because the other types of draws require two batches.
757 // TODO we should figure out a way to combine the stencil and cover steps into one batch
758 GrStrokeInfo stroke(SkStrokeRec::kHairline_InitStyle);
759 SkPath path = GrTest::TestPath(random);
760
761 // Compute srcSpaceTol
762 SkRect bounds = path.getBounds();
763 SkScalar tol = GrPathUtils::kDefaultTolerance;
764 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
765
766 DefaultPathBatch::Geometry geometry;
767 geometry.fColor = color;
768 geometry.fPath = path;
769 geometry.fTolerance = srcSpaceTol;
770
771 viewMatrix.mapRect(&bounds);
772 uint8_t coverage = GrRandomCoverage(random);
773 return DefaultPathBatch::Create(geometry, coverage, viewMatrix, true, bounds);
774}
775
776#endif