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
24GrDefaultPathRenderer::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
36GR_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
45GR_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.
54GR_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
68GR_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.
80GR_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.
92GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
93    kIncWrap_StencilOp,
94    kKeep_StencilOp,
95    kAlwaysIfInClip_StencilFunc,
96    0xffff,
97    0xffff,
98    0xffff);
99
100GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
101    kDecWrap_StencilOp,
102    kKeep_StencilOp,
103    kAlwaysIfInClip_StencilFunc,
104    0xffff,
105    0xffff,
106    0xffff);
107
108GR_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);
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.
146GR_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
159static 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
170GrPathRenderer::StencilSupport
171GrDefaultPathRenderer::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
182static 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
196static 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
214class DefaultPathBatch : public GrBatch {
215public:
216    struct Geometry {
217        GrColor fColor;
218        SkPath fPath;
219        SkScalar fTolerance;
220    };
221
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
228    const char* name() const override { return "DefaultPathBatch"; }
229
230    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    }
234    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
235        out->setKnownSingleComponent(this->coverage());
236    }
237
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
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
381    SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
382
383private:
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
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
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
516    GrColor color() const { return fBatch.fColor; }
517    uint8_t coverage() const { return fBatch.fCoverage; }
518    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
519    const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
520    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
536bool 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
709bool 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
721bool 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
737void 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
751BATCH_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