ethannicholas 2016-04-04 07:57:39 -07:00 committed by Commit bot
parent 3bc969264d
commit 47a2dc8e22
7 changed files with 919 additions and 125 deletions

View File

@ -229,6 +229,8 @@
'<(skia_src_path)/gpu/batches/GrDrawPathBatch.h',
'<(skia_src_path)/gpu/batches/GrDrawVerticesBatch.cpp',
'<(skia_src_path)/gpu/batches/GrDrawVerticesBatch.h',
'<(skia_src_path)/gpu/batches/GrMSAAPathRenderer.cpp',
'<(skia_src_path)/gpu/batches/GrMSAAPathRenderer.h',
'<(skia_src_path)/gpu/batches/GrNonAAFillRectBatch.h',
'<(skia_src_path)/gpu/batches/GrNonAAFillRectBatch.cpp',
'<(skia_src_path)/gpu/batches/GrNonAAStrokeRectBatch.cpp',

View File

@ -20,9 +20,10 @@
#include "batches/GrAALinearizingConvexPathRenderer.h"
#include "batches/GrDashLinePathRenderer.h"
#include "batches/GrDefaultPathRenderer.h"
#include "batches/GrMSAAPathRenderer.h"
#include "batches/GrPLSPathRenderer.h"
#include "batches/GrStencilAndCoverPathRenderer.h"
#include "batches/GrTessellatingPathRenderer.h"
#include "batches/GrPLSPathRenderer.h"
GrPathRendererChain::GrPathRendererChain(GrContext* context) {
const GrCaps& caps = *context->caps();
@ -32,6 +33,9 @@ GrPathRendererChain::GrPathRendererChain(GrContext* context) {
caps)) {
this->addPathRenderer(pr)->unref();
}
if (caps.sampleShadingSupport()) {
this->addPathRenderer(new GrMSAAPathRenderer)->unref();
}
this->addPathRenderer(new GrTessellatingPathRenderer)->unref();
this->addPathRenderer(new GrAAHairLinePathRenderer)->unref();
this->addPathRenderer(new GrAAConvexPathRenderer)->unref();

View File

