| /* |
| * Copyright 2016 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "GrMSAAPathRenderer.h" |
| |
| #include "GrAuditTrail.h" |
| #include "GrClip.h" |
| #include "GrDefaultGeoProcFactory.h" |
| #include "GrFixedClip.h" |
| #include "GrMesh.h" |
| #include "GrOpFlushState.h" |
| #include "GrPathStencilSettings.h" |
| #include "GrPathUtils.h" |
| #include "GrPipelineBuilder.h" |
| #include "SkAutoMalloc.h" |
| #include "SkGeometry.h" |
| #include "SkTraceEvent.h" |
| #include "gl/GrGLVaryingHandler.h" |
| #include "glsl/GrGLSLFragmentShaderBuilder.h" |
| #include "glsl/GrGLSLGeometryProcessor.h" |
| #include "glsl/GrGLSLProgramDataManager.h" |
| #include "glsl/GrGLSLUtil.h" |
| #include "glsl/GrGLSLVertexShaderBuilder.h" |
| #include "ops/GrMeshDrawOp.h" |
| #include "ops/GrNonAAFillRectOp.h" |
| |
| static const float kTolerance = 0.5f; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Helpers for drawPath |
| |
| static inline bool single_pass_shape(const GrShape& shape) { |
| if (!shape.inverseFilled()) { |
| return shape.knownToBeConvex(); |
| } |
| return false; |
| } |
| |
| GrPathRenderer::StencilSupport GrMSAAPathRenderer::onGetStencilSupport(const GrShape& shape) const { |
| if (single_pass_shape(shape)) { |
| return GrPathRenderer::kNoRestriction_StencilSupport; |
| } else { |
| return GrPathRenderer::kStencilOnly_StencilSupport; |
| } |
| } |
| |
| struct MSAALineVertices { |
| struct Vertex { |
| SkPoint fPosition; |
| SkColor fColor; |
| }; |
| Vertex* vertices; |
| Vertex* nextVertex; |
| #ifdef SK_DEBUG |
| Vertex* verticesEnd; |
| #endif |
| uint16_t* indices; |
| uint16_t* nextIndex; |
| }; |
| |
| struct MSAAQuadVertices { |
| struct Vertex { |
| SkPoint fPosition; |
| SkPoint fUV; |
| SkColor fColor; |
| }; |
| Vertex* vertices; |
| Vertex* nextVertex; |
| #ifdef SK_DEBUG |
| Vertex* verticesEnd; |
| #endif |
| uint16_t* indices; |
| uint16_t* nextIndex; |
| }; |
| |
| static inline void append_contour_edge_indices(uint16_t fanCenterIdx, |
| uint16_t edgeV0Idx, |
| MSAALineVertices& lines) { |
| *(lines.nextIndex++) = fanCenterIdx; |
| *(lines.nextIndex++) = edgeV0Idx; |
| *(lines.nextIndex++) = edgeV0Idx + 1; |
| } |
| |
| static inline void add_quad(MSAALineVertices& lines, MSAAQuadVertices& quads, const SkPoint pts[], |
| SkColor color, bool indexed, uint16_t subpathLineIdxStart) { |
| SkASSERT(lines.nextVertex < lines.verticesEnd); |
| *lines.nextVertex = { pts[2], color }; |
| if (indexed) { |
| int prevIdx = (uint16_t) (lines.nextVertex - lines.vertices - 1); |
| if (prevIdx > subpathLineIdxStart) { |
| append_contour_edge_indices(subpathLineIdxStart, prevIdx, lines); |
| } |
| } |
| lines.nextVertex++; |
| |
| SkASSERT(quads.nextVertex + 2 < quads.verticesEnd); |
| // the texture coordinates are drawn from the Loop-Blinn rendering algorithm |
| *(quads.nextVertex++) = { pts[0], SkPoint::Make(0.0, 0.0), color }; |
| *(quads.nextVertex++) = { pts[1], SkPoint::Make(0.5, 0.0), color }; |
| *(quads.nextVertex++) = { pts[2], SkPoint::Make(1.0, 1.0), color }; |
| if (indexed) { |
| uint16_t offset = (uint16_t) (quads.nextVertex - quads.vertices) - 3; |
| *(quads.nextIndex++) = offset++; |
| *(quads.nextIndex++) = offset++; |
| *(quads.nextIndex++) = offset++; |
| } |
| } |
| |
| class MSAAQuadProcessor : public GrGeometryProcessor { |
| public: |
| static GrGeometryProcessor* Create(const SkMatrix& viewMatrix) { |
| return new MSAAQuadProcessor(viewMatrix); |
| } |
| |
| ~MSAAQuadProcessor() override {} |
| |
| const char* name() const override { return "MSAAQuadProcessor"; } |
| |
| const Attribute* inPosition() const { return fInPosition; } |
| const Attribute* inUV() const { return fInUV; } |
| const Attribute* inColor() const { return fInColor; } |
| const SkMatrix& viewMatrix() const { return fViewMatrix; } |
| |
| class GLSLProcessor : public GrGLSLGeometryProcessor { |
| public: |
| GLSLProcessor(const GrGeometryProcessor& qpr) {} |
| |
| void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { |
| const MSAAQuadProcessor& qp = args.fGP.cast<MSAAQuadProcessor>(); |
| GrGLSLVertexBuilder* vsBuilder = args.fVertBuilder; |
| GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; |
| GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
| |
| // emit attributes |
| varyingHandler->emitAttributes(qp); |
| varyingHandler->addPassThroughAttribute(qp.inColor(), args.fOutputColor); |
| |
| GrGLSLVertToFrag uv(kVec2f_GrSLType); |
| varyingHandler->addVarying("uv", &uv, kHigh_GrSLPrecision); |
| vsBuilder->codeAppendf("%s = %s;", uv.vsOut(), qp.inUV()->fName); |
| |
| // Setup position |
| this->setupPosition(vsBuilder, uniformHandler, gpArgs, qp.inPosition()->fName, |
| qp.viewMatrix(), &fViewMatrixUniform); |
| |
| // emit transforms |
| this->emitTransforms(vsBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, |
| qp.inPosition()->fName, SkMatrix::I(), |
| args.fFPCoordTransformHandler); |
| |
| GrGLSLPPFragmentBuilder* fsBuilder = args.fFragBuilder; |
| fsBuilder->codeAppendf("if (%s.x * %s.x >= %s.y) discard;", uv.fsIn(), uv.fsIn(), |
| uv.fsIn()); |
| fsBuilder->codeAppendf("%s = vec4(1.0);", args.fOutputCoverage); |
| } |
| |
| static inline void GenKey(const GrGeometryProcessor& gp, |
| const GrShaderCaps&, |
| GrProcessorKeyBuilder* b) { |
| const MSAAQuadProcessor& qp = gp.cast<MSAAQuadProcessor>(); |
| uint32_t key = 0; |
| key |= qp.viewMatrix().hasPerspective() ? 0x1 : 0x0; |
| key |= qp.viewMatrix().isIdentity() ? 0x2: 0x0; |
| b->add32(key); |
| } |
| |
| void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp, |
| FPCoordTransformIter&& transformIter) override { |
| const MSAAQuadProcessor& qp = gp.cast<MSAAQuadProcessor>(); |
| if (!qp.viewMatrix().isIdentity()) { |
| float viewMatrix[3 * 3]; |
| GrGLSLGetMatrix<3>(viewMatrix, qp.viewMatrix()); |
| pdman.setMatrix3f(fViewMatrixUniform, viewMatrix); |
| } |
| this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); |
| } |
| |
| private: |
| typedef GrGLSLGeometryProcessor INHERITED; |
| |
| UniformHandle fViewMatrixUniform; |
| }; |
| |
| virtual void getGLSLProcessorKey(const GrShaderCaps& caps, |
| GrProcessorKeyBuilder* b) const override { |
| GLSLProcessor::GenKey(*this, caps, b); |
| } |
| |
| virtual GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { |
| return new GLSLProcessor(*this); |
| } |
| |
| private: |
| MSAAQuadProcessor(const SkMatrix& viewMatrix) |
| : fViewMatrix(viewMatrix) { |
| this->initClassID<MSAAQuadProcessor>(); |
| fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType, |
| kHigh_GrSLPrecision); |
| fInUV = &this->addVertexAttrib("inUV", kVec2f_GrVertexAttribType, kHigh_GrSLPrecision); |
| fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType); |
| this->setSampleShading(1.0f); |
| } |
| |
| const Attribute* fInPosition; |
| const Attribute* fInUV; |
| const Attribute* fInColor; |
| SkMatrix fViewMatrix; |
| |
| GR_DECLARE_GEOMETRY_PROCESSOR_TEST; |
| |
| typedef GrGeometryProcessor INHERITED; |
| }; |
| |
| class MSAAPathOp final : public GrLegacyMeshDrawOp { |
| public: |
| DEFINE_OP_CLASS_ID |
| static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkPath& path, |
| const SkMatrix& viewMatrix, |
| const SkRect& devBounds) { |
| int contourCount; |
| int maxLineVertices; |
| int maxQuadVertices; |
| ComputeWorstCasePointCount(path, viewMatrix, &contourCount, &maxLineVertices, |
| &maxQuadVertices); |
| bool isIndexed = contourCount > 1; |
| if (isIndexed && |
| (maxLineVertices > kMaxIndexedVertexCnt || maxQuadVertices > kMaxIndexedVertexCnt)) { |
| return nullptr; |
| } |
| |
| return std::unique_ptr<GrLegacyMeshDrawOp>(new MSAAPathOp( |
| color, path, viewMatrix, devBounds, maxLineVertices, maxQuadVertices, isIndexed)); |
| } |
| |
| const char* name() const override { return "MSAAPathOp"; } |
| |
| SkString dumpInfo() const override { |
| SkString string; |
| string.appendf("Indexed: %d\n", fIsIndexed); |
| for (const auto& path : fPaths) { |
| string.appendf("Color: 0x%08x\n", path.fColor); |
| } |
| string.append(DumpPipelineInfo(*this->pipeline())); |
| string.append(INHERITED::dumpInfo()); |
| return string; |
| } |
| |
| private: |
| MSAAPathOp(GrColor color, const SkPath& path, const SkMatrix& viewMatrix, |
| const SkRect& devBounds, int maxLineVertices, int maxQuadVertices, bool isIndexed) |
| : INHERITED(ClassID()) |
| , fViewMatrix(viewMatrix) |
| , fMaxLineVertices(maxLineVertices) |
| , fMaxQuadVertices(maxQuadVertices) |
| , fIsIndexed(isIndexed) { |
| fPaths.emplace_back(PathInfo{color, path}); |
| this->setBounds(devBounds, HasAABloat::kNo, IsZeroArea::kNo); |
| } |
| |
| void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color, |
| GrProcessorAnalysisCoverage* coverage) const override { |
| color->setToConstant(fPaths[0].fColor); |
| *coverage = GrProcessorAnalysisCoverage::kNone; |
| } |
| |
| void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override { |
| optimizations.getOverrideColorIfSet(&fPaths[0].fColor); |
| } |
| |
| static void ComputeWorstCasePointCount(const SkPath& path, const SkMatrix& m, int* subpaths, |
| int* outLinePointCount, int* outQuadPointCount) { |
| SkScalar tolerance = GrPathUtils::scaleToleranceToSrc(kTolerance, m, path.getBounds()); |
| int linePointCount = 0; |
| int quadPointCount = 0; |
| *subpaths = 1; |
| |
| bool first = true; |
| |
| SkPath::Iter iter(path, true); |
| SkPath::Verb verb; |
| |
| SkPoint pts[4]; |
| while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { |
| switch (verb) { |
| case SkPath::kLine_Verb: |
| linePointCount += 1; |
| break; |
| case SkPath::kConic_Verb: { |
| SkScalar weight = iter.conicWeight(); |
| SkAutoConicToQuads converter; |
| converter.computeQuads(pts, weight, tolerance); |
| int quadPts = converter.countQuads(); |
| linePointCount += quadPts; |
| quadPointCount += 3 * quadPts; |
| } |
| case SkPath::kQuad_Verb: |
| linePointCount += 1; |
| quadPointCount += 3; |
| break; |
| case SkPath::kCubic_Verb: { |
| SkSTArray<15, SkPoint, true> quadPts; |
| GrPathUtils::convertCubicToQuads(pts, tolerance, &quadPts); |
| int count = quadPts.count(); |
| linePointCount += count / 3; |
| quadPointCount += count; |
| break; |
| } |
| case SkPath::kMove_Verb: |
| linePointCount += 1; |
| if (!first) { |
| ++(*subpaths); |
| } |
| break; |
| default: |
| break; |
| } |
| first = false; |
| } |
| *outLinePointCount = linePointCount; |
| *outQuadPointCount = quadPointCount; |
| } |
| |
| void onPrepareDraws(Target* target) const override { |
| if (fMaxLineVertices == 0) { |
| SkASSERT(fMaxQuadVertices == 0); |
| return; |
| } |
| |
| GrPrimitiveType primitiveType = fIsIndexed ? GrPrimitiveType::kTriangles |
| : GrPrimitiveType::kTriangleFan; |
| |
| // allocate vertex / index buffers |
| const GrBuffer* lineVertexBuffer; |
| int firstLineVertex; |
| MSAALineVertices lines; |
| int lineVertexStride = sizeof(MSAALineVertices::Vertex); |
| lines.vertices = (MSAALineVertices::Vertex*) target->makeVertexSpace(lineVertexStride, |
| fMaxLineVertices, |
| &lineVertexBuffer, |
| &firstLineVertex); |
| if (!lines.vertices) { |
| SkDebugf("Could not allocate vertices\n"); |
| return; |
| } |
| lines.nextVertex = lines.vertices; |
| SkDEBUGCODE(lines.verticesEnd = lines.vertices + fMaxLineVertices;) |
| |
| MSAAQuadVertices quads; |
| int quadVertexStride = sizeof(MSAAQuadVertices::Vertex); |
| SkAutoMalloc quadVertexPtr(fMaxQuadVertices * quadVertexStride); |
| quads.vertices = (MSAAQuadVertices::Vertex*) quadVertexPtr.get(); |
| quads.nextVertex = quads.vertices; |
| SkDEBUGCODE(quads.verticesEnd = quads.vertices + fMaxQuadVertices;) |
| |
| const GrBuffer* lineIndexBuffer = nullptr; |
| int firstLineIndex; |
| if (fIsIndexed) { |
| lines.indices = |
| target->makeIndexSpace(3 * fMaxLineVertices, &lineIndexBuffer, &firstLineIndex); |
| if (!lines.indices) { |
| SkDebugf("Could not allocate indices\n"); |
| return; |
| } |
| lines.nextIndex = lines.indices; |
| } else { |
| lines.indices = nullptr; |
| lines.nextIndex = nullptr; |
| } |
| |
| SkAutoFree quadIndexPtr; |
| if (fIsIndexed) { |
| quads.indices = (uint16_t*)sk_malloc_throw(3 * fMaxQuadVertices * sizeof(uint16_t)); |
| quadIndexPtr.reset(quads.indices); |
| quads.nextIndex = quads.indices; |
| } else { |
| quads.indices = nullptr; |
| quads.nextIndex = nullptr; |
| } |
| // fill buffers |
| for (int i = 0; i < fPaths.count(); i++) { |
| const PathInfo& pathInfo = fPaths[i]; |
| if (!this->createGeom(lines, |
| quads, |
| pathInfo.fPath, |
| fViewMatrix, |
| pathInfo.fColor, |
| fIsIndexed)) { |
| return; |
| } |
| } |
| int lineVertexOffset = (int) (lines.nextVertex - lines.vertices); |
| int lineIndexOffset = (int) (lines.nextIndex - lines.indices); |
| SkASSERT(lineVertexOffset <= fMaxLineVertices && lineIndexOffset <= 3 * fMaxLineVertices); |
| int quadVertexOffset = (int) (quads.nextVertex - quads.vertices); |
| int quadIndexOffset = (int) (quads.nextIndex - quads.indices); |
| SkASSERT(quadVertexOffset <= fMaxQuadVertices && quadIndexOffset <= 3 * fMaxQuadVertices); |
| |
| if (lineVertexOffset) { |
| sk_sp<GrGeometryProcessor> lineGP; |
| { |
| using namespace GrDefaultGeoProcFactory; |
| lineGP = GrDefaultGeoProcFactory::Make(Color(Color::kPremulGrColorAttribute_Type), |
| Coverage::kSolid_Type, |
| LocalCoords(LocalCoords::kUnused_Type), |
| fViewMatrix); |
| } |
| SkASSERT(lineVertexStride == lineGP->getVertexStride()); |
| |
| GrMesh lineMeshes(primitiveType); |
| if (!fIsIndexed) { |
| lineMeshes.setNonIndexedNonInstanced(lineVertexOffset); |
| } else { |
| lineMeshes.setIndexed(lineIndexBuffer, lineIndexOffset, firstLineIndex, |
| 0, lineVertexOffset - 1); |
| } |
| lineMeshes.setVertexData(lineVertexBuffer, firstLineVertex); |
| |
| // We can get line vertices from path moveTos with no actual segments and thus no index |
| // count. We assert that indexed draws contain a positive index count, so bail here in |
| // that case. |
| if (!fIsIndexed || lineIndexOffset) { |
| target->draw(lineGP.get(), this->pipeline(), lineMeshes); |
| } |
| } |
| |
| if (quadVertexOffset) { |
| sk_sp<const GrGeometryProcessor> quadGP(MSAAQuadProcessor::Create(fViewMatrix)); |
| SkASSERT(quadVertexStride == quadGP->getVertexStride()); |
| |
| const GrBuffer* quadVertexBuffer; |
| int firstQuadVertex; |
| MSAAQuadVertices::Vertex* quadVertices = (MSAAQuadVertices::Vertex*) |
| target->makeVertexSpace(quadVertexStride, quadVertexOffset, &quadVertexBuffer, |
| &firstQuadVertex); |
| memcpy(quadVertices, quads.vertices, quadVertexStride * quadVertexOffset); |
| GrMesh quadMeshes(GrPrimitiveType::kTriangles); |
| if (!fIsIndexed) { |
| quadMeshes.setNonIndexedNonInstanced(quadVertexOffset); |
| } else { |
| const GrBuffer* quadIndexBuffer; |
| int firstQuadIndex; |
| uint16_t* quadIndices = (uint16_t*) target->makeIndexSpace(quadIndexOffset, |
| &quadIndexBuffer, |
| &firstQuadIndex); |
| memcpy(quadIndices, quads.indices, sizeof(uint16_t) * quadIndexOffset); |
| quadMeshes.setIndexed(quadIndexBuffer, quadIndexOffset, firstQuadIndex, |
| 0, quadVertexOffset - 1); |
| } |
| quadMeshes.setVertexData(quadVertexBuffer, firstQuadVertex); |
| target->draw(quadGP.get(), this->pipeline(), quadMeshes); |
| } |
| } |
| |
| bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { |
| MSAAPathOp* that = t->cast<MSAAPathOp>(); |
| if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), |
| that->bounds(), caps)) { |
| return false; |
| } |
| |
| if (this->bounds().intersects(that->bounds())) { |
| return false; |
| } |
| |
| if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) { |
| return false; |
| } |
| |
| // If we grow to include 2+ paths we will be indexed. |
| if (((fMaxLineVertices + that->fMaxLineVertices) > kMaxIndexedVertexCnt) || |
| ((fMaxQuadVertices + that->fMaxQuadVertices) > kMaxIndexedVertexCnt)) { |
| return false; |
| } |
| |
| fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin()); |
| this->joinBounds(*that); |
| fIsIndexed = true; |
| fMaxLineVertices += that->fMaxLineVertices; |
| fMaxQuadVertices += that->fMaxQuadVertices; |
| return true; |
| } |
| |
| bool createGeom(MSAALineVertices& lines, |
| MSAAQuadVertices& quads, |
| const SkPath& path, |
| const SkMatrix& m, |
| SkColor color, |
| bool isIndexed) const { |
| { |
| const SkScalar tolerance = GrPathUtils::scaleToleranceToSrc(kTolerance, m, |
| path.getBounds()); |
| uint16_t subpathIdxStart = (uint16_t) (lines.nextVertex - lines.vertices); |
| |
| SkPoint pts[4]; |
| |
| bool first = true; |
| SkPath::Iter iter(path, true); |
| |
| bool done = false; |
| while (!done) { |
| SkPath::Verb verb = iter.next(pts); |
| switch (verb) { |
| case SkPath::kMove_Verb: |
| if (!first) { |
| uint16_t currIdx = (uint16_t) (lines.nextVertex - lines.vertices); |
| subpathIdxStart = currIdx; |
| } |
| SkASSERT(lines.nextVertex < lines.verticesEnd); |
| *(lines.nextVertex++) = { pts[0], color }; |
| break; |
| case SkPath::kLine_Verb: |
| if (isIndexed) { |
| uint16_t prevIdx = (uint16_t) (lines.nextVertex - lines.vertices - 1); |
| if (prevIdx > subpathIdxStart) { |
| append_contour_edge_indices(subpathIdxStart, prevIdx, lines); |
| } |
| } |
| SkASSERT(lines.nextVertex < lines.verticesEnd); |
| *(lines.nextVertex++) = { pts[1], color }; |
| break; |
| case SkPath::kConic_Verb: { |
| SkScalar weight = iter.conicWeight(); |
| SkAutoConicToQuads converter; |
| const SkPoint* quadPts = converter.computeQuads(pts, weight, tolerance); |
| for (int i = 0; i < converter.countQuads(); ++i) { |
| add_quad(lines, quads, quadPts + i * 2, color, isIndexed, |
| subpathIdxStart); |
| } |
| break; |
| } |
| case SkPath::kQuad_Verb: { |
| add_quad(lines, quads, pts, color, isIndexed, subpathIdxStart); |
| break; |
| } |
| case SkPath::kCubic_Verb: { |
| SkSTArray<15, SkPoint, true> quadPts; |
| GrPathUtils::convertCubicToQuads(pts, tolerance, &quadPts); |
| int count = quadPts.count(); |
| for (int i = 0; i < count; i += 3) { |
| add_quad(lines, quads, &quadPts[i], color, isIndexed, subpathIdxStart); |
| } |
| break; |
| } |
| case SkPath::kClose_Verb: |
| break; |
| case SkPath::kDone_Verb: |
| done = true; |
| } |
| first = false; |
| } |
| } |
| return true; |
| } |
| |
| // Lines and quads may render with an index buffer. However, we don't have any support for |
| // overflowing the max index. |
| static constexpr int kMaxIndexedVertexCnt = SK_MaxU16 / 3; |
| struct PathInfo { |
| GrColor fColor; |
| SkPath fPath; |
| }; |
| |
| SkSTArray<1, PathInfo, true> fPaths; |
| |
| SkMatrix fViewMatrix; |
| int fMaxLineVertices; |
| int fMaxQuadVertices; |
| bool fIsIndexed; |
| |
| typedef GrLegacyMeshDrawOp INHERITED; |
| }; |
| |
| bool GrMSAAPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext, |
| GrPaint&& paint, |
| GrAAType aaType, |
| const GrUserStencilSettings& userStencilSettings, |
| const GrClip& clip, |
| const SkMatrix& viewMatrix, |
| const GrShape& shape, |
| bool stencilOnly) { |
| SkASSERT(shape.style().isSimpleFill()); |
| SkPath path; |
| shape.asPath(&path); |
| |
| const GrUserStencilSettings* passes[2] = {nullptr, nullptr}; |
| bool reverse = false; |
| |
| if (single_pass_shape(shape)) { |
| if (stencilOnly) { |
| passes[0] = &gDirectToStencil; |
| } else { |
| passes[0] = &userStencilSettings; |
| } |
| } else { |
| switch (path.getFillType()) { |
| case SkPath::kInverseEvenOdd_FillType: |
| reverse = true; |
| // fallthrough |
| case SkPath::kEvenOdd_FillType: |
| passes[0] = &gEOStencilPass; |
| if (!stencilOnly) { |
| passes[1] = reverse ? &gInvEOColorPass : &gEOColorPass; |
| } |
| break; |
| |
| case SkPath::kInverseWinding_FillType: |
| reverse = true; |
| // fallthrough |
| case SkPath::kWinding_FillType: |
| passes[0] = &gWindStencilPass; |
| if (!stencilOnly) { |
| passes[1] = reverse ? &gInvWindColorPass : &gWindColorPass; |
| } |
| break; |
| default: |
| SkDEBUGFAIL("Unknown path fFill!"); |
| return false; |
| } |
| } |
| |
| SkRect devBounds; |
| GetPathDevBounds(path, renderTargetContext->width(), renderTargetContext->height(), viewMatrix, |
| &devBounds); |
| |
| SkASSERT(passes[0]); |
| { // First pass |
| std::unique_ptr<GrLegacyMeshDrawOp> op = |
| MSAAPathOp::Make(paint.getColor(), path, viewMatrix, devBounds); |
| if (!op) { |
| return false; |
| } |
| bool firstPassIsStencil = stencilOnly || passes[1]; |
| // If we have a cover pass then we ignore the paint in the first pass and apply it in the |
| // second. |
| GrPaint::MoveOrNew firstPassPaint(paint, firstPassIsStencil); |
| if (firstPassIsStencil) { |
| firstPassPaint.paint().setXPFactory(GrDisableColorXPFactory::Get()); |
| } |
| GrPipelineBuilder pipelineBuilder(std::move(firstPassPaint), aaType); |
| pipelineBuilder.setUserStencil(passes[0]); |
| renderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op)); |
| } |
| |
| if (passes[1]) { |
| SkRect bounds; |
| SkMatrix localMatrix = SkMatrix::I(); |
| if (reverse) { |
| // draw over the dev bounds (which will be the whole dst surface for inv fill). |
| bounds = devBounds; |
| SkMatrix vmi; |
| // mapRect through persp matrix may not be correct |
| if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) { |
| vmi.mapRect(&bounds); |
| } else { |
| if (!viewMatrix.invert(&localMatrix)) { |
| return false; |
| } |
| } |
| } else { |
| bounds = path.getBounds(); |
| } |
| const SkMatrix& viewM = |
| (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() : viewMatrix; |
| renderTargetContext->addDrawOp( |
| clip, |
| GrNonAAFillRectOp::Make(std::move(paint), viewM, bounds, nullptr, &localMatrix, |
| aaType, passes[1])); |
| } |
| return true; |
| } |
| |
| bool GrMSAAPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { |
| // If we aren't a single_pass_shape, we require stencil buffers. |
| if (!single_pass_shape(*args.fShape) && args.fCaps->avoidStencilBuffers()) { |
| return false; |
| } |
| // This path renderer only fills and relies on MSAA for antialiasing. Stroked shapes are |
| // handled by passing on the original shape and letting the caller compute the stroked shape |
| // which will have a fill style. |
| return args.fShape->style().isSimpleFill() && (GrAAType::kCoverage != args.fAAType); |
| } |
| |
| bool GrMSAAPathRenderer::onDrawPath(const DrawPathArgs& args) { |
| GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(), |
| "GrMSAAPathRenderer::onDrawPath"); |
| SkTLazy<GrShape> tmpShape; |
| const GrShape* shape = args.fShape; |
| if (shape->style().applies()) { |
| SkScalar styleScale = GrStyle::MatrixToScaleFactor(*args.fViewMatrix); |
| tmpShape.init(args.fShape->applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, styleScale)); |
| shape = tmpShape.get(); |
| } |
| return this->internalDrawPath(args.fRenderTargetContext, |
| std::move(args.fPaint), |
| args.fAAType, |
| *args.fUserStencilSettings, |
| *args.fClip, |
| *args.fViewMatrix, |
| *shape, |
| false); |
| } |
| |
| void GrMSAAPathRenderer::onStencilPath(const StencilPathArgs& args) { |
| GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(), |
| "GrMSAAPathRenderer::onStencilPath"); |
| SkASSERT(args.fShape->style().isSimpleFill()); |
| SkASSERT(!args.fShape->mayBeInverseFilledAfterStyling()); |
| |
| GrPaint paint; |
| paint.setXPFactory(GrDisableColorXPFactory::Get()); |
| |
| this->internalDrawPath(args.fRenderTargetContext, std::move(paint), args.fAAType, |
| GrUserStencilSettings::kUnused, *args.fClip, *args.fViewMatrix, |
| *args.fShape, true); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |