Home | History | Annotate | Download | only in ops
      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 #include "GrContext.h"
     10 #include "GrDefaultGeoProcFactory.h"
     11 #include "GrDrawOpTest.h"
     12 #include "GrFixedClip.h"
     13 #include "GrMesh.h"
     14 #include "GrOpFlushState.h"
     15 #include "GrPathUtils.h"
     16 #include "GrSimpleMeshDrawOpHelper.h"
     17 #include "SkGeometry.h"
     18 #include "SkString.h"
     19 #include "SkStrokeRec.h"
     20 #include "SkTLazy.h"
     21 #include "SkTraceEvent.h"
     22 #include "ops/GrMeshDrawOp.h"
     23 #include "ops/GrRectOpFactory.h"
     24 
     25 GrDefaultPathRenderer::GrDefaultPathRenderer() {
     26 }
     27 
     28 ////////////////////////////////////////////////////////////////////////////////
     29 // Helpers for drawPath
     30 
     31 #define STENCIL_OFF     0   // Always disable stencil (even when needed)
     32 
     33 static inline bool single_pass_shape(const GrShape& shape) {
     34 #if STENCIL_OFF
     35     return true;
     36 #else
     37     // Inverse fill is always two pass.
     38     if (shape.inverseFilled()) {
     39         return false;
     40     }
     41     // This path renderer only accepts simple fill paths or stroke paths that are either hairline
     42     // or have a stroke width small enough to treat as hairline. Hairline paths are always single
     43     // pass. Filled paths are single pass if they're convex.
     44     if (shape.style().isSimpleFill()) {
     45         return shape.knownToBeConvex();
     46     }
     47     return true;
     48 #endif
     49 }
     50 
     51 GrPathRenderer::StencilSupport
     52 GrDefaultPathRenderer::onGetStencilSupport(const GrShape& shape) const {
     53     if (single_pass_shape(shape)) {
     54         return GrPathRenderer::kNoRestriction_StencilSupport;
     55     } else {
     56         return GrPathRenderer::kStencilOnly_StencilSupport;
     57     }
     58 }
     59 
     60 namespace {
     61 
     62 class PathGeoBuilder {
     63 public:
     64     PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
     65                    GrGeometryProcessor* geometryProcessor, const GrPipeline* pipeline)
     66             : fMesh(primitiveType)
     67             , fTarget(target)
     68             , fVertexStride(sizeof(SkPoint))
     69             , fGeometryProcessor(geometryProcessor)
     70             , fPipeline(pipeline)
     71             , fIndexBuffer(nullptr)
     72             , fFirstIndex(0)
     73             , fIndicesInChunk(0)
     74             , fIndices(nullptr) {
     75         this->allocNewBuffers();
     76     }
     77 
     78     ~PathGeoBuilder() {
     79         this->emitMeshAndPutBackReserve();
     80     }
     81 
     82     /**
     83      *  Path verbs
     84      */
     85     void moveTo(const SkPoint& p) {
     86         needSpace(1);
     87 
     88         fSubpathIndexStart = this->currentIndex();
     89         *(fCurVert++) = p;
     90     }
     91 
     92     void addLine(const SkPoint& p) {
     93         needSpace(1, this->indexScale());
     94 
     95         if (this->isIndexed()) {
     96             uint16_t prevIdx = this->currentIndex() - 1;
     97             appendCountourEdgeIndices(prevIdx);
     98         }
     99         *(fCurVert++) = p;
    100     }
    101 
    102     void addQuad(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
    103         this->needSpace(GrPathUtils::kMaxPointsPerCurve,
    104                         GrPathUtils::kMaxPointsPerCurve * this->indexScale());
    105 
    106         // First pt of quad is the pt we ended on in previous step
    107         uint16_t firstQPtIdx = this->currentIndex() - 1;
    108         uint16_t numPts = (uint16_t)GrPathUtils::generateQuadraticPoints(
    109                 pts[0], pts[1], pts[2], srcSpaceTolSqd, &fCurVert,
    110                 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
    111         if (this->isIndexed()) {
    112             for (uint16_t i = 0; i < numPts; ++i) {
    113                 appendCountourEdgeIndices(firstQPtIdx + i);
    114             }
    115         }
    116     }
    117 
    118     void addConic(SkScalar weight, const SkPoint pts[], SkScalar srcSpaceTolSqd,
    119                   SkScalar srcSpaceTol) {
    120         SkAutoConicToQuads converter;
    121         const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
    122         for (int i = 0; i < converter.countQuads(); ++i) {
    123             this->addQuad(quadPts + i * 2, srcSpaceTolSqd, srcSpaceTol);
    124         }
    125     }
    126 
    127     void addCubic(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
    128         this->needSpace(GrPathUtils::kMaxPointsPerCurve,
    129                         GrPathUtils::kMaxPointsPerCurve * this->indexScale());
    130 
    131         // First pt of cubic is the pt we ended on in previous step
    132         uint16_t firstCPtIdx = this->currentIndex() - 1;
    133         uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
    134                 pts[0], pts[1], pts[2], pts[3], srcSpaceTolSqd, &fCurVert,
    135                 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
    136         if (this->isIndexed()) {
    137             for (uint16_t i = 0; i < numPts; ++i) {
    138                 appendCountourEdgeIndices(firstCPtIdx + i);
    139             }
    140         }
    141     }
    142 
    143     void addPath(const SkPath& path, SkScalar srcSpaceTol) {
    144         SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
    145 
    146         SkPath::Iter iter(path, false);
    147         SkPoint pts[4];
    148 
    149         bool done = false;
    150         while (!done) {
    151             SkPath::Verb verb = iter.next(pts);
    152             switch (verb) {
    153                 case SkPath::kMove_Verb:
    154                     this->moveTo(pts[0]);
    155                     break;
    156                 case SkPath::kLine_Verb:
    157                     this->addLine(pts[1]);
    158                     break;
    159                 case SkPath::kConic_Verb:
    160                     this->addConic(iter.conicWeight(), pts, srcSpaceTolSqd, srcSpaceTol);
    161                     break;
    162                 case SkPath::kQuad_Verb:
    163                     this->addQuad(pts, srcSpaceTolSqd, srcSpaceTol);
    164                     break;
    165                 case SkPath::kCubic_Verb:
    166                     this->addCubic(pts, srcSpaceTolSqd, srcSpaceTol);
    167                     break;
    168                 case SkPath::kClose_Verb:
    169                     break;
    170                 case SkPath::kDone_Verb:
    171                     done = true;
    172             }
    173         }
    174     }
    175 
    176     static bool PathHasMultipleSubpaths(const SkPath& path) {
    177         bool first = true;
    178 
    179         SkPath::Iter iter(path, false);
    180         SkPath::Verb verb;
    181 
    182         SkPoint pts[4];
    183         while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
    184             if (SkPath::kMove_Verb == verb && !first) {
    185                 return true;
    186             }
    187             first = false;
    188         }
    189         return false;
    190     }
    191 
    192 private:
    193     /**
    194      *  Derived properties
    195      *  TODO: Cache some of these for better performance, rather than re-computing?
    196      */
    197     bool isIndexed() const {
    198         return GrPrimitiveType::kLines == fMesh.primitiveType() ||
    199                GrPrimitiveType::kTriangles == fMesh.primitiveType();
    200     }
    201     bool isHairline() const {
    202         return GrPrimitiveType::kLines == fMesh.primitiveType() ||
    203                GrPrimitiveType::kLineStrip == fMesh.primitiveType();
    204     }
    205     int indexScale() const {
    206         switch (fMesh.primitiveType()) {
    207             case GrPrimitiveType::kLines:
    208                 return 2;
    209             case GrPrimitiveType::kTriangles:
    210                 return 3;
    211             default:
    212                 return 0;
    213         }
    214     }
    215 
    216     uint16_t currentIndex() const { return fCurVert - fVertices; }
    217 
    218     // Allocate vertex and (possibly) index buffers
    219     void allocNewBuffers() {
    220         // Ensure that we always get enough verts for a worst-case quad/cubic, plus leftover points
    221         // from previous mesh piece (up to two verts to continue fanning). If we can't get that
    222         // many, ask for a much larger number. This needs to be fairly big to handle  quads/cubics,
    223         // which have a worst-case of 1k points.
    224         static const int kMinVerticesPerChunk = GrPathUtils::kMaxPointsPerCurve + 2;
    225         static const int kFallbackVerticesPerChunk = 16384;
    226 
    227         fVertices = static_cast<SkPoint*>(fTarget->makeVertexSpaceAtLeast(fVertexStride,
    228                                                                           kMinVerticesPerChunk,
    229                                                                           kFallbackVerticesPerChunk,
    230                                                                           &fVertexBuffer,
    231                                                                           &fFirstVertex,
    232                                                                           &fVerticesInChunk));
    233 
    234         if (this->isIndexed()) {
    235             // Similar to above: Ensure we get enough indices for one worst-case quad/cubic.
    236             // No extra indices are needed for stitching, though. If we can't get that many, ask
    237             // for enough to match our large vertex request.
    238             const int kMinIndicesPerChunk = GrPathUtils::kMaxPointsPerCurve * this->indexScale();
    239             const int kFallbackIndicesPerChunk = kFallbackVerticesPerChunk * this->indexScale();
    240 
    241             fIndices = fTarget->makeIndexSpaceAtLeast(kMinIndicesPerChunk, kFallbackIndicesPerChunk,
    242                                                       &fIndexBuffer, &fFirstIndex,
    243                                                       &fIndicesInChunk);
    244         }
    245 
    246         fCurVert = fVertices;
    247         fCurIdx = fIndices;
    248         fSubpathIndexStart = 0;
    249     }
    250 
    251     void appendCountourEdgeIndices(uint16_t edgeV0Idx) {
    252         // When drawing lines we're appending line segments along the countour. When applying the
    253         // other fill rules we're drawing triangle fans around the start of the current (sub)path.
    254         if (!this->isHairline()) {
    255             *(fCurIdx++) = fSubpathIndexStart;
    256         }
    257         *(fCurIdx++) = edgeV0Idx;
    258         *(fCurIdx++) = edgeV0Idx + 1;
    259     }
    260 
    261     // Emits a single draw with all accumulated vertex/index data
    262     void emitMeshAndPutBackReserve() {
    263         int vertexCount = fCurVert - fVertices;
    264         int indexCount = fCurIdx - fIndices;
    265         SkASSERT(vertexCount <= fVerticesInChunk);
    266         SkASSERT(indexCount <= fIndicesInChunk);
    267 
    268         if (vertexCount > 0) {
    269             if (!this->isIndexed()) {
    270                 fMesh.setNonIndexedNonInstanced(vertexCount);
    271             } else {
    272                 fMesh.setIndexed(fIndexBuffer, indexCount, fFirstIndex, 0, vertexCount - 1);
    273             }
    274             fMesh.setVertexData(fVertexBuffer, fFirstVertex);
    275             fTarget->draw(fGeometryProcessor, fPipeline, fMesh);
    276         }
    277 
    278         fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
    279         fTarget->putBackVertices((size_t)(fVerticesInChunk - vertexCount), fVertexStride);
    280     }
    281 
    282     void needSpace(int vertsNeeded, int indicesNeeded = 0) {
    283         if (fCurVert + vertsNeeded > fVertices + fVerticesInChunk ||
    284             fCurIdx + indicesNeeded > fIndices + fIndicesInChunk) {
    285             // We are about to run out of space (possibly)
    286 
    287             // To maintain continuity, we need to remember one or two points from the current mesh.
    288             // Lines only need the last point, fills need the first point from the current contour.
    289             // We always grab both here, and append the ones we need at the end of this process.
    290             SkPoint lastPt = *(fCurVert - 1);
    291             SkASSERT(fSubpathIndexStart < fVerticesInChunk);
    292             SkPoint subpathStartPt = fVertices[fSubpathIndexStart];
    293 
    294             // Draw the mesh we've accumulated, and put back any unused space
    295             this->emitMeshAndPutBackReserve();
    296 
    297             // Get new buffers
    298             this->allocNewBuffers();
    299 
    300             // Append copies of the points we saved so the two meshes will weld properly
    301             if (!this->isHairline()) {
    302                 *(fCurVert++) = subpathStartPt;
    303             }
    304             *(fCurVert++) = lastPt;
    305         }
    306     }
    307 
    308     GrMesh fMesh;
    309     GrMeshDrawOp::Target* fTarget;
    310     size_t fVertexStride;
    311     GrGeometryProcessor* fGeometryProcessor;
    312     const GrPipeline* fPipeline;
    313 
    314     const GrBuffer* fVertexBuffer;
    315     int fFirstVertex;
    316     int fVerticesInChunk;
    317     SkPoint* fVertices;
    318     SkPoint* fCurVert;
    319 
    320     const GrBuffer* fIndexBuffer;
    321     int fFirstIndex;
    322     int fIndicesInChunk;
    323     uint16_t* fIndices;
    324     uint16_t* fCurIdx;
    325     uint16_t fSubpathIndexStart;
    326 };
    327 
    328 class DefaultPathOp final : public GrMeshDrawOp {
    329 private:
    330     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
    331 
    332 public:
    333     DEFINE_OP_CLASS_ID
    334 
    335     static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkPath& path, SkScalar tolerance,
    336                                           uint8_t coverage, const SkMatrix& viewMatrix,
    337                                           bool isHairline, GrAAType aaType, const SkRect& devBounds,
    338                                           const GrUserStencilSettings* stencilSettings) {
    339         return Helper::FactoryHelper<DefaultPathOp>(std::move(paint), path, tolerance, coverage,
    340                                                     viewMatrix, isHairline, aaType, devBounds,
    341                                                     stencilSettings);
    342     }
    343 
    344     const char* name() const override { return "DefaultPathOp"; }
    345 
    346     SkString dumpInfo() const override {
    347         SkString string;
    348         string.appendf("Color: 0x%08x Count: %d\n", fColor, fPaths.count());
    349         for (const auto& path : fPaths) {
    350             string.appendf("Tolerance: %.2f\n", path.fTolerance);
    351         }
    352         string += fHelper.dumpInfo();
    353         string += INHERITED::dumpInfo();
    354         return string;
    355     }
    356 
    357     DefaultPathOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkPath& path,
    358                   SkScalar tolerance, uint8_t coverage, const SkMatrix& viewMatrix, bool isHairline,
    359                   GrAAType aaType, const SkRect& devBounds,
    360                   const GrUserStencilSettings* stencilSettings)
    361             : INHERITED(ClassID())
    362             , fHelper(helperArgs, aaType, stencilSettings)
    363             , fColor(color)
    364             , fCoverage(coverage)
    365             , fViewMatrix(viewMatrix)
    366             , fIsHairline(isHairline) {
    367         fPaths.emplace_back(PathData{path, tolerance});
    368 
    369         this->setBounds(devBounds, HasAABloat::kNo,
    370                         isHairline ? IsZeroArea::kYes : IsZeroArea::kNo);
    371     }
    372 
    373     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
    374 
    375     RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
    376         GrProcessorAnalysisCoverage gpCoverage =
    377                 this->coverage() == 0xFF ? GrProcessorAnalysisCoverage::kNone
    378                                          : GrProcessorAnalysisCoverage::kSingleChannel;
    379         return fHelper.xpRequiresDstTexture(caps, clip, gpCoverage, &fColor);
    380     }
    381 
    382 private:
    383     void onPrepareDraws(Target* target) const override {
    384         sk_sp<GrGeometryProcessor> gp;
    385         {
    386             using namespace GrDefaultGeoProcFactory;
    387             Color color(this->color());
    388             Coverage coverage(this->coverage());
    389             LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
    390                                                               : LocalCoords::kUnused_Type);
    391             gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, this->viewMatrix());
    392         }
    393 
    394         SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
    395 
    396         int instanceCount = fPaths.count();
    397 
    398         // We will use index buffers if we have multiple paths or one path with multiple contours
    399         bool isIndexed = instanceCount > 1;
    400         for (int i = 0; !isIndexed && i < instanceCount; i++) {
    401             const PathData& args = fPaths[i];
    402             isIndexed = isIndexed || PathGeoBuilder::PathHasMultipleSubpaths(args.fPath);
    403         }
    404 
    405         // determine primitiveType
    406         GrPrimitiveType primitiveType;
    407         if (this->isHairline()) {
    408             primitiveType = isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
    409         } else {
    410             primitiveType = isIndexed ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleFan;
    411         }
    412 
    413         PathGeoBuilder pathGeoBuilder(primitiveType, target, gp.get(),
    414                                       fHelper.makePipeline(target));
    415 
    416         // fill buffers
    417         for (int i = 0; i < instanceCount; i++) {
    418             const PathData& args = fPaths[i];
    419             pathGeoBuilder.addPath(args.fPath, args.fTolerance);
    420         }
    421     }
    422 
    423     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
    424         DefaultPathOp* that = t->cast<DefaultPathOp>();
    425         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
    426             return false;
    427         }
    428 
    429         if (this->color() != that->color()) {
    430             return false;
    431         }
    432 
    433         if (this->coverage() != that->coverage()) {
    434             return false;
    435         }
    436 
    437         if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
    438             return false;
    439         }
    440 
    441         if (this->isHairline() != that->isHairline()) {
    442             return false;
    443         }
    444 
    445         fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
    446         this->joinBounds(*that);
    447         return true;
    448     }
    449 
    450     GrColor color() const { return fColor; }
    451     uint8_t coverage() const { return fCoverage; }
    452     const SkMatrix& viewMatrix() const { return fViewMatrix; }
    453     bool isHairline() const { return fIsHairline; }
    454 
    455     struct PathData {
    456         SkPath fPath;
    457         SkScalar fTolerance;
    458     };
    459 
    460     SkSTArray<1, PathData, true> fPaths;
    461     Helper fHelper;
    462     GrColor fColor;
    463     uint8_t fCoverage;
    464     SkMatrix fViewMatrix;
    465     bool fIsHairline;
    466 
    467     typedef GrMeshDrawOp INHERITED;
    468 };
    469 
    470 }  // anonymous namespace
    471 
    472 bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
    473                                              GrPaint&& paint,
    474                                              GrAAType aaType,
    475                                              const GrUserStencilSettings& userStencilSettings,
    476                                              const GrClip& clip,
    477                                              const SkMatrix& viewMatrix,
    478                                              const GrShape& shape,
    479                                              bool stencilOnly) {
    480     SkASSERT(GrAAType::kCoverage != aaType);
    481     SkPath path;
    482     shape.asPath(&path);
    483 
    484     SkScalar hairlineCoverage;
    485     uint8_t newCoverage = 0xff;
    486     bool isHairline = false;
    487     if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
    488         newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
    489         isHairline = true;
    490     } else {
    491         SkASSERT(shape.style().isSimpleFill());
    492     }
    493 
    494     int                          passCount = 0;
    495     const GrUserStencilSettings* passes[2];
    496     bool                         reverse = false;
    497     bool                         lastPassIsBounds;
    498 
    499     if (isHairline) {
    500         passCount = 1;
    501         if (stencilOnly) {
    502             passes[0] = &gDirectToStencil;
    503         } else {
    504             passes[0] = &userStencilSettings;
    505         }
    506         lastPassIsBounds = false;
    507     } else {
    508         if (single_pass_shape(shape)) {
    509             passCount = 1;
    510             if (stencilOnly) {
    511                 passes[0] = &gDirectToStencil;
    512             } else {
    513                 passes[0] = &userStencilSettings;
    514             }
    515             lastPassIsBounds = false;
    516         } else {
    517             switch (path.getFillType()) {
    518                 case SkPath::kInverseEvenOdd_FillType:
    519                     reverse = true;
    520                     // fallthrough
    521                 case SkPath::kEvenOdd_FillType:
    522                     passes[0] = &gEOStencilPass;
    523                     if (stencilOnly) {
    524                         passCount = 1;
    525                         lastPassIsBounds = false;
    526                     } else {
    527                         passCount = 2;
    528                         lastPassIsBounds = true;
    529                         if (reverse) {
    530                             passes[1] = &gInvEOColorPass;
    531                         } else {
    532                             passes[1] = &gEOColorPass;
    533                         }
    534                     }
    535                     break;
    536 
    537                 case SkPath::kInverseWinding_FillType:
    538                     reverse = true;
    539                     // fallthrough
    540                 case SkPath::kWinding_FillType:
    541                     passes[0] = &gWindStencilPass;
    542                     passCount = 2;
    543                     if (stencilOnly) {
    544                         lastPassIsBounds = false;
    545                         --passCount;
    546                     } else {
    547                         lastPassIsBounds = true;
    548                         if (reverse) {
    549                             passes[passCount-1] = &gInvWindColorPass;
    550                         } else {
    551                             passes[passCount-1] = &gWindColorPass;
    552                         }
    553                     }
    554                     break;
    555                 default:
    556                     SkDEBUGFAIL("Unknown path fFill!");
    557                     return false;
    558             }
    559         }
    560     }
    561 
    562     SkScalar tol = GrPathUtils::kDefaultTolerance;
    563     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
    564 
    565     SkRect devBounds;
    566     GetPathDevBounds(path, renderTargetContext->width(), renderTargetContext->height(), viewMatrix,
    567                      &devBounds);
    568 
    569     for (int p = 0; p < passCount; ++p) {
    570         if (lastPassIsBounds && (p == passCount-1)) {
    571             SkRect bounds;
    572             SkMatrix localMatrix = SkMatrix::I();
    573             if (reverse) {
    574                 // draw over the dev bounds (which will be the whole dst surface for inv fill).
    575                 bounds = devBounds;
    576                 SkMatrix vmi;
    577                 // mapRect through persp matrix may not be correct
    578                 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
    579                     vmi.mapRect(&bounds);
    580                 } else {
    581                     if (!viewMatrix.invert(&localMatrix)) {
    582                         return false;
    583                     }
    584                 }
    585             } else {
    586                 bounds = path.getBounds();
    587             }
    588             const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
    589                                                                                viewMatrix;
    590             renderTargetContext->addDrawOp(
    591                     clip,
    592                     GrRectOpFactory::MakeNonAAFillWithLocalMatrix(
    593                             std::move(paint), viewM, localMatrix, bounds, aaType, passes[p]));
    594         } else {
    595             bool stencilPass = stencilOnly || passCount > 1;
    596             GrPaint::MoveOrNew passPaint(paint, stencilPass);
    597             if (stencilPass) {
    598                 passPaint.paint().setXPFactory(GrDisableColorXPFactory::Get());
    599             }
    600             std::unique_ptr<GrDrawOp> op =
    601                     DefaultPathOp::Make(std::move(passPaint), path, srcSpaceTol, newCoverage,
    602                                         viewMatrix, isHairline, aaType, devBounds, passes[p]);
    603             renderTargetContext->addDrawOp(clip, std::move(op));
    604         }
    605     }
    606     return true;
    607 }
    608 
    609 bool GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
    610     bool isHairline = IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr);
    611     // If we aren't a single_pass_shape or hairline, we require stencil buffers.
    612     if (!(single_pass_shape(*args.fShape) || isHairline) && args.fCaps->avoidStencilBuffers()) {
    613         return false;
    614     }
    615     // This can draw any path with any simple fill style but doesn't do coverage-based antialiasing.
    616     return GrAAType::kCoverage != args.fAAType &&
    617            (args.fShape->style().isSimpleFill() || isHairline);
    618 }
    619 
    620 bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
    621     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
    622                               "GrDefaultPathRenderer::onDrawPath");
    623     return this->internalDrawPath(args.fRenderTargetContext,
    624                                   std::move(args.fPaint),
    625                                   args.fAAType,
    626                                   *args.fUserStencilSettings,
    627                                   *args.fClip,
    628                                   *args.fViewMatrix,
    629                                   *args.fShape,
    630                                   false);
    631 }
    632 
    633 void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
    634     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
    635                               "GrDefaultPathRenderer::onStencilPath");
    636     SkASSERT(!args.fShape->inverseFilled());
    637 
    638     GrPaint paint;
    639     paint.setXPFactory(GrDisableColorXPFactory::Get());
    640 
    641     this->internalDrawPath(args.fRenderTargetContext, std::move(paint), args.fAAType,
    642                            GrUserStencilSettings::kUnused, *args.fClip, *args.fViewMatrix,
    643                            *args.fShape, true);
    644 }
    645 
    646 ///////////////////////////////////////////////////////////////////////////////////////////////////
    647 
    648 #if GR_TEST_UTILS
    649 
    650 GR_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
    651     SkMatrix viewMatrix = GrTest::TestMatrix(random);
    652 
    653     // For now just hairlines because the other types of draws require two ops.
    654     // TODO we should figure out a way to combine the stencil and cover steps into one op.
    655     GrStyle style(SkStrokeRec::kHairline_InitStyle);
    656     SkPath path = GrTest::TestPath(random);
    657 
    658     // Compute srcSpaceTol
    659     SkRect bounds = path.getBounds();
    660     SkScalar tol = GrPathUtils::kDefaultTolerance;
    661     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
    662 
    663     viewMatrix.mapRect(&bounds);
    664     uint8_t coverage = GrRandomCoverage(random);
    665     GrAAType aaType = GrAAType::kNone;
    666     if (GrFSAAType::kUnifiedMSAA == fsaaType && random->nextBool()) {
    667         aaType = GrAAType::kMSAA;
    668     }
    669     return DefaultPathOp::Make(std::move(paint), path, srcSpaceTol, coverage, viewMatrix, true,
    670                                aaType, bounds, GrGetRandomStencil(random, context));
    671 }
    672 
    673 #endif
    674