@ -29,130 +29,6 @@ GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
, fStencilWrapOps(stencilWrapOpsSupport) {
}
////////////////////////////////////////////////////////////////////////////////
// Stencil rules for paths
////// Even/Odd
GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass,
kInvert_StencilOp,
kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xffff,
0xffff,
0xffff);
// ok not to check clip b/c stencil pass only wrote inside clip
GR_STATIC_CONST_SAME_STENCIL(gEOColorPass,
kZero_StencilOp,
kZero_StencilOp,
kNotEqual_StencilFunc,
0xffff,
0x0000,
0xffff);
// have to check clip b/c outside clip will always be zero.
GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass,
kZero_StencilOp,
kZero_StencilOp,
kEqualIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
////// Winding
// when we have separate stencil we increment front faces / decrement back faces
// when we don't have wrap incr and decr we use the stencil test to simulate
// them.
GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap,
kIncWrap_StencilOp, kDecWrap_StencilOp,
kKeep_StencilOp, kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
0xffff, 0xffff,
0xffff, 0xffff,
0xffff, 0xffff);
// if inc'ing the max value, invert to make 0
// if dec'ing zero invert to make all ones.
// we can't avoid touching the stencil on both passing and
// failing, so we can't resctrict ourselves to the clip.
GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap,
kInvert_StencilOp, kInvert_StencilOp,
kIncClamp_StencilOp, kDecClamp_StencilOp,
kEqual_StencilFunc, kEqual_StencilFunc,
0xffff, 0xffff,
0xffff, 0x0000,
0xffff, 0xffff);
// When there are no separate faces we do two passes to setup the winding rule
// stencil. First we draw the front faces and inc, then we draw the back faces
// and dec. These are same as the above two split into the incrementing and
// decrementing passes.
GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
kIncWrap_StencilOp,
kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xffff,
0xffff,
0xffff);
GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
kDecWrap_StencilOp,
kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xffff,
0xffff,
0xffff);
GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc,
kInvert_StencilOp,
kIncClamp_StencilOp,
kEqual_StencilFunc,
0xffff,
0xffff,
0xffff);
GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec,
kInvert_StencilOp,
kDecClamp_StencilOp,
kEqual_StencilFunc,
0xffff,
0x0000,
0xffff);
// Color passes are the same whether we use the two-sided stencil or two passes
GR_STATIC_CONST_SAME_STENCIL(gWindColorPass,
kZero_StencilOp,
kZero_StencilOp,
kNonZeroIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass,
kZero_StencilOp,
kZero_StencilOp,
kEqualIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
////// Normal render to stencil
// Sometimes the default path renderer can draw a path directly to the stencil
// buffer without having to first resolve the interior / exterior.
GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil,
kZero_StencilOp,
kIncClamp_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
////////////////////////////////////////////////////////////////////////////////
// Helpers for drawPath

View File

@ -9,6 +9,7 @@
#define GrDefaultPathRenderer_DEFINED
#include "GrPathRenderer.h"
#include "GrPathStencilSettings.h"
#include "SkTypes.h"
/**

View File

@ -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) {
return false;
}
SkASSERT(fMaxQuadIndices + that->fMaxQuadIndices <= SK_MaxU16);
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);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,35 @@
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrMSAAPathRenderer_DEFINED
#define GrMSAAPathRenderer_DEFINED
#include "GrPathRenderer.h"
#include "SkTypes.h"
class SK_API GrMSAAPathRenderer : public GrPathRenderer {
private:
StencilSupport onGetStencilSupport(const SkPath&, const GrStrokeInfo&) const override;
bool onCanDrawPath(const CanDrawPathArgs&) const override;
bool onDrawPath(const DrawPathArgs&) override;
void onStencilPath(const StencilPathArgs&) override;
bool internalDrawPath(GrDrawTarget*,
GrPipelineBuilder*,
GrColor,
const SkMatrix& viewMatrix,
const SkPath&,
const GrStrokeInfo&,
bool stencilOnly);
typedef GrPathRenderer INHERITED;
};
#endif

View File

@ -0,0 +1,131 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrPathStencilSettings_DEFINED
#define GrPathStencilSettings_DEFINED
////// Even/Odd
GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass,
kInvert_StencilOp,
kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xffff,
0xffff,
0xffff);
// ok not to check clip b/c stencil pass only wrote inside clip
GR_STATIC_CONST_SAME_STENCIL(gEOColorPass,
kZero_StencilOp,
kZero_StencilOp,
kNotEqual_StencilFunc,
0xffff,
0x0000,
0xffff);
// have to check clip b/c outside clip will always be zero.
GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass,
kZero_StencilOp,
kZero_StencilOp,
kEqualIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
////// Winding
// when we have separate stencil we increment front faces / decrement back faces
// when we don't have wrap incr and decr we use the stencil test to simulate
// them.
GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap,
kIncWrap_StencilOp, kDecWrap_StencilOp,
kKeep_StencilOp, kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
0xffff, 0xffff,
0xffff, 0xffff,
0xffff, 0xffff);
// if inc'ing the max value, invert to make 0
// if dec'ing zero invert to make all ones.
// we can't avoid touching the stencil on both passing and
// failing, so we can't resctrict ourselves to the clip.
GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap,
kInvert_StencilOp, kInvert_StencilOp,
kIncClamp_StencilOp, kDecClamp_StencilOp,
kEqual_StencilFunc, kEqual_StencilFunc,
0xffff, 0xffff,
0xffff, 0x0000,
0xffff, 0xffff);
// When there are no separate faces we do two passes to setup the winding rule
// stencil. First we draw the front faces and inc, then we draw the back faces
// and dec. These are same as the above two split into the incrementing and
// decrementing passes.
GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
kIncWrap_StencilOp,
kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xffff,
0xffff,
0xffff);
GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
kDecWrap_StencilOp,
kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xffff,
0xffff,
0xffff);
GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc,
kInvert_StencilOp,
kIncClamp_StencilOp,
kEqual_StencilFunc,
0xffff,
0xffff,
0xffff);
GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec,
kInvert_StencilOp,
kDecClamp_StencilOp,
kEqual_StencilFunc,
0xffff,
0x0000,
0xffff);
// Color passes are the same whether we use the two-sided stencil or two passes
GR_STATIC_CONST_SAME_STENCIL(gWindColorPass,
kZero_StencilOp,
kZero_StencilOp,
kNonZeroIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass,
kZero_StencilOp,
kZero_StencilOp,
kEqualIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
////// Normal render to stencil
// Sometimes the default path renderer can draw a path directly to the stencil
// buffer without having to first resolve the interior / exterior.
GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil,
kZero_StencilOp,
kIncClamp_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
#endif