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, false);
    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, false)) != 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 (this->isIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
    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     void visitProxies(const VisitProxyFunc& func) const override {
    347         fHelper.visitProxies(func);
    348     }
    349 
    350     SkString dumpInfo() const override {
    351         SkString string;
    352         string.appendf("Color: 0x%08x Count: %d\n", fColor, fPaths.count());
    353         for (const auto& path : fPaths) {
    354             string.appendf("Tolerance: %.2f\n", path.fTolerance);
    355         }
    356         string += fHelper.dumpInfo();
    357         string += INHERITED::dumpInfo();
    358         return string;
    359     }
    360 
    361     DefaultPathOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkPath& path,
    362                   SkScalar tolerance, uint8_t coverage, const SkMatrix& viewMatrix, bool isHairline,
    363                   GrAAType aaType, const SkRect& devBounds,
    364                   const GrUserStencilSettings* stencilSettings)
    365             : INHERITED(ClassID())
    366             , fHelper(helperArgs, aaType, stencilSettings)
    367             , fColor(color)
    368             , fCoverage(coverage)
    369             , fViewMatrix(viewMatrix)
    370             , fIsHairline(isHairline) {
    371         fPaths.emplace_back(PathData{path, tolerance});
    372 
    373         this->setBounds(devBounds, HasAABloat::kNo,
    374                         isHairline ? IsZeroArea::kYes : IsZeroArea::kNo);
    375     }
    376 
    377     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
    378 
    379     RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
    380                                 GrPixelConfigIsClamped dstIsClamped) override {
    381         GrProcessorAnalysisCoverage gpCoverage =
    382                 this->coverage() == 0xFF ? GrProcessorAnalysisCoverage::kNone
    383                                          : GrProcessorAnalysisCoverage::kSingleChannel;
    384         return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, gpCoverage, &fColor);
    385     }
    386 
    387 private:
    388     void onPrepareDraws(Target* target) override {
    389         sk_sp<GrGeometryProcessor> gp;
    390         {
    391             using namespace GrDefaultGeoProcFactory;
    392             Color color(this->color());
    393             Coverage coverage(this->coverage());
    394             LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
    395                                                               : LocalCoords::kUnused_Type);
    396             gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, this->viewMatrix());
    397         }
    398 
    399         SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
    400 
    401         int instanceCount = fPaths.count();
    402 
    403         // We will use index buffers if we have multiple paths or one path with multiple contours
    404         bool isIndexed = instanceCount > 1;
    405         for (int i = 0; !isIndexed && i < instanceCount; i++) {
    406             const PathData& args = fPaths[i];
    407             isIndexed = isIndexed || PathGeoBuilder::PathHasMultipleSubpaths(args.fPath);
    408         }
    409 
    410         // determine primitiveType
    411         GrPrimitiveType primitiveType;
    412         if (this->isHairline()) {
    413             primitiveType = isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
    414         } else {
    415             primitiveType = isIndexed ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleFan;
    416         }
    417 
    418         PathGeoBuilder pathGeoBuilder(primitiveType, target, gp.get(),
    419                                       fHelper.makePipeline(target));
    420 
    421         // fill buffers
    422         for (int i = 0; i < instanceCount; i++) {
    423             const PathData& args = fPaths[i];
    424             pathGeoBuilder.addPath(args.fPath, args.fTolerance);
    425         }
    426     }
    427 
    428     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
    429         DefaultPathOp* that = t->cast<DefaultPathOp>();
    430         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
    431             return false;
    432         }
    433 
    434         if (this->color() != that->color()) {
    435             return false;
    436         }
    437 
    438         if (this->coverage() != that->coverage()) {
    439             return false;
    440         }
    441 
    442         if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
    443             return false;
    444         }
    445 
    446         if (this->isHairline() != that->isHairline()) {
    447             return false;
    448         }
    449 
    450         fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
    451         this->joinBounds(*that);
    452         return true;
    453     }
    454 
    455     GrColor color() const { return fColor; }
    456     uint8_t coverage() const { return fCoverage; }
    457     const SkMatrix& viewMatrix() const { return fViewMatrix; }
    458     bool isHairline() const { return fIsHairline; }
    459 
    460     struct PathData {
    461         SkPath fPath;
    462         SkScalar fTolerance;
    463     };
    464 
    465     SkSTArray<1, PathData, true> fPaths;
    466     Helper fHelper;
    467     GrColor fColor;
    468     uint8_t fCoverage;
    469     SkMatrix fViewMatrix;
    470     bool fIsHairline;
    471 
    472     typedef GrMeshDrawOp INHERITED;
    473 };
    474 
    475 }  // anonymous namespace
    476 
    477 bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
    478                                              GrPaint&& paint,
    479                                              GrAAType aaType,
    480                                              const GrUserStencilSettings& userStencilSettings,
    481                                              const GrClip& clip,
    482                                              const SkMatrix& viewMatrix,
    483                                              const GrShape& shape,
    484                                              bool stencilOnly) {
    485     SkASSERT(GrAAType::kCoverage != aaType);
    486     SkPath path;
    487     shape.asPath(&path);
    488 
    489     SkScalar hairlineCoverage;
    490     uint8_t newCoverage = 0xff;
    491     bool isHairline = false;
    492     if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
    493         newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
    494         isHairline = true;
    495     } else {
    496         SkASSERT(shape.style().isSimpleFill());
    497     }
    498 
    499     int                          passCount = 0;
    500     const GrUserStencilSettings* passes[2];
    501     bool                         reverse = false;
    502     bool                         lastPassIsBounds;
    503 
    504     if (isHairline) {
    505         passCount = 1;
    506         if (stencilOnly) {
    507             passes[0] = &gDirectToStencil;
    508         } else {
    509             passes[0] = &userStencilSettings;
    510         }
    511         lastPassIsBounds = false;
    512     } else {
    513         if (single_pass_shape(shape)) {
    514             passCount = 1;
    515             if (stencilOnly) {
    516                 passes[0] = &gDirectToStencil;
    517             } else {
    518                 passes[0] = &userStencilSettings;
    519             }
    520             lastPassIsBounds = false;
    521         } else {
    522             switch (path.getFillType()) {
    523                 case SkPath::kInverseEvenOdd_FillType:
    524                     reverse = true;
    525                     // fallthrough
    526                 case SkPath::kEvenOdd_FillType:
    527                     passes[0] = &gEOStencilPass;
    528                     if (stencilOnly) {
    529                         passCount = 1;
    530                         lastPassIsBounds = false;
    531                     } else {
    532                         passCount = 2;
    533                         lastPassIsBounds = true;
    534                         if (reverse) {
    535                             passes[1] = &gInvEOColorPass;
    536                         } else {
    537                             passes[1] = &gEOColorPass;
    538                         }
    539                     }
    540                     break;
    541 
    542                 case SkPath::kInverseWinding_FillType:
    543                     reverse = true;
    544                     // fallthrough
    545                 case SkPath::kWinding_FillType:
    546                     passes[0] = &gWindStencilPass;
    547                     passCount = 2;
    548                     if (stencilOnly) {
    549                         lastPassIsBounds = false;
    550                         --passCount;
    551                     } else {
    552                         lastPassIsBounds = true;
    553                         if (reverse) {
    554                             passes[passCount-1] = &gInvWindColorPass;
    555                         } else {
    556                             passes[passCount-1] = &gWindColorPass;
    557                         }
    558                     }
    559                     break;
    560                 default:
    561                     SkDEBUGFAIL("Unknown path fFill!");
    562                     return false;
    563             }
    564         }
    565     }
    566 
    567     SkScalar tol = GrPathUtils::kDefaultTolerance;
    568     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
    569 
    570     SkRect devBounds;
    571     GetPathDevBounds(path,
    572                      renderTargetContext->asRenderTargetProxy()->worstCaseWidth(),
    573                      renderTargetContext->asRenderTargetProxy()->worstCaseHeight(),
    574                      viewMatrix, &devBounds);
    575 
    576     for (int p = 0; p < passCount; ++p) {
    577         if (lastPassIsBounds && (p == passCount-1)) {
    578             SkRect bounds;
    579             SkMatrix localMatrix = SkMatrix::I();
    580             if (reverse) {
    581                 // draw over the dev bounds (which will be the whole dst surface for inv fill).
    582                 bounds = devBounds;
    583                 SkMatrix vmi;
    584                 // mapRect through persp matrix may not be correct
    585                 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
    586                     vmi.mapRect(&bounds);
    587                 } else {
    588                     if (!viewMatrix.invert(&localMatrix)) {
    589                         return false;
    590                     }
    591                 }
    592             } else {
    593                 bounds = path.getBounds();
    594             }
    595             const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
    596                                                                                viewMatrix;
    597             renderTargetContext->addDrawOp(
    598                     clip,
    599                     GrRectOpFactory::MakeNonAAFillWithLocalMatrix(
    600                             std::move(paint), viewM, localMatrix, bounds, aaType, passes[p]));
    601         } else {
    602             bool stencilPass = stencilOnly || passCount > 1;
    603             std::unique_ptr<GrDrawOp> op;
    604             if (stencilPass) {
    605                 GrPaint stencilPaint;
    606                 stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
    607                 op = DefaultPathOp::Make(std::move(stencilPaint), path, srcSpaceTol, newCoverage,
    608                                          viewMatrix, isHairline, aaType, devBounds, passes[p]);
    609             } else {
    610                 op = DefaultPathOp::Make(std::move(paint), path, srcSpaceTol, newCoverage,
    611                                          viewMatrix, isHairline, aaType, devBounds, passes[p]);
    612             }
    613             renderTargetContext->addDrawOp(clip, std::move(op));
    614         }
    615     }
    616     return true;
    617 }
    618 
    619 GrPathRenderer::CanDrawPath
    620 GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
    621     bool isHairline = IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr);
    622     // If we aren't a single_pass_shape or hairline, we require stencil buffers.
    623     if (!(single_pass_shape(*args.fShape) || isHairline) && args.fCaps->avoidStencilBuffers()) {
    624         return CanDrawPath::kNo;
    625     }
    626     // This can draw any path with any simple fill style but doesn't do coverage-based antialiasing.
    627     if (GrAAType::kCoverage == args.fAAType ||
    628         (!args.fShape->style().isSimpleFill() && !isHairline)) {
    629         return CanDrawPath::kNo;
    630     }
    631     // This is the fallback renderer for when a path is too complicated for the others to draw.
    632     return CanDrawPath::kAsBackup;
    633 }
    634 
    635 bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
    636     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
    637                               "GrDefaultPathRenderer::onDrawPath");
    638     return this->internalDrawPath(args.fRenderTargetContext,
    639                                   std::move(args.fPaint),
    640                                   args.fAAType,
    641                                   *args.fUserStencilSettings,
    642                                   *args.fClip,
    643                                   *args.fViewMatrix,
    644                                   *args.fShape,
    645                                   false);
    646 }
    647 
    648 void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
    649     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
    650                               "GrDefaultPathRenderer::onStencilPath");
    651     SkASSERT(!args.fShape->inverseFilled());
    652 
    653     GrPaint paint;
    654     paint.setXPFactory(GrDisableColorXPFactory::Get());
    655 
    656     this->internalDrawPath(args.fRenderTargetContext, std::move(paint), args.fAAType,
    657                            GrUserStencilSettings::kUnused, *args.fClip, *args.fViewMatrix,
    658                            *args.fShape, true);
    659 }
    660 
    661 ///////////////////////////////////////////////////////////////////////////////////////////////////
    662 
    663 #if GR_TEST_UTILS
    664 
    665 GR_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
    666     SkMatrix viewMatrix = GrTest::TestMatrix(random);
    667 
    668     // For now just hairlines because the other types of draws require two ops.
    669     // TODO we should figure out a way to combine the stencil and cover steps into one op.
    670     GrStyle style(SkStrokeRec::kHairline_InitStyle);
    671     SkPath path = GrTest::TestPath(random);
    672 
    673     // Compute srcSpaceTol
    674     SkRect bounds = path.getBounds();
    675     SkScalar tol = GrPathUtils::kDefaultTolerance;
    676     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
    677 
    678     viewMatrix.mapRect(&bounds);
    679     uint8_t coverage = GrRandomCoverage(random);
    680     GrAAType aaType = GrAAType::kNone;
    681     if (GrFSAAType::kUnifiedMSAA == fsaaType && random->nextBool()) {
    682         aaType = GrAAType::kMSAA;
    683     }
    684     return DefaultPathOp::Make(std::move(paint), path, srcSpaceTol, coverage, viewMatrix, true,
    685                                aaType, bounds, GrGetRandomStencil(random, context));
    686 }
    687 
    688 #endif
    689