blob: 6d7aabb1e08da9fae32f7e31e2b2d2a6351e66f5 [file] [log] [blame]
#include "GrPathRenderer.h"
#include "GrPoint.h"
#include "GrDrawTarget.h"
#include "GrPathIter.h"
#include "GrMemory.h"
#include "GrTexture.h"
GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
bool stencilWrapOpsSupport)
: fSeparateStencil(separateStencilSupport),
fStencilWrapOps(stencilWrapOpsSupport) {
}
////////////////////////////////////////////////////////////////////////////////
// Stencil rules for paths
////// Even/Odd
static const GrStencilSettings gEOStencilPass = {
kInvert_StencilOp, kInvert_StencilOp,
kKeep_StencilOp, kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff
};
// ok not to check clip b/c stencil pass only wrote inside clip
static const GrStencilSettings gEOColorPass = {
kZero_StencilOp, kZero_StencilOp,
kZero_StencilOp, kZero_StencilOp,
kNotEqual_StencilFunc, kNotEqual_StencilFunc,
0xffffffff, 0xffffffff,
0x0, 0x0,
0xffffffff, 0xffffffff
};
// have to check clip b/c outside clip will always be zero.
static const GrStencilSettings gInvEOColorPass = {
kZero_StencilOp, kZero_StencilOp,
kZero_StencilOp, kZero_StencilOp,
kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
0xffffffff, 0xffffffff,
0x0, 0x0,
0xffffffff, 0xffffffff
};
////// Winding
// when we have separate stencil we increment front faces / decrement back faces
// when we don't have wrap incr and decr we use the stencil test to simulate
// them.
static const GrStencilSettings gWindStencilSeparateWithWrap = {
kIncWrap_StencilOp, kDecWrap_StencilOp,
kKeep_StencilOp, kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff
};
// if inc'ing the max value, invert to make 0
// if dec'ing zero invert to make all ones.
// we can't avoid touching the stencil on both passing and
// failing, so we can't resctrict ourselves to the clip.
static const GrStencilSettings gWindStencilSeparateNoWrap = {
kInvert_StencilOp, kInvert_StencilOp,
kIncClamp_StencilOp, kDecClamp_StencilOp,
kEqual_StencilFunc, kEqual_StencilFunc,
0xffffffff, 0xffffffff,
0xffffffff, 0x0,
0xffffffff, 0xffffffff
};
// When there are no separate faces we do two passes to setup the winding rule
// stencil. First we draw the front faces and inc, then we draw the back faces
// and dec. These are same as the above two split into the incrementing and
// decrementing passes.
static const GrStencilSettings gWindSingleStencilWithWrapInc = {
kIncWrap_StencilOp, kIncWrap_StencilOp,
kKeep_StencilOp, kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff
};
static const GrStencilSettings gWindSingleStencilWithWrapDec = {
kDecWrap_StencilOp, kDecWrap_StencilOp,
kKeep_StencilOp, kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff
};
static const GrStencilSettings gWindSingleStencilNoWrapInc = {
kInvert_StencilOp, kInvert_StencilOp,
kIncClamp_StencilOp, kIncClamp_StencilOp,
kEqual_StencilFunc, kEqual_StencilFunc,
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff
};
static const GrStencilSettings gWindSingleStencilNoWrapDec = {
kInvert_StencilOp, kInvert_StencilOp,
kDecClamp_StencilOp, kDecClamp_StencilOp,
kEqual_StencilFunc, kEqual_StencilFunc,
0xffffffff, 0xffffffff,
0x0, 0x0,
0xffffffff, 0xffffffff
};
static const GrStencilSettings gWindColorPass = {
kZero_StencilOp, kZero_StencilOp,
kZero_StencilOp, kZero_StencilOp,
kNonZeroIfInClip_StencilFunc, kNonZeroIfInClip_StencilFunc,
0xffffffff, 0xffffffff,
0x0, 0x0,
0xffffffff, 0xffffffff
};
static const GrStencilSettings gInvWindColorPass = {
kZero_StencilOp, kZero_StencilOp,
kZero_StencilOp, kZero_StencilOp,
kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
0xffffffff, 0xffffffff,
0x0, 0x0,
0xffffffff, 0xffffffff
};
////// Normal render to stencil
// Sometimes the default path renderer can draw a path directly to the stencil
// buffer without having to first resolve the interior / exterior.
static const GrStencilSettings gDirectToStencil = {
kZero_StencilOp, kZero_StencilOp,
kIncClamp_StencilOp, kIncClamp_StencilOp,
kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
0xffffffff, 0xffffffff,
0x0, 0x0,
0xffffffff, 0xffffffff
};
////////////////////////////////////////////////////////////////////////////////
// Helpers for drawPath
#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;
GrPathCmd cmd;
GrPoint pts[4];
while ((cmd = path->next(pts)) != kEnd_PathCmd) {
switch (cmd) {
case kLine_PathCmd:
pointCount += 1;
break;
case kQuadratic_PathCmd:
pointCount += quadratic_point_count(pts, tol);
break;
case kCubic_PathCmd:
pointCount += cubic_point_count(pts, tol);
break;
case kMove_PathCmd:
pointCount += 1;
if (!first) {
++(*subpaths);
}
break;
default:
break;
}
first = false;
}
return pointCount;
}
static inline bool single_pass_path(const GrDrawTarget& target,
const GrPathIter& path,
GrPathFill fill) {
#if STENCIL_OFF
return true;
#else
if (kEvenOdd_PathFill == fill) {
GrConvexHint hint = path.convexHint();
return hint == kConvex_ConvexHint ||
hint == kNonOverlappingConvexPieces_ConvexHint;
} else if (kWinding_PathFill == fill) {
GrConvexHint hint = path.convexHint();
return hint == kConvex_ConvexHint ||
hint == kNonOverlappingConvexPieces_ConvexHint ||
(hint == kSameWindingConvexPieces_ConvexHint &&
target.canDisableBlend() && !target.isDitherState());
}
return false;
#endif
}
bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target,
GrPathIter* path,
GrPathFill fill) const {
return single_pass_path(*target, *path, fill);
}
void GrDefaultPathRenderer::drawPathHelper(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
GrPathIter* path,
GrPathFill fill,
const GrPoint* translate,
bool stencilOnly) {
GrDrawTarget::AutoStateRestore asr(target);
bool colorWritesWereDisabled = target->isColorWriteDisabled();
// face culling doesn't make sense here
GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
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);
path->rewind();
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;
const GrStencilSettings* passes[3];
GrDrawTarget::DrawFace drawFace[3];
bool reverse = false;
bool lastPassIsBounds;
if (kHairLine_PathFill == fill) {
type = kLineStrip_PrimitiveType;
passCount = 1;
if (stencilOnly) {
passes[0] = &gDirectToStencil;
} else {
passes[0] = NULL;
}
lastPassIsBounds = false;
drawFace[0] = GrDrawTarget::kBoth_DrawFace;
} else {
type = kTriangleFan_PrimitiveType;
if (single_pass_path(*target, *path, fill)) {
passCount = 1;
if (stencilOnly) {
passes[0] = &gDirectToStencil;
} else {
passes[0] = NULL;
}
drawFace[0] = GrDrawTarget::kBoth_DrawFace;
lastPassIsBounds = false;
} else {
switch (fill) {
case kInverseEvenOdd_PathFill:
reverse = true;
// fallthrough
case kEvenOdd_PathFill:
passes[0] = &gEOStencilPass;
if (stencilOnly) {
passCount = 1;
lastPassIsBounds = false;
} else {
passCount = 2;
lastPassIsBounds = true;
if (reverse) {
passes[1] = &gInvEOColorPass;
} else {
passes[1] = &gEOColorPass;
}
}
drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace;
break;
case kInverseWinding_PathFill:
reverse = true;
// fallthrough
case kWinding_PathFill:
if (fSeparateStencil) {
if (fStencilWrapOps) {
passes[0] = &gWindStencilSeparateWithWrap;
} else {
passes[0] = &gWindStencilSeparateNoWrap;
}
passCount = 2;
drawFace[0] = GrDrawTarget::kBoth_DrawFace;
} else {
if (fStencilWrapOps) {
passes[0] = &gWindSingleStencilWithWrapInc;
passes[1] = &gWindSingleStencilWithWrapDec;
} else {
passes[0] = &gWindSingleStencilNoWrapInc;
passes[1] = &gWindSingleStencilNoWrapDec;
}
// which is cw and which is ccw is arbitrary.
drawFace[0] = GrDrawTarget::kCW_DrawFace;
drawFace[1] = GrDrawTarget::kCCW_DrawFace;
passCount = 3;
}
if (stencilOnly) {
lastPassIsBounds = false;
--passCount;
} else {
lastPassIsBounds = true;
drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace;
if (reverse) {
passes[passCount-1] = &gInvWindColorPass;
} else {
passes[passCount-1] = &gWindColorPass;
}
}
break;
default:
GrAssert(!"Unknown path fill!");
return;
}
}
}
GrPoint pts[4];
bool first = true;
int subpath = 0;
for (;;) {
GrPathCmd cmd = path->next(pts);
switch (cmd) {
case kMove_PathCmd:
if (!first) {
subpathVertCount[subpath] = vert-subpathBase;
subpathBase = vert;
++subpath;
}
*vert = pts[0];
vert++;
break;
case kLine_PathCmd:
*vert = pts[1];
vert++;
break;
case kQuadratic_PathCmd: {
generate_quadratic_points(pts[0], pts[1], pts[2],
tolSqd, &vert,
quadratic_point_count(pts, tol));
break;
}
case kCubic_PathCmd: {
generate_cubic_points(pts[0], pts[1], pts[2], pts[3],
tolSqd, &vert,
cubic_point_count(pts, tol));
break;
}
case kClose_PathCmd:
break;
case kEnd_PathCmd:
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);
}
}
// if we're stenciling we will follow with a pass that draws
// a bounding rect to set the color. We're stenciling when
// passCount > 1.
const int& boundVertexStart = maxPts;
GrPoint* boundsVerts = base + boundVertexStart;
if (lastPassIsBounds) {
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->setDrawFace(drawFace[p]);
if (NULL != passes[p]) {
target->setStencil(*passes[p]);
}
if (lastPassIsBounds && (p == passCount-1)) {
if (!colorWritesWereDisabled) {
target->disableState(GrDrawTarget::kNoColorWrites_StateBit);
}
target->drawNonIndexed(kTriangleFan_PrimitiveType,
boundVertexStart, 4);
} else {
if (passCount > 1) {
target->enableState(GrDrawTarget::kNoColorWrites_StateBit);
}
int baseVertex = 0;
for (int sp = 0; sp < subpathCnt; ++sp) {
target->drawNonIndexed(type,
baseVertex,
subpathVertCount[sp]);
baseVertex += subpathVertCount[sp];
}
}
}
}
void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
GrPathIter* path,
GrPathFill fill,
const GrPoint* translate) {
this->drawPathHelper(target, stages, path, fill, translate, false);
}
void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target,
GrPathIter* path,
GrPathFill fill,
const GrPoint* translate) {
GrAssert(kInverseEvenOdd_PathFill != fill);
GrAssert(kInverseWinding_PathFill != fill);
this->drawPathHelper(target, 0, path, fill, translate, true);
}