fix interior issues in convex path renderer

git-svn-id: http://skia.googlecode.com/svn/trunk@3078 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
bsalomon@google.com 2012-01-21 14:48:36 +00:00
parent 13a847a82c
commit 495e210eb1
4 changed files with 255 additions and 226 deletions

View File

@ -14,7 +14,7 @@ namespace skiagm {
class ConvexPathsGM : public GM {
public:
ConvexPathsGM() {
this->setBGColor(0xFFDDDDDD);
this->setBGColor(0xFFFFFFFF);
this->makePaths();
}
@ -63,10 +63,6 @@ protected:
100 * SK_Scalar1),
SkPath::kCCW_Direction);
fPaths.push_back().addRect(0, 0,
100 * SK_Scalar1, 100 * SK_Scalar1,
SkPath::kCCW_Direction);
fPaths.push_back().addRoundRect(SkRect::MakeXYWH(0, 0,
SK_Scalar1 * 100,
SK_Scalar1 * 100),
@ -78,6 +74,12 @@ protected:
SK_Scalar1 * 100),
20 * SK_Scalar1, 40 * SK_Scalar1,
SkPath::kCCW_Direction);
// shallow diagonals
fPaths.push_back().lineTo(100 * SK_Scalar1, SK_Scalar1);
fPaths.back().lineTo(98 * SK_Scalar1, 100 * SK_Scalar1);
fPaths.back().lineTo(3 * SK_Scalar1, 96 * SK_Scalar1);
/*
It turns out arcTos are not automatically marked as convex and they
may in fact be ever so slightly concave.
@ -87,6 +89,14 @@ protected:
25 * SK_Scalar1, 130 * SK_Scalar1, false);
*/
// cubics
fPaths.push_back().cubicTo( 1 * SK_Scalar1, 1 * SK_Scalar1,
10 * SK_Scalar1, 90 * SK_Scalar1,
0 * SK_Scalar1, 100 * SK_Scalar1);
fPaths.push_back().cubicTo(100 * SK_Scalar1, 50 * SK_Scalar1,
20 * SK_Scalar1, 100 * SK_Scalar1,
0 * SK_Scalar1, 0 * SK_Scalar1);
// point degenerate
fPaths.push_back().moveTo(50 * SK_Scalar1, 50 * SK_Scalar1);
fPaths.back().lineTo(50 * SK_Scalar1, 50 * SK_Scalar1);
@ -117,7 +127,7 @@ protected:
paint.setAntiAlias(true);
SkRandom rand;
canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1);
for (int i = 0 ; i < fPaths.count(); ++i) {
for (int i = 0; i < fPaths.count(); ++i) {
canvas->save();
// position the path, and make it at off-integer coords.
canvas->translate(SK_Scalar1 * 200 * (i % 5) + SK_Scalar1 / 4,

View File

@ -29,18 +29,28 @@ bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget::Caps& targetCaps,
namespace {
struct Segment {
enum {
kLine,
kQuad
} fType;
// line uses a, quad uses a and b (first point comes from prev. segment)
GrPoint fA, fB;
// normal to edge ending at a and b
GrVec fANorm, fBNorm;
// mid vector at a that splits angle with previous edge
GrVec fPrevMid;
// line uses one pt, quad uses 2 pts
GrPoint fPts[2];
// normal to edge ending at each pt
GrVec fNorms[2];
// is the corner where the previous segment meets this segment
// sharp. If so, fMid is a normalized bisector facing outward.
GrVec fMid;
int countPoints() {
return (kLine == fType) ? 1 : 2;
}
const SkPoint& endPt() const {
return (kLine == fType) ? fPts[0] : fPts[1];
};
const SkPoint& endNorm() const {
return (kLine == fType) ? fNorms[0] : fNorms[1];
};
};
typedef SkTArray<Segment, true> SegmentArray;
@ -50,7 +60,6 @@ bool is_path_degenerate(const GrPath& path) {
if (n < 3) {
return true;
}
// compute a line from the first two points that are not equal, look for
// a third pt that is off the line.
static const SkScalar TOL = (SK_Scalar1 / 16);
@ -80,12 +89,84 @@ bool is_path_degenerate(const GrPath& path) {
return true;
}
void center_of_mass(const SegmentArray& segments, SkPoint* c) {
GrScalar area = 0;
SkPoint center;
center.set(0, 0);
int count = segments.count();
for (int i = 0; i < count; ++i) {
const SkPoint& pi = segments[i].endPt();
int j = (i + 1) % count;
const SkPoint& pj = segments[j].endPt();
GrScalar t = GrMul(pi.fX, pj.fY) - GrMul(pj.fX, pi.fY);
area += t;
center.fX += (pi.fX + pj.fX) * t;
center.fY += (pi.fY + pj.fY) * t;
}
area *= 3;
area = GrScalarDiv(GR_Scalar1, area);
center.fX = GrScalarMul(center.fX, area);
center.fY = GrScalarMul(center.fY, area);
*c = center;
}
void compute_vectors(SegmentArray* segments,
SkPoint* fanPt,
int* vCount,
int* iCount) {
center_of_mass(*segments, fanPt);
int count = segments->count();
// figure out which way the normals should point
GrPoint::Side normSide;
fanPt->distanceToLineBetweenSqd((*segments)[0].endPt(),
(*segments)[1].endPt(),
&normSide);
*vCount = 0;
*iCount = 0;
// compute normals at all points
for (int a = 0; a < count; ++a) {
const Segment& sega = (*segments)[a];
int b = (a + 1) % count;
Segment& segb = (*segments)[b];
const GrPoint* prevPt = &sega.endPt();
int n = segb.countPoints();
for (int p = 0; p < n; ++p) {
segb.fNorms[p] = segb.fPts[p] - *prevPt;
segb.fNorms[p].normalize();
segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
prevPt = &segb.fPts[p];
}
if (Segment::kLine == segb.fType) {
*vCount += 5;
*iCount += 9;
} else {
*vCount += 6;
*iCount += 12;
}
}
// compute mid-vectors where segments meet. TODO: Detect shallow corners
// and leave out the wedges and close gaps by stitching segments together.
for (int a = 0; a < count; ++a) {
const Segment& sega = (*segments)[a];
int b = (a + 1) % count;
Segment& segb = (*segments)[b];
segb.fMid = segb.fNorms[0] + sega.endNorm();
segb.fMid.normalize();
// corner wedges
*vCount += 4;
*iCount += 6;
}
}
bool get_segments(const GrPath& path,
SegmentArray* segments,
int* quadCnt,
int* lineCnt) {
*quadCnt = 0;
*lineCnt = 0;
SkPoint* fanPt,
int* vCount,
int* iCount) {
SkPath::Iter iter(path, true);
// This renderer overemphasises very thin path regions. We use the distance
// to the path from the sample to compute coverage. Every pixel intersected
@ -104,16 +185,14 @@ bool get_segments(const GrPath& path,
case kLine_PathCmd: {
segments->push_back();
segments->back().fType = Segment::kLine;
segments->back().fA = pts[1];
++(*lineCnt);
segments->back().fPts[0] = pts[1];
break;
}
case kQuadratic_PathCmd:
segments->push_back();
segments->back().fType = Segment::kQuad;
segments->back().fA = pts[1];
segments->back().fB = pts[2];
++(*quadCnt);
segments->back().fPts[0] = pts[1];
segments->back().fPts[1] = pts[2];
break;
case kCubic_PathCmd: {
SkSTArray<15, SkPoint, true> quads;
@ -122,14 +201,13 @@ bool get_segments(const GrPath& path,
for (int q = 0; q < count; q += 3) {
segments->push_back();
segments->back().fType = Segment::kQuad;
segments->back().fA = quads[q + 1];
segments->back().fB = quads[q + 2];
++(*quadCnt);
segments->back().fPts[0] = quads[q + 1];
segments->back().fPts[1] = quads[q + 2];
}
break;
};
case kEnd_PathCmd:
GrAssert(*quadCnt + *lineCnt == segments->count());
compute_vectors(segments, fanPt, vCount, iCount);
return true;
default:
break;
@ -139,201 +217,139 @@ bool get_segments(const GrPath& path,
struct QuadVertex {
GrPoint fPos;
union {
GrPoint fQuadUV;
GrScalar fEdge[4];
};
GrPoint fUV;
GrScalar fD0;
GrScalar fD1;
};
void get_counts(int quadCount, int lineCount, int* vCount, int* iCount) {
*vCount = 9 * lineCount + 11 * quadCount;
*iCount = 15 * lineCount + 24 * quadCount;
}
// This macro can be defined for visual debugging purposes. It exagerates the AA
// smear at the edges by increasing the size of the extruded geometry used for
// AA. However, the coverage value computed in the shader will still go to zero
// at distance > .5 outside the curves. So, the shader code has be modified as
// well to stretch out the AA smear.
//#define STRETCH_AA
#define STRETCH_FACTOR (20 * SK_Scalar1)
void create_vertices(SegmentArray* segments,
const GrPoint& fanPt,
QuadVertex* verts,
uint16_t* idxs) {
int count = segments->count();
GrAssert(count > 1);
int prevS = count - 1;
const Segment& lastSeg = (*segments)[prevS];
// walk the segments and compute normals to each edge and
// bisectors at vertices. The loop relies on having the end point and normal
// from previous segment so we first compute that. Also, we determine
// whether normals point left or right to face outside the path.
GrVec prevPt;
GrPoint prevPrevPt;
GrVec prevNorm;
if (Segment::kLine == lastSeg.fType) {
prevPt = lastSeg.fA;
const Segment& secondLastSeg = (*segments)[prevS - 1];
prevPrevPt = (Segment::kLine == secondLastSeg.fType) ?
secondLastSeg.fA :
secondLastSeg.fB;
} else {
prevPt = lastSeg.fB;
prevPrevPt = lastSeg.fA;
}
GrVec::Side outside;
// we will compute our edge vectors so that they are pointing along the
// direction in which we are iterating the path. So here we take an opposite
// vector and get the side that the fan pt lies relative to it.
fanPt.distanceToLineBetweenSqd(prevPrevPt, prevPt, &outside);
prevNorm = prevPt - prevPrevPt;
prevNorm.normalize();
prevNorm.setOrthog(prevNorm, outside);
#ifdef STRETCH_AA
prevNorm.scale(STRETCH_FACTOR);
#endif
// compute the normals and bisectors
for (int s = 0; s < count; ++s, ++prevS) {
Segment& curr = (*segments)[s];
GrVec currVec = curr.fA - prevPt;
currVec.normalize();
curr.fANorm.setOrthog(currVec, outside);
#ifdef STRETCH_AA
curr.fANorm.scale(STRETCH_FACTOR);
#endif
curr.fPrevMid = prevNorm + curr.fANorm;
curr.fPrevMid.normalize();
#ifdef STRETCH_AA
curr.fPrevMid.scale(STRETCH_FACTOR);
#endif
if (Segment::kLine == curr.fType) {
prevPt = curr.fA;
prevNorm = curr.fANorm;
} else {
currVec = curr.fB - curr.fA;
currVec.normalize();
curr.fBNorm.setOrthog(currVec, outside);
#ifdef STRETCH_AA
curr.fBNorm.scale(STRETCH_FACTOR);
#endif
prevPt = curr.fB;
prevNorm = curr.fBNorm;
}
}
// compute the vertices / indices
if (Segment::kLine == lastSeg.fType) {
prevPt = lastSeg.fA;
prevNorm = lastSeg.fANorm;
} else {
prevPt = lastSeg.fB;
prevNorm = lastSeg.fBNorm;
}
void create_vertices(const SegmentArray& segments,
const SkPoint& fanPt,
QuadVertex* verts,
uint16_t* idxs) {
int v = 0;
int i = 0;
for (int s = 0; s < count; ++s, ++prevS) {
Segment& curr = (*segments)[s];
verts[v + 0].fPos = prevPt;
verts[v + 1].fPos = prevPt + prevNorm;
verts[v + 2].fPos = prevPt + curr.fPrevMid;
verts[v + 3].fPos = prevPt + curr.fANorm;
verts[v + 0].fQuadUV.set(0, 0);
verts[v + 1].fQuadUV.set(0, -SK_Scalar1);
verts[v + 2].fQuadUV.set(0, -SK_Scalar1);
verts[v + 3].fQuadUV.set(0, -SK_Scalar1);
int count = segments.count();
for (int a = 0; a < count; ++a) {
const Segment& sega = segments[a];
int b = (a + 1) % count;
const Segment& segb = segments[b];
// FIXME: These tris are inset in the 1 unit arc around the corner
verts[v + 0].fPos = sega.endPt();
verts[v + 1].fPos = verts[v + 0].fPos + sega.endNorm();
verts[v + 2].fPos = verts[v + 0].fPos + segb.fMid;
verts[v + 3].fPos = verts[v + 0].fPos + segb.fNorms[0];
verts[v + 0].fUV.set(0,0);
verts[v + 1].fUV.set(0,-SK_Scalar1);
verts[v + 2].fUV.set(0,-SK_Scalar1);
verts[v + 3].fUV.set(0,-SK_Scalar1);
verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
idxs[i + 0] = v + 0;
idxs[i + 1] = v + 1;
idxs[i + 2] = v + 2;
idxs[i + 1] = v + 2;
idxs[i + 2] = v + 1;
idxs[i + 3] = v + 0;
idxs[i + 4] = v + 2;
idxs[i + 5] = v + 3;
idxs[i + 4] = v + 3;
idxs[i + 5] = v + 2;
v += 4;
i += 6;
if (Segment::kLine == curr.fType) {
if (Segment::kLine == segb.fType) {
verts[v + 0].fPos = fanPt;
verts[v + 1].fPos = prevPt;
verts[v + 2].fPos = curr.fA;
verts[v + 3].fPos = prevPt + curr.fANorm;
verts[v + 4].fPos = curr.fA + curr.fANorm;
GrScalar lineC = -curr.fANorm.dot(curr.fA);
GrScalar fanDist = curr.fANorm.dot(fanPt) - lineC;
verts[v + 0].fQuadUV.set(0, SkScalarAbs(fanDist));
verts[v + 1].fQuadUV.set(0, 0);
verts[v + 2].fQuadUV.set(0, 0);
verts[v + 3].fQuadUV.set(0, -GR_Scalar1);
verts[v + 4].fQuadUV.set(0, -GR_Scalar1);
verts[v + 1].fPos = sega.endPt();
verts[v + 2].fPos = segb.fPts[0];
verts[v + 3].fPos = verts[v + 1].fPos + segb.fNorms[0];
verts[v + 4].fPos = verts[v + 2].fPos + segb.fNorms[0];
// we draw the line edge as a degenerate quad (u is 0, v is the
// signed distance to the edge)
GrScalar dist = fanPt.distanceToLineBetween(verts[v + 1].fPos,
verts[v + 2].fPos);
verts[v + 0].fUV.set(0, dist);
verts[v + 1].fUV.set(0, 0);
verts[v + 2].fUV.set(0, 0);
verts[v + 3].fUV.set(0, -SK_Scalar1);
verts[v + 4].fUV.set(0, -SK_Scalar1);
verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
verts[v + 4].fD0 = verts[v + 4].fD1 = -SK_Scalar1;
idxs[i + 0] = v + 0;
idxs[i + 1] = v + 1;
idxs[i + 2] = v + 2;
idxs[i + 3] = v + 1;
idxs[i + 4] = v + 3;
idxs[i + 5] = v + 4;
idxs[i + 6] = v + 1;
idxs[i + 7] = v + 4;
idxs[i + 1] = v + 2;
idxs[i + 2] = v + 1;
idxs[i + 3] = v + 3;
idxs[i + 4] = v + 1;
idxs[i + 5] = v + 2;
idxs[i + 6] = v + 4;
idxs[i + 7] = v + 3;
idxs[i + 8] = v + 2;
i += 9;
v += 5;
prevPt = curr.fA;
prevNorm = curr.fANorm;
i += 9;
} else {
GrVec splitVec = curr.fANorm + curr.fBNorm;
splitVec.normalize();
#ifdef STRETCH_AA
splitVec.scale(STRETCH_FACTOR);
#endif
GrPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
verts[v + 0].fPos = prevPt;
verts[v + 1].fPos = curr.fA;
verts[v + 2].fPos = curr.fB;
verts[v + 3].fPos = fanPt;
verts[v + 4].fPos = prevPt + curr.fANorm;
verts[v + 5].fPos = curr.fA + splitVec;
verts[v + 6].fPos = curr.fB + curr.fBNorm;
GrVec midVec = segb.fNorms[0] + segb.fNorms[1];
midVec.normalize();
verts[v + 0].fPos = fanPt;
verts[v + 1].fPos = qpts[0];
verts[v + 2].fPos = qpts[2];
verts[v + 3].fPos = qpts[0] + segb.fNorms[0];
verts[v + 4].fPos = qpts[2] + segb.fNorms[1];
verts[v + 5].fPos = qpts[1] + midVec;
GrScalar c = segb.fNorms[0].dot(qpts[0]);
verts[v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
verts[v + 1].fD0 = 0.f;
verts[v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
verts[v + 3].fD0 = -GR_ScalarMax/100;
verts[v + 4].fD0 = -GR_ScalarMax/100;
verts[v + 5].fD0 = -GR_ScalarMax/100;
c = segb.fNorms[1].dot(qpts[2]);
verts[v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
verts[v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
verts[v + 2].fD1 = 0.f;
verts[v + 3].fD1 = -GR_ScalarMax/100;
verts[v + 4].fD1 = -GR_ScalarMax/100;
verts[v + 5].fD1 = -GR_ScalarMax/100;
verts[v + 0].fQuadUV.set(0, 0);
verts[v + 1].fQuadUV.set(GR_ScalarHalf, 0);
verts[v + 2].fQuadUV.set(GR_Scalar1, GR_Scalar1);
GrMatrix toUV;
GrPoint pts[] = {prevPt, curr.fA, curr.fB};
GrPathUtils::quadDesignSpaceToUVCoordsMatrix(pts, &toUV);
toUV.mapPointsWithStride(&verts[v + 3].fQuadUV,
&verts[v + 3].fPos,
sizeof(QuadVertex), 4);
GrPathUtils::quadDesignSpaceToUVCoordsMatrix(qpts, &toUV);
toUV.mapPointsWithStride(&verts[v].fUV,
&verts[v].fPos,
sizeof(QuadVertex),
6);
idxs[i + 0] = v + 3;
idxs[i + 1] = v + 0;
idxs[i + 2] = v + 1;
idxs[i + 3] = v + 3;
idxs[i + 4] = v + 1;
idxs[i + 5] = v + 2;
idxs[i + 6] = v + 0;
idxs[i + 7] = v + 4;
idxs[i + 8] = v + 1;
idxs[i + 9] = v + 4;
idxs[i + 10] = v + 1;
idxs[i + 11] = v + 5;
idxs[i + 12] = v + 5;
idxs[i + 13] = v + 1;
idxs[i + 14] = v + 2;
idxs[i + 15] = v + 5;
idxs[i + 16] = v + 2;
idxs[i + 17] = v + 6;
idxs[i + 0] = v + 3;
idxs[i + 1] = v + 1;
idxs[i + 2] = v + 2;
idxs[i + 3] = v + 4;
idxs[i + 4] = v + 3;
idxs[i + 5] = v + 2;
i += 18;
v += 7;
prevPt = curr.fB;
prevNorm = curr.fBNorm;
idxs[i + 6] = v + 5;
idxs[i + 7] = v + 3;
idxs[i + 8] = v + 4;
idxs[i + 9] = v + 0;
idxs[i + 10] = v + 2;
idxs[i + 11] = v + 1;
v += 6;
i += 12;
}
}
}
@ -357,13 +373,9 @@ void GrAAConvexPathRenderer::drawPath(GrDrawState::StageMask stageMask) {
}
drawState->setViewMatrix(GrMatrix::I());
SkPath path;
fPath->transform(vm, &path);
SkPoint fanPt = {path.getBounds().centerX(),
path.getBounds().centerY()};
GrVertexLayout layout = 0;
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
if ((1 << s) & stageMask) {
@ -375,15 +387,13 @@ void GrAAConvexPathRenderer::drawPath(GrDrawState::StageMask stageMask) {
QuadVertex *verts;
uint16_t* idxs;
int nQuads;
int nLines;
SegmentArray segments;
if (!get_segments(path, &segments, &nQuads, &nLines)) {
return;
}
int vCount;
int iCount;
get_counts(nQuads, nLines, &vCount, &iCount);
SegmentArray segments;
SkPoint fanPt;
if (!get_segments(path, &segments, &fanPt, &vCount, &iCount)) {
return;
}
if (!fTarget->reserveVertexSpace(layout,
vCount,
@ -395,7 +405,7 @@ void GrAAConvexPathRenderer::drawPath(GrDrawState::StageMask stageMask) {
return;
}
create_vertices(&segments, fanPt, verts, idxs);
create_vertices(segments, fanPt, verts, idxs);
drawState->setVertexEdgeType(GrDrawState::kQuad_EdgeType);
fTarget->drawIndexed(kTriangles_PrimitiveType,

View File

@ -18,9 +18,7 @@ void GrPathRenderer::AddPathRenderers(GrContext* ctx,
if (GrPathRenderer* pr = GrAAHairLinePathRenderer::Create(ctx)) {
chain->addPathRenderer(pr)->unref();
}
// Disabled for now. Need to fix issue where some hairlines don't
// wind up going to the hairline renderer and get rendered by this
// PR looking speckly.
// Disabled for now.
//chain->addPathRenderer(new GrAAConvexPathRenderer())->unref();
}
}

View File

@ -528,23 +528,34 @@ void GrGLProgram::genEdgeCoverage(const GrGLInterface* gl,
if (GrDrawState::kHairLine_EdgeType == fProgramDesc.fVertexEdgeType) {
segments->fFSCode.appendf("\tfloat edgeAlpha = abs(dot(vec3(gl_FragCoord.xy,1), %s.xyz));\n", fsName);
segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
} else if (GrDrawState::kQuad_EdgeType == fProgramDesc.fVertexEdgeType) {
segments->fFSCode.appendf("\tfloat edgeAlpha;\n");
// keep the derivative instructions outside the conditional
segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
segments->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
segments->fFSCode.appendf("\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName);
// today we know z and w are in device space. We could use derivatives
segments->fFSCode.appendf("\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName, fsName);
segments->fFSCode.append ("\t} else {\n");
segments->fFSCode.appendf("\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
"\t\t 2.0*%s.x*duvdy.x - duvdy.y);\n",
fsName, fsName);
segments->fFSCode.appendf("\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
segments->fFSCode.appendf("\t\tedgeAlpha = clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n"
"\t}\n");
if (gl->supportsES2()) {
segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
}
} else {
GrAssert(GrDrawState::kHairQuad_EdgeType == fProgramDesc.fVertexEdgeType ||
GrDrawState::kQuad_EdgeType == fProgramDesc.fVertexEdgeType);
// for now we know we're not in perspective, so we could compute this
// per-quadratic rather than per pixel
GrAssert(GrDrawState::kHairQuad_EdgeType == fProgramDesc.fVertexEdgeType);
segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
segments->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
segments->fFSCode.appendf("\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
"\t 2.0*%s.x*duvdy.x - duvdy.y);\n",
fsName, fsName);
segments->fFSCode.appendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
if (GrDrawState::kQuad_EdgeType == fProgramDesc.fVertexEdgeType) {
segments->fFSCode.append("\tedgeAlpha = clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n");
} else {
segments->fFSCode.append("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n");
segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
}
segments->fFSCode.append("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n");
segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
if (gl->supportsES2()) {
segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
}