Edge antialiasing for convex shapes in Ganesh
This patch implements edge antialiasing for convex shapes, using the fragment
shader to compare against the edge equations for each triangle. Currently, it
only works for flat shaded primitives (i.e., it was not integrated into the
"active stages" path). The skia.gyp changes cause this code to be compiled into
SampleApp, but do not enable the tesselated path by default.
Notes: the SkOSWindow_Unix.cpp change is to silence a valgrind warning about
memcpy() with overlapping regions. The GrBinHashKey change is to avoid running
a two-pass hash (GrProgramDesc is now 52 bytes or so, exceeding the 32 byte
default size).
Review URL: http://codereview.appspot.com/4519054/
git-svn-id: http://skia.googlecode.com/svn/trunk@1314 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index 668dc3a..5e51513 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -478,6 +478,11 @@
///////////////////////////////////////////////////////////////////////////////
bool GrDrawTarget::canDisableBlend() const {
+ // If we're using edge antialiasing, we can't force blend off.
+ if (fCurrDrawState.fFlagBits & kAntialias_StateBit) {
+ return false;
+ }
+
if ((kOne_BlendCoeff == fCurrDrawState.fSrcBlend) &&
(kZero_BlendCoeff == fCurrDrawState.fDstBlend)) {
return true;
@@ -524,6 +529,13 @@
// ...then we disable blend.
return true;
}
+
+///////////////////////////////////////////////////////////////////////////////
+void GrDrawTarget::setEdgeAAData(const float edges[18]) {
+ memcpy(fCurrDrawState.fEdgeAAEdges, edges, sizeof(fCurrDrawState.fEdgeAAEdges));
+}
+
+
///////////////////////////////////////////////////////////////////////////////
void GrDrawTarget::drawRect(const GrRect& rect,
const GrMatrix* matrix,
diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp
index 1c0db25..ce4e7db 100644
--- a/gpu/src/GrGLProgram.cpp
+++ b/gpu/src/GrGLProgram.cpp
@@ -54,6 +54,7 @@
#define POS_ATTR_NAME "aPosition"
#define COL_ATTR_NAME "aColor"
#define COL_UNI_NAME "uColor"
+#define EDGES_UNI_NAME "uEdges"
#define COL_FILTER_UNI_NAME "uColorFilter"
static inline void tex_attr_name(int coordIdx, GrStringBuilder* s) {
@@ -269,6 +270,10 @@
break;
}
+ if (fProgramDesc.fUsesEdgeAA) {
+ segments.fFSUnis.append("uniform vec3 " EDGES_UNI_NAME "[6];\n");
+ }
+
if (fProgramDesc.fEmitsPointSize){
segments.fVSCode.append("\tgl_PointSize = 1.0;\n");
}
@@ -352,6 +357,23 @@
}
} else {
+ if (fProgramDesc.fUsesEdgeAA) {
+ // FIXME: put the a's in a loop
+ segments.fFSCode.append(
+ "\tvec3 pos = vec3(gl_FragCoord.xy, 1);\n"
+ "\tfloat a0 = clamp(dot(uEdges[0], pos), 0.0, 1.0);\n"
+ "\tfloat a1 = clamp(dot(uEdges[1], pos), 0.0, 1.0);\n"
+ "\tfloat a2 = clamp(dot(uEdges[2], pos), 0.0, 1.0);\n"
+ "\tfloat a3 = clamp(dot(uEdges[3], pos), 0.0, 1.0);\n"
+ "\tfloat a4 = clamp(dot(uEdges[4], pos), 0.0, 1.0);\n"
+ "\tfloat a5 = clamp(dot(uEdges[5], pos), 0.0, 1.0);\n"
+ "\tfloat edgeAlpha = min(min(a0 * a1, a2 * a3), a4 * a5);\n");
+ if (inColor.size()) {
+ inColor.append(" * edgeAlpha");
+ } else {
+ inColor = "vec4(edgeAlpha)";
+ }
+ }
// we may not have any incoming color
const char * incomingColor = (inColor.size() ? inColor.c_str()
: "vec4(1,1,1,1)");
@@ -583,6 +605,14 @@
GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni);
}
+ if (fProgramDesc.fUsesEdgeAA) {
+ programData->fUniLocations.fEdgesUni =
+ GR_GL(GetUniformLocation(progID, EDGES_UNI_NAME));
+ GrAssert(kUnusedUniform != programData->fUniLocations.fEdgesUni);
+ } else {
+ programData->fUniLocations.fEdgesUni = kUnusedUniform;
+ }
+
for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
StageUniLocations& locations = programData->fUniLocations.fStages[s];
if (fProgramDesc.fStages[s].fEnabled) {
diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h
index 0890238..1611ca2 100644
--- a/gpu/src/GrGLProgram.h
+++ b/gpu/src/GrGLProgram.h
@@ -108,6 +108,7 @@
} fColorType;
bool fEmitsPointSize;
+ bool fUsesEdgeAA;
SkXfermode::Mode fColorFilterXfermode;
@@ -163,11 +164,13 @@
struct UniLocations {
GrGLint fViewMatrixUni;
GrGLint fColorUni;
+ GrGLint fEdgesUni;
GrGLint fColorFilterUni;
StageUniLocations fStages[GrDrawTarget::kNumStages];
void reset() {
fViewMatrixUni = kUnusedUniform;
fColorUni = kUnusedUniform;
+ fEdgesUni = kUnusedUniform;
fColorFilterUni = kUnusedUniform;
for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
fStages[s].reset();
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
index 937c8ed..8965b06 100644
--- a/gpu/src/GrGpuGLShaders.cpp
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -36,7 +36,7 @@
#if GR_DEBUG
typedef GrBinHashKey<Entry, 4> ProgramHashKey; // Flex the dynamic allocation muscle in debug
#else
- typedef GrBinHashKey<Entry, 32> ProgramHashKey;
+ typedef GrBinHashKey<Entry, 64> ProgramHashKey;
#endif
class Entry : public ::GrNoncopyable {
@@ -396,6 +396,22 @@
}
}
+void GrGpuGLShaders::flushEdgeAAData() {
+ const int& uni = fProgramData->fUniLocations.fEdgesUni;
+ if (GrGLProgram::kUnusedUniform != uni) {
+ float edges[18];
+ memcpy(edges, fCurrDrawState.fEdgeAAEdges, sizeof(edges));
+ // Flip the edges in Y
+ float height = fCurrDrawState.fRenderTarget->height();
+ for (int i = 0; i < 6; ++i) {
+ float b = edges[i * 3 + 1];
+ edges[i * 3 + 1] = -b;
+ edges[i * 3 + 2] += b * height;
+ }
+ GR_GL(Uniform3fv(uni, 6, edges));
+ }
+}
+
static const float ONE_OVER_255 = 1.f / 255.f;
#define GR_COLOR_TO_VEC4(color) {\
@@ -500,6 +516,7 @@
this->flushTexelSize(s);
}
+ this->flushEdgeAAData();
resetDirtyFlags();
return true;
}
@@ -632,6 +649,8 @@
desc.fColorType = GrGLProgram::ProgramDesc::kAttribute_ColorType;
}
+ desc.fUsesEdgeAA = fCurrDrawState.fFlagBits & kEdgeAA_StateBit;
+
for (int s = 0; s < kNumStages; ++s) {
GrGLProgram::ProgramDesc::StageDesc& stage = desc.fStages[s];
diff --git a/gpu/src/GrGpuGLShaders.h b/gpu/src/GrGpuGLShaders.h
index a1bcaf0..bb3024f 100644
--- a/gpu/src/GrGpuGLShaders.h
+++ b/gpu/src/GrGpuGLShaders.h
@@ -63,6 +63,9 @@
// flushes the normalized texel size
void flushTexelSize(int stage);
+ // flushes the edges for edge AA
+ void flushEdgeAAData();
+
static void DeleteProgram(GrGLProgram::CachedData* programData);
void ProgramUnitTest();
diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp
index 8ed2c22..3993adb 100644
--- a/gpu/src/GrTesselatedPathRenderer.cpp
+++ b/gpu/src/GrTesselatedPathRenderer.cpp
@@ -55,8 +55,8 @@
{
PolygonData* polygonData = static_cast<PolygonData*>(data);
int index = polygonData->fVertices->count();
- *polygonData->fVertices->append() = GrPoint(static_cast<float>(coords[0]),
- static_cast<float>(coords[1]));
+ *polygonData->fVertices->append() = GrPoint::Make(static_cast<float>(coords[0]),
+ static_cast<float>(coords[1]));
*outData = reinterpret_cast<void*>(index);
}
@@ -83,6 +83,59 @@
GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
}
+class Edge {
+ public:
+ Edge() {}
+ Edge(float x, float y, float z) : fX(x), fY(y), fZ(z) {}
+ GrPoint intersect(const Edge& other) {
+ return GrPoint::Make(
+ (fY * other.fZ - other.fY * fZ) / (fX * other.fY - other.fX * fY),
+ (fX * other.fZ - other.fX * fZ) / (other.fX * fY - fX * other.fY));
+ }
+ float fX, fY, fZ;
+};
+
+typedef GrTDArray<Edge> EdgeArray;
+
+bool isCCW(const GrPoint* v)
+{
+ GrVec v1 = v[1] - v[0];
+ GrVec v2 = v[2] - v[1];
+ return v1.cross(v2) < 0;
+}
+
+static size_t computeEdgesAndOffsetVertices(const GrMatrix& matrix,
+ const GrMatrix& inverse,
+ GrPoint* vertices,
+ size_t numVertices,
+ EdgeArray* edges)
+{
+ GrPoint p = vertices[numVertices - 1];
+ matrix.mapPoints(&p, 1);
+ float sign = isCCW(vertices) ? -1.0f : 1.0f;
+ for (size_t i = 0; i < numVertices; ++i) {
+ GrPoint q = vertices[i];
+ matrix.mapPoints(&q, 1);
+ 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;
+ Edge edge(tangent.fX * scale,
+ tangent.fY * scale,
+ cross2 * scale + 0.5f);
+ *edges->append() = edge;
+ p = q;
+ }
+ Edge prev_edge = *edges->back();
+ for (size_t i = 0; i < edges->count(); ++i) {
+ Edge edge = edges->at(i);
+ vertices[i] = prev_edge.intersect(edge);
+ inverse.mapPoints(&vertices[i], 1);
+ prev_edge = edge;
+ }
+ return edges->count();
+}
+
void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
GrPathIter* path,
@@ -193,10 +246,10 @@
if (target->getViewInverse(&vmi)) {
vmi.mapRect(&bounds);
}
- *vert++ = GrPoint(bounds.fLeft, bounds.fTop);
- *vert++ = GrPoint(bounds.fLeft, bounds.fBottom);
- *vert++ = GrPoint(bounds.fRight, bounds.fBottom);
- *vert++ = GrPoint(bounds.fRight, bounds.fTop);
+ *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
+ *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
+ *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
+ *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
subpathVertCount[subpath++] = 4;
}
@@ -205,9 +258,40 @@
size_t count = vert - base;
+ if (count < 3) {
+ delete[] base;
+ return;
+ }
+
if (subpathCnt == 1 && !inverted && path->convexHint() == kConvex_ConvexHint) {
- target->setVertexSourceToArray(layout, base, count);
- target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
+ if (target->isAntialiasState()) {
+ target->enableState(GrDrawTarget::kEdgeAA_StateBit);
+ EdgeArray edges;
+ GrMatrix inverse, matrix = target->getViewMatrix();
+ target->getViewInverse(&inverse);
+
+ count = computeEdgesAndOffsetVertices(matrix, inverse, base, count, &edges);
+ GrPoint triangle[3];
+ triangle[0] = base[0];
+ Edge triangleEdges[6];
+ triangleEdges[0] = *edges.back();
+ triangleEdges[1] = edges[0];
+ for (size_t i = 1; i < count - 1; i++) {
+ triangle[1] = base[i];
+ triangle[2] = base[i + 1];
+ triangleEdges[2] = edges[i - 1];
+ triangleEdges[3] = edges[i];
+ triangleEdges[4] = edges[i];
+ triangleEdges[5] = edges[i + 1];
+ target->setVertexSourceToArray(layout, triangle, 3);
+ target->setEdgeAAData(&triangleEdges[0].fX);
+ target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
+ }
+ target->disableState(GrDrawTarget::kEdgeAA_StateBit);
+ } else {
+ target->setVertexSourceToArray(layout, base, count);
+ target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
+ }
delete[] base;
return;
}
@@ -241,7 +325,7 @@
int end = start + subpathVertCount[sp];
for (; i < end; ++i) {
double* inVertex = &inVertices[i * 3];
- *vertices.append() = GrPoint(inVertex[0], inVertex[1]);
+ *vertices.append() = GrPoint::Make(inVertex[0], inVertex[1]);
internal_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
}
internal_gluTessEndContour(tess);
@@ -275,3 +359,14 @@
const GrPoint* translate) {
GrAlwaysAssert(!"multipass stencil should not be needed");
}
+
+bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
+ GrPathIter* path,
+ GrPathFill fill) {
+ int subpathCnt = 0;
+ int tol = GrPathUtils::gTolerance;
+ GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
+ return (subpathCnt == 1 &&
+ !IsFillInverted(fill) &&
+ path->convexHint() == kConvex_ConvexHint);
+}