| Index: src/gpu/batches/GrMSAAPathRenderer.cpp
|
| diff --git a/src/gpu/batches/GrMSAAPathRenderer.cpp b/src/gpu/batches/GrMSAAPathRenderer.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..988952822abc591c5020324762839266d73a5333
|
| --- /dev/null
|
| +++ b/src/gpu/batches/GrMSAAPathRenderer.cpp
|
| @@ -0,0 +1,745 @@
|
| +/*
|
| + * 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 "GrBatchFlushState.h"
|
| +#include "GrDefaultGeoProcFactory.h"
|
| +#include "GrPathStencilSettings.h"
|
| +#include "GrPathUtils.h"
|
| +#include "GrPipelineBuilder.h"
|
| +#include "GrMesh.h"
|
| +#include "SkGeometry.h"
|
| +#include "SkTraceEvent.h"
|
| +#include "glsl/GrGLSLGeometryProcessor.h"
|
| +#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
| +#include "glsl/GrGLSLVertexShaderBuilder.h"
|
| +#include "glsl/GrGLSLProgramDataManager.h"
|
| +#include "glsl/GrGLSLUtil.h"
|
| +#include "gl/GrGLVaryingHandler.h"
|
| +#include "batches/GrRectBatchFactory.h"
|
| +#include "batches/GrVertexBatch.h"
|
| +
|
| +static const float kTolerance = 0.5f;
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// Helpers for drawPath
|
| +
|
| +static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) {
|
| + if (!path.isInverseFillType()) {
|
| + return path.isConvex();
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +GrPathRenderer::StencilSupport
|
| +GrMSAAPathRenderer::onGetStencilSupport(const SkPath& path, const GrStrokeInfo& stroke) const {
|
| + if (single_pass_path(path, stroke)) {
|
| + 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);
|
| + }
|
| +
|
| + virtual ~MSAAQuadProcessor() {}
|
| +
|
| + 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; }
|
| + const SkMatrix& localMatrix() const { return SkMatrix::I(); }
|
| +
|
| + 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.fTransformsIn,
|
| + args.fTransformsOut);
|
| +
|
| + 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 GrGLSLCaps&,
|
| + 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);
|
| + }
|
| +
|
| + virtual void setData(const GrGLSLProgramDataManager& pdman,
|
| + const GrPrimitiveProcessor& gp) override {
|
| + const MSAAQuadProcessor& qp = gp.cast<MSAAQuadProcessor>();
|
| + if (!qp.viewMatrix().isIdentity()) {
|
| + float viewMatrix[3 * 3];
|
| + GrGLSLGetMatrix<3>(viewMatrix, qp.viewMatrix());
|
| + pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
|
| + }
|
| + }
|
| +
|
| + void setTransformData(const GrPrimitiveProcessor& primProc,
|
| + const GrGLSLProgramDataManager& pdman,
|
| + int index,
|
| + const SkTArray<const GrCoordTransform*, true>& transforms) override {
|
| + this->setTransformDataHelper<MSAAQuadProcessor>(primProc, pdman, index, transforms);
|
| + }
|
| +
|
| + private:
|
| + typedef GrGLSLGeometryProcessor INHERITED;
|
| +
|
| + UniformHandle fViewMatrixUniform;
|
| + };
|
| +
|
| + virtual void getGLSLProcessorKey(const GrGLSLCaps& caps,
|
| + GrProcessorKeyBuilder* b) const override {
|
| + GLSLProcessor::GenKey(*this, caps, b);
|
| + }
|
| +
|
| + virtual GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
|
| + return new GLSLProcessor(*this);
|
| + }
|
| +
|
| +private:
|
| + MSAAQuadProcessor(const SkMatrix& viewMatrix)
|
| + : fViewMatrix(viewMatrix) {
|
| + this->initClassID<MSAAQuadProcessor>();
|
| + fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
|
| + kHigh_GrSLPrecision));
|
| + fInUV = &this->addVertexAttrib(Attribute("inUV", kVec2f_GrVertexAttribType,
|
| + kHigh_GrSLPrecision));
|
| + fInColor = &this->addVertexAttrib(Attribute("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 MSAAPathBatch : public GrVertexBatch {
|
| +public:
|
| + DEFINE_BATCH_CLASS_ID
|
| +
|
| + struct Geometry {
|
| + GrColor fColor;
|
| + SkPath fPath;
|
| + SkScalar fTolerance;
|
| + };
|
| +
|
| + static MSAAPathBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix,
|
| + const SkRect& devBounds) {
|
| + return new MSAAPathBatch(geometry, viewMatrix, devBounds);
|
| + }
|
| +
|
| + const char* name() const override { return "MSAAPathBatch"; }
|
| +
|
| + void computePipelineOptimizations(GrInitInvariantOutput* color,
|
| + GrInitInvariantOutput* coverage,
|
| + GrBatchToXPOverrides* overrides) const override {
|
| + // When this is called on a batch, there is only one geometry bundle
|
| + color->setKnownFourComponents(fGeoData[0].fColor);
|
| + coverage->setKnownSingleComponent(0xff);
|
| + }
|
| +
|
| + bool isValid() const {
|
| + return !fIsIndexed || fMaxLineIndices <= SK_MaxU16;
|
| + }
|
| +
|
| +private:
|
| + void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
|
| + // Handle any color overrides
|
| + if (!overrides.readsColor()) {
|
| + fGeoData[0].fColor = GrColor_ILLEGAL;
|
| + }
|
| + overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
|
| + }
|
| +
|
| + void computeWorstCasePointCount(const SkPath& path, int* subpaths, SkScalar tol,
|
| + int* outLinePointCount, int* outQuadPointCount) const {
|
| + int linePointCount = 0;
|
| + int quadPointCount = 0;
|
| + *subpaths = 1;
|
| +
|
| + bool first = true;
|
| +
|
| + SkPath::Iter iter(path, false);
|
| + 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, kTolerance);
|
| + 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, kTolerance, &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 {
|
| + SkASSERT(this->isValid());
|
| + if (fMaxLineVertices == 0) {
|
| + SkASSERT(fMaxQuadVertices == 0);
|
| + return;
|
| + }
|
| +
|
| + GrPrimitiveType primitiveType = fIsIndexed ? kTriangles_GrPrimitiveType
|
| + : kTriangleFan_GrPrimitiveType;
|
| +
|
| + // allocate vertex / index buffers
|
| + const GrBuffer* lineVertexBuffer;
|
| + int firstLineVertex;
|
| + MSAALineVertices lines;
|
| + size_t 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;
|
| + size_t quadVertexStride = sizeof(MSAAQuadVertices::Vertex);
|
| + SkAutoFree quadVertexPtr(sk_malloc_throw(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(fMaxLineIndices, &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(fMaxQuadIndices * sizeof(uint16_t));
|
| + quadIndexPtr.set(quads.indices);
|
| + quads.nextIndex = quads.indices;
|
| + } else {
|
| + quads.indices = nullptr;
|
| + quads.nextIndex = nullptr;
|
| + }
|
| +
|
| + // fill buffers
|
| + for (int i = 0; i < fGeoData.count(); i++) {
|
| + const Geometry& args = fGeoData[i];
|
| +
|
| + if (!this->createGeom(lines,
|
| + quads,
|
| + args.fPath,
|
| + args.fTolerance,
|
| + fViewMatrix,
|
| + args.fColor,
|
| + fIsIndexed)) {
|
| + return;
|
| + }
|
| + }
|
| + int lineVertexOffset = (int) (lines.nextVertex - lines.vertices);
|
| + int lineIndexOffset = (int) (lines.nextIndex - lines.indices);
|
| + SkASSERT(lineVertexOffset <= fMaxLineVertices && lineIndexOffset <= fMaxLineIndices);
|
| + int quadVertexOffset = (int) (quads.nextVertex - quads.vertices);
|
| + int quadIndexOffset = (int) (quads.nextIndex - quads.indices);
|
| + SkASSERT(quadVertexOffset <= fMaxQuadVertices && quadIndexOffset <= fMaxQuadIndices);
|
| +
|
| + if (lineVertexOffset) {
|
| + SkAutoTUnref<const GrGeometryProcessor> lineGP;
|
| + {
|
| + using namespace GrDefaultGeoProcFactory;
|
| + lineGP.reset(GrDefaultGeoProcFactory::Create(Color(Color::kAttribute_Type),
|
| + Coverage(255),
|
| + LocalCoords(LocalCoords::kUnused_Type),
|
| + fViewMatrix));
|
| + }
|
| + SkASSERT(lineVertexStride == lineGP->getVertexStride());
|
| +
|
| + GrMesh lineMeshes;
|
| + if (fIsIndexed) {
|
| + lineMeshes.initIndexed(primitiveType, lineVertexBuffer, lineIndexBuffer,
|
| + firstLineVertex, firstLineIndex, lineVertexOffset,
|
| + lineIndexOffset);
|
| + } else {
|
| + lineMeshes.init(primitiveType, lineVertexBuffer, firstLineVertex,
|
| + lineVertexOffset);
|
| + }
|
| + target->draw(lineGP, lineMeshes);
|
| + }
|
| +
|
| + if (quadVertexOffset) {
|
| + SkAutoTUnref<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;
|
| + if (fIsIndexed) {
|
| + const GrBuffer* quadIndexBuffer;
|
| + int firstQuadIndex;
|
| + uint16_t* quadIndices = (uint16_t*) target->makeIndexSpace(quadIndexOffset,
|
| + &quadIndexBuffer,
|
| + &firstQuadIndex);
|
| + memcpy(quadIndices, quads.indices, sizeof(uint16_t) * quadIndexOffset);
|
| + quadMeshes.initIndexed(kTriangles_GrPrimitiveType, quadVertexBuffer,
|
| + quadIndexBuffer, firstQuadVertex, firstQuadIndex,
|
| + quadVertexOffset, quadIndexOffset);
|
| + } else {
|
| + quadMeshes.init(kTriangles_GrPrimitiveType, quadVertexBuffer, firstQuadVertex,
|
| + quadVertexOffset);
|
| + }
|
| + target->draw(quadGP, quadMeshes);
|
| + }
|
| + }
|
| +
|
| + SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
|
| +
|
| + MSAAPathBatch(const Geometry& geometry, const SkMatrix& viewMatrix, const SkRect& devBounds)
|
| + : INHERITED(ClassID())
|
| + , fViewMatrix(viewMatrix) {
|
| + fGeoData.push_back(geometry);
|
| + this->setBounds(devBounds);
|
| + int contourCount;
|
| + this->computeWorstCasePointCount(geometry.fPath, &contourCount, kTolerance,
|
| + &fMaxLineVertices, &fMaxQuadVertices);
|
| + fMaxLineIndices = fMaxLineVertices * 3;
|
| + fMaxQuadIndices = fMaxQuadVertices * 3;
|
| + fIsIndexed = contourCount > 1;
|
| + }
|
| +
|
| + bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
|
| + MSAAPathBatch* that = t->cast<MSAAPathBatch>();
|
| + if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
|
| + that->bounds(), caps)) {
|
| + return false;
|
| + }
|
| +
|
| + if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) {
|
| + return false;
|
| + }
|
| +
|
| + if ((fMaxLineIndices + that->fMaxLineIndices > SK_MaxU16) ||
|
| + (fMaxQuadIndices + that->fMaxQuadIndices > SK_MaxU16)) {
|
| + return false;
|
| + }
|
| +
|
| + fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
|
| + this->joinBounds(that->bounds());
|
| + fIsIndexed = true;
|
| + fMaxLineVertices += that->fMaxLineVertices;
|
| + fMaxQuadVertices += that->fMaxQuadVertices;
|
| + fMaxLineIndices += that->fMaxLineIndices;
|
| + fMaxQuadIndices += that->fMaxQuadIndices;
|
| + return true;
|
| + }
|
| +
|
| + bool createGeom(MSAALineVertices& lines,
|
| + MSAAQuadVertices& quads,
|
| + const SkPath& path,
|
| + SkScalar srcSpaceTol,
|
| + const SkMatrix& m,
|
| + SkColor color,
|
| + bool isIndexed) const {
|
| + {
|
| + uint16_t subpathIdxStart = (uint16_t) (lines.nextVertex - lines.vertices);
|
| +
|
| + SkPoint pts[4];
|
| +
|
| + bool first = true;
|
| + SkPath::Iter iter(path, false);
|
| +
|
| + 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,
|
| + kTolerance);
|
| + 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, kTolerance, &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;
|
| + }
|
| +
|
| + SkSTArray<1, Geometry, true> fGeoData;
|
| +
|
| + SkMatrix fViewMatrix;
|
| + int fMaxLineVertices;
|
| + int fMaxQuadVertices;
|
| + int fMaxLineIndices;
|
| + int fMaxQuadIndices;
|
| + bool fIsIndexed;
|
| +
|
| + typedef GrVertexBatch INHERITED;
|
| +};
|
| +
|
| +bool GrMSAAPathRenderer::internalDrawPath(GrDrawTarget* target,
|
| + GrPipelineBuilder* pipelineBuilder,
|
| + GrColor color,
|
| + const SkMatrix& viewMatrix,
|
| + const SkPath& path,
|
| + const GrStrokeInfo& origStroke,
|
| + bool stencilOnly) {
|
| + SkTCopyOnFirstWrite<GrStrokeInfo> stroke(origStroke);
|
| +
|
| + const GrXPFactory* xpFactory = pipelineBuilder->getXPFactory();
|
| + SkAutoTUnref<const GrXPFactory> backupXPFactory(SkSafeRef(xpFactory));
|
| + // face culling doesn't make sense here
|
| + SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace());
|
| +
|
| + int passCount = 0;
|
| + const GrStencilSettings* passes[3];
|
| + GrPipelineBuilder::DrawFace drawFace[3];
|
| + bool reverse = false;
|
| + bool lastPassIsBounds;
|
| +
|
| + if (single_pass_path(path, *stroke)) {
|
| + passCount = 1;
|
| + if (stencilOnly) {
|
| + passes[0] = &gDirectToStencil;
|
| + } else {
|
| + passes[0] = nullptr;
|
| + }
|
| + drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
|
| + lastPassIsBounds = false;
|
| + } else {
|
| + switch (path.getFillType()) {
|
| + case SkPath::kInverseEvenOdd_FillType:
|
| + reverse = true;
|
| + // fallthrough
|
| + case SkPath::kEvenOdd_FillType:
|
| + passes[0] = &gEOStencilPass;
|
| + if (stencilOnly) {
|
| + passCount = 1;
|
| + lastPassIsBounds = false;
|
| + } else {
|
| + passCount = 2;
|
| + lastPassIsBounds = true;
|
| + if (reverse) {
|
| + passes[1] = &gInvEOColorPass;
|
| + } else {
|
| + passes[1] = &gEOColorPass;
|
| + }
|
| + }
|
| + drawFace[0] = drawFace[1] = GrPipelineBuilder::kBoth_DrawFace;
|
| + break;
|
| +
|
| + case SkPath::kInverseWinding_FillType:
|
| + reverse = true;
|
| + // fallthrough
|
| + case SkPath::kWinding_FillType:
|
| + passes[0] = &gWindStencilSeparateWithWrap;
|
| + passCount = 2;
|
| + drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
|
| + if (stencilOnly) {
|
| + lastPassIsBounds = false;
|
| + --passCount;
|
| + } else {
|
| + lastPassIsBounds = true;
|
| + drawFace[passCount-1] = GrPipelineBuilder::kBoth_DrawFace;
|
| + if (reverse) {
|
| + passes[passCount-1] = &gInvWindColorPass;
|
| + } else {
|
| + passes[passCount-1] = &gWindColorPass;
|
| + }
|
| + }
|
| + break;
|
| + default:
|
| + SkDEBUGFAIL("Unknown path fFill!");
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + SkRect devBounds;
|
| + GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds);
|
| +
|
| + for (int p = 0; p < passCount; ++p) {
|
| + pipelineBuilder->setDrawFace(drawFace[p]);
|
| + if (passes[p]) {
|
| + *pipelineBuilder->stencil() = *passes[p];
|
| + }
|
| +
|
| + if (lastPassIsBounds && (p == passCount-1)) {
|
| + // Reset the XP Factory on pipelineBuilder
|
| + pipelineBuilder->setXPFactory(backupXPFactory);
|
| + SkRect bounds;
|
| + SkMatrix localMatrix = SkMatrix::I();
|
| + if (reverse) {
|
| + SkASSERT(pipelineBuilder->getRenderTarget());
|
| + // 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;
|
| + SkAutoTUnref<GrDrawBatch> batch(
|
| + GrRectBatchFactory::CreateNonAAFill(color, viewM, bounds, nullptr,
|
| + &localMatrix));
|
| + target->drawBatch(*pipelineBuilder, batch);
|
| + } else {
|
| + if (passCount > 1) {
|
| + pipelineBuilder->setDisableColorXPFactory();
|
| + }
|
| +
|
| + MSAAPathBatch::Geometry geometry;
|
| + geometry.fColor = color;
|
| + geometry.fPath = path;
|
| + geometry.fTolerance = kTolerance;
|
| +
|
| + SkAutoTUnref<MSAAPathBatch> batch(MSAAPathBatch::Create(geometry, viewMatrix,
|
| + devBounds));
|
| + if (batch->isValid()) {
|
| + target->drawBatch(*pipelineBuilder, batch);
|
| + }
|
| + else {
|
| + return false;
|
| + }
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool GrMSAAPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
|
| + return !IsStrokeHairlineOrEquivalent(*args.fStroke, *args.fViewMatrix, nullptr) &&
|
| + !args.fAntiAlias;
|
| +}
|
| +
|
| +bool GrMSAAPathRenderer::onDrawPath(const DrawPathArgs& args) {
|
| + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), "GrMSAAPathRenderer::onDrawPath");
|
| + SkPath path;
|
| + GrStrokeInfo stroke(*args.fStroke);
|
| + if (stroke.isDashed()) {
|
| + if (!stroke.applyDashToPath(&path, &stroke, *args.fPath)) {
|
| + return false;
|
| + }
|
| + } else {
|
| + path = *args.fPath;
|
| + }
|
| + if (!stroke.isFillStyle()) {
|
| + stroke.setResScale(SkScalarAbs(args.fViewMatrix->getMaxScale()));
|
| + if (!stroke.applyToPath(&path, path)) {
|
| + return false;
|
| + }
|
| + stroke.setFillStyle();
|
| + }
|
| + return this->internalDrawPath(args.fTarget,
|
| + args.fPipelineBuilder,
|
| + args.fColor,
|
| + *args.fViewMatrix,
|
| + path,
|
| + stroke,
|
| + false);
|
| +}
|
| +
|
| +void GrMSAAPathRenderer::onStencilPath(const StencilPathArgs& args) {
|
| + GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(),"GrMSAAPathRenderer::onStencilPath");
|
| + SkASSERT(SkPath::kInverseEvenOdd_FillType != args.fPath->getFillType());
|
| + SkASSERT(SkPath::kInverseWinding_FillType != args.fPath->getFillType());
|
| + this->internalDrawPath(args.fTarget, args.fPipelineBuilder, GrColor_WHITE, *args.fViewMatrix,
|
| + *args.fPath, *args.fStroke, true);
|
| +}
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|