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:
parent
af9d9c2fc7
commit
129b8e3237
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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',
|
||||
|
146
samplecode/SampleConcavePaths.cpp
Normal file
146
samplecode/SampleConcavePaths.cpp
Normal 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);
|
||||
|
Loading…
Reference in New Issue
Block a user