From 129b8e3237b80b9d258a8f48e8f54c0073cafbdc Mon Sep 17 00:00:00 2001 From: "senorblanco@chromium.org" Date: Wed, 15 Jun 2011 17:52:09 +0000 Subject: [PATCH] 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 --- gpu/include/GrDrawTarget.h | 4 + gpu/src/GrGLProgram.cpp | 52 ++- gpu/src/GrGLProgram.h | 4 +- gpu/src/GrGpuGLShaders.cpp | 2 + gpu/src/GrPathUtils.cpp | 2 +- gpu/src/GrTesselatedPathRenderer.cpp | 485 +++++++++++++++++++++------ gyp/SampleApp.gyp | 1 + samplecode/SampleConcavePaths.cpp | 146 ++++++++ 8 files changed, 574 insertions(+), 122 deletions(-) create mode 100644 samplecode/SampleConcavePaths.cpp 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, // .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 #include -struct PolygonData { - PolygonData(GrTDArray* vertices, GrTDArray* indices) - : fVertices(vertices) - , fIndices(indices) - { - } - GrTDArray* fVertices; - GrTDArray* fIndices; -}; +typedef GrTDArray GrEdgeArray; +typedef GrTDArray GrPointArray; +typedef GrTDArray 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(data)->fIndices->append(); - *end = reinterpret_cast(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(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(data)->begin(type); + } + static void vertexCB(void* vertexData, void* data) { + static_cast(data)->vertex(reinterpret_cast(vertexData)); + } + static void edgeFlagCB(GLboolean flag, void* data) { + static_cast(data)->edgeFlag(flag != 0); + } + static void endCB(void* data) { + static_cast(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(vertexData[0]); + vertexIndex[1] = reinterpret_cast(vertexData[1]); + vertexIndex[2] = reinterpret_cast(vertexData[2]); + vertexIndex[3] = reinterpret_cast(vertexData[3]); + GrTess* tess = static_cast(data); + int outIndex = tess->combine(coords, vertexIndex, weight); + *reinterpret_cast(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(coords[0]), + static_cast(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(coords[0]), + static_cast(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(data); - int index = polygonData->fVertices->count(); - *polygonData->fVertices->append() = GrPoint::Make(static_cast(coords[0]), - static_cast(coords[1])); - *outData = reinterpret_cast(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 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 indices; - GrTDArray 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(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); + -- 2.34.1