1 /*
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
10 #include "GrBatchFlushState.h"
11 #include "GrBatchTest.h"
12 #include "GrContext.h"
13 #include "GrDefaultGeoProcFactory.h"
14 #include "GrPathUtils.h"
15 #include "GrPipelineBuilder.h"
16 #include "GrVertices.h"
17 #include "SkGeometry.h"
18 #include "SkString.h"
19 #include "SkStrokeRec.h"
20 #include "SkTLazy.h"
21 #include "SkTraceEvent.h"
22
23 #include "batches/GrRectBatchFactory.h"
24 #include "batches/GrVertexBatch.h"
25
GrDefaultPathRenderer(bool separateStencilSupport,bool stencilWrapOpsSupport)26 GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
27 bool stencilWrapOpsSupport)
28 : fSeparateStencil(separateStencilSupport)
29 , fStencilWrapOps(stencilWrapOpsSupport) {
30 }
31
32
33 ////////////////////////////////////////////////////////////////////////////////
34 // Stencil rules for paths
35
36 ////// Even/Odd
37
38 GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass,
39 kInvert_StencilOp,
40 kKeep_StencilOp,
41 kAlwaysIfInClip_StencilFunc,
42 0xffff,
43 0xffff,
44 0xffff);
45
46 // ok not to check clip b/c stencil pass only wrote inside clip
47 GR_STATIC_CONST_SAME_STENCIL(gEOColorPass,
48 kZero_StencilOp,
49 kZero_StencilOp,
50 kNotEqual_StencilFunc,
51 0xffff,
52 0x0000,
53 0xffff);
54
55 // have to check clip b/c outside clip will always be zero.
56 GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass,
57 kZero_StencilOp,
58 kZero_StencilOp,
59 kEqualIfInClip_StencilFunc,
60 0xffff,
61 0x0000,
62 0xffff);
63
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
70 GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap,
71 kIncWrap_StencilOp, kDecWrap_StencilOp,
72 kKeep_StencilOp, kKeep_StencilOp,
73 kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
74 0xffff, 0xffff,
75 0xffff, 0xffff,
76 0xffff, 0xffff);
77
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.
82 GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap,
83 kInvert_StencilOp, kInvert_StencilOp,
84 kIncClamp_StencilOp, kDecClamp_StencilOp,
85 kEqual_StencilFunc, kEqual_StencilFunc,
86 0xffff, 0xffff,
87 0xffff, 0x0000,
88 0xffff, 0xffff);
89
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.
94 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
95 kIncWrap_StencilOp,
96 kKeep_StencilOp,
97 kAlwaysIfInClip_StencilFunc,
98 0xffff,
99 0xffff,
100 0xffff);
101
102 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
103 kDecWrap_StencilOp,
104 kKeep_StencilOp,
105 kAlwaysIfInClip_StencilFunc,
106 0xffff,
107 0xffff,
108 0xffff);
109
110 GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc,
111 kInvert_StencilOp,
112 kIncClamp_StencilOp,
113 kEqual_StencilFunc,
114 0xffff,
115 0xffff,
116 0xffff);
117
118 GR_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
128 GR_STATIC_CONST_SAME_STENCIL(gWindColorPass,
129 kZero_StencilOp,
130 kZero_StencilOp,
131 kNonZeroIfInClip_StencilFunc,
132 0xffff,
133 0x0000,
134 0xffff);
135
136 GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass,
137 kZero_StencilOp,
138 kZero_StencilOp,
139 kEqualIfInClip_StencilFunc,
140 0xffff,
141 0x0000,
142 0xffff);
143
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.
148 GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil,
149 kZero_StencilOp,
150 kIncClamp_StencilOp,
151 kAlwaysIfInClip_StencilFunc,
152 0xffff,
153 0x0000,
154 0xffff);
155
156 ////////////////////////////////////////////////////////////////////////////////
157 // Helpers for drawPath
158
159 #define STENCIL_OFF 0 // Always disable stencil (even when needed)
160
single_pass_path(const SkPath & path,const SkStrokeRec & stroke)161 static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) {
162 #if STENCIL_OFF
163 return true;
164 #else
165 if (!stroke.isHairlineStyle() && !path.isInverseFillType()) {
166 return path.isConvex();
167 }
168 return false;
169 #endif
170 }
171
172 GrPathRenderer::StencilSupport
onGetStencilSupport(const SkPath & path,const GrStrokeInfo & stroke) const173 GrDefaultPathRenderer::onGetStencilSupport(const SkPath& path, const GrStrokeInfo& stroke) const {
174 if (single_pass_path(path, stroke)) {
175 return GrPathRenderer::kNoRestriction_StencilSupport;
176 } else {
177 return GrPathRenderer::kStencilOnly_StencilSupport;
178 }
179 }
180
append_countour_edge_indices(bool hairLine,uint16_t fanCenterIdx,uint16_t edgeV0Idx,uint16_t ** indices)181 static inline void append_countour_edge_indices(bool hairLine,
182 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.
188 if (!hairLine) {
189 *((*indices)++) = fanCenterIdx;
190 }
191 *((*indices)++) = edgeV0Idx;
192 *((*indices)++) = edgeV0Idx + 1;
193 }
194
add_quad(SkPoint ** vert,const SkPoint * base,const SkPoint pts[],SkScalar srcSpaceTolSqd,SkScalar srcSpaceTol,bool indexed,bool isHairline,uint16_t subpathIdxStart,int offset,uint16_t ** idx)195 static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[],
196 SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed,
197 bool isHairline, uint16_t subpathIdxStart, int offset, uint16_t** idx) {
198 // first pt of quad is the pt we ended on in previous step
199 uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1 + offset;
200 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
213 class DefaultPathBatch : public GrVertexBatch {
214 public:
215 DEFINE_BATCH_CLASS_ID
216
217 struct Geometry {
218 GrColor fColor;
219 SkPath fPath;
220 SkScalar fTolerance;
221 };
222
Create(const Geometry & geometry,uint8_t coverage,const SkMatrix & viewMatrix,bool isHairline,const SkRect & devBounds)223 static GrDrawBatch* Create(const Geometry& geometry, uint8_t coverage,
224 const SkMatrix& viewMatrix, bool isHairline,
225 const SkRect& devBounds) {
226 return new DefaultPathBatch(geometry, coverage, viewMatrix, isHairline, devBounds);
227 }
228
name() const229 const char* name() const override { return "DefaultPathBatch"; }
230
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const231 void computePipelineOptimizations(GrInitInvariantOutput* color,
232 GrInitInvariantOutput* coverage,
233 GrBatchToXPOverrides* overrides) const override {
234 // When this is called on a batch, there is only one geometry bundle
235 color->setKnownFourComponents(fGeoData[0].fColor);
236 coverage->setKnownSingleComponent(this->coverage());
237 }
238
239 private:
initBatchTracker(const GrXPOverridesForBatch & overrides)240 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
241 // Handle any color overrides
242 if (!overrides.readsColor()) {
243 fGeoData[0].fColor = GrColor_ILLEGAL;
244 }
245 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
246
247 // setup batch properties
248 fBatch.fColorIgnored = !overrides.readsColor();
249 fBatch.fColor = fGeoData[0].fColor;
250 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
251 fBatch.fCoverageIgnored = !overrides.readsCoverage();
252 }
253
onPrepareDraws(Target * target) const254 void onPrepareDraws(Target* target) const override {
255 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 }
268
269 size_t vertexStride = gp->getVertexStride();
270 SkASSERT(vertexStride == sizeof(SkPoint));
271
272 target->initDraw(gp, this->pipeline());
273
274 int instanceCount = fGeoData.count();
275
276 // compute number of vertices
277 int maxVertices = 0;
278
279 // We will use index buffers if we have multiple paths or one path with multiple contours
280 bool isIndexed = instanceCount > 1;
281 for (int i = 0; i < instanceCount; i++) {
282 const Geometry& args = fGeoData[i];
283
284 int contourCount;
285 maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount,
286 args.fTolerance);
287
288 isIndexed = isIndexed || contourCount > 1;
289 }
290
291 if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) {
292 //SkDebugf("Cannot render path (%d)\n", maxVertices);
293 return;
294 }
295
296 // determine primitiveType
297 int maxIndices = 0;
298 GrPrimitiveType primitiveType;
299 if (this->isHairline()) {
300 if (isIndexed) {
301 maxIndices = 2 * maxVertices;
302 primitiveType = kLines_GrPrimitiveType;
303 } else {
304 primitiveType = kLineStrip_GrPrimitiveType;
305 }
306 } else {
307 if (isIndexed) {
308 maxIndices = 3 * maxVertices;
309 primitiveType = kTriangles_GrPrimitiveType;
310 } else {
311 primitiveType = kTriangleFan_GrPrimitiveType;
312 }
313 }
314
315 // allocate vertex / index buffers
316 const GrVertexBuffer* vertexBuffer;
317 int firstVertex;
318
319 void* verts = target->makeVertexSpace(vertexStride, maxVertices,
320 &vertexBuffer, &firstVertex);
321
322 if (!verts) {
323 SkDebugf("Could not allocate vertices\n");
324 return;
325 }
326
327 const GrIndexBuffer* indexBuffer = nullptr;
328 int firstIndex = 0;
329
330 void* indices = nullptr;
331 if (isIndexed) {
332 indices = target->makeIndexSpace(maxIndices, &indexBuffer, &firstIndex);
333
334 if (!indices) {
335 SkDebugf("Could not allocate indices\n");
336 return;
337 }
338 }
339
340 // fill buffers
341 int vertexOffset = 0;
342 int indexOffset = 0;
343 for (int i = 0; i < instanceCount; i++) {
344 const Geometry& args = fGeoData[i];
345
346 int vertexCnt = 0;
347 int indexCnt = 0;
348 if (!this->createGeom(verts,
349 vertexOffset,
350 indices,
351 indexOffset,
352 &vertexCnt,
353 &indexCnt,
354 args.fPath,
355 args.fTolerance,
356 isIndexed)) {
357 return;
358 }
359
360 vertexOffset += vertexCnt;
361 indexOffset += indexCnt;
362 SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
363 }
364
365 GrVertices vertices;
366 if (isIndexed) {
367 vertices.initIndexed(primitiveType, vertexBuffer, indexBuffer, firstVertex, firstIndex,
368 vertexOffset, indexOffset);
369 } else {
370 vertices.init(primitiveType, vertexBuffer, firstVertex, vertexOffset);
371 }
372 target->draw(vertices);
373
374 // put back reserves
375 target->putBackIndices((size_t)(maxIndices - indexOffset));
376 target->putBackVertices((size_t)(maxVertices - vertexOffset), (size_t)vertexStride);
377 }
378
geoData()379 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
380
DefaultPathBatch(const Geometry & geometry,uint8_t coverage,const SkMatrix & viewMatrix,bool isHairline,const SkRect & devBounds)381 DefaultPathBatch(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
382 bool isHairline, const SkRect& devBounds)
383 : INHERITED(ClassID()) {
384 fBatch.fCoverage = coverage;
385 fBatch.fIsHairline = isHairline;
386 fBatch.fViewMatrix = viewMatrix;
387 fGeoData.push_back(geometry);
388
389 this->setBounds(devBounds);
390
391 // This is b.c. hairlines are notionally infinitely thin so without expansion
392 // two overlapping lines could be reordered even though they hit the same pixels.
393 if (isHairline) {
394 fBounds.outset(0.5f, 0.5f);
395 }
396 }
397
onCombineIfPossible(GrBatch * t,const GrCaps & caps)398 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
399 DefaultPathBatch* that = t->cast<DefaultPathBatch>();
400 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
401 that->bounds(), caps)) {
402 return false;
403 }
404
405 if (this->color() != that->color()) {
406 return false;
407 }
408
409 if (this->coverage() != that->coverage()) {
410 return false;
411 }
412
413 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
414 return false;
415 }
416
417 if (this->isHairline() != that->isHairline()) {
418 return false;
419 }
420
421 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
422 this->joinBounds(that->bounds());
423 return true;
424 }
425
createGeom(void * vertices,size_t vertexOffset,void * indices,size_t indexOffset,int * vertexCnt,int * indexCnt,const SkPath & path,SkScalar srcSpaceTol,bool isIndexed) const426 bool createGeom(void* vertices,
427 size_t vertexOffset,
428 void* indices,
429 size_t indexOffset,
430 int* vertexCnt,
431 int* indexCnt,
432 const SkPath& path,
433 SkScalar srcSpaceTol,
434 bool isIndexed) const {
435 {
436 SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol);
437
438 uint16_t indexOffsetU16 = (uint16_t)indexOffset;
439 uint16_t vertexOffsetU16 = (uint16_t)vertexOffset;
440
441 uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16;
442 uint16_t* idx = idxBase;
443 uint16_t subpathIdxStart = vertexOffsetU16;
444
445 SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset;
446 SkPoint* vert = base;
447
448 SkPoint pts[4];
449
450 bool first = true;
451 int subpath = 0;
452
453 SkPath::Iter iter(path, false);
454
455 bool done = false;
456 while (!done) {
457 SkPath::Verb verb = iter.next(pts);
458 switch (verb) {
459 case SkPath::kMove_Verb:
460 if (!first) {
461 uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16;
462 subpathIdxStart = currIdx;
463 ++subpath;
464 }
465 *vert = pts[0];
466 vert++;
467 break;
468 case SkPath::kLine_Verb:
469 if (isIndexed) {
470 uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
471 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
472 prevIdx, &idx);
473 }
474 *(vert++) = pts[1];
475 break;
476 case SkPath::kConic_Verb: {
477 SkScalar weight = iter.conicWeight();
478 SkAutoConicToQuads converter;
479 // Converting in src-space, hance the finer tolerance (0.25)
480 // TODO: find a way to do this in dev-space so the tolerance means something
481 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
482 for (int i = 0; i < converter.countQuads(); ++i) {
483 add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol,
484 isIndexed, this->isHairline(), subpathIdxStart,
485 (int)vertexOffset, &idx);
486 }
487 break;
488 }
489 case SkPath::kQuad_Verb:
490 add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed,
491 this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx);
492 break;
493 case SkPath::kCubic_Verb: {
494 // first pt of cubic is the pt we ended on in previous step
495 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
496 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
497 pts[0], pts[1], pts[2], pts[3],
498 srcSpaceTolSqd, &vert,
499 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
500 if (isIndexed) {
501 for (uint16_t i = 0; i < numPts; ++i) {
502 append_countour_edge_indices(this->isHairline(), subpathIdxStart,
503 firstCPtIdx + i, &idx);
504 }
505 }
506 break;
507 }
508 case SkPath::kClose_Verb:
509 break;
510 case SkPath::kDone_Verb:
511 done = true;
512 }
513 first = false;
514 }
515
516 *vertexCnt = static_cast<int>(vert - base);
517 *indexCnt = static_cast<int>(idx - idxBase);
518
519 }
520 return true;
521 }
522
color() const523 GrColor color() const { return fBatch.fColor; }
coverage() const524 uint8_t coverage() const { return fBatch.fCoverage; }
usesLocalCoords() const525 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const526 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
isHairline() const527 bool isHairline() const { return fBatch.fIsHairline; }
coverageIgnored() const528 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
529
530 struct BatchTracker {
531 GrColor fColor;
532 uint8_t fCoverage;
533 SkMatrix fViewMatrix;
534 bool fUsesLocalCoords;
535 bool fColorIgnored;
536 bool fCoverageIgnored;
537 bool fIsHairline;
538 };
539
540 BatchTracker fBatch;
541 SkSTArray<1, Geometry, true> fGeoData;
542
543 typedef GrVertexBatch INHERITED;
544 };
545
internalDrawPath(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & origStroke,bool stencilOnly)546 bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
547 GrPipelineBuilder* pipelineBuilder,
548 GrColor color,
549 const SkMatrix& viewMatrix,
550 const SkPath& path,
551 const GrStrokeInfo& origStroke,
552 bool stencilOnly) {
553 SkTCopyOnFirstWrite<GrStrokeInfo> stroke(origStroke);
554
555 SkScalar hairlineCoverage;
556 uint8_t newCoverage = 0xff;
557 if (IsStrokeHairlineOrEquivalent(*stroke, viewMatrix, &hairlineCoverage)) {
558 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
559
560 if (!stroke->isHairlineStyle()) {
561 stroke.writable()->setHairlineStyle();
562 }
563 }
564
565 const bool isHairline = stroke->isHairlineStyle();
566
567 // Save the current xp on the draw state so we can reset it if needed
568 const GrXPFactory* xpFactory = pipelineBuilder->getXPFactory();
569 SkAutoTUnref<const GrXPFactory> backupXPFactory(SkSafeRef(xpFactory));
570 // face culling doesn't make sense here
571 SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace());
572
573 int passCount = 0;
574 const GrStencilSettings* passes[3];
575 GrPipelineBuilder::DrawFace drawFace[3];
576 bool reverse = false;
577 bool lastPassIsBounds;
578
579 if (isHairline) {
580 passCount = 1;
581 if (stencilOnly) {
582 passes[0] = &gDirectToStencil;
583 } else {
584 passes[0] = nullptr;
585 }
586 lastPassIsBounds = false;
587 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
588 } else {
589 if (single_pass_path(path, *stroke)) {
590 passCount = 1;
591 if (stencilOnly) {
592 passes[0] = &gDirectToStencil;
593 } else {
594 passes[0] = nullptr;
595 }
596 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
597 lastPassIsBounds = false;
598 } else {
599 switch (path.getFillType()) {
600 case SkPath::kInverseEvenOdd_FillType:
601 reverse = true;
602 // fallthrough
603 case SkPath::kEvenOdd_FillType:
604 passes[0] = &gEOStencilPass;
605 if (stencilOnly) {
606 passCount = 1;
607 lastPassIsBounds = false;
608 } else {
609 passCount = 2;
610 lastPassIsBounds = true;
611 if (reverse) {
612 passes[1] = &gInvEOColorPass;
613 } else {
614 passes[1] = &gEOColorPass;
615 }
616 }
617 drawFace[0] = drawFace[1] = GrPipelineBuilder::kBoth_DrawFace;
618 break;
619
620 case SkPath::kInverseWinding_FillType:
621 reverse = true;
622 // fallthrough
623 case SkPath::kWinding_FillType:
624 if (fSeparateStencil) {
625 if (fStencilWrapOps) {
626 passes[0] = &gWindStencilSeparateWithWrap;
627 } else {
628 passes[0] = &gWindStencilSeparateNoWrap;
629 }
630 passCount = 2;
631 drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
632 } else {
633 if (fStencilWrapOps) {
634 passes[0] = &gWindSingleStencilWithWrapInc;
635 passes[1] = &gWindSingleStencilWithWrapDec;
636 } else {
637 passes[0] = &gWindSingleStencilNoWrapInc;
638 passes[1] = &gWindSingleStencilNoWrapDec;
639 }
640 // which is cw and which is ccw is arbitrary.
641 drawFace[0] = GrPipelineBuilder::kCW_DrawFace;
642 drawFace[1] = GrPipelineBuilder::kCCW_DrawFace;
643 passCount = 3;
644 }
645 if (stencilOnly) {
646 lastPassIsBounds = false;
647 --passCount;
648 } else {
649 lastPassIsBounds = true;
650 drawFace[passCount-1] = GrPipelineBuilder::kBoth_DrawFace;
651 if (reverse) {
652 passes[passCount-1] = &gInvWindColorPass;
653 } else {
654 passes[passCount-1] = &gWindColorPass;
655 }
656 }
657 break;
658 default:
659 SkDEBUGFAIL("Unknown path fFill!");
660 return false;
661 }
662 }
663 }
664
665 SkScalar tol = GrPathUtils::kDefaultTolerance;
666 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
667
668 SkRect devBounds;
669 GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds);
670
671 for (int p = 0; p < passCount; ++p) {
672 pipelineBuilder->setDrawFace(drawFace[p]);
673 if (passes[p]) {
674 *pipelineBuilder->stencil() = *passes[p];
675 }
676
677 if (lastPassIsBounds && (p == passCount-1)) {
678 // Reset the XP Factory on pipelineBuilder
679 pipelineBuilder->setXPFactory(backupXPFactory);
680 SkRect bounds;
681 SkMatrix localMatrix = SkMatrix::I();
682 if (reverse) {
683 SkASSERT(pipelineBuilder->getRenderTarget());
684 // draw over the dev bounds (which will be the whole dst surface for inv fill).
685 bounds = devBounds;
686 SkMatrix vmi;
687 // mapRect through persp matrix may not be correct
688 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
689 vmi.mapRect(&bounds);
690 } else {
691 if (!viewMatrix.invert(&localMatrix)) {
692 return false;
693 }
694 }
695 } else {
696 bounds = path.getBounds();
697 }
698 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
699 viewMatrix;
700 SkAutoTUnref<GrDrawBatch> batch(
701 GrRectBatchFactory::CreateNonAAFill(color, viewM, bounds, nullptr,
702 &localMatrix));
703 target->drawBatch(*pipelineBuilder, batch);
704 } else {
705 if (passCount > 1) {
706 pipelineBuilder->setDisableColorXPFactory();
707 }
708
709 DefaultPathBatch::Geometry geometry;
710 geometry.fColor = color;
711 geometry.fPath = path;
712 geometry.fTolerance = srcSpaceTol;
713
714 SkAutoTUnref<GrDrawBatch> batch(DefaultPathBatch::Create(geometry, newCoverage,
715 viewMatrix, isHairline,
716 devBounds));
717
718 target->drawBatch(*pipelineBuilder, batch);
719 }
720 }
721 return true;
722 }
723
onCanDrawPath(const CanDrawPathArgs & args) const724 bool GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
725 // this class can draw any path with any fill but doesn't do any anti-aliasing.
726 return !args.fAntiAlias && (args.fStroke->isFillStyle() ||
727 IsStrokeHairlineOrEquivalent(*args.fStroke, *args.fViewMatrix,
728 nullptr));
729 }
730
onDrawPath(const DrawPathArgs & args)731 bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
732 GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), "GrDefaultPathRenderer::onDrawPath");
733 return this->internalDrawPath(args.fTarget,
734 args.fPipelineBuilder,
735 args.fColor,
736 *args.fViewMatrix,
737 *args.fPath,
738 *args.fStroke,
739 false);
740 }
741
onStencilPath(const StencilPathArgs & args)742 void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
743 GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(),"GrDefaultPathRenderer::onStencilPath");
744 SkASSERT(SkPath::kInverseEvenOdd_FillType != args.fPath->getFillType());
745 SkASSERT(SkPath::kInverseWinding_FillType != args.fPath->getFillType());
746 this->internalDrawPath(args.fTarget, args.fPipelineBuilder, GrColor_WHITE, *args.fViewMatrix,
747 *args.fPath, *args.fStroke, true);
748 }
749
750 ///////////////////////////////////////////////////////////////////////////////////////////////////
751
752 #ifdef GR_TEST_UTILS
753
DRAW_BATCH_TEST_DEFINE(DefaultPathBatch)754 DRAW_BATCH_TEST_DEFINE(DefaultPathBatch) {
755 GrColor color = GrRandomColor(random);
756 SkMatrix viewMatrix = GrTest::TestMatrix(random);
757
758 // For now just hairlines because the other types of draws require two batches.
759 // TODO we should figure out a way to combine the stencil and cover steps into one batch
760 GrStrokeInfo stroke(SkStrokeRec::kHairline_InitStyle);
761 SkPath path = GrTest::TestPath(random);
762
763 // Compute srcSpaceTol
764 SkRect bounds = path.getBounds();
765 SkScalar tol = GrPathUtils::kDefaultTolerance;
766 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
767
768 DefaultPathBatch::Geometry geometry;
769 geometry.fColor = color;
770 geometry.fPath = path;
771 geometry.fTolerance = srcSpaceTol;
772
773 viewMatrix.mapRect(&bounds);
774 uint8_t coverage = GrRandomCoverage(random);
775 return DefaultPathBatch::Create(geometry, coverage, viewMatrix, true, bounds);
776 }
777
778 #endif
779