blob: f039cee198d2ffe630cca44794565a44b1edd00a [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
joshualitt332c7292015-02-23 08:44:31 -080010#include "GrBatch.h"
11#include "GrBatchTarget.h"
joshualitt622d3ad2015-05-07 08:13:11 -070012#include "GrBatchTest.h"
bsalomon@google.com30085192011-08-19 15:42:31 +000013#include "GrContext.h"
joshualitt5478d422014-11-14 16:00:38 -080014#include "GrDefaultGeoProcFactory.h"
bsalomon@google.com30085192011-08-19 15:42:31 +000015#include "GrPathUtils.h"
egdaniel8dd688b2015-01-22 10:16:09 -080016#include "GrPipelineBuilder.h"
bsalomoncb8979d2015-05-05 09:51:38 -070017#include "GrVertices.h"
egdanielaf18a092015-01-05 10:22:28 -080018#include "SkGeometry.h"
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000019#include "SkString.h"
sugoi@google.com5f74cf82012-12-17 21:16:45 +000020#include "SkStrokeRec.h"
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +000021#include "SkTLazy.h"
commit-bot@chromium.org933e65d2014-03-20 20:00:24 +000022#include "SkTraceEvent.h"
bsalomon@google.com30085192011-08-19 15:42:31 +000023
bsalomon@google.com30085192011-08-19 15:42:31 +000024GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
25 bool stencilWrapOpsSupport)
26 : fSeparateStencil(separateStencilSupport)
bsalomon@google.comc2099d22012-03-02 21:26:50 +000027 , fStencilWrapOps(stencilWrapOpsSupport) {
bsalomon@google.com289533a2011-10-27 12:34:25 +000028}
29
30
bsalomon@google.com30085192011-08-19 15:42:31 +000031////////////////////////////////////////////////////////////////////////////////
32// Stencil rules for paths
33
34////// Even/Odd
35
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000036GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass,
37 kInvert_StencilOp,
38 kKeep_StencilOp,
39 kAlwaysIfInClip_StencilFunc,
40 0xffff,
41 0xffff,
42 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000043
44// ok not to check clip b/c stencil pass only wrote inside clip
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000045GR_STATIC_CONST_SAME_STENCIL(gEOColorPass,
46 kZero_StencilOp,
47 kZero_StencilOp,
48 kNotEqual_StencilFunc,
49 0xffff,
50 0x0000,
51 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000052
53// have to check clip b/c outside clip will always be zero.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000054GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass,
55 kZero_StencilOp,
56 kZero_StencilOp,
57 kEqualIfInClip_StencilFunc,
58 0xffff,
59 0x0000,
60 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000061
62////// Winding
63
64// when we have separate stencil we increment front faces / decrement back faces
65// when we don't have wrap incr and decr we use the stencil test to simulate
66// them.
67
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000068GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap,
bsalomon@google.com30085192011-08-19 15:42:31 +000069 kIncWrap_StencilOp, kDecWrap_StencilOp,
70 kKeep_StencilOp, kKeep_StencilOp,
71 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
tomhudson@google.com62b09682011-11-09 16:39:17 +000072 0xffff, 0xffff,
73 0xffff, 0xffff,
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000074 0xffff, 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000075
76// if inc'ing the max value, invert to make 0
77// if dec'ing zero invert to make all ones.
78// we can't avoid touching the stencil on both passing and
79// failing, so we can't resctrict ourselves to the clip.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000080GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap,
bsalomon@google.com30085192011-08-19 15:42:31 +000081 kInvert_StencilOp, kInvert_StencilOp,
82 kIncClamp_StencilOp, kDecClamp_StencilOp,
83 kEqual_StencilFunc, kEqual_StencilFunc,
tomhudson@google.com62b09682011-11-09 16:39:17 +000084 0xffff, 0xffff,
85 0xffff, 0x0000,
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000086 0xffff, 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000087
88// When there are no separate faces we do two passes to setup the winding rule
89// stencil. First we draw the front faces and inc, then we draw the back faces
90// and dec. These are same as the above two split into the incrementing and
91// decrementing passes.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000092GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
93 kIncWrap_StencilOp,
94 kKeep_StencilOp,
95 kAlwaysIfInClip_StencilFunc,
96 0xffff,
97 0xffff,
98 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000099
bsalomon@google.com6b2445e2011-12-15 19:47:46 +0000100GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
101 kDecWrap_StencilOp,
102 kKeep_StencilOp,
103 kAlwaysIfInClip_StencilFunc,
104 0xffff,
105 0xffff,
106 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000107
bsalomon@google.com6b2445e2011-12-15 19:47:46 +0000108GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc,
109 kInvert_StencilOp,
110 kIncClamp_StencilOp,
111 kEqual_StencilFunc,
112 0xffff,
113 0xffff,
114 0xffff);
115
116GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec,
117 kInvert_StencilOp,
118 kDecClamp_StencilOp,
119 kEqual_StencilFunc,
120 0xffff,
121 0x0000,
122 0xffff);
123
124// Color passes are the same whether we use the two-sided stencil or two passes
125
126GR_STATIC_CONST_SAME_STENCIL(gWindColorPass,
127 kZero_StencilOp,
128 kZero_StencilOp,
129 kNonZeroIfInClip_StencilFunc,
130 0xffff,
131 0x0000,
132 0xffff);
133
134GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass,
135 kZero_StencilOp,
136 kZero_StencilOp,
137 kEqualIfInClip_StencilFunc,
138 0xffff,
139 0x0000,
140 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000141
142////// Normal render to stencil
143
144// Sometimes the default path renderer can draw a path directly to the stencil
145// buffer without having to first resolve the interior / exterior.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +0000146GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil,
147 kZero_StencilOp,
148 kIncClamp_StencilOp,
149 kAlwaysIfInClip_StencilFunc,
150 0xffff,
151 0x0000,
152 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000153
154////////////////////////////////////////////////////////////////////////////////
155// Helpers for drawPath
156
bsalomon@google.com30085192011-08-19 15:42:31 +0000157#define STENCIL_OFF 0 // Always disable stencil (even when needed)
158
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000159static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000160#if STENCIL_OFF
161 return true;
162#else
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000163 if (!stroke.isHairlineStyle() && !path.isInverseFillType()) {
bsalomon@google.com7d72c452012-01-30 14:02:44 +0000164 return path.isConvex();
bsalomon@google.com30085192011-08-19 15:42:31 +0000165 }
166 return false;
167#endif
168}
169
joshualitt9853cce2014-11-17 14:22:48 -0800170GrPathRenderer::StencilSupport
171GrDefaultPathRenderer::onGetStencilSupport(const GrDrawTarget*,
egdaniel8dd688b2015-01-22 10:16:09 -0800172 const GrPipelineBuilder*,
joshualitt9853cce2014-11-17 14:22:48 -0800173 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700174 const GrStrokeInfo& stroke) const {
kkinnunend156d362015-05-18 22:23:54 -0700175 if (single_pass_path(path, stroke)) {
bsalomon@google.com45a15f52012-12-10 19:10:17 +0000176 return GrPathRenderer::kNoRestriction_StencilSupport;
177 } else {
178 return GrPathRenderer::kStencilOnly_StencilSupport;
179 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000180}
181
sugoi@google.com12b4e272012-12-06 20:13:11 +0000182static inline void append_countour_edge_indices(bool hairLine,
bsalomon@google.com30085192011-08-19 15:42:31 +0000183 uint16_t fanCenterIdx,
184 uint16_t edgeV0Idx,
185 uint16_t** indices) {
186 // when drawing lines we're appending line segments along
187 // the contour. When applying the other fill rules we're
188 // drawing triangle fans around fanCenterIdx.
sugoi@google.com12b4e272012-12-06 20:13:11 +0000189 if (!hairLine) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000190 *((*indices)++) = fanCenterIdx;
191 }
192 *((*indices)++) = edgeV0Idx;
193 *((*indices)++) = edgeV0Idx + 1;
194}
195
egdanielaf18a092015-01-05 10:22:28 -0800196static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[],
197 SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed,
joshualitt332c7292015-02-23 08:44:31 -0800198 bool isHairline, uint16_t subpathIdxStart, int offset, uint16_t** idx) {
egdanielaf18a092015-01-05 10:22:28 -0800199 // first pt of quad is the pt we ended on in previous step
joshualitt332c7292015-02-23 08:44:31 -0800200 uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1 + offset;
egdanielaf18a092015-01-05 10:22:28 -0800201 uint16_t numPts = (uint16_t)
202 GrPathUtils::generateQuadraticPoints(
203 pts[0], pts[1], pts[2],
204 srcSpaceTolSqd, vert,
205 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
206 if (indexed) {
207 for (uint16_t i = 0; i < numPts; ++i) {
208 append_countour_edge_indices(isHairline, subpathIdxStart,
209 firstQPtIdx + i, idx);
210 }
211 }
212}
213
joshualitt332c7292015-02-23 08:44:31 -0800214class DefaultPathBatch : public GrBatch {
215public:
216 struct Geometry {
217 GrColor fColor;
218 SkPath fPath;
219 SkScalar fTolerance;
joshualitt332c7292015-02-23 08:44:31 -0800220 };
bsalomon@google.com30085192011-08-19 15:42:31 +0000221
joshualitt332c7292015-02-23 08:44:31 -0800222 static GrBatch* Create(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
joshualitt99c7c072015-05-01 13:43:30 -0700223 bool isHairline, const SkRect& devBounds) {
224 return SkNEW_ARGS(DefaultPathBatch, (geometry, coverage, viewMatrix, isHairline,
225 devBounds));
bsalomon@google.com30085192011-08-19 15:42:31 +0000226 }
227
mtklein36352bf2015-03-25 18:17:31 -0700228 const char* name() const override { return "DefaultPathBatch"; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000229
mtklein36352bf2015-03-25 18:17:31 -0700230 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt332c7292015-02-23 08:44:31 -0800231 // When this is called on a batch, there is only one geometry bundle
232 out->setKnownFourComponents(fGeoData[0].fColor);
233 }
mtklein36352bf2015-03-25 18:17:31 -0700234 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt233c6ce2015-02-24 08:17:49 -0800235 out->setKnownSingleComponent(this->coverage());
joshualitt332c7292015-02-23 08:44:31 -0800236 }
sugoi@google.com12b4e272012-12-06 20:13:11 +0000237
mtklein36352bf2015-03-25 18:17:31 -0700238 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt332c7292015-02-23 08:44:31 -0800239 // Handle any color overrides
240 if (init.fColorIgnored) {
241 fGeoData[0].fColor = GrColor_ILLEGAL;
242 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
243 fGeoData[0].fColor = init.fOverrideColor;
bsalomon@google.com30085192011-08-19 15:42:31 +0000244 }
joshualitt332c7292015-02-23 08:44:31 -0800245
246 // setup batch properties
247 fBatch.fColorIgnored = init.fColorIgnored;
248 fBatch.fColor = fGeoData[0].fColor;
249 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
250 fBatch.fCoverageIgnored = init.fCoverageIgnored;
251 }
252
mtklein36352bf2015-03-25 18:17:31 -0700253 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt332c7292015-02-23 08:44:31 -0800254 SkAutoTUnref<const GrGeometryProcessor> gp(
255 GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
256 this->color(),
joshualittb8c241a2015-05-19 08:23:30 -0700257 this->usesLocalCoords(),
258 this->coverageIgnored(),
joshualitt332c7292015-02-23 08:44:31 -0800259 this->viewMatrix(),
260 SkMatrix::I(),
joshualitt332c7292015-02-23 08:44:31 -0800261 this->coverage()));
262
263 size_t vertexStride = gp->getVertexStride();
264 SkASSERT(vertexStride == sizeof(SkPoint));
265
266 batchTarget->initDraw(gp, pipeline);
267
joshualitt332c7292015-02-23 08:44:31 -0800268 int instanceCount = fGeoData.count();
269
270 // compute number of vertices
271 int maxVertices = 0;
272
273 // We will use index buffers if we have multiple paths or one path with multiple contours
274 bool isIndexed = instanceCount > 1;
275 for (int i = 0; i < instanceCount; i++) {
276 Geometry& args = fGeoData[i];
277
278 int contourCount;
279 maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount,
280 args.fTolerance);
281
282 isIndexed = isIndexed || contourCount > 1;
bsalomon@google.com30085192011-08-19 15:42:31 +0000283 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000284
joshualitt5b27b142015-02-24 12:58:46 -0800285 if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) {
286 SkDebugf("Cannot render path (%d)\n", maxVertices);
287 return;
288 }
289
joshualitt332c7292015-02-23 08:44:31 -0800290 // determine primitiveType
291 int maxIndices = 0;
292 GrPrimitiveType primitiveType;
293 if (this->isHairline()) {
294 if (isIndexed) {
295 maxIndices = 2 * maxVertices;
296 primitiveType = kLines_GrPrimitiveType;
297 } else {
298 primitiveType = kLineStrip_GrPrimitiveType;
bsalomon@google.com30085192011-08-19 15:42:31 +0000299 }
joshualitt332c7292015-02-23 08:44:31 -0800300 } else {
301 if (isIndexed) {
302 maxIndices = 3 * maxVertices;
303 primitiveType = kTriangles_GrPrimitiveType;
304 } else {
305 primitiveType = kTriangleFan_GrPrimitiveType;
306 }
307 }
308
309 // allocate vertex / index buffers
310 const GrVertexBuffer* vertexBuffer;
311 int firstVertex;
312
robertphillipse40d3972015-05-07 09:51:43 -0700313 void* verts = batchTarget->makeVertSpace(vertexStride, maxVertices,
314 &vertexBuffer, &firstVertex);
joshualitt332c7292015-02-23 08:44:31 -0800315
bsalomoncb8979d2015-05-05 09:51:38 -0700316 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800317 SkDebugf("Could not allocate vertices\n");
318 return;
319 }
320
bsalomonb5238a72015-05-05 07:49:49 -0700321 const GrIndexBuffer* indexBuffer = NULL;
322 int firstIndex = 0;
joshualitt332c7292015-02-23 08:44:31 -0800323
324 void* indices = NULL;
325 if (isIndexed) {
robertphillipse40d3972015-05-07 09:51:43 -0700326 indices = batchTarget->makeIndexSpace(maxIndices, &indexBuffer, &firstIndex);
joshualitt4b31de82015-03-05 14:33:41 -0800327
328 if (!indices) {
329 SkDebugf("Could not allocate indices\n");
330 return;
331 }
joshualitt332c7292015-02-23 08:44:31 -0800332 }
333
334 // fill buffers
335 int vertexOffset = 0;
336 int indexOffset = 0;
337 for (int i = 0; i < instanceCount; i++) {
338 Geometry& args = fGeoData[i];
339
340 int vertexCnt = 0;
341 int indexCnt = 0;
bsalomoncb8979d2015-05-05 09:51:38 -0700342 if (!this->createGeom(verts,
joshualitt332c7292015-02-23 08:44:31 -0800343 vertexOffset,
344 indices,
345 indexOffset,
346 &vertexCnt,
347 &indexCnt,
348 args.fPath,
349 args.fTolerance,
350 isIndexed)) {
351 return;
352 }
353
354 vertexOffset += vertexCnt;
355 indexOffset += indexCnt;
356 SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
357 }
358
bsalomoncb8979d2015-05-05 09:51:38 -0700359 GrVertices vertices;
joshualitt332c7292015-02-23 08:44:31 -0800360 if (isIndexed) {
bsalomoncb8979d2015-05-05 09:51:38 -0700361 vertices.initIndexed(primitiveType, vertexBuffer, indexBuffer, firstVertex, firstIndex,
bsalomonb5238a72015-05-05 07:49:49 -0700362 vertexOffset, indexOffset);
joshualitt332c7292015-02-23 08:44:31 -0800363 } else {
bsalomoncb8979d2015-05-05 09:51:38 -0700364 vertices.init(primitiveType, vertexBuffer, firstVertex, vertexOffset);
joshualitt332c7292015-02-23 08:44:31 -0800365 }
bsalomoncb8979d2015-05-05 09:51:38 -0700366 batchTarget->draw(vertices);
joshualitt6065b882015-02-24 13:20:59 -0800367
368 // put back reserves
369 batchTarget->putBackIndices((size_t)(maxIndices - indexOffset));
370 batchTarget->putBackVertices((size_t)(maxVertices - vertexOffset), (size_t)vertexStride);
joshualitt332c7292015-02-23 08:44:31 -0800371 }
372
373 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
374
375private:
376 DefaultPathBatch(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
joshualitt99c7c072015-05-01 13:43:30 -0700377 bool isHairline, const SkRect& devBounds) {
joshualitt332c7292015-02-23 08:44:31 -0800378 this->initClassID<DefaultPathBatch>();
379 fBatch.fCoverage = coverage;
380 fBatch.fIsHairline = isHairline;
381 fBatch.fViewMatrix = viewMatrix;
382 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700383
384 this->setBounds(devBounds);
joshualitt332c7292015-02-23 08:44:31 -0800385 }
386
mtklein36352bf2015-03-25 18:17:31 -0700387 bool onCombineIfPossible(GrBatch* t) override {
joshualitt332c7292015-02-23 08:44:31 -0800388 DefaultPathBatch* that = t->cast<DefaultPathBatch>();
389
390 if (this->color() != that->color()) {
391 return false;
392 }
393
394 if (this->coverage() != that->coverage()) {
395 return false;
396 }
397
398 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
399 return false;
400 }
401
402 if (this->isHairline() != that->isHairline()) {
403 return false;
404 }
405
406 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700407 this->joinBounds(that->bounds());
joshualitt332c7292015-02-23 08:44:31 -0800408 return true;
409 }
410
411 bool createGeom(void* vertices,
412 size_t vertexOffset,
413 void* indices,
414 size_t indexOffset,
415 int* vertexCnt,
416 int* indexCnt,
417 const SkPath& path,
418 SkScalar srcSpaceTol,
419 bool isIndexed) {
420 {
421 SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol);
422
423 uint16_t indexOffsetU16 = (uint16_t)indexOffset;
424 uint16_t vertexOffsetU16 = (uint16_t)vertexOffset;
425
426 uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16;
427 uint16_t* idx = idxBase;
428 uint16_t subpathIdxStart = vertexOffsetU16;
429
430 SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset;
431 SkPoint* vert = base;
432
433 SkPoint pts[4];
434
435 bool first = true;
436 int subpath = 0;
437
438 SkPath::Iter iter(path, false);
439
440 bool done = false;
441 while (!done) {
442 SkPath::Verb verb = iter.next(pts);
443 switch (verb) {
444 case SkPath::kMove_Verb:
445 if (!first) {
446 uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16;
447 subpathIdxStart = currIdx;
448 ++subpath;
449 }
450 *vert = pts[0];
451 vert++;
452 break;
453 case SkPath::kLine_Verb:
454 if (isIndexed) {
455 uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
456 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
457 prevIdx, &idx);
458 }
459 *(vert++) = pts[1];
460 break;
461 case SkPath::kConic_Verb: {
462 SkScalar weight = iter.conicWeight();
463 SkAutoConicToQuads converter;
464 // Converting in src-space, hance the finer tolerance (0.25)
465 // TODO: find a way to do this in dev-space so the tolerance means something
466 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
467 for (int i = 0; i < converter.countQuads(); ++i) {
468 add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol,
469 isIndexed, this->isHairline(), subpathIdxStart,
470 (int)vertexOffset, &idx);
471 }
472 break;
bsalomon@google.com30085192011-08-19 15:42:31 +0000473 }
joshualitt332c7292015-02-23 08:44:31 -0800474 case SkPath::kQuad_Verb:
475 add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed,
476 this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx);
477 break;
478 case SkPath::kCubic_Verb: {
479 // first pt of cubic is the pt we ended on in previous step
480 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
481 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
482 pts[0], pts[1], pts[2], pts[3],
483 srcSpaceTolSqd, &vert,
484 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
485 if (isIndexed) {
486 for (uint16_t i = 0; i < numPts; ++i) {
487 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
488 firstCPtIdx + i, &idx);
489 }
490 }
491 break;
492 }
493 case SkPath::kClose_Verb:
494 break;
495 case SkPath::kDone_Verb:
496 done = true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000497 }
joshualitt332c7292015-02-23 08:44:31 -0800498 first = false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000499 }
joshualitt332c7292015-02-23 08:44:31 -0800500
501 *vertexCnt = static_cast<int>(vert - base);
502 *indexCnt = static_cast<int>(idx - idxBase);
503
bsalomon@google.com30085192011-08-19 15:42:31 +0000504 }
joshualitt332c7292015-02-23 08:44:31 -0800505 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000506 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000507
joshualitt332c7292015-02-23 08:44:31 -0800508 GrColor color() const { return fBatch.fColor; }
509 uint8_t coverage() const { return fBatch.fCoverage; }
510 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
511 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
512 bool isHairline() const { return fBatch.fIsHairline; }
joshualittb8c241a2015-05-19 08:23:30 -0700513 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000514
joshualitt332c7292015-02-23 08:44:31 -0800515 struct BatchTracker {
516 GrColor fColor;
517 uint8_t fCoverage;
518 SkMatrix fViewMatrix;
519 bool fUsesLocalCoords;
520 bool fColorIgnored;
521 bool fCoverageIgnored;
522 bool fIsHairline;
523 };
524
525 BatchTracker fBatch;
526 SkSTArray<1, Geometry, true> fGeoData;
527};
bsalomon@google.com30085192011-08-19 15:42:31 +0000528
joshualitt9853cce2014-11-17 14:22:48 -0800529bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800530 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800531 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800532 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800533 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700534 const GrStrokeInfo& origStroke,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000535 bool stencilOnly) {
kkinnunen18996512015-04-26 23:18:49 -0700536 SkTCopyOnFirstWrite<GrStrokeInfo> stroke(origStroke);
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000537
538 SkScalar hairlineCoverage;
joshualitt2e3b3e32014-12-09 13:31:14 -0800539 uint8_t newCoverage = 0xff;
joshualitt8059eb92014-12-29 15:10:07 -0800540 if (IsStrokeHairlineOrEquivalent(*stroke, viewMatrix, &hairlineCoverage)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800541 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000542
kkinnunend156d362015-05-18 22:23:54 -0700543 if (!stroke->isHairlineStyle()) {
544 stroke.writable()->setHairlineStyle();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000545 }
546 }
547
kkinnunend156d362015-05-18 22:23:54 -0700548 const bool isHairline = stroke->isHairlineStyle();
bsalomon@google.com30085192011-08-19 15:42:31 +0000549
egdaniel080e6732014-12-22 07:35:52 -0800550 // Save the current xp on the draw state so we can reset it if needed
egdaniel8dd688b2015-01-22 10:16:09 -0800551 SkAutoTUnref<const GrXPFactory> backupXPFactory(SkRef(pipelineBuilder->getXPFactory()));
bsalomon@google.com30085192011-08-19 15:42:31 +0000552 // face culling doesn't make sense here
egdaniel8dd688b2015-01-22 10:16:09 -0800553 SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace());
bsalomon@google.com30085192011-08-19 15:42:31 +0000554
555 int passCount = 0;
556 const GrStencilSettings* passes[3];
joshualitt332c7292015-02-23 08:44:31 -0800557 GrPipelineBuilder::DrawFace drawFace[3];
bsalomon@google.com30085192011-08-19 15:42:31 +0000558 bool reverse = false;
559 bool lastPassIsBounds;
560
joshualitt332c7292015-02-23 08:44:31 -0800561 if (isHairline) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000562 passCount = 1;
563 if (stencilOnly) {
564 passes[0] = &gDirectToStencil;
565 } else {
566 passes[0] = NULL;
567 }
568 lastPassIsBounds = false;
egdaniel8dd688b2015-01-22 10:16:09 -0800569 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000570 } else {
kkinnunend156d362015-05-18 22:23:54 -0700571 if (single_pass_path(path, *stroke)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000572 passCount = 1;
573 if (stencilOnly) {
574 passes[0] = &gDirectToStencil;
575 } else {
576 passes[0] = NULL;
577 }
egdaniel8dd688b2015-01-22 10:16:09 -0800578 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000579 lastPassIsBounds = false;
580 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000581 switch (path.getFillType()) {
sugoi@google.com12b4e272012-12-06 20:13:11 +0000582 case SkPath::kInverseEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000583 reverse = true;
584 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000585 case SkPath::kEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000586 passes[0] = &gEOStencilPass;
587 if (stencilOnly) {
588 passCount = 1;
589 lastPassIsBounds = false;
590 } else {
591 passCount = 2;
592 lastPassIsBounds = true;
593 if (reverse) {
594 passes[1] = &gInvEOColorPass;
595 } else {
596 passes[1] = &gEOColorPass;
597 }
598 }
egdaniel8dd688b2015-01-22 10:16:09 -0800599 drawFace[0] = drawFace[1] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000600 break;
601
sugoi@google.com12b4e272012-12-06 20:13:11 +0000602 case SkPath::kInverseWinding_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000603 reverse = true;
604 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000605 case SkPath::kWinding_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000606 if (fSeparateStencil) {
607 if (fStencilWrapOps) {
608 passes[0] = &gWindStencilSeparateWithWrap;
609 } else {
610 passes[0] = &gWindStencilSeparateNoWrap;
611 }
612 passCount = 2;
egdaniel8dd688b2015-01-22 10:16:09 -0800613 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000614 } else {
615 if (fStencilWrapOps) {
616 passes[0] = &gWindSingleStencilWithWrapInc;
617 passes[1] = &gWindSingleStencilWithWrapDec;
618 } else {
619 passes[0] = &gWindSingleStencilNoWrapInc;
620 passes[1] = &gWindSingleStencilNoWrapDec;
621 }
622 // which is cw and which is ccw is arbitrary.
egdaniel8dd688b2015-01-22 10:16:09 -0800623 drawFace[0] = GrPipelineBuilder::kCW_DrawFace;
624 drawFace[1] = GrPipelineBuilder::kCCW_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000625 passCount = 3;
626 }
627 if (stencilOnly) {
628 lastPassIsBounds = false;
629 --passCount;
630 } else {
631 lastPassIsBounds = true;
egdaniel8dd688b2015-01-22 10:16:09 -0800632 drawFace[passCount-1] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000633 if (reverse) {
634 passes[passCount-1] = &gInvWindColorPass;
635 } else {
636 passes[passCount-1] = &gWindColorPass;
637 }
638 }
639 break;
640 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000641 SkDEBUGFAIL("Unknown path fFill!");
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000642 return false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000643 }
644 }
645 }
646
senorblanco2b4bb072015-04-22 13:45:18 -0700647 SkScalar tol = GrPathUtils::kDefaultTolerance;
joshualitt332c7292015-02-23 08:44:31 -0800648 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
649
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000650 SkRect devBounds;
egdaniel8dd688b2015-01-22 10:16:09 -0800651 GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds);
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000652
bsalomon@google.com30085192011-08-19 15:42:31 +0000653 for (int p = 0; p < passCount; ++p) {
egdaniel8dd688b2015-01-22 10:16:09 -0800654 pipelineBuilder->setDrawFace(drawFace[p]);
bsalomon49f085d2014-09-05 13:34:00 -0700655 if (passes[p]) {
egdaniel8dd688b2015-01-22 10:16:09 -0800656 *pipelineBuilder->stencil() = *passes[p];
bsalomon@google.com30085192011-08-19 15:42:31 +0000657 }
658
659 if (lastPassIsBounds && (p == passCount-1)) {
egdaniel8dd688b2015-01-22 10:16:09 -0800660 // Reset the XP Factory on pipelineBuilder
661 pipelineBuilder->setXPFactory(backupXPFactory);
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000662 SkRect bounds;
joshualittd27f73e2014-12-29 07:43:36 -0800663 SkMatrix localMatrix = SkMatrix::I();
bsalomon@google.com30085192011-08-19 15:42:31 +0000664 if (reverse) {
egdaniel8dd688b2015-01-22 10:16:09 -0800665 SkASSERT(pipelineBuilder->getRenderTarget());
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000666 // draw over the dev bounds (which will be the whole dst surface for inv fill).
667 bounds = devBounds;
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000668 SkMatrix vmi;
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000669 // mapRect through persp matrix may not be correct
joshualitt8059eb92014-12-29 15:10:07 -0800670 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000671 vmi.mapRect(&bounds);
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000672 } else {
joshualittd27f73e2014-12-29 07:43:36 -0800673 if (!viewMatrix.invert(&localMatrix)) {
674 return false;
675 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000676 }
677 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000678 bounds = path.getBounds();
bsalomon@google.com30085192011-08-19 15:42:31 +0000679 }
joshualitt8059eb92014-12-29 15:10:07 -0800680 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
681 viewMatrix;
robertphillipsea461502015-05-26 11:38:03 -0700682 target->drawBWRect(pipelineBuilder, color, viewM, bounds, NULL, &localMatrix);
bsalomon@google.com30085192011-08-19 15:42:31 +0000683 } else {
684 if (passCount > 1) {
egdaniel8dd688b2015-01-22 10:16:09 -0800685 pipelineBuilder->setDisableColorXPFactory();
bsalomon@google.com30085192011-08-19 15:42:31 +0000686 }
joshualitt332c7292015-02-23 08:44:31 -0800687
688 DefaultPathBatch::Geometry geometry;
689 geometry.fColor = color;
690 geometry.fPath = path;
691 geometry.fTolerance = srcSpaceTol;
joshualitt332c7292015-02-23 08:44:31 -0800692
693 SkAutoTUnref<GrBatch> batch(DefaultPathBatch::Create(geometry, newCoverage, viewMatrix,
joshualitt99c7c072015-05-01 13:43:30 -0700694 isHairline, devBounds));
joshualitt332c7292015-02-23 08:44:31 -0800695
joshualitt99c7c072015-05-01 13:43:30 -0700696 target->drawBatch(pipelineBuilder, batch);
bsalomon@google.com30085192011-08-19 15:42:31 +0000697 }
698 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000699 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000700}
701
joshualitt9853cce2014-11-17 14:22:48 -0800702bool GrDefaultPathRenderer::canDrawPath(const GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800703 const GrPipelineBuilder* pipelineBuilder,
joshualitt8059eb92014-12-29 15:10:07 -0800704 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800705 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700706 const GrStrokeInfo& stroke,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000707 bool antiAlias) const {
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000708 // this class can draw any path with any fill but doesn't do any anti-aliasing.
egdanielaf18a092015-01-05 10:22:28 -0800709 return !antiAlias && (stroke.isFillStyle() || IsStrokeHairlineOrEquivalent(stroke,
710 viewMatrix,
711 NULL));
bsalomon@google.com30085192011-08-19 15:42:31 +0000712}
713
joshualitt9853cce2014-11-17 14:22:48 -0800714bool GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800715 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800716 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800717 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800718 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700719 const GrStrokeInfo& stroke,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000720 bool antiAlias) {
joshualitt9853cce2014-11-17 14:22:48 -0800721 return this->internalDrawPath(target,
egdaniel8dd688b2015-01-22 10:16:09 -0800722 pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800723 color,
joshualitt8059eb92014-12-29 15:10:07 -0800724 viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800725 path,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000726 stroke,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000727 false);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000728}
729
joshualitt9853cce2014-11-17 14:22:48 -0800730void GrDefaultPathRenderer::onStencilPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800731 GrPipelineBuilder* pipelineBuilder,
joshualitt8059eb92014-12-29 15:10:07 -0800732 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800733 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700734 const GrStrokeInfo& stroke) {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000735 SkASSERT(SkPath::kInverseEvenOdd_FillType != path.getFillType());
736 SkASSERT(SkPath::kInverseWinding_FillType != path.getFillType());
egdaniel8dd688b2015-01-22 10:16:09 -0800737 this->internalDrawPath(target, pipelineBuilder, GrColor_WHITE, viewMatrix, path, stroke, true);
bsalomon@google.com30085192011-08-19 15:42:31 +0000738}
joshualitt622d3ad2015-05-07 08:13:11 -0700739
740///////////////////////////////////////////////////////////////////////////////////////////////////
741
742#ifdef GR_TEST_UTILS
743
joshualitt6c891102015-05-13 08:51:49 -0700744BATCH_TEST_DEFINE(DefaultPathBatch) {
joshualitt622d3ad2015-05-07 08:13:11 -0700745 GrColor color = GrRandomColor(random);
746 SkMatrix viewMatrix = GrTest::TestMatrix(random);
747
748 // For now just hairlines because the other types of draws require two batches.
749 // TODO we should figure out a way to combine the stencil and cover steps into one batch
750 GrStrokeInfo stroke(SkStrokeRec::kHairline_InitStyle);
751 SkPath path = GrTest::TestPath(random);
752
753 // Compute srcSpaceTol
754 SkRect bounds = path.getBounds();
755 SkScalar tol = GrPathUtils::kDefaultTolerance;
756 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
757
758 DefaultPathBatch::Geometry geometry;
759 geometry.fColor = color;
760 geometry.fPath = path;
761 geometry.fTolerance = srcSpaceTol;
762
763 viewMatrix.mapRect(&bounds);
764 uint8_t coverage = GrRandomCoverage(random);
765 return DefaultPathBatch::Create(geometry, coverage, viewMatrix, true, bounds);
766}
767
768#endif