blob: 302aad77959732313360a26afffc6a61338af7ee [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"
12#include "GrBufferAllocPool.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"
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
bsalomon@google.com30085192011-08-19 15:42:31 +000023GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
24 bool stencilWrapOpsSupport)
25 : fSeparateStencil(separateStencilSupport)
bsalomon@google.comc2099d22012-03-02 21:26:50 +000026 , fStencilWrapOps(stencilWrapOpsSupport) {
bsalomon@google.com289533a2011-10-27 12:34:25 +000027}
28
29
bsalomon@google.com30085192011-08-19 15:42:31 +000030////////////////////////////////////////////////////////////////////////////////
31// Stencil rules for paths
32
33////// Even/Odd
34
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000035GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass,
36 kInvert_StencilOp,
37 kKeep_StencilOp,
38 kAlwaysIfInClip_StencilFunc,
39 0xffff,
40 0xffff,
41 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000042
43// ok not to check clip b/c stencil pass only wrote inside clip
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000044GR_STATIC_CONST_SAME_STENCIL(gEOColorPass,
45 kZero_StencilOp,
46 kZero_StencilOp,
47 kNotEqual_StencilFunc,
48 0xffff,
49 0x0000,
50 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000051
52// have to check clip b/c outside clip will always be zero.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000053GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass,
54 kZero_StencilOp,
55 kZero_StencilOp,
56 kEqualIfInClip_StencilFunc,
57 0xffff,
58 0x0000,
59 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000060
61////// Winding
62
63// when we have separate stencil we increment front faces / decrement back faces
64// when we don't have wrap incr and decr we use the stencil test to simulate
65// them.
66
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000067GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap,
bsalomon@google.com30085192011-08-19 15:42:31 +000068 kIncWrap_StencilOp, kDecWrap_StencilOp,
69 kKeep_StencilOp, kKeep_StencilOp,
70 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
tomhudson@google.com62b09682011-11-09 16:39:17 +000071 0xffff, 0xffff,
72 0xffff, 0xffff,
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000073 0xffff, 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000074
75// if inc'ing the max value, invert to make 0
76// if dec'ing zero invert to make all ones.
77// we can't avoid touching the stencil on both passing and
78// failing, so we can't resctrict ourselves to the clip.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000079GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap,
bsalomon@google.com30085192011-08-19 15:42:31 +000080 kInvert_StencilOp, kInvert_StencilOp,
81 kIncClamp_StencilOp, kDecClamp_StencilOp,
82 kEqual_StencilFunc, kEqual_StencilFunc,
tomhudson@google.com62b09682011-11-09 16:39:17 +000083 0xffff, 0xffff,
84 0xffff, 0x0000,
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000085 0xffff, 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000086
87// When there are no separate faces we do two passes to setup the winding rule
88// stencil. First we draw the front faces and inc, then we draw the back faces
89// and dec. These are same as the above two split into the incrementing and
90// decrementing passes.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000091GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
92 kIncWrap_StencilOp,
93 kKeep_StencilOp,
94 kAlwaysIfInClip_StencilFunc,
95 0xffff,
96 0xffff,
97 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +000098
bsalomon@google.com6b2445e2011-12-15 19:47:46 +000099GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
100 kDecWrap_StencilOp,
101 kKeep_StencilOp,
102 kAlwaysIfInClip_StencilFunc,
103 0xffff,
104 0xffff,
105 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000106
bsalomon@google.com6b2445e2011-12-15 19:47:46 +0000107GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc,
108 kInvert_StencilOp,
109 kIncClamp_StencilOp,
110 kEqual_StencilFunc,
111 0xffff,
112 0xffff,
113 0xffff);
114
115GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec,
116 kInvert_StencilOp,
117 kDecClamp_StencilOp,
118 kEqual_StencilFunc,
119 0xffff,
120 0x0000,
121 0xffff);
122
123// Color passes are the same whether we use the two-sided stencil or two passes
124
125GR_STATIC_CONST_SAME_STENCIL(gWindColorPass,
126 kZero_StencilOp,
127 kZero_StencilOp,
128 kNonZeroIfInClip_StencilFunc,
129 0xffff,
130 0x0000,
131 0xffff);
132
133GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass,
134 kZero_StencilOp,
135 kZero_StencilOp,
136 kEqualIfInClip_StencilFunc,
137 0xffff,
138 0x0000,
139 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000140
141////// Normal render to stencil
142
143// Sometimes the default path renderer can draw a path directly to the stencil
144// buffer without having to first resolve the interior / exterior.
bsalomon@google.com6b2445e2011-12-15 19:47:46 +0000145GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil,
146 kZero_StencilOp,
147 kIncClamp_StencilOp,
148 kAlwaysIfInClip_StencilFunc,
149 0xffff,
150 0x0000,
151 0xffff);
bsalomon@google.com30085192011-08-19 15:42:31 +0000152
153////////////////////////////////////////////////////////////////////////////////
154// Helpers for drawPath
155
bsalomon@google.com30085192011-08-19 15:42:31 +0000156#define STENCIL_OFF 0 // Always disable stencil (even when needed)
157
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000158static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000159#if STENCIL_OFF
160 return true;
161#else
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000162 if (!stroke.isHairlineStyle() && !path.isInverseFillType()) {
bsalomon@google.com7d72c452012-01-30 14:02:44 +0000163 return path.isConvex();
bsalomon@google.com30085192011-08-19 15:42:31 +0000164 }
165 return false;
166#endif
167}
168
joshualitt9853cce2014-11-17 14:22:48 -0800169GrPathRenderer::StencilSupport
170GrDefaultPathRenderer::onGetStencilSupport(const GrDrawTarget*,
egdaniel8dd688b2015-01-22 10:16:09 -0800171 const GrPipelineBuilder*,
joshualitt9853cce2014-11-17 14:22:48 -0800172 const SkPath& path,
173 const SkStrokeRec& stroke) const {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000174 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
joshualitt332c7292015-02-23 08:44:31 -0800213class DefaultPathBatch : public GrBatch {
214public:
215 struct Geometry {
216 GrColor fColor;
217 SkPath fPath;
218 SkScalar fTolerance;
219 SkDEBUGCODE(SkRect fDevBounds;)
220 };
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,
223 bool isHairline) {
224 return SkNEW_ARGS(DefaultPathBatch, (geometry, coverage, viewMatrix, isHairline));
bsalomon@google.com30085192011-08-19 15:42:31 +0000225 }
226
joshualitt332c7292015-02-23 08:44:31 -0800227 const char* name() const SK_OVERRIDE { return "DefaultPathBatch"; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000228
joshualitt332c7292015-02-23 08:44:31 -0800229 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
230 // When this is called on a batch, there is only one geometry bundle
231 out->setKnownFourComponents(fGeoData[0].fColor);
232 }
233 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt233c6ce2015-02-24 08:17:49 -0800234 out->setKnownSingleComponent(this->coverage());
joshualitt332c7292015-02-23 08:44:31 -0800235 }
sugoi@google.com12b4e272012-12-06 20:13:11 +0000236
joshualitt332c7292015-02-23 08:44:31 -0800237 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
238 // Handle any color overrides
239 if (init.fColorIgnored) {
240 fGeoData[0].fColor = GrColor_ILLEGAL;
241 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
242 fGeoData[0].fColor = init.fOverrideColor;
bsalomon@google.com30085192011-08-19 15:42:31 +0000243 }
joshualitt332c7292015-02-23 08:44:31 -0800244
245 // setup batch properties
246 fBatch.fColorIgnored = init.fColorIgnored;
247 fBatch.fColor = fGeoData[0].fColor;
248 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
249 fBatch.fCoverageIgnored = init.fCoverageIgnored;
250 }
251
252 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
253 SkAutoTUnref<const GrGeometryProcessor> gp(
254 GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
255 this->color(),
256 this->viewMatrix(),
257 SkMatrix::I(),
258 false,
259 this->coverage()));
260
261 size_t vertexStride = gp->getVertexStride();
262 SkASSERT(vertexStride == sizeof(SkPoint));
263
264 batchTarget->initDraw(gp, pipeline);
265
266 // TODO this is hacky, but the only way we have to initialize the GP is to use the
267 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
268 // everywhere we can remove this nastiness
269 GrPipelineInfo init;
270 init.fColorIgnored = fBatch.fColorIgnored;
271 init.fOverrideColor = GrColor_ILLEGAL;
272 init.fCoverageIgnored = fBatch.fCoverageIgnored;
273 init.fUsesLocalCoords = this->usesLocalCoords();
274 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
275
276 int instanceCount = fGeoData.count();
277
278 // compute number of vertices
279 int maxVertices = 0;
280
281 // We will use index buffers if we have multiple paths or one path with multiple contours
282 bool isIndexed = instanceCount > 1;
283 for (int i = 0; i < instanceCount; i++) {
284 Geometry& args = fGeoData[i];
285
286 int contourCount;
287 maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount,
288 args.fTolerance);
289
290 isIndexed = isIndexed || contourCount > 1;
bsalomon@google.com30085192011-08-19 15:42:31 +0000291 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000292
joshualitt5b27b142015-02-24 12:58:46 -0800293 if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) {
294 SkDebugf("Cannot render path (%d)\n", maxVertices);
295 return;
296 }
297
joshualitt332c7292015-02-23 08:44:31 -0800298 // determine primitiveType
299 int maxIndices = 0;
300 GrPrimitiveType primitiveType;
301 if (this->isHairline()) {
302 if (isIndexed) {
303 maxIndices = 2 * maxVertices;
304 primitiveType = kLines_GrPrimitiveType;
305 } else {
306 primitiveType = kLineStrip_GrPrimitiveType;
bsalomon@google.com30085192011-08-19 15:42:31 +0000307 }
joshualitt332c7292015-02-23 08:44:31 -0800308 } else {
309 if (isIndexed) {
310 maxIndices = 3 * maxVertices;
311 primitiveType = kTriangles_GrPrimitiveType;
312 } else {
313 primitiveType = kTriangleFan_GrPrimitiveType;
314 }
315 }
316
317 // allocate vertex / index buffers
318 const GrVertexBuffer* vertexBuffer;
319 int firstVertex;
320
321 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
322 maxVertices,
323 &vertexBuffer,
324 &firstVertex);
325
326 const GrIndexBuffer* indexBuffer;
327 int firstIndex;
328
329 void* indices = NULL;
330 if (isIndexed) {
331 indices = batchTarget->indexPool()->makeSpace(maxIndices,
332 &indexBuffer,
333 &firstIndex);
334 }
335
336 // fill buffers
337 int vertexOffset = 0;
338 int indexOffset = 0;
339 for (int i = 0; i < instanceCount; i++) {
340 Geometry& args = fGeoData[i];
341
342 int vertexCnt = 0;
343 int indexCnt = 0;
344 if (!this->createGeom(vertices,
345 vertexOffset,
346 indices,
347 indexOffset,
348 &vertexCnt,
349 &indexCnt,
350 args.fPath,
351 args.fTolerance,
352 isIndexed)) {
353 return;
354 }
355
356 vertexOffset += vertexCnt;
357 indexOffset += indexCnt;
358 SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
359 }
360
361 GrDrawTarget::DrawInfo drawInfo;
362 drawInfo.setPrimitiveType(primitiveType);
363 drawInfo.setVertexBuffer(vertexBuffer);
364 drawInfo.setStartVertex(firstVertex);
365 drawInfo.setVertexCount(vertexOffset);
366 if (isIndexed) {
367 drawInfo.setIndexBuffer(indexBuffer);
368 drawInfo.setStartIndex(firstIndex);
369 drawInfo.setIndexCount(indexOffset);
370 } else {
371 drawInfo.setStartIndex(0);
372 drawInfo.setIndexCount(0);
373 }
374 batchTarget->draw(drawInfo);
joshualitt6065b882015-02-24 13:20:59 -0800375
376 // put back reserves
377 batchTarget->putBackIndices((size_t)(maxIndices - indexOffset));
378 batchTarget->putBackVertices((size_t)(maxVertices - vertexOffset), (size_t)vertexStride);
joshualitt332c7292015-02-23 08:44:31 -0800379 }
380
381 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
382
383private:
384 DefaultPathBatch(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
385 bool isHairline) {
386 this->initClassID<DefaultPathBatch>();
387 fBatch.fCoverage = coverage;
388 fBatch.fIsHairline = isHairline;
389 fBatch.fViewMatrix = viewMatrix;
390 fGeoData.push_back(geometry);
391 }
392
393 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
394 DefaultPathBatch* that = t->cast<DefaultPathBatch>();
395
396 if (this->color() != that->color()) {
397 return false;
398 }
399
400 if (this->coverage() != that->coverage()) {
401 return false;
402 }
403
404 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
405 return false;
406 }
407
408 if (this->isHairline() != that->isHairline()) {
409 return false;
410 }
411
412 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
413 return true;
414 }
415
416 bool createGeom(void* vertices,
417 size_t vertexOffset,
418 void* indices,
419 size_t indexOffset,
420 int* vertexCnt,
421 int* indexCnt,
422 const SkPath& path,
423 SkScalar srcSpaceTol,
424 bool isIndexed) {
425 {
426 SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol);
427
428 uint16_t indexOffsetU16 = (uint16_t)indexOffset;
429 uint16_t vertexOffsetU16 = (uint16_t)vertexOffset;
430
431 uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16;
432 uint16_t* idx = idxBase;
433 uint16_t subpathIdxStart = vertexOffsetU16;
434
435 SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset;
436 SkPoint* vert = base;
437
438 SkPoint pts[4];
439
440 bool first = true;
441 int subpath = 0;
442
443 SkPath::Iter iter(path, false);
444
445 bool done = false;
446 while (!done) {
447 SkPath::Verb verb = iter.next(pts);
448 switch (verb) {
449 case SkPath::kMove_Verb:
450 if (!first) {
451 uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16;
452 subpathIdxStart = currIdx;
453 ++subpath;
454 }
455 *vert = pts[0];
456 vert++;
457 break;
458 case SkPath::kLine_Verb:
459 if (isIndexed) {
460 uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
461 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
462 prevIdx, &idx);
463 }
464 *(vert++) = pts[1];
465 break;
466 case SkPath::kConic_Verb: {
467 SkScalar weight = iter.conicWeight();
468 SkAutoConicToQuads converter;
469 // Converting in src-space, hance the finer tolerance (0.25)
470 // TODO: find a way to do this in dev-space so the tolerance means something
471 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
472 for (int i = 0; i < converter.countQuads(); ++i) {
473 add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol,
474 isIndexed, this->isHairline(), subpathIdxStart,
475 (int)vertexOffset, &idx);
476 }
477 break;
bsalomon@google.com30085192011-08-19 15:42:31 +0000478 }
joshualitt332c7292015-02-23 08:44:31 -0800479 case SkPath::kQuad_Verb:
480 add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed,
481 this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx);
482 break;
483 case SkPath::kCubic_Verb: {
484 // first pt of cubic is the pt we ended on in previous step
485 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
486 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
487 pts[0], pts[1], pts[2], pts[3],
488 srcSpaceTolSqd, &vert,
489 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
490 if (isIndexed) {
491 for (uint16_t i = 0; i < numPts; ++i) {
492 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
493 firstCPtIdx + i, &idx);
494 }
495 }
496 break;
497 }
498 case SkPath::kClose_Verb:
499 break;
500 case SkPath::kDone_Verb:
501 done = true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000502 }
joshualitt332c7292015-02-23 08:44:31 -0800503 first = false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000504 }
joshualitt332c7292015-02-23 08:44:31 -0800505
506 *vertexCnt = static_cast<int>(vert - base);
507 *indexCnt = static_cast<int>(idx - idxBase);
508
bsalomon@google.com30085192011-08-19 15:42:31 +0000509 }
joshualitt332c7292015-02-23 08:44:31 -0800510 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000511 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000512
joshualitt332c7292015-02-23 08:44:31 -0800513 GrColor color() const { return fBatch.fColor; }
514 uint8_t coverage() const { return fBatch.fCoverage; }
515 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
516 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
517 bool isHairline() const { return fBatch.fIsHairline; }
bsalomon@google.com30085192011-08-19 15:42:31 +0000518
joshualitt332c7292015-02-23 08:44:31 -0800519 struct BatchTracker {
520 GrColor fColor;
521 uint8_t fCoverage;
522 SkMatrix fViewMatrix;
523 bool fUsesLocalCoords;
524 bool fColorIgnored;
525 bool fCoverageIgnored;
526 bool fIsHairline;
527 };
528
529 BatchTracker fBatch;
530 SkSTArray<1, Geometry, true> fGeoData;
531};
bsalomon@google.com30085192011-08-19 15:42:31 +0000532
joshualitt9853cce2014-11-17 14:22:48 -0800533bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800534 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800535 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800536 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800537 const SkPath& path,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000538 const SkStrokeRec& origStroke,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000539 bool stencilOnly) {
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000540 SkTCopyOnFirstWrite<SkStrokeRec> stroke(origStroke);
541
542 SkScalar hairlineCoverage;
joshualitt2e3b3e32014-12-09 13:31:14 -0800543 uint8_t newCoverage = 0xff;
joshualitt8059eb92014-12-29 15:10:07 -0800544 if (IsStrokeHairlineOrEquivalent(*stroke, viewMatrix, &hairlineCoverage)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800545 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000546
547 if (!stroke->isHairlineStyle()) {
548 stroke.writable()->setHairlineStyle();
549 }
550 }
551
joshualitt332c7292015-02-23 08:44:31 -0800552 const bool isHairline = stroke->isHairlineStyle();
bsalomon@google.com30085192011-08-19 15:42:31 +0000553
egdaniel080e6732014-12-22 07:35:52 -0800554 // Save the current xp on the draw state so we can reset it if needed
egdaniel8dd688b2015-01-22 10:16:09 -0800555 SkAutoTUnref<const GrXPFactory> backupXPFactory(SkRef(pipelineBuilder->getXPFactory()));
bsalomon@google.com30085192011-08-19 15:42:31 +0000556 // face culling doesn't make sense here
egdaniel8dd688b2015-01-22 10:16:09 -0800557 SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace());
bsalomon@google.com30085192011-08-19 15:42:31 +0000558
559 int passCount = 0;
560 const GrStencilSettings* passes[3];
joshualitt332c7292015-02-23 08:44:31 -0800561 GrPipelineBuilder::DrawFace drawFace[3];
bsalomon@google.com30085192011-08-19 15:42:31 +0000562 bool reverse = false;
563 bool lastPassIsBounds;
564
joshualitt332c7292015-02-23 08:44:31 -0800565 if (isHairline) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000566 passCount = 1;
567 if (stencilOnly) {
568 passes[0] = &gDirectToStencil;
569 } else {
570 passes[0] = NULL;
571 }
572 lastPassIsBounds = false;
egdaniel8dd688b2015-01-22 10:16:09 -0800573 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000574 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000575 if (single_pass_path(path, *stroke)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000576 passCount = 1;
577 if (stencilOnly) {
578 passes[0] = &gDirectToStencil;
579 } else {
580 passes[0] = NULL;
581 }
egdaniel8dd688b2015-01-22 10:16:09 -0800582 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000583 lastPassIsBounds = false;
584 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000585 switch (path.getFillType()) {
sugoi@google.com12b4e272012-12-06 20:13:11 +0000586 case SkPath::kInverseEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000587 reverse = true;
588 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000589 case SkPath::kEvenOdd_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000590 passes[0] = &gEOStencilPass;
591 if (stencilOnly) {
592 passCount = 1;
593 lastPassIsBounds = false;
594 } else {
595 passCount = 2;
596 lastPassIsBounds = true;
597 if (reverse) {
598 passes[1] = &gInvEOColorPass;
599 } else {
600 passes[1] = &gEOColorPass;
601 }
602 }
egdaniel8dd688b2015-01-22 10:16:09 -0800603 drawFace[0] = drawFace[1] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000604 break;
605
sugoi@google.com12b4e272012-12-06 20:13:11 +0000606 case SkPath::kInverseWinding_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000607 reverse = true;
608 // fallthrough
sugoi@google.com12b4e272012-12-06 20:13:11 +0000609 case SkPath::kWinding_FillType:
bsalomon@google.com30085192011-08-19 15:42:31 +0000610 if (fSeparateStencil) {
611 if (fStencilWrapOps) {
612 passes[0] = &gWindStencilSeparateWithWrap;
613 } else {
614 passes[0] = &gWindStencilSeparateNoWrap;
615 }
616 passCount = 2;
egdaniel8dd688b2015-01-22 10:16:09 -0800617 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000618 } else {
619 if (fStencilWrapOps) {
620 passes[0] = &gWindSingleStencilWithWrapInc;
621 passes[1] = &gWindSingleStencilWithWrapDec;
622 } else {
623 passes[0] = &gWindSingleStencilNoWrapInc;
624 passes[1] = &gWindSingleStencilNoWrapDec;
625 }
626 // which is cw and which is ccw is arbitrary.
egdaniel8dd688b2015-01-22 10:16:09 -0800627 drawFace[0] = GrPipelineBuilder::kCW_DrawFace;
628 drawFace[1] = GrPipelineBuilder::kCCW_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000629 passCount = 3;
630 }
631 if (stencilOnly) {
632 lastPassIsBounds = false;
633 --passCount;
634 } else {
635 lastPassIsBounds = true;
egdaniel8dd688b2015-01-22 10:16:09 -0800636 drawFace[passCount-1] = GrPipelineBuilder::kBoth_DrawFace;
bsalomon@google.com30085192011-08-19 15:42:31 +0000637 if (reverse) {
638 passes[passCount-1] = &gInvWindColorPass;
639 } else {
640 passes[passCount-1] = &gWindColorPass;
641 }
642 }
643 break;
644 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000645 SkDEBUGFAIL("Unknown path fFill!");
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000646 return false;
bsalomon@google.com30085192011-08-19 15:42:31 +0000647 }
648 }
649 }
650
joshualitt332c7292015-02-23 08:44:31 -0800651 SkScalar tol = SK_Scalar1;
652 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
653
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000654 SkRect devBounds;
egdaniel8dd688b2015-01-22 10:16:09 -0800655 GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds);
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000656
bsalomon@google.com30085192011-08-19 15:42:31 +0000657 for (int p = 0; p < passCount; ++p) {
egdaniel8dd688b2015-01-22 10:16:09 -0800658 pipelineBuilder->setDrawFace(drawFace[p]);
bsalomon49f085d2014-09-05 13:34:00 -0700659 if (passes[p]) {
egdaniel8dd688b2015-01-22 10:16:09 -0800660 *pipelineBuilder->stencil() = *passes[p];
bsalomon@google.com30085192011-08-19 15:42:31 +0000661 }
662
663 if (lastPassIsBounds && (p == passCount-1)) {
egdaniel8dd688b2015-01-22 10:16:09 -0800664 // Reset the XP Factory on pipelineBuilder
665 pipelineBuilder->setXPFactory(backupXPFactory);
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000666 SkRect bounds;
joshualittd27f73e2014-12-29 07:43:36 -0800667 SkMatrix localMatrix = SkMatrix::I();
bsalomon@google.com30085192011-08-19 15:42:31 +0000668 if (reverse) {
egdaniel8dd688b2015-01-22 10:16:09 -0800669 SkASSERT(pipelineBuilder->getRenderTarget());
bsalomon@google.com1dd9baa2013-05-20 16:49:06 +0000670 // draw over the dev bounds (which will be the whole dst surface for inv fill).
671 bounds = devBounds;
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000672 SkMatrix vmi;
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000673 // mapRect through persp matrix may not be correct
joshualitt8059eb92014-12-29 15:10:07 -0800674 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
bsalomon@google.com30085192011-08-19 15:42:31 +0000675 vmi.mapRect(&bounds);
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000676 } else {
joshualittd27f73e2014-12-29 07:43:36 -0800677 if (!viewMatrix.invert(&localMatrix)) {
678 return false;
679 }
bsalomon@google.com30085192011-08-19 15:42:31 +0000680 }
681 } else {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000682 bounds = path.getBounds();
bsalomon@google.com30085192011-08-19 15:42:31 +0000683 }
joshualitt9853cce2014-11-17 14:22:48 -0800684 GrDrawTarget::AutoGeometryPush agp(target);
joshualitt8059eb92014-12-29 15:10:07 -0800685 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
686 viewMatrix;
egdaniel8dd688b2015-01-22 10:16:09 -0800687 target->drawRect(pipelineBuilder, color, viewM, bounds, NULL, &localMatrix);
bsalomon@google.com30085192011-08-19 15:42:31 +0000688 } else {
689 if (passCount > 1) {
egdaniel8dd688b2015-01-22 10:16:09 -0800690 pipelineBuilder->setDisableColorXPFactory();
bsalomon@google.com30085192011-08-19 15:42:31 +0000691 }
joshualitt332c7292015-02-23 08:44:31 -0800692
693 DefaultPathBatch::Geometry geometry;
694 geometry.fColor = color;
695 geometry.fPath = path;
696 geometry.fTolerance = srcSpaceTol;
697 SkDEBUGCODE(geometry.fDevBounds = devBounds;)
698
699 SkAutoTUnref<GrBatch> batch(DefaultPathBatch::Create(geometry, newCoverage, viewMatrix,
700 isHairline));
701
702 target->drawBatch(pipelineBuilder, batch, &devBounds);
bsalomon@google.com30085192011-08-19 15:42:31 +0000703 }
704 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000705 return true;
bsalomon@google.com30085192011-08-19 15:42:31 +0000706}
707
joshualitt9853cce2014-11-17 14:22:48 -0800708bool GrDefaultPathRenderer::canDrawPath(const GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800709 const GrPipelineBuilder* pipelineBuilder,
joshualitt8059eb92014-12-29 15:10:07 -0800710 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800711 const SkPath& path,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000712 const SkStrokeRec& stroke,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000713 bool antiAlias) const {
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000714 // this class can draw any path with any fill but doesn't do any anti-aliasing.
egdanielaf18a092015-01-05 10:22:28 -0800715 return !antiAlias && (stroke.isFillStyle() || IsStrokeHairlineOrEquivalent(stroke,
716 viewMatrix,
717 NULL));
bsalomon@google.com30085192011-08-19 15:42:31 +0000718}
719
joshualitt9853cce2014-11-17 14:22:48 -0800720bool GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800721 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800722 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800723 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800724 const SkPath& path,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000725 const SkStrokeRec& stroke,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000726 bool antiAlias) {
joshualitt9853cce2014-11-17 14:22:48 -0800727 return this->internalDrawPath(target,
egdaniel8dd688b2015-01-22 10:16:09 -0800728 pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800729 color,
joshualitt8059eb92014-12-29 15:10:07 -0800730 viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800731 path,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000732 stroke,
robertphillips@google.come79f3202014-02-11 16:30:21 +0000733 false);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000734}
735
joshualitt9853cce2014-11-17 14:22:48 -0800736void GrDefaultPathRenderer::onStencilPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800737 GrPipelineBuilder* pipelineBuilder,
joshualitt8059eb92014-12-29 15:10:07 -0800738 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800739 const SkPath& path,
740 const SkStrokeRec& stroke) {
robertphillips@google.come79f3202014-02-11 16:30:21 +0000741 SkASSERT(SkPath::kInverseEvenOdd_FillType != path.getFillType());
742 SkASSERT(SkPath::kInverseWinding_FillType != path.getFillType());
egdaniel8dd688b2015-01-22 10:16:09 -0800743 this->internalDrawPath(target, pipelineBuilder, GrColor_WHITE, viewMatrix, path, stroke, true);
bsalomon@google.com30085192011-08-19 15:42:31 +0000744}