blob: c47b6e522c2e686c0ecc25f887ccdafe3761ef81 [file] [log] [blame]
#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];
}
}
}
}