Implement edge AA for concave polys in the tesselated path renderer.

Review URL:  http://codereview.appspot.com/4571072/



git-svn-id: http://skia.googlecode.com/svn/trunk@1600 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
senorblanco@chromium.org 2011-06-15 17:52:09 +00:00
parent af9d9c2fc7
commit 129b8e3237
8 changed files with 575 additions and 123 deletions

View File

@ -89,6 +89,10 @@ public:
kNoColorWrites_StateBit = 0x08, //<! If set it disables writing colors.
// Useful while performing stencil
// ops.
kEdgeAAConcave_StateBit = 0x10,//<! If set, edge AA will test edge
// pairs for convexity while
// rasterizing. Set this if the
// source polygon is non-convex.
// subclass may use additional bits internally
kDummyStateBit,

View File

@ -510,21 +510,39 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
segments.fFSCode.appendS32(i);
segments.fFSCode.append("], pos), 0.0, 1.0);\n");
}
segments.fFSCode.append("\tfloat edgeAlpha = ");
for (int i = 0; i < count - 1; i++) {
segments.fFSCode.append("min(a");
segments.fFSCode.appendS32(i);
segments.fFSCode.append(" * a");
segments.fFSCode.appendS32(i + 1);
segments.fFSCode.append(", ");
if (fProgramDesc.fEdgeAAConcave && (count & 0x01) == 0) {
// For concave polys, we consider the edges in pairs.
segments.fFSFunctions.append("float cross2(vec2 a, vec2 b) {\n");
segments.fFSFunctions.append("\treturn dot(a, vec2(b.y, -b.x));\n");
segments.fFSFunctions.append("}\n");
for (int i = 0; i < count; i += 2) {
segments.fFSCode.appendf("\tfloat eb%d;\n", i / 2);
segments.fFSCode.appendf("\tif (cross2(" EDGES_UNI_NAME "[%d].xy, " EDGES_UNI_NAME "[%d].xy) < 0.0) {\n", i, i + 1);
segments.fFSCode.appendf("\t\teb%d = a%d * a%d;\n", i / 2, i, i + 1);
segments.fFSCode.append("\t} else {\n");
segments.fFSCode.appendf("\t\teb%d = a%d + a%d - a%d * a%d;\n", i / 2, i, i + 1, i, i + 1);
segments.fFSCode.append("\t}\n");
}
segments.fFSCode.append("\tfloat edgeAlpha = ");
for (int i = 0; i < count / 2 - 1; i++) {
segments.fFSCode.appendf("min(eb%d, ", i);
}
segments.fFSCode.appendf("eb%d", count / 2 - 1);
for (int i = 0; i < count / 2 - 1; i++) {
segments.fFSCode.append(")");
}
segments.fFSCode.append(";\n");
} else {
segments.fFSCode.append("\tfloat edgeAlpha = ");
for (int i = 0; i < count - 1; i++) {
segments.fFSCode.appendf("min(a%d * a%d, ", i, i + 1);
}
segments.fFSCode.appendf("a%d * a0", count - 1);
for (int i = 0; i < count - 1; i++) {
segments.fFSCode.append(")");
}
segments.fFSCode.append(";\n");
}
segments.fFSCode.append("a");
segments.fFSCode.appendS32(count - 1);
segments.fFSCode.append(" * a0");
for (int i = 0; i < count - 1; i++) {
segments.fFSCode.append(")");
}
segments.fFSCode.append(";\n");
inCoverage = "edgeAlpha";
}
@ -702,6 +720,11 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
lengths[stringCnt] = segments.fFSOutputs.size();
++stringCnt;
}
if (segments.fFSFunctions.size()) {
strings[stringCnt] = segments.fFSFunctions.c_str();
lengths[stringCnt] = segments.fFSFunctions.size();
++stringCnt;
}
GrAssert(segments.fFSCode.size());
strings[stringCnt] = segments.fFSCode.c_str();
@ -714,6 +737,7 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
GrPrintf(segments.fFSUnis.c_str());
GrPrintf(segments.fVaryings.c_str());
GrPrintf(segments.fFSOutputs.c_str());
GrPrintf(segments.fFSFunctions.c_str());
GrPrintf(segments.fFSCode.c_str());
GrPrintf("\n");
#endif

