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