aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-08-30 18:13:44 +0000
committerGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-08-30 18:13:44 +0000
commitaeb2160b1dd34f8e640e8e56544fe407d4ff6311 (patch)
tree029e885629354c8968e74c944dd7abc31adc9522
parentdec9f2d3517c5b78d3bff0b965a7e07042b19875 (diff)
Add GrAAHairLinePathRenderer
Review URL: http://codereview.appspot.com/4926045 git-svn-id: http://skia.googlecode.com/svn/trunk@2196 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--gpu/src/GrAAHairLinePathRenderer.cpp633
-rw-r--r--gpu/src/GrAAHairLinePathRenderer.h62
-rw-r--r--gpu/src/GrAddPathRenderers_aahairline.cpp20
-rw-r--r--gpu/src/GrContext.cpp7
-rw-r--r--gpu/src/GrDefaultPathRenderer.h3
-rw-r--r--gpu/src/GrDrawTarget.cpp87
-rw-r--r--gpu/src/GrDrawTarget.h74
-rw-r--r--gpu/src/GrGLProgram.cpp24
-rw-r--r--gpu/src/GrGLProgram.h4
-rw-r--r--gpu/src/GrGpuGLFixed.cpp11
-rw-r--r--gpu/src/GrGpuGLShaders.cpp60
-rw-r--r--gpu/src/GrPathRenderer.h17
-rw-r--r--gpu/src/GrPathRendererChain.cpp2
-rw-r--r--gpu/src/GrTesselatedPathRenderer.cpp3
-rw-r--r--gpu/src/GrTesselatedPathRenderer.h3
-rw-r--r--gyp/SampleApp.gyp3
-rw-r--r--gyp/gpu.gyp4
-rw-r--r--samplecode/SampleHairCurves.cpp112
18 files changed, 1082 insertions, 47 deletions
diff --git a/gpu/src/GrAAHairLinePathRenderer.cpp b/gpu/src/GrAAHairLinePathRenderer.cpp
new file mode 100644
index 0000000000..40722b8eb2
--- /dev/null
+++ b/gpu/src/GrAAHairLinePathRenderer.cpp
@@ -0,0 +1,633 @@
+#include "GrAAHairLinePathRenderer.h"
+
+#include "GrContext.h"
+#include "GrGpu.h"
+#include "GrIndexBuffer.h"
+#include "SkGeometry.h"
+#include "SkTemplates.h"
+
+namespace {
+// quadratics are rendered as 5-sided polys in order to bound the
+// AA stroke around the center-curve. See comments in push_quad_index_buffer and
+// bloat_quad.
+static const int kVertsPerQuad = 5;
+static const int kIdxsPerQuad = 9;
+
+static const int kVertsPerLineSeg = 4;
+static const int kIdxsPerLineSeg = 6;
+
+static const int kNumQuadsInIdxBuffer = 256;
+static const size_t kQuadIdxSBufize = kIdxsPerQuad *
+ sizeof(uint16_t) *
+ kNumQuadsInIdxBuffer;
+
+bool push_quad_index_data(GrIndexBuffer* qIdxBuffer) {
+ uint16_t* data = (uint16_t*) qIdxBuffer->lock();
+ bool tempData = NULL == data;
+ if (tempData) {
+ data = new uint16_t[kNumQuadsInIdxBuffer * kIdxsPerQuad];
+ }
+ for (int i = 0; i < kNumQuadsInIdxBuffer; ++i) {
+
+ // Each quadratic is rendered as a five sided polygon. This poly bounds
+ // the quadratic's bounding triangle but has been expanded so that the
+ // 1-pixel wide area around the curve is inside the poly.
+ // If a,b,c are the original control points then the poly a0,b0,c0,c1,a1
+ // that is rendered would look like this:
+ // b0
+ // b
+ //
+ // a0 c0
+ // a c
+ // a1 c1
+ // Each is drawn as three triagnles specified by these 9 indices:
+ int baseIdx = i * kIdxsPerQuad;
+ uint16_t baseVert = (uint16_t)(i * kVertsPerQuad);
+ data[0 + baseIdx] = baseVert + 0; // a0
+ data[1 + baseIdx] = baseVert + 1; // a1
+ data[2 + baseIdx] = baseVert + 2; // b0
+ data[3 + baseIdx] = baseVert + 2; // b0
+ data[4 + baseIdx] = baseVert + 4; // c1
+ data[5 + baseIdx] = baseVert + 3; // c0
+ data[6 + baseIdx] = baseVert + 1; // a1
+ data[7 + baseIdx] = baseVert + 4; // c1
+ data[8 + baseIdx] = baseVert + 2; // b0
+ }
+ if (tempData) {
+ bool ret = qIdxBuffer->updateData(data, kQuadIdxSBufize);
+ delete[] data;
+ return ret;
+ } else {
+ qIdxBuffer->unlock();
+ return true;
+ }
+}
+}
+
+GrPathRenderer* GrAAHairLinePathRenderer::Create(GrContext* context) {
+ if (CanBeUsed(context)) {
+ const GrIndexBuffer* lIdxBuffer = context->getQuadIndexBuffer();
+ if (NULL == lIdxBuffer) {
+ return NULL;
+ }
+ GrGpu* gpu = context->getGpu();
+ GrIndexBuffer* qIdxBuf = gpu->createIndexBuffer(kQuadIdxSBufize, false);
+ SkAutoTUnref<GrIndexBuffer> qIdxBuffer(qIdxBuf); // cons will take a ref
+ if (NULL == qIdxBuf ||
+ !push_quad_index_data(qIdxBuffer.get())) {
+ return NULL;
+ }
+ return new GrAAHairLinePathRenderer(context,
+ lIdxBuffer,
+ qIdxBuf);
+ } else {
+ return NULL;
+ }
+}
+
+bool GrAAHairLinePathRenderer::CanBeUsed(const GrContext* context) {
+ return context->getGpu()->supportsShaderDerivatives();
+
+}
+
+GrAAHairLinePathRenderer::GrAAHairLinePathRenderer(
+ const GrContext* context,
+ const GrIndexBuffer* linesIndexBuffer,
+ const GrIndexBuffer* quadsIndexBuffer) {
+ GrAssert(CanBeUsed(context));
+ fLinesIndexBuffer = linesIndexBuffer;
+ linesIndexBuffer->ref();
+ fQuadsIndexBuffer = quadsIndexBuffer;
+ quadsIndexBuffer->ref();
+ this->resetGeom();
+}
+
+GrAAHairLinePathRenderer::~GrAAHairLinePathRenderer() {
+ fLinesIndexBuffer->unref();
+ fQuadsIndexBuffer->unref();
+}
+
+bool GrAAHairLinePathRenderer::supportsAA(GrDrawTarget* target,
+ const SkPath& path,
+ GrPathFill fill) {
+ return kHairLine_PathFill == fill;
+}
+
+bool GrAAHairLinePathRenderer::canDrawPath(const GrDrawTarget* target,
+ const SkPath& path,
+ GrPathFill fill) const {
+ // TODO: support perspective
+ return kHairLine_PathFill == fill &&
+ !target->getViewMatrix().hasPerspective();
+}
+
+void GrAAHairLinePathRenderer::pathWillClear() {
+ this->resetGeom();
+}
+
+void GrAAHairLinePathRenderer::resetGeom() {
+ fPreviousStages = ~0;
+ fPreviousRTHeight = ~0;
+ fPreviousViewMatrix = GrMatrix::InvalidMatrix();
+ fLineSegmentCnt = 0;
+ fQuadCnt = 0;
+ if ((fQuadCnt || fLineSegmentCnt) && NULL != fTarget) {
+ fTarget->resetVertexSource();
+ }
+}
+
+namespace {
+
+typedef GrTArray<SkPoint, true> PtArray;
+typedef GrTArray<int, true> IntArray;
+
+/**
+ * We convert cubics to quadratics (for now).
+ */
+void convert_noninflect_cubic_to_quads(const SkPoint p[4],
+ PtArray* quads,
+ int sublevel = 0) {
+ SkVector ab = p[1];
+ ab -= p[0];
+ SkVector dc = p[2];
+ dc -= p[3];
+
+ static const SkScalar gLengthScale = 3 * SK_Scalar1 / 2;
+ static const SkScalar gDistanceSqdTol = 2 * SK_Scalar1;
+ static const int kMaxSubdivs = 30;
+
+ ab.scale(gLengthScale);
+ dc.scale(gLengthScale);
+
+ SkVector c0 = p[0];
+ c0 += ab;
+ SkVector c1 = p[3];
+ c1 += dc;
+
+ SkScalar dSqd = c0.distanceToSqd(c1);
+ if (sublevel > kMaxSubdivs || dSqd <= gDistanceSqdTol) {
+ SkPoint cAvg = c0;
+ cAvg += c1;
+ cAvg.scale(SK_ScalarHalf);
+
+ int idx = quads->count();
+ quads->push_back_n(3);
+ (*quads)[idx+0] = p[0];
+ (*quads)[idx+1] = cAvg;
+ (*quads)[idx+2] = p[3];
+
+ return;
+ } else {
+ SkPoint choppedPts[7];
+ SkChopCubicAtHalf(p, choppedPts);
+ convert_noninflect_cubic_to_quads(choppedPts + 0, quads, sublevel + 1);
+ convert_noninflect_cubic_to_quads(choppedPts + 3, quads, sublevel + 1);
+ }
+}
+
+void convert_cubic_to_quads(const SkPoint p[4], PtArray* quads) {
+ SkPoint chopped[13];
+ int count = SkChopCubicAtInflections(p, chopped);
+
+ for (int i = 0; i < count; ++i) {
+ SkPoint* cubic = chopped + 3*i;
+ convert_noninflect_cubic_to_quads(cubic, quads);
+ }
+}
+
+// Takes 178th time of logf on Z600 / VC2010
+int get_float_exp(float x) {
+ GR_STATIC_ASSERT(sizeof(int) == sizeof(float));
+#if GR_DEBUG
+ static bool tested;
+ if (!tested) {
+ tested = true;
+ GrAssert(get_float_exp(0.25f) == -2);
+ GrAssert(get_float_exp(0.3f) == -2);
+ GrAssert(get_float_exp(0.5f) == -1);
+ GrAssert(get_float_exp(1.f) == 0);
+ GrAssert(get_float_exp(2.f) == 1);
+ GrAssert(get_float_exp(2.5f) == 1);
+ GrAssert(get_float_exp(8.f) == 3);
+ GrAssert(get_float_exp(100.f) == 6);
+ GrAssert(get_float_exp(1000.f) == 9);
+ GrAssert(get_float_exp(1024.f) == 10);
+ GrAssert(get_float_exp(3000000.f) == 21);
+ }
+#endif
+ return (((*(int*)&x) & 0x7f800000) >> 23) - 127;
+}
+
+// we subdivide the quads to avoid huge overfill
+// if it returns -1 then should be drawn as lines
+int num_quad_subdivs(const SkPoint p[3]) {
+ static const SkScalar gDegenerateToLineTol = SK_Scalar1;
+
+ GrScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
+ if (dsqd < gDegenerateToLineTol*gDegenerateToLineTol) {
+ return -1;
+ }
+ if (p[2].distanceToLineBetweenSqd(p[1],p[0]) <
+ gDegenerateToLineTol*gDegenerateToLineTol) {
+ return -1;
+ }
+
+ static const int kMaxSub = 4;
+ // tolerance of triangle height in pixels
+ // tuned on windows Quadro FX 380 / Z600
+ // trade off of fill vs cpu time on verts
+ // maybe different when do this using gpu (geo or tess shaders)
+ static const SkScalar gSubdivTol = 175 * SK_Scalar1;
+
+ if (dsqd <= gSubdivTol*gSubdivTol) {
+ return 0;
+ } else {
+ // subdividing the quad reduces d by 4. so we want x = log4(d/tol)
+ // = log4(d*d/tol*tol)/2
+ // = log2(d*d/tol*tol)
+
+#ifdef SK_SCALAR_IS_FLOAT
+ // +1 since we're ignoring the mantissa contribution.
+ int log = get_float_exp(dsqd/(gSubdivTol*gSubdivTol)) + 1;
+ log = GrMin(GrMax(0, log), kMaxSub);
+ return log;
+#else
+ SkScalar log = SkScalarLog(SkScalarDiv(dsqd,kTol*kTol));
+ static const SkScalar conv = SkScalarInvert(SkScalarLog(2));
+ log = SkScalarMul(log, conv);
+ return GrMin(GrMax(0, SkScalarCeilToInt(log)),kMaxSub);
+#endif
+ }
+}
+
+int get_lines_and_quads(const SkPath& path, const SkMatrix& m, GrIRect clip,
+ PtArray* lines, PtArray* quads,
+ IntArray* quadSubdivCnts) {
+ SkPath::Iter iter(path, false);
+
+ int totalQuadCount = 0;
+ GrRect bounds;
+ GrIRect ibounds;
+ for (;;) {
+ GrPoint pts[4];
+ GrPathCmd cmd = (GrPathCmd)iter.next(pts);
+ switch (cmd) {
+ case kMove_PathCmd:
+ break;
+ case kLine_PathCmd:
+ m.mapPoints(pts,2);
+ bounds.setBounds(pts, 2);
+ bounds.outset(SK_Scalar1, SK_Scalar1);
+ bounds.roundOut(&ibounds);
+ if (SkIRect::Intersects(clip, ibounds)) {
+ lines->push_back() = pts[0];
+ lines->push_back() = pts[1];
+ }
+ break;
+ case kQuadratic_PathCmd: {
+ bounds.setBounds(pts, 3);
+ bounds.outset(SK_Scalar1, SK_Scalar1);
+ bounds.roundOut(&ibounds);
+ if (SkIRect::Intersects(clip, ibounds)) {
+ m.mapPoints(pts, 3);
+ int subdiv = num_quad_subdivs(pts);
+ GrAssert(subdiv >= -1);
+ if (-1 == subdiv) {
+ lines->push_back() = pts[0];
+ lines->push_back() = pts[1];
+ lines->push_back() = pts[1];
+ lines->push_back() = pts[2];
+ } else {
+ quads->push_back() = pts[0];
+ quads->push_back() = pts[1];
+ quads->push_back() = pts[2];
+ quadSubdivCnts->push_back() = subdiv;
+ totalQuadCount += 1 << subdiv;
+ }
+ }
+ } break;
+ case kCubic_PathCmd: {
+ bounds.setBounds(pts, 4);
+ bounds.outset(SK_Scalar1, SK_Scalar1);
+ bounds.roundOut(&ibounds);
+ if (SkIRect::Intersects(clip, ibounds)) {
+ m.mapPoints(pts, 4);
+ SkPoint stackStorage[32];
+ PtArray q((void*)stackStorage, 32);
+ convert_cubic_to_quads(pts, &q);
+ for (int i = 0; i < q.count(); i += 3) {
+ bounds.setBounds(&q[i], 3);
+ bounds.outset(SK_Scalar1, SK_Scalar1);
+ bounds.roundOut(&ibounds);
+ if (SkIRect::Intersects(clip, ibounds)) {
+ int subdiv = num_quad_subdivs(&q[i]);
+ GrAssert(subdiv >= -1);
+ if (-1 == subdiv) {
+ lines->push_back() = q[0 + i];
+ lines->push_back() = q[1 + i];
+ lines->push_back() = q[1 + i];
+ lines->push_back() = q[2 + i];
+ } else {
+ quads->push_back() = q[0 + i];
+ quads->push_back() = q[1 + i];
+ quads->push_back() = q[2 + i];
+ quadSubdivCnts->push_back() = subdiv;
+ totalQuadCount += 1 << subdiv;
+ }
+ }
+ }
+ }
+ } break;
+ case kClose_PathCmd:
+ break;
+ case kEnd_PathCmd:
+ return totalQuadCount;
+ }
+ }
+}
+
+struct Vertex {
+ GrPoint fPos;
+ union {
+ struct {
+ GrScalar fA;
+ GrScalar fB;
+ GrScalar fC;
+ } fLine;
+ GrVec fQuadCoord;
+ struct {
+ GrScalar fBogus[4];
+ };
+ };
+};
+GR_STATIC_ASSERT(sizeof(Vertex) == 3 * sizeof(GrPoint));
+
+void intersect_lines(const SkPoint& ptA, const SkVector& normA,
+ const SkPoint& ptB, const SkVector& normB,
+ SkPoint* result) {
+
+ SkScalar lineAW = -normA.dot(ptA);
+ SkScalar lineBW = -normB.dot(ptB);
+
+ SkScalar wInv = SkScalarMul(normA.fX, normB.fY) -
+ SkScalarMul(normA.fY, normB.fX);
+ wInv = SkScalarInvert(wInv);
+
+ result->fX = SkScalarMul(normA.fY, lineBW) - SkScalarMul(lineAW, normB.fY);
+ result->fX = SkScalarMul(result->fX, wInv);
+
+ result->fY = SkScalarMul(lineAW, normB.fX) - SkScalarMul(normA.fX, lineBW);
+ result->fY = SkScalarMul(result->fY, wInv);
+}
+
+void bloat_quad(const SkPoint qpts[3], Vertex verts[kVertsPerQuad]) {
+ // original quad is specified by tri a,b,c
+ const SkPoint& a = qpts[0];
+ const SkPoint& b = qpts[1];
+ const SkPoint& c = qpts[2];
+ // make a new poly where we replace a and c by a 1-pixel wide edges orthog
+ // to edges ab and bc:
+ //
+ // before | after
+ // | b0
+ // b |
+ // |
+ // | a0 c0
+ // a c | a1 c1
+ //
+ // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c,
+ // respectively.
+ Vertex& a0 = verts[0];
+ Vertex& a1 = verts[1];
+ Vertex& b0 = verts[2];
+ Vertex& c0 = verts[3];
+ Vertex& c1 = verts[4];
+
+ // compute a matrix that goes from device coords to U,V quad params
+ SkMatrix DevToUV;
+ DevToUV.setAll(a.fX, b.fX, c.fX,
+ a.fY, b.fY, c.fY,
+ SK_Scalar1, SK_Scalar1, SK_Scalar1);
+ DevToUV.invert(&DevToUV);
+ // can't make this static, no cons :(
+ SkMatrix UVpts;
+ UVpts.setAll(0, SK_ScalarHalf, SK_Scalar1,
+ 0, 0, SK_Scalar1,
+ SK_Scalar1, SK_Scalar1, SK_Scalar1);
+ DevToUV.postConcat(UVpts);
+
+ // We really want to avoid perspective matrix muls.
+ // These may wind up really close to zero
+ DevToUV.setPerspX(0);
+ DevToUV.setPerspY(0);
+
+ SkVector ab = b;
+ ab -= a;
+ SkVector ac = c;
+ ac -= a;
+ SkVector cb = b;
+ cb -= c;
+
+ // We should have already handled degenerates
+ GrAssert(ab.length() > 0 && cb.length() > 0);
+
+ ab.normalize();
+ SkVector abN;
+ abN.setOrthog(ab, SkVector::kLeft_Side);
+ if (abN.dot(ac) > 0) {
+ abN.negate();
+ }
+
+ cb.normalize();
+ SkVector cbN;
+ cbN.setOrthog(cb, SkVector::kLeft_Side);
+ if (cbN.dot(ac) < 0) {
+ cbN.negate();
+ }
+
+ a0.fPos = a;
+ a0.fPos += abN;
+ a1.fPos = a;
+ a1.fPos -= abN;
+
+ c0.fPos = c;
+ c0.fPos += cbN;
+ c1.fPos = c;
+ c1.fPos -= cbN;
+
+ intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos);
+
+ DevToUV.mapPointsWithStride(&verts[0].fQuadCoord,
+ &verts[0].fPos, sizeof(Vertex), kVertsPerQuad);
+}
+
+void add_quads(const SkPoint p[3],
+ int subdiv,
+ Vertex** vert) {
+ GrAssert(subdiv >= 0);
+ if (subdiv) {
+ SkPoint newP[5];
+ SkChopQuadAtHalf(p, newP);
+ add_quads(newP + 0, subdiv-1, vert);
+ add_quads(newP + 2, subdiv-1, vert);
+ } else {
+ bloat_quad(p, *vert);
+ *vert += kVertsPerQuad;
+ }
+}
+
+void add_line(const SkPoint p[2],
+ int rtHeight,
+ Vertex** vert) {
+ const SkPoint& a = p[0];
+ const SkPoint& b = p[1];
+
+ SkVector orthVec = b;
+ orthVec -= a;
+
+ if (orthVec.setLength(SK_Scalar1)) {
+ orthVec.setOrthog(orthVec);
+
+ // the values we pass down to the frag shader
+ // have to be in y-points-up space;
+ SkVector normal;
+ normal.fX = orthVec.fX;
+ normal.fY = -orthVec.fY;
+ SkPoint aYDown;
+ aYDown.fX = a.fX;
+ aYDown.fY = rtHeight - a.fY;
+
+ SkScalar lineC = -(aYDown.dot(normal));
+ for (int i = 0; i < kVertsPerLineSeg; ++i) {
+ (*vert)[i].fPos = (i < 2) ? a : b;
+ if (0 == i || 3 == i) {
+ (*vert)[i].fPos -= orthVec;
+ } else {
+ (*vert)[i].fPos += orthVec;
+ }
+ (*vert)[i].fLine.fA = normal.fX;
+ (*vert)[i].fLine.fB = normal.fY;
+ (*vert)[i].fLine.fC = lineC;
+ }
+ } else {
+ // just make it degenerate and likely offscreen
+ (*vert)[0].fPos.set(SK_ScalarMax, SK_ScalarMax);
+ (*vert)[1].fPos.set(SK_ScalarMax, SK_ScalarMax);
+ (*vert)[2].fPos.set(SK_ScalarMax, SK_ScalarMax);
+ (*vert)[3].fPos.set(SK_ScalarMax, SK_ScalarMax);
+ }
+
+ *vert += kVertsPerLineSeg;
+}
+
+}
+
+bool GrAAHairLinePathRenderer::createGeom(GrDrawTarget::StageBitfield stages) {
+
+ int rtHeight = fTarget->getRenderTarget()->height();
+
+ GrIRect clip;
+ if (fTarget->getClip().hasConservativeBounds()) {
+ GrRect clipRect = fTarget->getClip().getConservativeBounds();
+ clipRect.roundOut(&clip);
+ } else {
+ clip.setLargest();
+ }
+
+ if (stages == fPreviousStages &&
+ fPreviousViewMatrix == fTarget->getViewMatrix() &&
+ rtHeight == fPreviousRTHeight &&
+ fClipRect == clip) {
+ return true;
+ }
+
+ GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
+ for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
+ if ((1 << s) & stages) {
+ layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
+ }
+ }
+
+ GrMatrix viewM = fTarget->getViewMatrix();
+ viewM.preTranslate(fTranslate.fX, fTranslate.fY);
+
+ GrAlignedSTStorage<128, GrPoint> lineStorage;
+ GrAlignedSTStorage<128, GrPoint> quadStorage;
+ PtArray lines(&lineStorage);
+ PtArray quads(&quadStorage);
+ IntArray qSubdivs;
+ fQuadCnt = get_lines_and_quads(*fPath, viewM, clip,
+ &lines, &quads, &qSubdivs);
+
+ fLineSegmentCnt = lines.count() / 2;
+ int vertCnt = kVertsPerLineSeg * fLineSegmentCnt + kVertsPerQuad * fQuadCnt;
+
+ GrAssert(sizeof(Vertex) == GrDrawTarget::VertexSize(layout));
+
+ Vertex* verts;
+ if (!fTarget->reserveVertexSpace(layout, vertCnt, (void**)&verts)) {
+ return false;
+ }
+
+ for (int i = 0; i < fLineSegmentCnt; ++i) {
+ add_line(&lines[2*i], rtHeight, &verts);
+ }
+ int unsubdivQuadCnt = quads.count() / 3;
+ for (int i = 0; i < unsubdivQuadCnt; ++i) {
+ GrAssert(qSubdivs[i] >= 0);
+ add_quads(&quads[3*i], qSubdivs[i], &verts);
+ }
+
+ fPreviousStages = stages;
+ fPreviousViewMatrix = fTarget->getViewMatrix();
+ fPreviousRTHeight = rtHeight;
+ fClipRect = clip;
+ return true;
+}
+
+void GrAAHairLinePathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
+ GrDrawTarget::AutoStateRestore asr(fTarget);
+
+ GrMatrix ivm;
+
+ if (!this->createGeom(stages)) {
+ return;
+ }
+
+ if (fTarget->getViewInverse(&ivm)) {
+ fTarget->preConcatSamplerMatrices(stages, ivm);
+ }
+
+ fTarget->setViewMatrix(GrMatrix::I());
+
+ // TODO: See whether rendering lines as degenerate quads improves perf
+ // when we have a mix
+ fTarget->setIndexSourceToBuffer(fLinesIndexBuffer);
+ int lines = 0;
+ int nBufLines = fLinesIndexBuffer->maxQuads();
+ while (lines < fLineSegmentCnt) {
+ int n = GrMin(fLineSegmentCnt-lines, nBufLines);
+ fTarget->setVertexEdgeType(GrDrawTarget::kHairLine_EdgeType);
+ fTarget->drawIndexed(kTriangles_PrimitiveType,
+ kVertsPerLineSeg*lines, // startV
+ 0, // startI
+ kVertsPerLineSeg*n, // vCount
+ kIdxsPerLineSeg*n); // iCount
+ lines += n;
+ }
+
+ fTarget->setIndexSourceToBuffer(fQuadsIndexBuffer);
+ int quads = 0;
+ while (quads < fQuadCnt) {
+ int n = GrMin(fQuadCnt-quads, kNumQuadsInIdxBuffer);
+ fTarget->setVertexEdgeType(GrDrawTarget::kHairQuad_EdgeType);
+ fTarget->drawIndexed(kTriangles_PrimitiveType,
+ 4*fLineSegmentCnt + kVertsPerQuad*quads, // startV
+ 0, // startI
+ kVertsPerQuad*n, // vCount
+ kIdxsPerQuad*n); // iCount
+ quads += n;
+ }
+
+}
+
diff --git a/gpu/src/GrAAHairLinePathRenderer.h b/gpu/src/GrAAHairLinePathRenderer.h
new file mode 100644
index 0000000000..c7d2dc722a
--- /dev/null
+++ b/gpu/src/GrAAHairLinePathRenderer.h
@@ -0,0 +1,62 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrAAHairLinePathRenderer_DEFINED
+#define GrAAHairLinePathRenderer_DEFINED
+
+#include "GrPathRenderer.h"
+
+class GrAAHairLinePathRenderer : public GrPathRenderer {
+public:
+ virtual ~GrAAHairLinePathRenderer();
+
+ static GrPathRenderer* Create(GrContext* context);
+ // GrPathRenderer overrides
+ virtual bool supportsAA(GrDrawTarget* target,
+ const SkPath& path,
+ GrPathFill fill);
+ virtual bool canDrawPath(const GrDrawTarget* target,
+ const SkPath& path,
+ GrPathFill fill) const;
+ virtual void drawPath(GrDrawTarget::StageBitfield stages);
+
+protected:
+
+ // GrPathRenderer overrides
+ virtual void pathWillClear();
+
+private:
+ void resetGeom();
+
+ static bool CanBeUsed(const GrContext* context);
+ GrAAHairLinePathRenderer(const GrContext* context,
+ const GrIndexBuffer* fLinesIndexBuffer,
+ const GrIndexBuffer* fQuadsIndexBuffer);
+
+ bool createGeom(GrDrawTarget::StageBitfield stages);
+
+ GrContext* fContext;
+ const GrIndexBuffer* fLinesIndexBuffer;
+ const GrIndexBuffer* fQuadsIndexBuffer;
+
+ // have to recreate geometry if stages in use changes :(
+ GrDrawTarget::StageBitfield fPreviousStages;
+ int fPreviousRTHeight;
+ GrIRect fClipRect;
+
+ // this path renderer draws everything in device coordinates
+ GrMatrix fPreviousViewMatrix;
+ int fLineSegmentCnt;
+ int fQuadCnt;
+
+ typedef GrPathRenderer INHERITED;
+};
+
+
+#endif
+
diff --git a/gpu/src/GrAddPathRenderers_aahairline.cpp b/gpu/src/GrAddPathRenderers_aahairline.cpp
new file mode 100644
index 0000000000..a7df66e980
--- /dev/null
+++ b/gpu/src/GrAddPathRenderers_aahairline.cpp
@@ -0,0 +1,20 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrAAHairLinePathRenderer.h"
+
+void GrPathRenderer::AddPathRenderers(GrContext* ctx,
+ GrPathRendererChain::UsageFlags flags,
+ GrPathRendererChain* chain) {
+ if (!(GrPathRendererChain::kNonAAOnly_UsageFlag & flags)) {
+ if (GrPathRenderer* pr = GrAAHairLinePathRenderer::Create(ctx)) {
+ chain->addPathRenderer(pr)->unref();
+ }
+ }
+}
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 6692023a73..a97c857490 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -1176,7 +1176,7 @@ void GrContext::drawRect(const GrPaint& paint,
GrRect devRect = rect;
GrMatrix combinedMatrix;
- bool doAA = apply_aa_to_rect(target, paint, rect, width, matrix,
+ bool doAA = apply_aa_to_rect(target, paint, rect, width, matrix,
&combinedMatrix, &devRect);
if (doAA) {
@@ -1378,9 +1378,12 @@ void GrContext::drawVertices(const GrPaint& paint,
}
int texOffsets[GrDrawTarget::kMaxTexCoords];
int colorOffset;
+ int edgeOffset;
GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
texOffsets,
- &colorOffset);
+ &colorOffset,
+ &edgeOffset);
+ GrAssert(-1 == edgeOffset);
void* curVertex = geo.vertices();
for (int i = 0; i < vertexCount; ++i) {
diff --git a/gpu/src/GrDefaultPathRenderer.h b/gpu/src/GrDefaultPathRenderer.h
index e11716ed61..dd5964122d 100644
--- a/gpu/src/GrDefaultPathRenderer.h
+++ b/gpu/src/GrDefaultPathRenderer.h
@@ -20,7 +20,8 @@ public:
GrDefaultPathRenderer(bool separateStencilSupport,
bool stencilWrapOpsSupport);
- virtual bool canDrawPath(const SkPath& path,
+ virtual bool canDrawPath(const GrDrawTarget* target,
+ const SkPath& path,
GrPathFill fill) const { return true; }
virtual bool requiresStencilPass(const GrDrawTarget* target,
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index 29a37a469e..51cff6dec3 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -63,6 +63,17 @@ bool check_layout(GrVertexLayout layout) {
return true;
}
+int num_tex_coords(GrVertexLayout layout) {
+ int cnt = 0;
+ // figure out how many tex coordinates are present
+ for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
+ if (tex_coord_idx_mask(t) & layout) {
+ ++cnt;
+ }
+ }
+ return cnt;
+}
+
} //unnamed namespace
size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
@@ -73,14 +84,13 @@ size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
sizeof(GrPoint);
size_t size = vecSize; // position
- for (int t = 0; t < kMaxTexCoords; ++t) {
- if (tex_coord_idx_mask(t) & vertexLayout) {
- size += vecSize;
- }
- }
+ size += num_tex_coords(vertexLayout) * vecSize;
if (vertexLayout & kColor_VertexLayoutBit) {
size += sizeof(GrColor);
}
+ if (vertexLayout & kEdge_VertexLayoutBit) {
+ size += 4 * sizeof(GrScalar);
+ }
return size;
}
@@ -111,16 +121,27 @@ int GrDrawTarget::VertexStageCoordOffset(int stage, GrVertexLayout vertexLayout)
int GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) {
GrAssert(check_layout(vertexLayout));
+ // color is after the pos and tex coords
if (vertexLayout & kColor_VertexLayoutBit) {
int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
sizeof(GrGpuTextVertex) :
sizeof(GrPoint);
- int offset = vecSize; // position
- // figure out how many tex coordinates are present and precede this one.
- for (int t = 0; t < kMaxTexCoords; ++t) {
- if (tex_coord_idx_mask(t) & vertexLayout) {
- offset += vecSize;
- }
+ return vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
+ }
+ return -1;
+}
+
+int GrDrawTarget::VertexEdgeOffset(GrVertexLayout vertexLayout) {
+ GrAssert(check_layout(vertexLayout));
+
+ // edge pts are after the pos, tex coords, and color
+ if (vertexLayout & kEdge_VertexLayoutBit) {
+ int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+ sizeof(GrGpuTextVertex) :
+ sizeof(GrPoint);
+ int offset = vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
+ if (vertexLayout & kColor_VertexLayoutBit) {
+ offset += sizeof(GrColor);
}
return offset;
}
@@ -129,11 +150,13 @@ int GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) {
int GrDrawTarget::VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
int texCoordOffsetsByIdx[kMaxTexCoords],
- int* colorOffset) {
+ int* colorOffset,
+ int* edgeOffset) {
GrAssert(check_layout(vertexLayout));
GrAssert(NULL != texCoordOffsetsByIdx);
GrAssert(NULL != colorOffset);
+ GrAssert(NULL != edgeOffset);
int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
sizeof(GrGpuTextVertex) :
@@ -154,21 +177,30 @@ int GrDrawTarget::VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
} else {
*colorOffset = -1;
}
+ if (kEdge_VertexLayoutBit & vertexLayout) {
+ *edgeOffset = size;
+ size += 4 * sizeof(GrScalar);
+ } else {
+ *edgeOffset = -1;
+ }
return size;
}
int GrDrawTarget::VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout,
int texCoordOffsetsByStage[kNumStages],
- int* colorOffset) {
+ int* colorOffset,
+ int* edgeOffset) {
GrAssert(check_layout(vertexLayout));
GrAssert(NULL != texCoordOffsetsByStage);
GrAssert(NULL != colorOffset);
+ GrAssert(NULL != edgeOffset);
int texCoordOffsetsByIdx[kMaxTexCoords];
int size = VertexSizeAndOffsetsByIdx(vertexLayout,
texCoordOffsetsByIdx,
- colorOffset);
+ colorOffset,
+ edgeOffset);
for (int s = 0; s < kNumStages; ++s) {
int tcIdx;
if (StagePosAsTexCoordVertexLayoutBit(s) & vertexLayout) {
@@ -250,22 +282,39 @@ void GrDrawTarget::VertexLayoutUnitTest() {
GrAssert(VertexUsesStage(s2, posAsTex));
GrAssert(2*sizeof(GrPoint) == VertexSize(posAsTex));
GrAssert(-1 == VertexTexCoordsForStage(s2, posAsTex));
+ GrAssert(-1 == VertexEdgeOffset(posAsTex));
}
+ GrAssert(-1 == VertexEdgeOffset(tcMask));
+ GrAssert(-1 == VertexColorOffset(tcMask));
#if GR_DEBUG
GrVertexLayout withColor = tcMask | kColor_VertexLayoutBit;
#endif
GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColor));
GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColor));
+ #if GR_DEBUG
+ GrVertexLayout withEdge = tcMask | kEdge_VertexLayoutBit;
+ #endif
+ GrAssert(-1 == VertexColorOffset(withEdge));
+ GrAssert(2*sizeof(GrPoint) == VertexEdgeOffset(withEdge));
+ GrAssert(4*sizeof(GrPoint) == VertexSize(withEdge));
+ #if GR_DEBUG
+ GrVertexLayout withColorAndEdge = withColor | kEdge_VertexLayoutBit;
+ #endif
+ GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColorAndEdge));
+ GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexEdgeOffset(withColorAndEdge));
+ GrAssert(4*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColorAndEdge));
}
GrAssert(tex_coord_idx_mask(t) == tcMask);
GrAssert(check_layout(tcMask));
int stageOffsets[kNumStages];
int colorOffset;
+ int edgeOffset;
int size;
- size = VertexSizeAndOffsetsByStage(tcMask, stageOffsets, &colorOffset);
+ size = VertexSizeAndOffsetsByStage(tcMask, stageOffsets, &colorOffset, &edgeOffset);
GrAssert(2*sizeof(GrPoint) == size);
GrAssert(-1 == colorOffset);
+ GrAssert(-1 == edgeOffset);
for (int s = 0; s < kNumStages; ++s) {
GrAssert(VertexUsesStage(s, tcMask));
GrAssert(sizeof(GrPoint) == stageOffsets[s]);
@@ -698,7 +747,8 @@ void GrDrawTarget::drawNonIndexed(GrPrimitiveType type,
bool GrDrawTarget::CanDisableBlend(GrVertexLayout layout, const DrState& state) {
// If we compute a coverage value (using edge AA or a coverage stage) then
// we can't force blending off.
- if (state.fEdgeAANumEdges > 0) {
+ if (state.fEdgeAANumEdges > 0 ||
+ layout & kEdge_VertexLayoutBit) {
return false;
}
for (int s = state.fFirstCoverageStage; s < kNumStages; ++s) {
@@ -837,8 +887,11 @@ void GrDrawTarget::SetRectVertices(const GrRect& rect,
int stageOffsets[kNumStages];
int colorOffset;
- int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets, &colorOffset);
+ int edgeOffset;
+ int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets,
+ &colorOffset, &edgeOffset);
GrAssert(-1 == colorOffset);
+ GrAssert(-1 == edgeOffset);
GrTCast<GrPoint*>(vertices)->setRectFan(rect.fLeft, rect.fTop,
rect.fRight, rect.fBottom,
diff --git a/gpu/src/GrDrawTarget.h b/gpu/src/GrDrawTarget.h
index f170fda10a..42723f6da3 100644
--- a/gpu/src/GrDrawTarget.h
+++ b/gpu/src/GrDrawTarget.h
@@ -59,6 +59,20 @@ public:
kMaxEdges = 32
};
+ /**
+ * When specifying edges as vertex data this enum specifies what type of
+ * edges are in use. The edges are always 4 GrScalars in memory, even when
+ * the edge type requires fewer than 4.
+ */
+ enum VertexEdgeType {
+ /* 1-pixel wide line
+ 2D implicit line eq (a*x + b*y +c = 0). 4th component unused */
+ kHairLine_EdgeType,
+ /* 1-pixel wide quadratic
+ u^2-v canonical coords (only 2 components used) */
+ kHairQuad_EdgeType
+ };
+
/**
* Bitfield used to indicate which stages are in use.
*/
@@ -167,6 +181,7 @@ protected:
GrStencilSettings fStencilSettings;
GrMatrix fViewMatrix;
+ VertexEdgeType fVertexEdgeType;
Edge fEdgeAAEdges[kMaxEdges];
int fEdgeAANumEdges;
bool operator ==(const DrState& s) const {
@@ -256,7 +271,7 @@ public:
}
/**
- * Shortcut for preConcatSamplerMatrix on all stages in mask with same
+ * Shortcut for preConcatSamplerMatrix on all stages in mask with same
* matrix
*/
void preConcatSamplerMatrices(int stageMask, const GrMatrix& matrix) {
@@ -268,6 +283,18 @@ public:
}
/**
+ * Shortcut for preConcatSamplerMatrix on all enabled stages in mask with
+ * same matrix
+ *
+ * @param stage the stage of the sampler to set
+ * @param matrix the matrix to concat
+ */
+ void preConcatEnabledSamplerMatrices(const GrMatrix& matrix) {
+ StageBitfield stageMask = this->enabledStages();
+ this->preConcatSamplerMatrices(stageMask, matrix);
+ }
+
+ /**
* Gets the matrix of a stage's sampler
*
* @param stage the stage to of sampler to get
@@ -535,6 +562,15 @@ public:
*/
bool canDisableBlend() const;
+ /**
+ * Determines the interpretation per-vertex edge data when the
+ * kEdge_VertexLayoutBit is set (see below). When per-vertex edges are not
+ * specified the value of this setting has no effect.
+ */
+ void setVertexEdgeType(VertexEdgeType type) {
+ fCurrDrawState.fVertexEdgeType = type;
+ }
+
/**
* Given the current draw state, vertex layout, and hw support, will HW AA
* lines be used (if line primitive type is drawn)? (Note that lines are
@@ -575,15 +611,14 @@ public:
* Additional Bits that can be specified in GrVertexLayout.
*/
enum VertexLayoutBits {
-
+ /* vertices have colors */
kColor_VertexLayoutBit = 1 << (STAGE_BIT_CNT + 0),
- //<! vertices have colors
+
+ /* Use text vertices. (Pos and tex coords may be a different type for
+ text [GrGpuTextVertex vs GrPoint].) */
kTextFormat_VertexLayoutBit = 1 << (STAGE_BIT_CNT + 1),
- //<! use text vertices. (Pos
- // and tex coords may be
- // a different type for
- // text [GrGpuTextVertex vs
- // GrPoint].)
+
+ kEdge_VertexLayoutBit = 1 << (STAGE_BIT_CNT + 2),
// for below assert
kDummyVertexLayoutBit,
kHighVertexLayoutBit = kDummyVertexLayoutBit - 1
@@ -1030,6 +1065,13 @@ public:
*/
static int VertexColorOffset(GrVertexLayout vertexLayout);
+ /**
+ * Helper function to compute the offset of the edge pts in a vertex
+ * @return offset of edge in vertex layout or -1 if the
+ * layout has no edge.
+ */
+ static int VertexEdgeOffset(GrVertexLayout vertexLayout);
+
/**
* Helper function to determine if vertex layout contains explicit texture
* coordinates of some index.
@@ -1069,7 +1111,8 @@ public:
*/
static int VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
int texCoordOffsetsByIdx[kMaxTexCoords],
- int *colorOffset);
+ int *colorOffset,
+ int* edgeOffset);
/**
* Helper function to compute the size of each vertex and the offsets of
@@ -1086,7 +1129,8 @@ public:
*/
static int VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout,
int texCoordOffsetsByStage[kNumStages],
- int *colorOffset);
+ int *colorOffset,
+ int* edgeOffset);
/**
* Accessing positions, texture coords, or colors, of a vertex within an
@@ -1185,7 +1229,7 @@ protected:
// given a vertex layout and a draw state, will a stage be used?
static bool StageWillBeUsed(int stage, GrVertexLayout layout,
- const DrState& state) {
+ const DrState& state) {
return NULL != state.fTextures[stage] && VertexUsesStage(stage, layout);
}
@@ -1194,6 +1238,14 @@ protected:
fCurrDrawState);
}
+ StageBitfield enabledStages() const {
+ StageBitfield mask = 0;
+ for (int s = 0; s < kNumStages; ++s) {
+ mask |= this->isStageEnabled(s) ? 1 : 0;
+ }
+ return mask;
+ }
+
// Helpers for GrDrawTarget subclasses that won't have private access to
// SavedDrawState but need to peek at the state values.
static DrState& accessSavedDrawState(SavedDrawState& sds)
diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp
index ed3cd120f6..bfe6629135 100644
--- a/gpu/src/GrGLProgram.cpp
+++ b/gpu/src/GrGLProgram.cpp
@@ -44,6 +44,7 @@ const char* GrShaderPrecision(const GrGLInterface* gl) {
#define POS_ATTR_NAME "aPosition"
#define COL_ATTR_NAME "aColor"
+#define EDGE_ATTR_NAME "aEdge"
#define COL_UNI_NAME "uColor"
#define EDGES_UNI_NAME "uEdges"
#define COL_FILTER_UNI_NAME "uColorFilter"
@@ -539,6 +540,25 @@ bool GrGLProgram::genProgram(const GrGLInterface* gl,
segments.fFSCode.append(";\n");
}
inCoverage = "edgeAlpha";
+ } else if (layout & GrDrawTarget::kEdge_VertexLayoutBit) {
+ segments.fVSAttrs.append("attribute vec4 " EDGE_ATTR_NAME ";\n");
+ segments.fVaryings.append("varying vec4 vEdge;\n");
+ segments.fVSCode.append("\tvEdge = " EDGE_ATTR_NAME ";\n");
+ if (GrDrawTarget::kHairLine_EdgeType == fProgramDesc.fVertexEdgeType) {
+ segments.fFSCode.append("\tfloat edgeAlpha = abs(dot(vec3(gl_FragCoord.xy,1), vEdge.xyz));\n");
+ } else {
+ GrAssert(GrDrawTarget::kHairQuad_EdgeType == fProgramDesc.fVertexEdgeType);
+ // for now we know we're not in perspective, so we could compute this
+ // per-quadratic rather than per pixel
+ segments.fFSCode.append("\tvec2 duvdx = dFdx(vEdge.xy);\n");
+ segments.fFSCode.append("\tvec2 duvdy = dFdy(vEdge.xy);\n");
+ segments.fFSCode.append("\tfloat dfdx = 2.0*vEdge.x*duvdx.x - duvdx.y;\n");
+ segments.fFSCode.append("\tfloat dfdy = 2.0*vEdge.x*duvdy.x - duvdy.y;\n");
+ segments.fFSCode.append("\tfloat edgeAlpha = (vEdge.x*vEdge.x - vEdge.y);\n");
+ segments.fFSCode.append("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / (dfdx*dfdx + dfdy*dfdy));\n");
+ }
+ segments.fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
+ inCoverage = "edgeAlpha";
}
GrStringBuilder outCoverage;
@@ -839,8 +859,10 @@ bool GrGLProgram::bindOutputsAttribsAndLinkProgram(
}
}
- GR_GL_CALL(gl, BindAttribLocation(progID, ColorAttributeIdx(),
+ GR_GL_CALL(gl, BindAttribLocation(progID, ColorAttributeIdx(),
COL_ATTR_NAME));
+ GR_GL_CALL(gl, BindAttribLocation(progID, EdgeAttributeIdx(),
+ EDGE_ATTR_NAME));
GR_GL_CALL(gl, LinkProgram(progID));
diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h
index b89653fb81..78d25b5f9d 100644
--- a/gpu/src/GrGLProgram.h
+++ b/gpu/src/GrGLProgram.h
@@ -65,6 +65,8 @@ public:
static int PositionAttributeIdx() { return 0; }
static int TexCoordAttributeIdx(int tcIdx) { return 1 + tcIdx; }
static int ColorAttributeIdx() { return 1 + GrDrawTarget::kMaxTexCoords; }
+ static int EdgeAttributeIdx() { return 2 + GrDrawTarget::kMaxTexCoords; }
+
static int ViewMatrixAttributeIdx() {
return 2 + GrDrawTarget::kMaxTexCoords;
}
@@ -150,6 +152,8 @@ private:
kDualSrcOutputCnt
};
+ GrDrawTarget::VertexEdgeType fVertexEdgeType;
+
// stripped of bits that don't affect prog generation
GrVertexLayout fVertexLayout;
diff --git a/gpu/src/GrGpuGLFixed.cpp b/gpu/src/GrGpuGLFixed.cpp
index 69bf94f533..2c97e69ce2 100644
--- a/gpu/src/GrGpuGLFixed.cpp
+++ b/gpu/src/GrGpuGLFixed.cpp
@@ -276,15 +276,22 @@ void GrGpuGLFixed::setupGeometry(int* startVertex,
int newColorOffset;
int newTexCoordOffsets[kNumStages];
+ int newEdgeOffset;
GrGLsizei newStride = VertexSizeAndOffsetsByStage(this->getGeomSrc().fVertexLayout,
newTexCoordOffsets,
- &newColorOffset);
+ &newColorOffset,
+ &newEdgeOffset);
+ GrAssert(-1 == newEdgeOffset); // not supported by fixed pipe
+
int oldColorOffset;
int oldTexCoordOffsets[kNumStages];
+ int oldEdgeOffset;
GrGLsizei oldStride = VertexSizeAndOffsetsByStage(fHWGeometryState.fVertexLayout,
oldTexCoordOffsets,
- &oldColorOffset);
+ &oldColorOffset,
+ &oldEdgeOffset);
+ GrAssert(-1 == oldEdgeOffset);
bool indexed = NULL != startIndex;
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
index 05b1e88088..a9a3953673 100644
--- a/gpu/src/GrGpuGLShaders.cpp
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -185,11 +185,24 @@ bool GrGpuGLShaders::programUnitTest() {
pdesc.fFirstCoverageStage = idx;
bool edgeAA = random.nextF() > .5f;
- if (edgeAA) {
- pdesc.fEdgeAANumEdges = random.nextF() * this->getMaxEdges() + 1;
- pdesc.fEdgeAAConcave = random.nextF() > .5f;
- } else {
- pdesc.fEdgeAANumEdges = 0;
+ if (edgeAA) {
+ bool vertexEdgeAA = random.nextF() > .5f;
+ if (vertexEdgeAA) {
+ pdesc.fVertexLayout |= GrDrawTarget::kEdge_VertexLayoutBit;
+ if (this->supportsShaderDerivatives()) {
+ pdesc.fVertexEdgeType = random.nextF() > 0.5f ?
+ kHairQuad_EdgeType :
+ kHairLine_EdgeType;
+ } else {
+ pdesc.fVertexEdgeType = kHairLine_EdgeType;
+ }
+ pdesc.fEdgeAANumEdges = 0;
+ } else {
+ pdesc.fEdgeAANumEdges = random.nextF() * this->getMaxEdges() + 1;
+ pdesc.fEdgeAAConcave = random.nextF() > .5f;
+ }
+ } else {
+ pdesc.fEdgeAANumEdges = 0;
}
if (fDualSourceBlendingSupport) {
@@ -307,6 +320,7 @@ void GrGpuGLShaders::resetContext() {
fHWGeometryState.fVertexLayout = 0;
fHWGeometryState.fVertexOffset = ~0;
GL_CALL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx()));
+ GL_CALL(DisableVertexAttribArray(GrGLProgram::EdgeAttributeIdx()));
for (int t = 0; t < kMaxTexCoords; ++t) {
GL_CALL(DisableVertexAttribArray(GrGLProgram::TexCoordAttributeIdx(t)));
}
@@ -641,17 +655,22 @@ void GrGpuGLShaders::setupGeometry(int* startVertex,
int newColorOffset;
int newTexCoordOffsets[kMaxTexCoords];
+ int newEdgeOffset;
GrGLsizei newStride = VertexSizeAndOffsetsByIdx(
this->getGeomSrc().fVertexLayout,
newTexCoordOffsets,
- &newColorOffset);
+ &newColorOffset,
+ &newEdgeOffset);
int oldColorOffset;
int oldTexCoordOffsets[kMaxTexCoords];
+ int oldEdgeOffset;
+
GrGLsizei oldStride = VertexSizeAndOffsetsByIdx(
fHWGeometryState.fVertexLayout,
oldTexCoordOffsets,
- &oldColorOffset);
+ &oldColorOffset,
+ &oldEdgeOffset);
bool indexed = NULL != startIndex;
int extraVertexOffset;
@@ -727,6 +746,21 @@ void GrGpuGLShaders::setupGeometry(int* startVertex,
GL_CALL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx()));
}
+ if (newEdgeOffset > 0) {
+ GrGLvoid* edgeOffset = (int8_t*)(vertexOffset + newEdgeOffset);
+ int idx = GrGLProgram::EdgeAttributeIdx();
+ if (oldEdgeOffset <= 0) {
+ GL_CALL(EnableVertexAttribArray(idx));
+ GL_CALL(VertexAttribPointer(idx, 4, scalarType,
+ false, newStride, edgeOffset));
+ } else if (allOffsetsChange || newEdgeOffset != oldEdgeOffset) {
+ GL_CALL(VertexAttribPointer(idx, 4, scalarType,
+ false, newStride, edgeOffset));
+ }
+ } else if (oldEdgeOffset > 0) {
+ GL_CALL(DisableVertexAttribArray(GrGLProgram::EdgeAttributeIdx()));
+ }
+
fHWGeometryState.fVertexLayout = this->getGeomSrc().fVertexLayout;
fHWGeometryState.fArrayPtrsDirty = false;
}
@@ -734,6 +768,11 @@ void GrGpuGLShaders::setupGeometry(int* startVertex,
void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
ProgramDesc& desc = fCurrentProgram.fProgramDesc;
+ // The descriptor is used as a cache key. Thus when a field of the
+ // descriptor will not affect program generation (because of the vertex
+ // layout in use or other descriptor field settings) it should be set
+ // to a canonical value to avoid duplicate programs with different keys.
+
// Must initialize all fields or cache will have false negatives!
desc.fVertexLayout = this->getGeomSrc().fVertexLayout;
@@ -767,6 +806,13 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
int lastEnabledStage = -1;
+ if (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit) {
+ desc.fVertexEdgeType = fCurrDrawState.fVertexEdgeType;
+ } else {
+ // use canonical value when not set to avoid cache misses
+ desc.fVertexEdgeType = GrDrawTarget::kHairLine_EdgeType;
+ }
+
for (int s = 0; s < kNumStages; ++s) {
StageDesc& stage = desc.fStages[s];
diff --git a/gpu/src/GrPathRenderer.h b/gpu/src/GrPathRenderer.h
index 25dfafb032..4694de5252 100644
--- a/gpu/src/GrPathRenderer.h
+++ b/gpu/src/GrPathRenderer.h
@@ -52,13 +52,28 @@ public:
/**
* Returns true if this path renderer is able to render the path.
* Returning false allows the caller to fallback to another path renderer.
+ * When searching for a path renderer capable of rendering a path this
+ * function is called. The path renderer can examine the path, fill rule,
+ * and draw settings that will be used (via the targetparameter). If "true"
+ * is reported note that the caller is permitted to make modifications to
+ * the following settings of the target between the calls to canDrawPath and
+ * drawPath:
+ * 1. view matrix: The matrix at drawPath time may have additional scale
+ * scale and translation applied
+ * 2. render target: The render target may change between canDrawPath
+ * and drawPath.
+ * The GrPathRenderer subclass's decision about whether to return true
+ * or false in its implementation of this function should consider these
+ * possible state changes.
*
* @param path The path to draw
* @param fill The fill rule to use
*
* @return true if the path can be drawn by this object, false otherwise.
*/
- virtual bool canDrawPath(const SkPath& path, GrPathFill fill) const = 0;
+ virtual bool canDrawPath(const GrDrawTarget* target,
+ const SkPath& path,
+ GrPathFill fill) const = 0;
/**
* For complex clips Gr uses the stencil buffer. The path renderer must be
diff --git a/gpu/src/GrPathRendererChain.cpp b/gpu/src/GrPathRendererChain.cpp
index 4c76720f9a..dc09d43c1e 100644
--- a/gpu/src/GrPathRendererChain.cpp
+++ b/gpu/src/GrPathRendererChain.cpp
@@ -43,7 +43,7 @@ GrPathRenderer* GrPathRendererChain::getPathRenderer(const GrDrawTarget* target,
!target->getRenderTarget()->isMultisampled();
GrPathRenderer* nonAAPR = NULL;
for (int i = 0; i < fChain.count(); ++i) {
- if (fChain[i]->canDrawPath(path, fill)) {
+ if (fChain[i]->canDrawPath(target, path, fill)) {
if (!preferAA || fChain[i]->supportsAA(target, path, fill)) {
return fChain[i];
} else {
diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp
index aca6ec2f4c..15e3cc0230 100644
--- a/gpu/src/GrTesselatedPathRenderer.cpp
+++ b/gpu/src/GrTesselatedPathRenderer.cpp
@@ -599,7 +599,8 @@ FINISHED:
}
}
-bool GrTesselatedPathRenderer::canDrawPath(const SkPath& path,
+bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
+ const SkPath& path,
GrPathFill fill) const {
return kHairLine_PathFill != fill;
}
diff --git a/gpu/src/GrTesselatedPathRenderer.h b/gpu/src/GrTesselatedPathRenderer.h
index c815f50857..d5bf15777b 100644
--- a/gpu/src/GrTesselatedPathRenderer.h
+++ b/gpu/src/GrTesselatedPathRenderer.h
@@ -17,7 +17,8 @@ public:
GrTesselatedPathRenderer();
virtual void drawPath(GrDrawTarget::StageBitfield stages);
- virtual bool canDrawPath(const GrPath& path,
+ virtual bool canDrawPath(const GrDrawTarget* target,
+ const GrPath& path,
GrPathFill fill) const;
virtual bool requiresStencilPass(const GrDrawTarget* target,
diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp
index 92d6e74707..a3f4017067 100644
--- a/gyp/SampleApp.gyp
+++ b/gyp/SampleApp.gyp
@@ -34,6 +34,7 @@
'../samplecode/ClockFaceView.cpp',
'../samplecode/OverView.cpp',
'../samplecode/Sample2PtRadial.cpp',
+ '../samplecode/SampleAARects.cpp',
'../samplecode/SampleAll.cpp',
'../samplecode/SampleAnimator.cpp',
'../samplecode/SampleApp.cpp',
@@ -68,6 +69,7 @@
'../samplecode/SampleFuzz.cpp',
'../samplecode/SampleGM.cpp',
'../samplecode/SampleGradients.cpp',
+ '../samplecode/SampleHairCurves.cpp',
'../samplecode/SampleHairline.cpp',
'../samplecode/SampleImage.cpp',
'../samplecode/SampleImageDir.cpp',
@@ -90,7 +92,6 @@
'../samplecode/SamplePicture.cpp',
'../samplecode/SamplePoints.cpp',
'../samplecode/SamplePolyToPoly.cpp',
- '../samplecode/SampleAARects.cpp',
'../samplecode/SampleRegion.cpp',
'../samplecode/SampleRepeatTile.cpp',
'../samplecode/SampleShaders.cpp',
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index b7fa1c85e1..f77add2dad 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -126,7 +126,9 @@
'../gpu/include/GrTypes.h',
'../gpu/include/GrUserConfig.h',
- '../gpu/src/GrAddPathRenderers_none.cpp',
+ '../gpu/src/GrAAHairlinePathRenderer.cpp',
+ '../gpu/src/GrAAHairlinePathRenderer.h',
+ '../gpu/src/GrAddPathRenderers_aahairline.cpp',
'../gpu/src/GrAllocPool.cpp',
'../gpu/src/GrAtlas.cpp',
'../gpu/src/GrBinHashKey.h',
diff --git a/samplecode/SampleHairCurves.cpp b/samplecode/SampleHairCurves.cpp
new file mode 100644
index 0000000000..152cbeb09c
--- /dev/null
+++ b/samplecode/SampleHairCurves.cpp
@@ -0,0 +1,112 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+
+class HairCurvesView : public SampleView {
+public:
+ HairCurvesView() {
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "HairCurves");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(-1);
+ canvas->save();
+ canvas->scale(1000 * SK_Scalar1, 1000 * SK_Scalar1);
+ SkRandom rand;
+ SkPath curves;
+ SkPath hulls;
+ SkPath ctrlPts;
+ for (int i = 0; i < 100; ++i) {
+ SkScalar pts[] = {
+ rand.nextUScalar1(), rand.nextUScalar1(),
+ rand.nextUScalar1(), rand.nextUScalar1(),
+ rand.nextUScalar1(), rand.nextUScalar1(),
+ rand.nextUScalar1(), rand.nextUScalar1()
+ };
+ curves.moveTo(pts[0], pts[1]);
+ curves.cubicTo(pts[2], pts[3],
+ pts[4], pts[5],
+ pts[6], pts[7]);
+
+ hulls.moveTo(pts[0], pts[1]);
+ hulls.lineTo(pts[2], pts[3]);
+ hulls.lineTo(pts[4], pts[5]);
+ hulls.lineTo(pts[6], pts[7]);
+
+ ctrlPts.addCircle(pts[0], pts[1], SK_Scalar1 / 200);
+ ctrlPts.addCircle(pts[2], pts[3], SK_Scalar1 / 200);
+ ctrlPts.addCircle(pts[4], pts[5], SK_Scalar1 / 200);
+ ctrlPts.addCircle(pts[6], pts[7], SK_Scalar1 / 200);
+ }
+ for (int i = 0; i < 100; ++i) {
+ SkScalar pts[] = {
+ rand.nextUScalar1(), rand.nextUScalar1(),
+ rand.nextUScalar1(), rand.nextUScalar1(),
+ rand.nextUScalar1(), rand.nextUScalar1(),
+ };
+ curves.moveTo(pts[0], pts[1]);
+ curves.quadTo(pts[2], pts[3],
+ pts[4], pts[5]);
+
+ hulls.moveTo(pts[0], pts[1]);
+ hulls.lineTo(pts[2], pts[3]);
+ hulls.lineTo(pts[4], pts[5]);
+
+ ctrlPts.addCircle(pts[0], pts[1], SK_Scalar1 / 200);
+ ctrlPts.addCircle(pts[2], pts[3], SK_Scalar1 / 200);
+ ctrlPts.addCircle(pts[4], pts[5], SK_Scalar1 / 200);
+ }
+ for (int i = 0; i < 100; ++i) {
+ SkScalar pts[] = {
+ rand.nextUScalar1(), rand.nextUScalar1(),
+ rand.nextUScalar1(), rand.nextUScalar1(),
+ };
+ curves.moveTo(pts[0], pts[1]);
+ curves.lineTo(pts[2], pts[3]);
+
+ ctrlPts.addCircle(pts[0], pts[1], SK_Scalar1 / 200);
+ ctrlPts.addCircle(pts[2], pts[3], SK_Scalar1 / 200);
+ }
+
+ paint.setColor(SK_ColorBLACK);
+ canvas->drawPath(curves, paint);
+ paint.setColor(SK_ColorRED);
+ //canvas->drawPath(hulls, paint);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(SK_ColorBLUE);
+ //canvas->drawPath(ctrlPts, paint);
+
+ canvas->restore();
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new HairCurvesView; }
+static SkViewRegister reg(MyFactory);
+