Add convex path renderer (disabled)

Review URL: http://codereview.appspot.com/5533061/



git-svn-id: http://skia.googlecode.com/svn/trunk@3040 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
new file mode 100644
index 0000000..7260962
--- /dev/null
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -0,0 +1,401 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrAAConvexPathRenderer.h"
+
+#include "GrContext.h"
+#include "GrDrawState.h"
+#include "GrPathUtils.h"
+#include "SkString.h"
+#include "SkTrace.h"
+
+
+GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
+}
+
+bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget::Caps& targetCaps,
+                                         const SkPath& path,
+                                         GrPathFill fill,
+                                         bool antiAlias) const {
+    return targetCaps.fShaderDerivativeSupport && antiAlias &&
+           kHairLine_PathFill != fill && !GrIsFillInverted(fill) &&
+           path.isConvex();
+}
+
+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;
+};
+
+typedef SkTArray<Segment, true> SegmentArray;
+
+bool is_path_degenerate(const GrPath& path) {
+    int n = path.countPoints();
+    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);
+    bool foundLine = false;
+    GrPoint firstPoint = path.getPoint(0);
+    GrVec lineV;
+    SkScalar lineC;
+    int i = 1;
+
+    do {
+        GrPoint pt = path.getPoint(i);
+        if (!foundLine) {
+            if (pt != firstPoint) {
+                lineV = pt - firstPoint;
+                lineV.normalize();
+                lineV.setOrthog(lineV);
+                lineC = lineV.dot(firstPoint);
+                foundLine = true;
+            }
+        } else {
+            if (SkScalarAbs(lineV.dot(pt) - lineC) > TOL) {
+                return false;
+            }
+        }
+        ++i;
+    } while (i < n);
+    return true;
+}
+
+bool get_segments(const GrPath& path,
+                 SegmentArray* segments,
+                 int* quadCnt,
+                 int* lineCnt) {
+    *quadCnt = 0;
+    *lineCnt = 0;
+    SkPath::Iter iter(path, true);
+    // This renderer overemphasis very thin paths (every pixel intersected by
+    // the path will be at least 1/2 on). When the path degenerates to a line
+    // this makes the path draw as a hairline. This is a pretty glaring error
+    // so we detect this case and will not draw.
+    if (is_path_degenerate(path)) {
+        return false;
+    }
+    for (;;) {
+        GrPoint pts[4];
+        GrPathCmd cmd = (GrPathCmd)iter.next(pts);
+        switch (cmd) {
+            case kLine_PathCmd: {
+                segments->push_back();
+                segments->back().fType = Segment::kLine;
+                segments->back().fA = pts[1];
+                ++(*lineCnt);
+                break;
+            }
+            case kQuadratic_PathCmd:
+                segments->push_back();
+                segments->back().fType = Segment::kQuad;
+                segments->back().fA = pts[1];
+                segments->back().fB = pts[2];
+                ++(*quadCnt);
+                break;
+            case kCubic_PathCmd: {
+                SkSTArray<15, SkPoint, true> quads;
+                GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, &quads);
+                int count = quads.count();
+                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);
+                }
+                break;
+            };
+            case kEnd_PathCmd:
+                GrAssert(*quadCnt + *lineCnt == segments->count());
+                return true;
+            default:
+                break;
+        }
+    }
+}
+
+struct QuadVertex {
+    GrPoint  fPos;
+    union {
+        GrPoint fQuadUV;
+        GrScalar fEdge[4];
+    };
+};
+
+void get_counts(int quadCount, int lineCount, int* vCount, int* iCount) {
+    *vCount = 9 * lineCount + 11 * quadCount;
+    *iCount = 15  * lineCount + 24 * quadCount;
+}
+
+// for visual debugging, exagerate the AA smear at the edges
+// requires modifying the distance calc in the shader actually shade differently
+//#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;
+    }
+    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);
+
+        idxs[i + 0] = v + 0;
+        idxs[i + 1] = v + 1;
+        idxs[i + 2] = v + 2;
+        idxs[i + 3] = v + 0;
+        idxs[i + 4] = v + 2;
+        idxs[i + 5] = v + 3;
+
+        v += 4;
+        i += 6;
+
+        if (Segment::kLine == curr.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);
+
+            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 + 8] = v + 2;
+
+            i += 9;
+            v += 5;
+
+            prevPt = curr.fA;
+            prevNorm = curr.fANorm;
+        } else {
+            GrVec splitVec = curr.fANorm + curr.fBNorm;
+            splitVec.normalize();
+#ifdef STRETCH_AA
+            splitVec.scale(STRETCH_FACTOR);
+#endif
+
+            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;
+
+            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);
+
+            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;
+
+            i += 18;
+            v += 7;
+            prevPt = curr.fB;
+            prevNorm = curr.fBNorm;
+        }
+    }
+}
+
+}
+
+void GrAAConvexPathRenderer::drawPath(GrDrawState::StageMask stageMask) {
+    GrAssert(fPath->isConvex());
+    if (fPath->isEmpty()) {
+        return;
+    }
+    GrDrawState* drawState = fTarget->drawState();
+
+    GrDrawTarget::AutoStateRestore asr;
+    GrMatrix vm = drawState->getViewMatrix();
+    vm.postTranslate(fTranslate.fX, fTranslate.fY);
+    asr.set(fTarget);
+    GrMatrix ivm;
+    if (vm.invert(&ivm)) {
+        drawState->preConcatSamplerMatrices(stageMask, ivm);
+    }
+    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) {
+            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
+        }
+    }
+    layout |= GrDrawTarget::kEdge_VertexLayoutBit;
+
+    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);
+
+    if (!fTarget->reserveVertexSpace(layout,
+                                     vCount,
+                                     reinterpret_cast<void**>(&verts))) {
+        return;
+    }
+    if (!fTarget->reserveIndexSpace(iCount, reinterpret_cast<void**>(&idxs))) {
+        fTarget->resetVertexSource();
+        return;
+    }
+
+    create_vertices(&segments, fanPt, verts, idxs);
+
+    drawState->setVertexEdgeType(GrDrawState::kQuad_EdgeType);
+    fTarget->drawIndexed(kTriangles_PrimitiveType,
+                         0,        // start vertex
+                         0,        // start index
+                         vCount,
+                         iCount);
+}
+