Make a separate path renderer object. Move enum types to GrTypes.h

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



git-svn-id: http://skia.googlecode.com/svn/trunk@829 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
new file mode 100644
index 0000000..c47b6e5
--- /dev/null
+++ b/gpu/src/GrPathRenderer.cpp
@@ -0,0 +1,352 @@
+#include "GrPathRenderer.h"
+
+#include "GrPoint.h"
+#include "GrDrawTarget.h"
+#include "GrPathIter.h"
+#include "GrMemory.h"
+#include "GrTexture.h"
+
+
+
+GrDefaultPathRenderer::GrDefaultPathRenderer(bool singlePassWindingStencil)
+    : fSinglePassWindingStencil(singlePassWindingStencil) {
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Helpers for draw Path
+
+#define STENCIL_OFF     0   // Always disable stencil (even when needed)
+static const GrScalar gTolerance = GR_Scalar1;
+
+static const uint32_t MAX_POINTS_PER_CURVE = 1 << 10;
+
+static uint32_t quadratic_point_count(const GrPoint points[], GrScalar tol) {
+    GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
+    if (d < tol) {
+        return 1;
+    } else {
+        // Each time we subdivide, d should be cut in 4. So we need to
+        // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
+        // points.
+        // 2^(log4(x)) = sqrt(x);
+        d = ceilf(sqrtf(d/tol));
+        return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE);
+    }
+}
+
+static uint32_t generate_quadratic_points(const GrPoint& p0,
+                                          const GrPoint& p1,
+                                          const GrPoint& p2,
+                                          GrScalar tolSqd,
+                                          GrPoint** points,
+                                          uint32_t pointsLeft) {
+    if (pointsLeft < 2 ||
+        (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) {
+        (*points)[0] = p2;
+        *points += 1;
+        return 1;
+    }
+
+    GrPoint q[] = {
+        GrPoint(GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY)),
+        GrPoint(GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY)),
+    };
+    GrPoint r(GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY));
+
+    pointsLeft >>= 1;
+    uint32_t a = generate_quadratic_points(p0, q[0], r, tolSqd, points, pointsLeft);
+    uint32_t b = generate_quadratic_points(r, q[1], p2, tolSqd, points, pointsLeft);
+    return a + b;
+}
+
+static uint32_t cubic_point_count(const GrPoint points[], GrScalar tol) {
+    GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
+                       points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
+    d = sqrtf(d);
+    if (d < tol) {
+        return 1;
+    } else {
+        d = ceilf(sqrtf(d/tol));
+        return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE);
+    }
+}
+
+static uint32_t generate_cubic_points(const GrPoint& p0,
+                                      const GrPoint& p1,
+                                      const GrPoint& p2,
+                                      const GrPoint& p3,
+                                      GrScalar tolSqd,
+                                      GrPoint** points,
+                                      uint32_t pointsLeft) {
+    if (pointsLeft < 2 ||
+        (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd &&
+         p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) {
+            (*points)[0] = p3;
+            *points += 1;
+            return 1;
+        }
+    GrPoint q[] = {
+        GrPoint(GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY)),
+        GrPoint(GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY)),
+        GrPoint(GrScalarAve(p2.fX, p3.fX), GrScalarAve(p2.fY, p3.fY))
+    };
+    GrPoint r[] = {
+        GrPoint(GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY)),
+        GrPoint(GrScalarAve(q[1].fX, q[2].fX), GrScalarAve(q[1].fY, q[2].fY))
+    };
+    GrPoint s(GrScalarAve(r[0].fX, r[1].fX), GrScalarAve(r[0].fY, r[1].fY));
+    pointsLeft >>= 1;
+    uint32_t a = generate_cubic_points(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
+    uint32_t b = generate_cubic_points(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
+    return a + b;
+}
+
+static int worst_case_point_count(GrPathIter* path,
+                                  int* subpaths,
+                                  GrScalar tol) {
+    int pointCount = 0;
+    *subpaths = 1;
+
+    bool first = true;
+
+    GrPathIter::Command cmd;
+
+    GrPoint pts[4];
+    while ((cmd = path->next(pts)) != GrPathIter::kEnd_Command) {
+
+        switch (cmd) {
+            case GrPathIter::kLine_Command:
+                pointCount += 1;
+                break;
+            case GrPathIter::kQuadratic_Command:
+                pointCount += quadratic_point_count(pts, tol);
+                break;
+            case GrPathIter::kCubic_Command:
+                pointCount += cubic_point_count(pts, tol);
+                break;
+            case GrPathIter::kMove_Command:
+                pointCount += 1;
+                if (!first) {
+                    ++(*subpaths);
+                }
+                break;
+            default:
+                break;
+        }
+        first = false;
+    }
+    return pointCount;
+}
+
+static inline bool single_pass_path(const GrPathIter& path,
+                                    GrPathFill fill,
+                                    const GrDrawTarget& target) {
+#if STENCIL_OFF
+    return true;
+#else
+    if (kEvenOdd_PathFill == fill) {
+        GrPathIter::ConvexHint hint = path.hint();
+        return hint == GrPathIter::kConvex_ConvexHint ||
+               hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint;
+    } else if (kWinding_PathFill == fill) {
+        GrPathIter::ConvexHint hint = path.hint();
+        return hint == GrPathIter::kConvex_ConvexHint ||
+               hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint ||
+               (hint == GrPathIter::kSameWindingConvexPieces_ConvexHint &&
+                target.canDisableBlend() && !target.isDitherState());
+
+    }
+    return false;
+#endif
+}
+
+void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
+                                     GrDrawTarget::StageBitfield stages,
+                                     GrPathIter* path,
+                                     GrPathFill fill,
+                                     const GrPoint* translate) {
+
+    GrDrawTarget::AutoStateRestore asr(target);
+
+    GrMatrix viewM = target->getViewMatrix();
+    // In order to tesselate the path we get a bound on how much the matrix can
+    // stretch when mapping to screen coordinates.
+    GrScalar stretch = viewM.getMaxStretch();
+    bool useStretch = stretch > 0;
+    GrScalar tol = gTolerance;
+
+    if (!useStretch) {
+        // TODO: deal with perspective in some better way.
+        tol /= 10;
+    } else {
+        GrScalar sinv = GR_Scalar1 / stretch;
+        tol = GrMul(tol, sinv);
+    }
+    GrScalar tolSqd = GrMul(tol, tol);
+
+    int subpathCnt;
+    int maxPts = worst_case_point_count(path,
+                                        &subpathCnt,
+                                        tol);
+
+    GrVertexLayout layout = 0;
+    for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
+        if ((1 << s) & stages) {
+            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
+        }
+    }
+
+    // add 4 to hold the bounding rect
+    GrDrawTarget::AutoReleaseGeometry arg(target, layout, maxPts + 4, 0);
+
+    GrPoint* base = (GrPoint*) arg.vertices();
+    GrPoint* vert = base;
+    GrPoint* subpathBase = base;
+
+    GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
+
+    path->rewind();
+
+    // TODO: use primitve restart if available rather than multiple draws
+    GrPrimitiveType             type;
+    int                         passCount = 0;
+    GrDrawTarget::StencilPass   passes[3];
+    bool                        reverse = false;
+
+    if (kHairLine_PathFill == fill) {
+        type = kLineStrip_PrimitiveType;
+        passCount = 1;
+        passes[0] = GrDrawTarget::kNone_StencilPass;
+    } else {
+        type = kTriangleFan_PrimitiveType;
+        if (single_pass_path(*path, fill, *target)) {
+            passCount = 1;
+            passes[0] = GrDrawTarget::kNone_StencilPass;
+        } else {
+            switch (fill) {
+                case kInverseEvenOdd_PathFill:
+                    reverse = true;
+                    // fallthrough
+                case kEvenOdd_PathFill:
+                    passCount = 2;
+                    passes[0] = GrDrawTarget::kEvenOddStencil_StencilPass;
+                    passes[1] = GrDrawTarget::kEvenOddColor_StencilPass;
+                    break;
+
+                case kInverseWinding_PathFill:
+                    reverse = true;
+                    // fallthrough
+                case kWinding_PathFill:
+                    passes[0] = GrDrawTarget::kWindingStencil1_StencilPass;
+                    if (fSinglePassWindingStencil) {
+                        passes[1] = GrDrawTarget::kWindingColor_StencilPass;
+                        passCount = 2;
+                    } else {
+                        passes[1] = GrDrawTarget::kWindingStencil2_StencilPass;
+                        passes[2] = GrDrawTarget::kWindingColor_StencilPass;
+                        passCount = 3;
+                    }
+                    break;
+                default:
+                    GrAssert(!"Unknown path fill!");
+                    return;
+            }
+        }
+    }
+    target->setReverseFill(reverse);
+
+    GrPoint pts[4];
+
+    bool first = true;
+    int subpath = 0;
+
+    for (;;) {
+        GrPathIter::Command cmd = path->next(pts);
+        switch (cmd) {
+            case GrPathIter::kMove_Command:
+                if (!first) {
+                    subpathVertCount[subpath] = vert-subpathBase;
+                    subpathBase = vert;
+                    ++subpath;
+                }
+                *vert = pts[0];
+                vert++;
+                break;
+            case GrPathIter::kLine_Command:
+                *vert = pts[1];
+                vert++;
+                break;
+            case GrPathIter::kQuadratic_Command: {
+                generate_quadratic_points(pts[0], pts[1], pts[2],
+                                          tolSqd, &vert,
+                                          quadratic_point_count(pts, tol));
+                break;
+            }
+            case GrPathIter::kCubic_Command: {
+                generate_cubic_points(pts[0], pts[1], pts[2], pts[3],
+                                      tolSqd, &vert,
+                                      cubic_point_count(pts, tol));
+                break;
+            }
+            case GrPathIter::kClose_Command:
+                break;
+            case GrPathIter::kEnd_Command:
+                subpathVertCount[subpath] = vert-subpathBase;
+                ++subpath; // this could be only in debug
+                goto FINISHED;
+        }
+        first = false;
+    }
+FINISHED:
+    GrAssert(subpath == subpathCnt);
+    GrAssert((vert - base) <= maxPts);
+
+    if (translate) {
+        int count = vert - base;
+        for (int i = 0; i < count; i++) {
+            base[i].offset(translate->fX, translate->fY);
+        }
+    }
+
+    // arbitrary path complexity cutoff
+    bool useBounds = fill != kHairLine_PathFill &&
+                    (reverse || (vert - base) > 8);
+    GrPoint* boundsVerts = base + maxPts;
+    if (useBounds) {
+        GrRect bounds;
+        if (reverse) {
+            GrAssert(NULL != target->getRenderTarget());
+            // draw over the whole world.
+            bounds.setLTRB(0, 0,
+                           GrIntToScalar(target->getRenderTarget()->width()),
+                           GrIntToScalar(target->getRenderTarget()->height()));
+            GrMatrix vmi;
+            if (target->getViewInverse(&vmi)) {
+                vmi.mapRect(&bounds);
+            }
+        } else {
+            bounds.setBounds((GrPoint*)base, vert - base);
+        }
+        boundsVerts[0].setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight,
+                                  bounds.fBottom);
+    }
+
+    for (int p = 0; p < passCount; ++p) {
+        target->setStencilPass(passes[p]);
+        if (useBounds && (GrDrawTarget::kEvenOddColor_StencilPass == passes[p] ||
+                          GrDrawTarget::kWindingColor_StencilPass == passes[p])) {
+            target->drawNonIndexed(kTriangleFan_PrimitiveType,
+                                 maxPts, 4);
+
+        } else {
+            int baseVertex = 0;
+            for (int sp = 0; sp < subpathCnt; ++sp) {
+                target->drawNonIndexed(type,
+                                     baseVertex,
+                                     subpathVertCount[sp]);
+                baseVertex += subpathVertCount[sp];
+            }
+        }
+    }
+}