View File

@ -32,6 +32,7 @@ struct ShaderCodeSegments {
GrStringBuilder fVaryings;
GrStringBuilder fFSUnis;
GrStringBuilder fFSOutputs;
GrStringBuilder fFSFunctions;
GrStringBuilder fVSCode;
GrStringBuilder fFSCode;
};
@ -161,11 +162,12 @@ private:
uint8_t fDualSrcOutput; // casts to enum DualSrcOutput
int8_t fFirstCoverageStage;
SkBool8 fEmitsPointSize;
SkBool8 fEdgeAAConcave;
int8_t fEdgeAANumEdges;
uint8_t fColorFilterXfermode; // casts to enum SkXfermode::Mode
uint8_t fPadTo32bLengthMultiple [2];
uint8_t fPadTo32bLengthMultiple [1];
} fProgramDesc;

View File

@ -199,6 +199,7 @@ void GrGpuGLShaders::ProgramUnitTest() {
pdesc.fFirstCoverageStage = idx;
pdesc.fEdgeAANumEdges = (random.nextF() * (getMaxEdges() + 1));
pdesc.fEdgeAAConcave = random.nextF() > .5f;
if (fDualSourceBlendingSupport) {
pdesc.fDualSrcOutput =
@ -731,6 +732,7 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
}
desc.fEdgeAANumEdges = fCurrDrawState.fEdgeAANumEdges;
desc.fEdgeAAConcave = desc.fEdgeAANumEdges > 0 && SkToBool(fCurrDrawState.fFlagBits & kEdgeAAConcave_StateBit);
int lastEnabledStage = -1;

View File

@ -111,7 +111,7 @@ int GrPathUtils::worstCasePointCount(const GrPath& path, int* subpaths,
bool first = true;
SkPath::Iter iter(path, true);
SkPath::Iter iter(path, false);
GrPathCmd cmd;
GrPoint pts[4];

View File

@ -21,49 +21,256 @@
#include "GrPoint.h"
#include "GrTDArray.h"
#include <limits.h>
#include <sk_glu.h>
struct PolygonData {
PolygonData(GrTDArray<GrPoint>* vertices, GrTDArray<short>* indices)
: fVertices(vertices)
, fIndices(indices)
{
typedef GrTDArray<GrDrawTarget::Edge> GrEdgeArray;
typedef GrTDArray<GrPoint> GrPointArray;
typedef GrTDArray<uint16_t> GrIndexArray;
typedef void (*TESSCB)();
// limit the allowable vertex range to approximately half of the representable
// IEEE exponent in order to avoid overflow when doing multiplies between
// vertex components,
const float kMaxVertexValue = 1e18;
static inline GrDrawTarget::Edge computeEdge(const GrPoint& p,
const GrPoint& q,
float sign) {
GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
float scale = sign / tangent.length();
float cross2 = p.fX * q.fY - q.fX * p.fY;
return GrDrawTarget::Edge(tangent.fX * scale,
tangent.fY * scale,
cross2 * scale);
}
static inline GrPoint sanitizePoint(const GrPoint& pt) {
GrPoint r;
r.fX = SkScalarPin(pt.fX, -kMaxVertexValue, kMaxVertexValue);
r.fY = SkScalarPin(pt.fY, -kMaxVertexValue, kMaxVertexValue);
return r;
}
class GrTess {
public:
GrTess(int count, unsigned winding_rule) {
fTess = Sk_gluNewTess();
Sk_gluTessProperty(fTess, GLU_TESS_WINDING_RULE, winding_rule);
Sk_gluTessNormal(fTess, 0.0f, 0.0f, 1.0f);
Sk_gluTessCallback(fTess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginCB);
Sk_gluTessCallback(fTess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexCB);
Sk_gluTessCallback(fTess, GLU_TESS_END_DATA, (TESSCB) &endCB);
Sk_gluTessCallback(fTess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagCB);
Sk_gluTessCallback(fTess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineCB);
fInVertices = new double[count * 3];
}
GrTDArray<GrPoint>* fVertices;
GrTDArray<short>* fIndices;
~GrTess() {
Sk_gluDeleteTess(fTess);
delete[] fInVertices;
}
void addVertex(const GrPoint& pt, int index) {
if (index > USHRT_MAX) return;
double* inVertex = &fInVertices[index * 3];
inVertex[0] = pt.fX;
inVertex[1] = pt.fY;
inVertex[2] = 0.0;
*fVertices.append() = pt;
Sk_gluTessVertex(fTess, inVertex, reinterpret_cast<void*>(index));
}
void addVertices(const GrPoint* points, const uint16_t* contours, int numContours) {
Sk_gluTessBeginPolygon(fTess, this);
size_t i = 0;
for (int j = 0; j < numContours; ++j) {
Sk_gluTessBeginContour(fTess);
size_t end = i + contours[j];
for (; i < end; ++i) {
addVertex(points[i], i);
}
Sk_gluTessEndContour(fTess);
}
Sk_gluTessEndPolygon(fTess);
}
GLUtesselator* tess() { return fTess; }
const GrPointArray& vertices() const { return fVertices; }
protected:
virtual void begin(GLenum type) = 0;
virtual void vertex(int index) = 0;
virtual void edgeFlag(bool flag) = 0;
virtual void end() = 0;
virtual int combine(GLdouble coords[3], int vertexIndices[4],
GLfloat weight[4]) = 0;
static void beginCB(GLenum type, void* data) {
static_cast<GrTess*>(data)->begin(type);
}
static void vertexCB(void* vertexData, void* data) {
static_cast<GrTess*>(data)->vertex(reinterpret_cast<long>(vertexData));
}
static void edgeFlagCB(GLboolean flag, void* data) {
static_cast<GrTess*>(data)->edgeFlag(flag != 0);
}
static void endCB(void* data) {
static_cast<GrTess*>(data)->end();
}
static void combineCB(GLdouble coords[3], void* vertexData[4],
GLfloat weight[4], void **outData, void* data) {
int vertexIndex[4];
vertexIndex[0] = reinterpret_cast<long>(vertexData[0]);
vertexIndex[1] = reinterpret_cast<long>(vertexData[1]);
vertexIndex[2] = reinterpret_cast<long>(vertexData[2]);
vertexIndex[3] = reinterpret_cast<long>(vertexData[3]);
GrTess* tess = static_cast<GrTess*>(data);
int outIndex = tess->combine(coords, vertexIndex, weight);
*reinterpret_cast<long*>(outData) = outIndex;
}
protected:
GLUtesselator* fTess;
GrPointArray fVertices;
double* fInVertices;
};
static void beginData(GLenum type, void* data)
{
GR_DEBUGASSERT(type == GL_TRIANGLES);
class GrPolygonTess : public GrTess {
public:
GrPolygonTess(int count, unsigned winding_rule)
: GrTess(count, winding_rule) {
}
~GrPolygonTess() {
}
const GrIndexArray& indices() const { return fIndices; }
protected:
virtual void begin(GLenum type) {
GR_DEBUGASSERT(type == GL_TRIANGLES);
}
virtual void vertex(int index) {
*fIndices.append() = index;
}
virtual void edgeFlag(bool flag) {}
virtual void end() {}
virtual int combine(GLdouble coords[3], int vertexIndices[4],
GLfloat weight[4]) {
int index = fVertices.count();
GrPoint p = GrPoint::Make(static_cast<float>(coords[0]),
static_cast<float>(coords[1]));
*fVertices.append() = p;
return index;
}
protected:
GrIndexArray fIndices;
};
class GrEdgePolygonTess : public GrPolygonTess {
public:
GrEdgePolygonTess(int count, unsigned winding_rule, const SkMatrix& matrix)
: GrPolygonTess(count, winding_rule),
fMatrix(matrix),
fEdgeFlag(false),
fEdgeVertex(-1),
fTriStartVertex(-1),
fEdges(NULL) {
}
~GrEdgePolygonTess() {
delete[] fEdges;
}
const GrDrawTarget::Edge* edges() const { return fEdges; }
private:
void addEdge(int index0, int index1) {
GrPoint p = fVertices[index0];
GrPoint q = fVertices[index1];
fMatrix.mapPoints(&p, 1);
fMatrix.mapPoints(&q, 1);
p = sanitizePoint(p);
q = sanitizePoint(q);
if (p == q) return;
GrDrawTarget::Edge edge = computeEdge(p, q, 1.0f);
fEdges[index0 * 2 + 1] = edge;
fEdges[index1 * 2] = edge;
}
virtual void begin(GLenum type) {
GR_DEBUGASSERT(type == GL_TRIANGLES);
int count = fVertices.count() * 2;
fEdges = new GrDrawTarget::Edge[count];
memset(fEdges, 0, count * sizeof(GrDrawTarget::Edge));
}
virtual void edgeFlag(bool flag) {
fEdgeFlag = flag;
}
virtual void vertex(int index) {
bool triStart = fIndices.count() % 3 == 0;
GrPolygonTess::vertex(index);
if (fEdgeVertex != -1) {
if (triStart) {
addEdge(fEdgeVertex, fTriStartVertex);
} else {
addEdge(fEdgeVertex, index);
}
}
if (triStart) {
fTriStartVertex = index;
}
if (fEdgeFlag) {
fEdgeVertex = index;
} else {
fEdgeVertex = -1;
}
}
virtual void end() {
if (fEdgeVertex != -1) {
addEdge(fEdgeVertex, fTriStartVertex);
}
}
GrMatrix fMatrix;
bool fEdgeFlag;
int fEdgeVertex, fTriStartVertex;
GrDrawTarget::Edge* fEdges;
};
class GrBoundaryTess : public GrTess {
public:
GrBoundaryTess(int count, unsigned winding_rule)
: GrTess(count, winding_rule),
fContourStart(0) {
Sk_gluTessProperty(fTess, GLU_TESS_BOUNDARY_ONLY, 1);
}
~GrBoundaryTess() {
}
GrPointArray& contourPoints() { return fContourPoints; }
const GrIndexArray& contours() const { return fContours; }
private:
virtual void begin(GLenum type) {
fContourStart = fContourPoints.count();
}
virtual void vertex(int index) {
*fContourPoints.append() = fVertices.at(index);
}
virtual void edgeFlag(bool flag) {}
virtual void end() {
*fContours.append() = fContourPoints.count() - fContourStart;
}
virtual int combine(GLdouble coords[3], int vertexIndices[4],
GLfloat weight[4]) {
int index = fVertices.count();
*fVertices.append() = GrPoint::Make(static_cast<float>(coords[0]),
static_cast<float>(coords[1]));
return index;
}
GrPointArray fContourPoints;
GrIndexArray fContours;
size_t fContourStart;
};
static bool nearlyEqual(float a, float b) {
return fabsf(a - b) < 0.0001f;
}
static void edgeFlagData(GLboolean flag, void* data)
{
static bool nearlyEqual(const GrPoint& a, const GrPoint& b) {
return nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY);
}
static void vertexData(void* vertexData, void* data)
{
short* end = static_cast<PolygonData*>(data)->fIndices->append();
*end = reinterpret_cast<long>(vertexData);
static bool parallel(const GrDrawTarget::Edge& a, const GrDrawTarget::Edge& b) {
return (nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY)) ||
(nearlyEqual(a.fX, -b.fX) && nearlyEqual(a.fY, -b.fY));
}
static void endData(void* data)
{
}
static void combineData(GLdouble coords[3], void* vertexData[4],
GLfloat weight[4], void **outData, void* data)
{
PolygonData* polygonData = static_cast<PolygonData*>(data);
int index = polygonData->fVertices->count();
*polygonData->fVertices->append() = GrPoint::Make(static_cast<float>(coords[0]),
static_cast<float>(coords[1]));
*outData = reinterpret_cast<void*>(index);
}
typedef void (*TESSCB)();
static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
switch (fill) {
case kWinding_PathFill:
@ -85,40 +292,59 @@ static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
}
typedef GrTDArray<GrDrawTarget::Edge> EdgeArray;
bool isCCW(const GrPoint* pts)
{
GrVec v1 = pts[1] - pts[0];
GrVec v2 = pts[2] - pts[1];
static bool isCCW(const GrPoint* pts, int count) {
GrVec v1, v2;
do {
v1 = pts[1] - pts[0];
v2 = pts[2] - pts[1];
pts++;
count--;
} while (nearlyEqual(v1, v2) && count > 3);
return v1.cross(v2) < 0;
}
static size_t computeEdgesAndOffsetVertices(const GrMatrix& matrix,
const GrMatrix& inverse,
GrPoint* vertices,
size_t numVertices,
EdgeArray* edges)
{
static bool validEdge(const GrDrawTarget::Edge& edge) {
return !(edge.fX == 0.0f && edge.fY == 0.0f && edge.fZ == 0.0f);
}
static size_t computeEdgesAndIntersect(const GrMatrix& matrix,
const GrMatrix& inverse,
GrPoint* vertices,
size_t numVertices,
GrEdgeArray* edges,
float sign) {
if (numVertices < 3) {
return 0;
}
matrix.mapPoints(vertices, numVertices);
GrPoint p = vertices[numVertices - 1];
float sign = isCCW(vertices) ? -1.0f : 1.0f;
if (sign == 0.0f) {
sign = isCCW(vertices, numVertices) ? -1.0f : 1.0f;
}
GrPoint p = sanitizePoint(vertices[numVertices - 1]);
for (size_t i = 0; i < numVertices; ++i) {
GrPoint q = vertices[i];
if (p == q) continue;
GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
float scale = sign / tangent.length();
float cross2 = p.fX * q.fY - q.fX * p.fY;
GrDrawTarget::Edge edge(tangent.fX * scale,
tangent.fY * scale,
cross2 * scale + 0.5f);
GrPoint q = sanitizePoint(vertices[i]);
if (p == q) {
continue;
}
GrDrawTarget::Edge edge = computeEdge(p, q, sign);
edge.fZ += 0.5f; // Offset by half a pixel along the tangent.
*edges->append() = edge;
p = q;
}
GrDrawTarget::Edge prev_edge = *edges->back();
for (int i = 0; i < edges->count(); ++i) {
GrDrawTarget::Edge edge = edges->at(i);
vertices[i] = prev_edge.intersect(edge);
int count = edges->count();
if (count == 0) {
return 0;
}
GrDrawTarget::Edge prev_edge = edges->at(0);
for (int i = 0; i < count; ++i) {
GrDrawTarget::Edge edge = edges->at(i < count - 1 ? i + 1 : 0);
if (parallel(edge, prev_edge)) {
// 3 points are collinear; offset by half the tangent instead
vertices[i].fX -= edge.fX * 0.5f;
vertices[i].fY -= edge.fY * 0.5f;
} else {
vertices[i] = prev_edge.intersect(edge);
}
inverse.mapPoints(&vertices[i], 1);
prev_edge = edge;
}
@ -164,14 +390,18 @@ void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
maxPts += 4;
subpathCnt++;
}
GrPoint* base = new GrPoint[maxPts];
if (maxPts > USHRT_MAX) {
return;
}
GrAutoSTMalloc<8, GrPoint> baseMem(maxPts);
GrPoint* base = (GrPoint*) baseMem;
GrPoint* vert = base;
GrPoint* subpathBase = base;
GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
GrPoint pts[4];
SkPath::Iter iter(path, true);
SkPath::Iter iter(path, false);
bool first = true;
int subpath = 0;
@ -242,18 +472,20 @@ FINISHED:
size_t count = vert - base;
if (count < 3) {
delete[] base;
return;
return;
}
if (subpathCnt == 1 && !inverted && path.isConvex()) {
if (target->isAntialiasState()) {
EdgeArray edges;
GrEdgeArray edges;
GrMatrix inverse, matrix = target->getViewMatrix();
target->getViewInverse(&inverse);
count = computeEdgesAndOffsetVertices(matrix, inverse, base, count, &edges);
count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
size_t maxEdges = target->getMaxEdges();
if (count == 0) {
return;
}
if (count <= maxEdges) {
// All edges fit; upload all edges and draw all verts as a fan
target->setVertexSourceToArray(layout, base, count);
@ -276,48 +508,96 @@ FINISHED:
target->setVertexSourceToArray(layout, base, count);
target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
}
delete[] base;
return;
}
// FIXME: This copy could be removed if we had (templated?) versions of
// generate_*_point above that wrote directly into doubles.
double* inVertices = new double[count * 3];
for (size_t i = 0; i < count; ++i) {
inVertices[i * 3] = base[i].fX;
inVertices[i * 3 + 1] = base[i].fY;
inVertices[i * 3 + 2] = 1.0;
}
if (target->isAntialiasState()) {
// Run the tesselator once to get the boundaries.
GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fill));
btess.addVertices(base, subpathVertCount, subpathCnt);
GLUtesselator* tess = Sk_gluNewTess();
unsigned windingRule = fill_type_to_glu_winding_rule(fill);
Sk_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
Sk_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
Sk_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
Sk_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
Sk_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
Sk_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
GrTDArray<short> indices;
GrTDArray<GrPoint> vertices;
PolygonData data(&vertices, &indices);
Sk_gluTessBeginPolygon(tess, &data);
size_t i = 0;
for (int sp = 0; sp < subpathCnt; ++sp) {
Sk_gluTessBeginContour(tess);
int start = i;
size_t end = start + subpathVertCount[sp];
for (; i < end; ++i) {
double* inVertex = &inVertices[i * 3];
*vertices.append() = GrPoint::Make(inVertex[0], inVertex[1]);
Sk_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
GrMatrix inverse, matrix = target->getViewMatrix();
if (!target->getViewInverse(&inverse)) {
return;
}
Sk_gluTessEndContour(tess);
if (btess.vertices().count() > USHRT_MAX) {
return;
}
// Inflate the boundary, and run the tesselator again to generate
// interior polys.
const GrPointArray& contourPoints = btess.contourPoints();
const GrIndexArray& contours = btess.contours();
GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix);
size_t i = 0;
Sk_gluTessBeginPolygon(ptess.tess(), &ptess);
for (int contour = 0; contour < contours.count(); ++contour) {
int count = contours[contour];
GrEdgeArray edges;
int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f);
Sk_gluTessBeginContour(ptess.tess());
for (int j = 0; j < newCount; j++) {
ptess.addVertex(contourPoints[i + j], ptess.vertices().count());
}
i += count;
Sk_gluTessEndContour(ptess.tess());
}
Sk_gluTessEndPolygon(ptess.tess());
if (ptess.vertices().count() > USHRT_MAX) {
return;
}
// Draw the resulting polys and upload their edge data.
target->enableState(GrDrawTarget::kEdgeAAConcave_StateBit);
const GrPointArray& vertices = ptess.vertices();
const GrIndexArray& indices = ptess.indices();
const GrDrawTarget::Edge* edges = ptess.edges();
GR_DEBUGASSERT(indices.count() % 3 == 0);
for (int i = 0; i < indices.count(); i += 3) {
GrPoint tri_verts[3];
int index0 = indices[i];
int index1 = indices[i + 1];
int index2 = indices[i + 2];
tri_verts[0] = vertices[index0];
tri_verts[1] = vertices[index1];
tri_verts[2] = vertices[index2];
GrDrawTarget::Edge tri_edges[6];
int t = 0;
const GrDrawTarget::Edge& edge0 = edges[index0 * 2];
const GrDrawTarget::Edge& edge1 = edges[index0 * 2 + 1];
const GrDrawTarget::Edge& edge2 = edges[index1 * 2];
const GrDrawTarget::Edge& edge3 = edges[index1 * 2 + 1];
const GrDrawTarget::Edge& edge4 = edges[index2 * 2];
const GrDrawTarget::Edge& edge5 = edges[index2 * 2 + 1];
if (validEdge(edge0) && validEdge(edge1)) {
tri_edges[t++] = edge0;
tri_edges[t++] = edge1;
}
if (validEdge(edge2) && validEdge(edge3)) {
tri_edges[t++] = edge2;
tri_edges[t++] = edge3;
}
if (validEdge(edge4) && validEdge(edge5)) {
tri_edges[t++] = edge4;
tri_edges[t++] = edge5;
}
target->setEdgeAAData(&tri_edges[0], t);
target->setVertexSourceToArray(layout, &tri_verts[0], 3);
target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
}
target->setEdgeAAData(NULL, 0);
target->disableState(GrDrawTarget::kEdgeAAConcave_StateBit);
return;
}
Sk_gluTessEndPolygon(tess);
Sk_gluDeleteTess(tess);
GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fill));
ptess.addVertices(base, subpathVertCount, subpathCnt);
const GrPointArray& vertices = ptess.vertices();
const GrIndexArray& indices = ptess.indices();
if (indices.count() > 0) {
target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
target->setIndexSourceToArray(indices.begin(), indices.count());
@ -327,8 +607,6 @@ FINISHED:
vertices.count(),
indices.count());
}
delete[] inVertices;
delete[] base;
}
bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
@ -347,10 +625,5 @@ void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
const SkPath& path,
GrPathFill fill) {
int subpathCnt = 0;
int tol = GrPathUtils::gTolerance;
GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
return (subpathCnt == 1 &&
!IsFillInverted(fill) &&
path.isConvex());
return true;
}

View File

@ -43,6 +43,7 @@
'../samplecode/SampleCode.h',
'../samplecode/SampleColorFilter.cpp',
'../samplecode/SampleComplexClip.cpp',
'../samplecode/SampleConcavePaths.cpp',
'../samplecode/SampleCull.cpp',
'../samplecode/SampleDecode.cpp',
'../samplecode/SampleDither.cpp',

View File

@ -0,0 +1,146 @@
#include "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "SkGradientShader.h"
#include "SkGraphics.h"
#include "SkImageDecoder.h"
#include "SkPath.h"
#include "SkRegion.h"
#include "SkShader.h"
#include "SkUtils.h"
#include "SkXfermode.h"
#include "SkColorPriv.h"
#include "SkColorFilter.h"
#include "SkParsePath.h"
#include "SkTime.h"
#include "SkTypeface.h"
#include "SkGeometry.h"
class ConcavePathView : public SampleView {
public:
ConcavePathView() {}
protected:
// overrides from SkEventSink
virtual bool onQuery(SkEvent* evt) {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "ConcavePaths");
return true;
}
return this->INHERITED::onQuery(evt);
}
virtual void onDrawContent(SkCanvas* canvas) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kFill_Style);
// Concave test
if (1) {
SkPath path;
canvas->translate(0, 0);
path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
canvas->drawPath(path, paint);
}
// Reverse concave test
if (1) {
SkPath path;
canvas->save();
canvas->translate(100, 0);
path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
canvas->drawPath(path, paint);
canvas->restore();
}
// Bowtie (intersection)
if (1) {
SkPath path;
canvas->save();
canvas->translate(200, 0);
path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
canvas->drawPath(path, paint);
canvas->restore();
}
// "fake" bowtie (concave, but no intersection)
if (1) {
SkPath path;
canvas->save();
canvas->translate(300, 0);
path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
path.lineTo(SkIntToScalar(50), SkIntToScalar(40));
path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
path.lineTo(SkIntToScalar(50), SkIntToScalar(60));
path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
canvas->drawPath(path, paint);
canvas->restore();
}
// Fish test (intersection/concave)
if (1) {
SkPath path;
canvas->save();
canvas->translate(0, 100);
path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
path.lineTo(SkIntToScalar(70), SkIntToScalar(50));
path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
path.lineTo(SkIntToScalar(0), SkIntToScalar(50));
canvas->drawPath(path, paint);
canvas->restore();
}
// Collinear test
if (1) {
SkPath path;
canvas->save();
canvas->translate(100, 100);
path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
path.lineTo(SkIntToScalar(50), SkIntToScalar(20));
path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
path.lineTo(SkIntToScalar(50), SkIntToScalar(80));
canvas->drawPath(path, paint);
canvas->restore();
}
// Hole test
if (1) {
SkPath path;
canvas->save();
canvas->translate(200, 100);
path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
path.moveTo(SkIntToScalar(30), SkIntToScalar(30));
path.lineTo(SkIntToScalar(30), SkIntToScalar(70));
path.lineTo(SkIntToScalar(70), SkIntToScalar(70));
path.lineTo(SkIntToScalar(70), SkIntToScalar(30));
canvas->drawPath(path, paint);
canvas->restore();
}
}
virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
this->inval(NULL);
return this->INHERITED::onFindClickHandler(x, y);
}
private:
typedef SampleView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new ConcavePathView; }
static SkViewRegister reg(MyFactory);