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];
+ }
+ }
+ }
+}