aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gpu/include/GrDrawTarget.h4
-rw-r--r--gpu/src/GrGLProgram.cpp52
-rw-r--r--gpu/src/GrGLProgram.h4
-rw-r--r--gpu/src/GrGpuGLShaders.cpp2
-rw-r--r--gpu/src/GrPathUtils.cpp2
-rw-r--r--gpu/src/GrTesselatedPathRenderer.cpp485
-rw-r--r--gyp/SampleApp.gyp1
-rw-r--r--samplecode/SampleConcavePaths.cpp146
8 files changed, 574 insertions, 122 deletions
diff --git a/gpu/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h
index d8d0498ae3..d576d8043c 100644
--- a/gpu/include/GrDrawTarget.h
+++ b/gpu/include/GrDrawTarget.h
@@ -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,
diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp
index ecb4753fea..b0d41aa9bd 100644
--- a/gpu/src/GrGLProgram.cpp
+++ b/gpu/src/GrGLProgram.cpp
@@ -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(", ");
- }
- segments.fFSCode.append("a");
- segments.fFSCode.appendS32(count - 1);
- segments.fFSCode.append(" * a0");
- for (int i = 0; i < count - 1; i++) {
- 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(";\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
diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h
index 473bcb65a0..5c4727b229 100644
--- a/gpu/src/GrGLProgram.h
+++ b/gpu/src/GrGLProgram.h
@@ -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;
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
index 0a933b5268..689c1fce9d 100644
--- a/gpu/src/GrGpuGLShaders.cpp
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -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;
diff --git a/gpu/src/GrPathUtils.cpp b/gpu/src/GrPathUtils.cpp
index 8a72ba87a7..1fb043c83f 100644
--- a/gpu/src/GrPathUtils.cpp
+++ b/gpu/src/GrPathUtils.cpp
@@ -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];
diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp
index 0e3389c25d..2783a9b1da 100644
--- a/gpu/src/GrTesselatedPathRenderer.cpp
+++ b/gpu/src/GrTesselatedPathRenderer.cpp
@@ -21,48 +21,255 @@
#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)
- {
- }
- GrTDArray<GrPoint>* fVertices;
- GrTDArray<short>* fIndices;
-};
+typedef GrTDArray<GrDrawTarget::Edge> GrEdgeArray;
+typedef GrTDArray<GrPoint> GrPointArray;
+typedef GrTDArray<uint16_t> GrIndexArray;
+typedef void (*TESSCB)();
-static void beginData(GLenum type, void* data)
-{
- GR_DEBUGASSERT(type == GL_TRIANGLES);
+// 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 void edgeFlagData(GLboolean flag, void* data)
-{
+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;
}
-static void vertexData(void* vertexData, void* data)
-{
- short* end = static_cast<PolygonData*>(data)->fIndices->append();
- *end = reinterpret_cast<long>(vertexData);
-}
+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];
+ }
+ ~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;
+};
+
+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;
+};
-static void endData(void* data)
-{
+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 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);
+static bool nearlyEqual(const GrPoint& a, const GrPoint& b) {
+ return nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY);
}
-typedef void (*TESSCB)();
+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 unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
switch (fill) {
@@ -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;
- }
-
- 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));
+ 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);
+
+ GrMatrix inverse, matrix = target->getViewMatrix();
+ if (!target->getViewInverse(&inverse)) {
+ return;
+ }
+
+ 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_gluTessEndContour(tess);
- }
- Sk_gluTessEndPolygon(tess);
- Sk_gluDeleteTess(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;
+ }
+ 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;
}
diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp
index f0446d9d94..04e07d6b8e 100644
--- a/gyp/SampleApp.gyp
+++ b/gyp/SampleApp.gyp
@@ -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',
diff --git a/samplecode/SampleConcavePaths.cpp b/samplecode/SampleConcavePaths.cpp
new file mode 100644
index 0000000000..e084af220d
--- /dev/null
+++ b/samplecode/SampleConcavePaths.cpp
@@ -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);
+