Fixups for clipstack, convexity test for paths.
Review URL http://codereview.appspot.com/4250056/
git-svn-id: http://skia.googlecode.com/svn/trunk@891 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index f42e316..4a46e98 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -74,7 +74,7 @@
fVertexPoolInUse(false),
fIndexPoolInUse(false) {
#if GR_DEBUG
-// gr_run_unittests();
+ //gr_run_unittests();
#endif
resetStats();
}
@@ -338,9 +338,20 @@
int clipBit = rt.stencilBits();
clipBit = (1 << (clipBit-1));
+ // often we'll see the first two elements of the clip are
+ // the full rt size and another element intersected with it.
+ // We can skip the first full-size rect and save a big rect draw.
+ int firstElement = 0;
+ if (clip.getElementCount() > 1 &&
+ kRect_ClipType == clip.getElementType(0) &&
+ kIntersect_SetOp == clip.getOp(1)&&
+ clip.getRect(0).contains(bounds)) {
+ firstElement = 1;
+ }
+
// walk through each clip element and perform its set op
// with the existing clip.
- for (int c = 0; c < count; ++c) {
+ for (int c = firstElement; c < count; ++c) {
GrPathFill fill;
// enabled at bottom of loop
this->disableState(kModifyStencilClip_StateBit);
@@ -351,15 +362,16 @@
fill = kEvenOdd_PathFill;
} else {
fill = clip.getPathFill(c);
- canDrawDirectToClip = getPathRenderer()->requiresStencilPass(clip.getPath(c));
+ GrPathRenderer* pr = this->getPathRenderer();
+ canDrawDirectToClip = pr->requiresStencilPass(this, clip.getPath(c), fill);
}
- GrSetOp op = 0 == c ? kReplace_SetOp : clip.getOp(c);
+ GrSetOp op = firstElement == c ? kReplace_SetOp : clip.getOp(c);
int passes;
GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
- canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canDrawDirectToClip,
- clipBit, &fill,
+ canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canDrawDirectToClip,
+ clipBit, IsFillInverted(fill),
&passes, stencilSettings);
// draw the element to the client stencil bits if necessary
@@ -378,7 +390,9 @@
this->drawSimpleRect(clip.getRect(c), NULL, 0);
} else {
SET_RANDOM_COLOR
- getPathRenderer()->drawPathToStencil(this, clip.getPath(c), fill, NULL);
+ getPathRenderer()->drawPathToStencil(this, clip.getPath(c),
+ NonInvertedFill(fill),
+ NULL);
}
}
@@ -393,7 +407,9 @@
this->drawSimpleRect(clip.getRect(c), NULL, 0);
} else {
SET_RANDOM_COLOR
- getPathRenderer()->drawPath(this, 0, clip.getPath(c), fill, NULL);
+ getPathRenderer()->drawPath(this, 0,
+ clip.getPath(c),
+ fill, NULL);
}
} else {
SET_RANDOM_COLOR
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index 1e9d7bf..3ee524b 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -432,6 +432,10 @@
fHWBlendDisabled = false;
GR_GL(Enable(GL_BLEND));
+ // we don't use the zb at all
+ GR_GL(Disable(GL_DEPTH_TEST));
+ GR_GL(DepthMask(GL_FALSE));
+
GR_GL(Disable(GL_CULL_FACE));
GR_GL(FrontFace(GL_CCW));
fHWDrawState.fDrawFace = kBoth_DrawFace;
@@ -1097,10 +1101,18 @@
void GrGpuGL::eraseStencilClip(const GrIRect& rect) {
GrAssert(NULL != fCurrDrawState.fRenderTarget);
+#if 0
GLint stencilBitCount = fCurrDrawState.fRenderTarget->stencilBits();
GrAssert(stencilBitCount > 0);
GLint clipStencilMask = (1 << (stencilBitCount - 1));
-
+#else
+ // we could just clear the clip bit but when we go through
+ // angle a partial stencil mask will cause clears to be
+ // turned into draws. Our contract on GrDrawTarget says that
+ // changing the clip between stencil passes may or may not
+ // zero the client's clip bits. So we just clear the whole thing.
+ static const GLint clipStencilMask = ~0;
+#endif
flushRenderTarget();
flushScissor(&rect);
GR_GL(StencilMask(clipStencilMask));
diff --git a/gpu/src/GrGpuGLShaders2.cpp b/gpu/src/GrGpuGLShaders2.cpp
index 14134e7..77847e9 100644
--- a/gpu/src/GrGpuGLShaders2.cpp
+++ b/gpu/src/GrGpuGLShaders2.cpp
@@ -1257,7 +1257,10 @@
// invalidate the immediate mode color
fHWDrawState.fColor = GrColor_ILLEGAL;
} else {
- if (fHWDrawState.fColor != fCurrDrawState.fColor) {
+ if (fHWDrawState.fColor != fCurrDrawState.fColor &&
+ (!GR_AGGRESSIVE_SHADER_OPTS || 0xffffffff != fCurrDrawState.fColor)) {
+ // avoid pushing the color attrib if the shader will optimize it out
+
// OpenGL ES only supports the float varities of glVertexAttrib
float c[] = {
GrColorUnpackR(fCurrDrawState.fColor) / 255.f,
diff --git a/gpu/src/GrPath.cpp b/gpu/src/GrPath.cpp
index ca5c43b..fc53ac2 100644
--- a/gpu/src/GrPath.cpp
+++ b/gpu/src/GrPath.cpp
@@ -1,6 +1,8 @@
#include "GrPath.h"
-GrPath::GrPath() {}
+GrPath::GrPath() {
+ fConvexHint = kNone_ConvexHint;
+}
GrPath::GrPath(const GrPath& src) : INHERITED() {
GrPath::Iter iter(src);
@@ -15,13 +17,13 @@
}
bool GrPath::operator ==(const GrPath& path) const {
- if (fVerbs.count() != path.fVerbs.count() ||
+ if (fCmds.count() != path.fCmds.count() ||
fPts.count() != path.fPts.count()) {
return false;
}
- for (int v = 0; v < fVerbs.count(); ++v) {
- if (fVerbs[v] != path.fVerbs[v]) {
+ for (int v = 0; v < fCmds.count(); ++v) {
+ if (fCmds[v] != path.fCmds[v]) {
return false;
}
}
@@ -35,31 +37,31 @@
}
void GrPath::ensureMoveTo() {
- if (fVerbs.isEmpty() || this->wasLastVerb(kClose)) {
- *fVerbs.append() = kMove;
+ if (fCmds.isEmpty() || this->wasLastVerb(kClose_PathCmd)) {
+ *fCmds.append() = kMove_PathCmd;
fPts.append()->set(0, 0);
}
}
void GrPath::moveTo(GrScalar x, GrScalar y) {
- if (this->wasLastVerb(kMove)) {
+ if (this->wasLastVerb(kMove_PathCmd)) {
// overwrite prev kMove value
fPts[fPts.count() - 1].set(x, y);
} else {
- *fVerbs.append() = kMove;
+ *fCmds.append() = kMove_PathCmd;
fPts.append()->set(x, y);
}
}
void GrPath::lineTo(GrScalar x, GrScalar y) {
this->ensureMoveTo();
- *fVerbs.append() = kLine;
+ *fCmds.append() = kLine_PathCmd;
fPts.append()->set(x, y);
}
void GrPath::quadTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1) {
this->ensureMoveTo();
- *fVerbs.append() = kQuad;
+ *fCmds.append() = kQuadratic_PathCmd;
fPts.append()->set(x0, y0);
fPts.append()->set(x1, y1);
}
@@ -67,100 +69,371 @@
void GrPath::cubicTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1,
GrScalar x2, GrScalar y2) {
this->ensureMoveTo();
- *fVerbs.append() = kCubic;
+ *fCmds.append() = kCubic_PathCmd;
fPts.append()->set(x0, y0);
fPts.append()->set(x1, y1);
fPts.append()->set(x2, y2);
}
void GrPath::close() {
- if (!fVerbs.isEmpty() && !this->wasLastVerb(kClose)) {
+ if (!fCmds.isEmpty() && !this->wasLastVerb(kClose_PathCmd)) {
// should we allow kMove followed by kClose?
- *fVerbs.append() = kClose;
+ *fCmds.append() = kClose_PathCmd;
}
}
///////////////////////////////////////////////////////////////////////////////
+static bool check_two_vecs(const GrVec& prevVec,
+ const GrVec& currVec,
+ GrScalar turnDir,
+ int* xDir,
+ int* yDir,
+ int* flipX,
+ int* flipY) {
+ if (currVec.fX * *xDir < 0) {
+ ++*flipX;
+ if (*flipX > 2) {
+ return false;
+ }
+ *xDir = -*xDir;
+ }
+ if (currVec.fY * *yDir < 0) {
+ ++*flipY;
+ if (*flipY > 2) {
+ return false;
+ }
+ *yDir = -*yDir;
+ }
+ GrScalar d = prevVec.cross(currVec);
+ return (d * turnDir) >= 0;
+}
+
+static void init_from_two_vecs(const GrVec& firstVec,
+ const GrVec& secondVec,
+ GrScalar* turnDir,
+ int* xDir, int* yDir) {
+ *turnDir = firstVec.cross(secondVec);
+ if (firstVec.fX > 0) {
+ *xDir = 1;
+ } else if (firstVec.fX < 0) {
+ *xDir = -1;
+ } else {
+ *xDir = 0;
+ }
+ if (firstVec.fY > 0) {
+ *yDir = 1;
+ } else if (firstVec.fY < 0) {
+ *yDir = -1;
+ } else {
+ *yDir = 0;
+ }
+}
+
void GrPath::resetFromIter(GrPathIter* iter) {
fPts.reset();
- fVerbs.reset();
+ fCmds.reset();
+ fConvexHint = iter->convexHint();
+
+ // first point of the subpath
+ GrPoint firstPt;
+ // first edge of the subpath
+ GrVec firstVec;
+ // vec of most recently processed edge, that wasn't degenerate
+ GrVec previousVec;
+ // most recently processed point
+ GrPoint previousPt;
+
+ // sign indicates whether we're bending left or right
+ GrScalar turnDir;
+ // number of times the direction has flipped in x or y
+
+ // we track which direction we are moving in x/y and the
+ // number of times it changes.
+ int xDir;
+ int yDir;
+ int flipX;
+ int flipY;
+
+ // counts number of sub path pts that didn't add a degenerate edge.
+ int subPathPts = 0;
+
+ int numSubPaths = 0;
+ iter->rewind();
+ GrPathCmd cmd;
GrPoint pts[4];
- GrPathIter::Command cmd;
-
- while ((cmd = iter->next(pts)) != GrPathIter::kEnd_Command) {
+ bool subPathClosed;
+ do {
+ cmd = iter->next(pts);
+ // If the convexity test is ever updated to handle multiple subpaths
+ // the loop has to be adjusted to handle moving to a new subpath without
+ // closing the previous one. Currently the implicit closing vectors for a
+ // filled path would never be examined.
switch (cmd) {
- case GrPathIter::kMove_Command:
+ case kMove_PathCmd:
this->moveTo(pts[0].fX, pts[0].fY);
+ subPathPts = 0;
+ subPathClosed = false;
break;
- case GrPathIter::kLine_Command:
+ case kLine_PathCmd:
this->lineTo(pts[1].fX, pts[1].fY);
break;
- case GrPathIter::kQuadratic_Command:
+ case kQuadratic_PathCmd:
this->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
break;
- case GrPathIter::kCubic_Command:
+ case kCubic_PathCmd:
this->cubicTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
pts[3].fX, pts[3].fY);
break;
- case GrPathIter::kClose_Command:
+ case kClose_PathCmd:
this->close();
+ subPathClosed = true;
break;
- case GrPathIter::kEnd_Command:
- // never get here, but include it to avoid the warning
+ case kEnd_PathCmd:
break;
}
+ int n = NumPathCmdPoints(cmd);
+ if (0 == subPathPts && n > 0) {
+ previousPt = pts[0];
+ firstPt = previousPt;
+ flipX = 0;
+ flipY = 0;
+ turnDir = 0;
+ subPathPts = 1;
+ ++numSubPaths;
+ }
+ // either we skip the first pt because it is redundant with
+ // last point of the previous subpath cmd or we just ate it
+ // in the above if.
+ int consumed = 1;
+ if (numSubPaths < 2 && kNone_ConvexHint == fConvexHint) {
+ while (consumed < n) {
+ GrAssert(pts[consumed-1] == previousPt);
+ GrVec vec;
+ vec.setBetween(previousPt, pts[consumed]);
+ if (vec.fX || vec.fY) {
+ if (subPathPts >= 2) {
+ if (0 == turnDir) {
+ firstVec = previousVec;
+ init_from_two_vecs(firstVec, vec,
+ &turnDir, &xDir, &yDir);
+ // here we aren't checking whether the x/y dirs
+ // change between the first and second edge. It
+ // gets covered when the path is closed.
+ } else {
+ if (!check_two_vecs(previousVec, vec, turnDir,
+ &xDir, &yDir,
+ &flipX, &flipY)) {
+ fConvexHint = kConcave_ConvexHint;
+ break;
+ }
+ }
+ }
+ previousVec = vec;
+ previousPt = pts[consumed];
+ ++subPathPts;
+ }
+ ++consumed;
+ }
+ if (subPathPts > 2 && (kClose_PathCmd == cmd ||
+ (!subPathClosed && kEnd_PathCmd == cmd ))) {
+ // if an additional vector is needed to close the loop check
+ // that it validates against the previous vector.
+ GrVec vec;
+ vec.setBetween(previousPt, firstPt);
+ if (vec.fX || vec.fY) {
+ if (!check_two_vecs(previousVec, vec, turnDir,
+ &xDir, &yDir, &flipX, &flipY)) {
+ fConvexHint = kConcave_ConvexHint;
+ break;
+ }
+ previousVec = vec;
+ }
+ // check that closing vector validates against the first vector.
+ if (!check_two_vecs(previousVec, firstVec, turnDir,
+ &xDir, &yDir, &flipX, &flipY)) {
+ fConvexHint = kConcave_ConvexHint;
+ break;
+ }
+ }
+ }
+ } while (cmd != kEnd_PathCmd);
+ if (kNone_ConvexHint == fConvexHint && numSubPaths < 2) {
+ fConvexHint = kConvex_ConvexHint;
+ } else {
+ bool recurse = false;
+ if (recurse) {
+ this->resetFromIter(iter);
+ }
}
- fConvexHint = iter->convexHint();
}
+void GrPath::ConvexUnitTest() {
+ GrPath testPath;
+ GrPath::Iter testIter;
+
+ GrPath pt;
+ pt.moveTo(0, 0);
+ pt.close();
+
+ testIter.reset(pt);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+ GrPath line;
+ line.moveTo(GrIntToScalar(12), GrIntToScalar(20));
+ line.lineTo(GrIntToScalar(-12), GrIntToScalar(-20));
+ line.close();
+
+ testIter.reset(line);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+ GrPath triLeft;
+ triLeft.moveTo(0, 0);
+ triLeft.lineTo(1, 0);
+ triLeft.lineTo(1, 1);
+ triLeft.close();
+
+ testIter.reset(triLeft);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+ GrPath triRight;
+ triRight.moveTo(0, 0);
+ triRight.lineTo(-1, 0);
+ triRight.lineTo(1, 1);
+ triRight.close();
+
+ testIter.reset(triRight);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+ GrPath square;
+ square.moveTo(0, 0);
+ square.lineTo(1, 0);
+ square.lineTo(1, 1);
+ square.lineTo(0, 1);
+ square.close();
+
+ testIter.reset(square);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+ GrPath redundantSquare;
+ square.moveTo(0, 0);
+ square.lineTo(0, 0);
+ square.lineTo(0, 0);
+ square.lineTo(1, 0);
+ square.lineTo(1, 0);
+ square.lineTo(1, 0);
+ square.lineTo(1, 1);
+ square.lineTo(1, 1);
+ square.lineTo(1, 1);
+ square.lineTo(0, 1);
+ square.lineTo(0, 1);
+ square.lineTo(0, 1);
+ square.close();
+
+ testIter.reset(redundantSquare);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+ GrPath bowTie;
+ bowTie.moveTo(0, 0);
+ bowTie.lineTo(0, 0);
+ bowTie.lineTo(0, 0);
+ bowTie.lineTo(1, 1);
+ bowTie.lineTo(1, 1);
+ bowTie.lineTo(1, 1);
+ bowTie.lineTo(1, 0);
+ bowTie.lineTo(1, 0);
+ bowTie.lineTo(1, 0);
+ bowTie.lineTo(0, 1);
+ bowTie.lineTo(0, 1);
+ bowTie.lineTo(0, 1);
+ bowTie.close();
+
+ testIter.reset(bowTie);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
+
+ GrPath spiral;
+ spiral.moveTo(0, 0);
+ spiral.lineTo(1, 0);
+ spiral.lineTo(1, 1);
+ spiral.lineTo(0, 1);
+ spiral.lineTo(0,.5);
+ spiral.lineTo(.5,.5);
+ spiral.lineTo(.5,.75);
+ spiral.close();
+
+ testIter.reset(spiral);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
+
+ GrPath dent;
+ dent.moveTo(0, 0);
+ dent.lineTo(1, 1);
+ dent.lineTo(0, 1);
+ dent.lineTo(-.5,2);
+ dent.lineTo(-2, 1);
+ dent.close();
+
+ testIter.reset(dent);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
+}
///////////////////////////////////////////////////////////////////////////////
-GrPath::Iter::Iter(const GrPath& path) : fPath(path) {
+GrPath::Iter::Iter() : fPath(NULL) {
+}
+
+GrPath::Iter::Iter(const GrPath& path) : fPath(&path) {
this->rewind();
}
-GrPathIter::Command GrPath::Iter::next(GrPoint points[]) {
- if (fVerbIndex == fPath.fVerbs.count()) {
- GrAssert(fPtIndex == fPath.fPts.count());
- return GrPathIter::kEnd_Command;
+GrPathCmd GrPath::Iter::next(GrPoint points[]) {
+ if (fCmdIndex == fPath->fCmds.count()) {
+ GrAssert(fPtIndex == fPath->fPts.count());
+ return kEnd_PathCmd;
} else {
- GrAssert(fVerbIndex < fPath.fVerbs.count());
+ GrAssert(fCmdIndex < fPath->fCmds.count());
}
- uint8_t cmd = fPath.fVerbs[fVerbIndex++];
- const GrPoint* srcPts = fPath.fPts.begin() + fPtIndex;
+ GrPathCmd cmd = fPath->fCmds[fCmdIndex++];
+ const GrPoint* srcPts = fPath->fPts.begin() + fPtIndex;
switch (cmd) {
- case kMove:
+ case kMove_PathCmd:
if (points) {
points[0] = srcPts[0];
}
fLastPt = srcPts[0];
- GrAssert(fPtIndex <= fPath.fPts.count() + 1);
+ GrAssert(fPtIndex <= fPath->fPts.count() + 1);
fPtIndex += 1;
break;
- case kLine:
+ case kLine_PathCmd:
if (points) {
points[0] = fLastPt;
points[1] = srcPts[0];
}
fLastPt = srcPts[0];
- GrAssert(fPtIndex <= fPath.fPts.count() + 1);
+ GrAssert(fPtIndex <= fPath->fPts.count() + 1);
fPtIndex += 1;
break;
- case kQuad:
+ case kQuadratic_PathCmd:
if (points) {
points[0] = fLastPt;
points[1] = srcPts[0];
points[2] = srcPts[1];
}
- fLastPt = srcPts[2];
- GrAssert(fPtIndex <= fPath.fPts.count() + 2);
+ fLastPt = srcPts[1];
+ GrAssert(fPtIndex <= fPath->fPts.count() + 2);
fPtIndex += 2;
break;
- case kCubic:
+ case kCubic_PathCmd:
if (points) {
points[0] = fLastPt;
points[1] = srcPts[0];
@@ -168,29 +441,33 @@
points[3] = srcPts[2];
}
fLastPt = srcPts[2];
- GrAssert(fPtIndex <= fPath.fPts.count() + 3);
+ GrAssert(fPtIndex <= fPath->fPts.count() + 3);
fPtIndex += 3;
break;
- case kClose:
+ case kClose_PathCmd:
break;
default:
- GrAssert(!"unknown grpath verb");
+ GrAssert(!"unknown grpath cmd");
break;
}
- return (GrPathIter::Command)cmd;
+ return cmd;
}
-GrPathIter::ConvexHint GrPath::Iter::convexHint() const {
- return fPath.getConvexHint();
+GrConvexHint GrPath::Iter::convexHint() const {
+ return fPath->getConvexHint();
}
-GrPathIter::Command GrPath::Iter::next() {
+GrPathCmd GrPath::Iter::next() {
return this->next(NULL);
}
void GrPath::Iter::rewind() {
- fVerbIndex = fPtIndex = 0;
+ this->reset(*fPath);
}
+void GrPath::Iter::reset(const GrPath& path) {
+ fPath = &path;
+ fCmdIndex = fPtIndex = 0;
+}
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index 3e2b4b3..6d7aabb 100644
--- a/gpu/src/GrPathRenderer.cpp
+++ b/gpu/src/GrPathRenderer.cpp
@@ -240,22 +240,22 @@
bool first = true;
- GrPathIter::Command cmd;
+ GrPathCmd cmd;
GrPoint pts[4];
- while ((cmd = path->next(pts)) != GrPathIter::kEnd_Command) {
+ while ((cmd = path->next(pts)) != kEnd_PathCmd) {
switch (cmd) {
- case GrPathIter::kLine_Command:
+ case kLine_PathCmd:
pointCount += 1;
break;
- case GrPathIter::kQuadratic_Command:
+ case kQuadratic_PathCmd:
pointCount += quadratic_point_count(pts, tol);
break;
- case GrPathIter::kCubic_Command:
+ case kCubic_PathCmd:
pointCount += cubic_point_count(pts, tol);
break;
- case GrPathIter::kMove_Command:
+ case kMove_PathCmd:
pointCount += 1;
if (!first) {
++(*subpaths);
@@ -269,21 +269,21 @@
return pointCount;
}
-static inline bool single_pass_path(const GrPathIter& path,
- GrPathFill fill,
- const GrDrawTarget& target) {
+static inline bool single_pass_path(const GrDrawTarget& target,
+ const GrPathIter& path,
+ GrPathFill fill) {
#if STENCIL_OFF
return true;
#else
if (kEvenOdd_PathFill == fill) {
- GrPathIter::ConvexHint hint = path.convexHint();
- return hint == GrPathIter::kConvex_ConvexHint ||
- hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint;
+ GrConvexHint hint = path.convexHint();
+ return hint == kConvex_ConvexHint ||
+ hint == kNonOverlappingConvexPieces_ConvexHint;
} else if (kWinding_PathFill == fill) {
- GrPathIter::ConvexHint hint = path.convexHint();
- return hint == GrPathIter::kConvex_ConvexHint ||
- hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint ||
- (hint == GrPathIter::kSameWindingConvexPieces_ConvexHint &&
+ GrConvexHint hint = path.convexHint();
+ return hint == kConvex_ConvexHint ||
+ hint == kNonOverlappingConvexPieces_ConvexHint ||
+ (hint == kSameWindingConvexPieces_ConvexHint &&
target.canDisableBlend() && !target.isDitherState());
}
@@ -291,6 +291,12 @@
#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,
@@ -358,18 +364,18 @@
if (stencilOnly) {
passes[0] = &gDirectToStencil;
} else {
- passes[0] = &GrStencilSettings::gDisabled;
+ passes[0] = NULL;
}
lastPassIsBounds = false;
drawFace[0] = GrDrawTarget::kBoth_DrawFace;
} else {
type = kTriangleFan_PrimitiveType;
- if (single_pass_path(*path, fill, *target)) {
+ if (single_pass_path(*target, *path, fill)) {
passCount = 1;
if (stencilOnly) {
passes[0] = &gDirectToStencil;
} else {
- passes[0] = &GrStencilSettings::gDisabled;
+ passes[0] = NULL;
}
drawFace[0] = GrDrawTarget::kBoth_DrawFace;
lastPassIsBounds = false;
@@ -446,9 +452,9 @@
int subpath = 0;
for (;;) {
- GrPathIter::Command cmd = path->next(pts);
+ GrPathCmd cmd = path->next(pts);
switch (cmd) {
- case GrPathIter::kMove_Command:
+ case kMove_PathCmd:
if (!first) {
subpathVertCount[subpath] = vert-subpathBase;
subpathBase = vert;
@@ -457,25 +463,25 @@
*vert = pts[0];
vert++;
break;
- case GrPathIter::kLine_Command:
+ case kLine_PathCmd:
*vert = pts[1];
vert++;
break;
- case GrPathIter::kQuadratic_Command: {
+ case kQuadratic_PathCmd: {
generate_quadratic_points(pts[0], pts[1], pts[2],
tolSqd, &vert,
quadratic_point_count(pts, tol));
break;
}
- case GrPathIter::kCubic_Command: {
+ case kCubic_PathCmd: {
generate_cubic_points(pts[0], pts[1], pts[2], pts[3],
tolSqd, &vert,
cubic_point_count(pts, tol));
break;
}
- case GrPathIter::kClose_Command:
+ case kClose_PathCmd:
break;
- case GrPathIter::kEnd_Command:
+ case kEnd_PathCmd:
subpathVertCount[subpath] = vert-subpathBase;
++subpath; // this could be only in debug
goto FINISHED;
@@ -519,7 +525,9 @@
for (int p = 0; p < passCount; ++p) {
target->setDrawFace(drawFace[p]);
- target->setStencil(*passes[p]);
+ if (NULL != passes[p]) {
+ target->setStencil(*passes[p]);
+ }
if (lastPassIsBounds && (p == passCount-1)) {
if (!colorWritesWereDisabled) {
diff --git a/gpu/src/GrPathRenderer.h b/gpu/src/GrPathRenderer.h
index 467b0a0..f99b922 100644
--- a/gpu/src/GrPathRenderer.h
+++ b/gpu/src/GrPathRenderer.h
@@ -60,29 +60,41 @@
* For complex clips Gr uses the stencil buffer. The path renderer must be
* able to render paths into the stencil buffer. However, the path renderer
* itself may require the stencil buffer to resolve the path fill rule. This
- * function queries whether the path render requires its own stencil
+ * function queries whether the path render needs its own stencil
* pass. If this returns false then drawPath() should not modify the
- * the target's stencil settings.
+ * the target's stencil settings but use those already set on target.
+ *
+ * @param target target that the path will be rendered to
+ * @param path the path that will be drawn
+ * @param fill the fill rule that will be used
*
* @return false if this path renderer can generate interior-only fragments
* without changing the stencil settings on the target. If it
* returns true the drawPathToStencil will be used when rendering
* clips.
*/
- virtual bool requiresStencilPass(GrPathIter*) const { return false; }
+ virtual bool requiresStencilPass(const GrDrawTarget* target,
+ GrPathIter* path,
+ GrPathFill fill) const { return false; }
- bool requiresStencilPass(const GrPath& path) const {
+ bool requiresStencilPass(const GrDrawTarget* target,
+ const GrPath& path,
+ GrPathFill fill) const {
GrPath::Iter iter(path);
- return requiresStencilPass(&iter);
+ return requiresStencilPass(target, &iter, fill);
}
/**
- * Draws a path to the stencil buffer. Assume the writable bits are zero
- * prior and write a nonzero value in interior samples. The default
- * implementation assumes the path filling algorithm doesn't require a
- * separate stencil pass and so just calls drawPath.
+ * Draws a path to the stencil buffer. Assume the writable stencil bits
+ * are already initialized to zero. Fill will always be either
+ * kWinding_PathFill or kEvenOdd_PathFill.
*
- * Fill will never be an inverse fill rule.
+ * Only called if requiresStencilPass returns true for the same combo of
+ * target, path, and fill (or inverse of the fill).
+ *
+ * The default implementation assumes the path filling algorithm doesn't
+ * require a separate stencil pass and so crashes.
+ *
*
* @param target the target to draw into.
* @param path the path to draw.
@@ -94,10 +106,7 @@
GrPathIter* path,
GrPathFill fill,
const GrPoint* translate) {
- GrAssert(kInverseEvenOdd_PathFill != fill);
- GrAssert(kInverseWinding_PathFill != fill);
-
- this->drawPath(target, 0, path, fill, translate);
+ GrCrash("Unexpected call to drawPathToStencil.");
}
void drawPathToStencil(GrDrawTarget* target,
@@ -119,7 +128,9 @@
GrPathIter* path,
GrPathFill fill,
const GrPoint* translate);
- virtual bool requiresStencilPass(GrPath&) const { return true; }
+ virtual bool requiresStencilPass(const GrDrawTarget* target,
+ GrPathIter* path,
+ GrPathFill fill) const;
virtual void drawPathToStencil(GrDrawTarget* target,
GrPathIter* path,
GrPathFill fill,
diff --git a/gpu/src/GrRedBlackTree.h b/gpu/src/GrRedBlackTree.h
index e6448fb..7ba326f 100644
--- a/gpu/src/GrRedBlackTree.h
+++ b/gpu/src/GrRedBlackTree.h
@@ -355,6 +355,8 @@
x->fChildren[kRight_Child] = NULL;
x->fItem = t;
+ Node* returnNode = x;
+
Node* gp = NULL;
Node* p = NULL;
Node* n = fRoot;
@@ -371,7 +373,6 @@
gp = p;
p = n;
n = p->fChildren[pc];
-
}
if (last) {
fLast = x;
@@ -385,7 +386,7 @@
x->fColor = kBlack_Color;
x->fParent = NULL;
GrAssert(1 == fCount);
- return Iter(x, this);
+ return Iter(returnNode, this);
}
p->fChildren[pc] = x;
x->fColor = kRed_Color;
@@ -404,7 +405,7 @@
// if x's parent is black then we didn't violate any of the
// red/black properties when we added x as red.
if (kBlack_Color == p->fColor) {
- return Iter(x, this);
+ return Iter(returnNode, this);
}
// gp must be valid because if p was the root then it is black
GrAssert(NULL != gp);
@@ -428,7 +429,7 @@
GrAssert(fRoot == x);
x->fColor = kBlack_Color;
validate();
- return Iter(x, this);
+ return Iter(returnNode, this);
}
gp = p->fParent;
pc = (p->fChildren[kLeft_Child] == x) ? kLeft_Child :
@@ -481,7 +482,7 @@
rotateLeft(gp);
}
validate();
- return Iter(x, this);
+ return Iter(returnNode, this);
}
@@ -656,6 +657,7 @@
Color xcolor = x->fColor;
p->fChildren[pc] = NULL;
delete x;
+ x = NULL;
// when x is red it can be with an implicit black leaf without
// violating any of the red-black tree properties.
if (kRed_Color == xcolor) {
@@ -716,9 +718,9 @@
}
// x and s are now both black.
GrAssert(kBlack_Color == s->fColor);
- GrAssert(kBlack_Color == x->fColor);
+ GrAssert(NULL == x || kBlack_Color == x->fColor);
GrAssert(p == s->fParent);
- GrAssert(p == x->fParent);
+ GrAssert(NULL == x || p == x->fParent);
// when x is deleted its subtree will have reduced black-height.
slRed = (NULL != sl && kRed_Color == sl->fColor);
diff --git a/gpu/src/GrStencil.cpp b/gpu/src/GrStencil.cpp
index 9d68c65..a1c8c09 100644
--- a/gpu/src/GrStencil.cpp
+++ b/gpu/src/GrStencil.cpp
@@ -243,84 +243,63 @@
0x00000000, 0x00000000 // set clip bit
};
-static const GrPathFill gNonInvertedFills[] = {
- kWinding_PathFill, // kWinding_PathFill
- kEvenOdd_PathFill, // kEvenOdd_PathFill
- kWinding_PathFill, // kInverseWinding_PathFill
- kEvenOdd_PathFill, // kInverseEvenOdd_PathFill
- kWinding_PathFill, // kHairLine_PathFill
-};
-
-static const bool gIsFillInverted[] = {
- false, // kWinding_PathFill
- false, // kEvenOdd_PathFill
- true, // kInverseWinding_PathFill
- true, // kInverseEvenOdd_PathFill
- false, // kHairLine_PathFill
-};
-GR_STATIC_ASSERT(0 == kWinding_PathFill);
-GR_STATIC_ASSERT(1 == kEvenOdd_PathFill);
-GR_STATIC_ASSERT(2 == kInverseWinding_PathFill);
-GR_STATIC_ASSERT(3 == kInverseEvenOdd_PathFill);
-GR_STATIC_ASSERT(4 == kHairLine_PathFill);
-GR_STATIC_ASSERT(5 == kPathFillCount);
-
bool GrStencilSettings::GetClipPasses(GrSetOp op,
bool canBeDirect,
unsigned int stencilClipMask,
- GrPathFill* fill,
+ bool invertedFill,
int* numPasses,
GrStencilSettings settings[kMaxStencilClipPasses]) {
if (canBeDirect) {
- if (!gIsFillInverted[*fill]) {
- *numPasses = 0;
- switch (op) {
- case kReplace_SetOp:
- *numPasses = 1;
- settings[0] = gReplaceClip;
- break;
- case kUnion_SetOp:
- *numPasses = 1;
- settings[0] = gUnionClip;
- break;
- case kXor_SetOp:
- *numPasses = 1;
- settings[0] = gXorClip;
- break;
- case kDifference_SetOp:
- *numPasses = 1;
- settings[0] = gDiffClip;
- break;
- default: // suppress warning
- break;
- }
- if (1 == *numPasses) {
- settings[0].fFrontFuncRef |= stencilClipMask;
- settings[0].fFrontWriteMask |= stencilClipMask;
- settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
- settings[0].fBackWriteMask = settings[0].fFrontWriteMask;
- return true;
- }
+ *numPasses = 0;
+ switch (op) {
+ case kReplace_SetOp:
+ *numPasses = 1;
+ settings[0] = gReplaceClip;
+ break;
+ case kUnion_SetOp:
+ *numPasses = 1;
+ settings[0] = gUnionClip;
+ break;
+ case kXor_SetOp:
+ *numPasses = 1;
+ settings[0] = gXorClip;
+ break;
+ case kDifference_SetOp:
+ *numPasses = 1;
+ settings[0] = gDiffClip;
+ break;
+ default: // suppress warning
+ break;
+ }
+ if (1 == *numPasses) {
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fFrontWriteMask |= stencilClipMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+ settings[0].fBackWriteMask = settings[0].fFrontWriteMask;
+ return true;
}
}
switch (op) {
+ // if we make the path renderer go to stencil we always give it a
+ // non-inverted fill and we use the stencil rules on the client->clipbit
+ // pass to select either the zeros or nonzeros.
case kReplace_SetOp:
*numPasses= 1;
- settings[0] = gIsFillInverted[*fill] ? gInvUserToClipReplace : gUserToClipReplace;
+ settings[0] = invertedFill ? gInvUserToClipReplace : gUserToClipReplace;
settings[0].fFrontFuncMask &= ~stencilClipMask;
settings[0].fFrontFuncRef |= stencilClipMask;
settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
-
+ break;
case kIntersect_SetOp:
*numPasses = 1;
- settings[0] = gIsFillInverted[*fill] ? gInvUserToClipIsect : gUserToClipIsect;
+ settings[0] = invertedFill ? gInvUserToClipIsect : gUserToClipIsect;
settings[0].fFrontFuncRef = stencilClipMask;
settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
break;
case kUnion_SetOp:
*numPasses = 2;
- if (gIsFillInverted[*fill]) {
+ if (invertedFill) {
settings[0] = gInvUserToClipUnionPass0;
settings[0].fFrontFuncRef |= stencilClipMask;
settings[0].fBackFuncRef = settings[0].fFrontFuncMask;
@@ -345,7 +324,7 @@
break;
case kXor_SetOp:
*numPasses = 2;
- if (gIsFillInverted[*fill]) {
+ if (invertedFill) {
settings[0] = gInvUserToClipXorPass0;
settings[0].fFrontFuncMask &= ~stencilClipMask;
settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
@@ -365,12 +344,12 @@
break;
case kDifference_SetOp:
*numPasses = 1;
- settings[0] = gIsFillInverted[*fill] ? gInvUserToClipDiff : gUserToClipDiff;
+ settings[0] = invertedFill ? gInvUserToClipDiff : gUserToClipDiff;
settings[0].fFrontFuncRef |= stencilClipMask;
settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
break;
case kReverseDifference_SetOp:
- if (gIsFillInverted[*fill]) {
+ if (invertedFill) {
*numPasses = 1;
settings[0] = gInvUserToClipRDiff;
settings[0].fFrontWriteMask |= stencilClipMask;
@@ -393,6 +372,5 @@
default:
GrCrash("Unknown set op");
}
- *fill = gNonInvertedFills[*fill];
return false;
}
\ No newline at end of file
diff --git a/gpu/src/gr_unittests.cpp b/gpu/src/gr_unittests.cpp
index 9caaa1f..16fd9dc 100644
--- a/gpu/src/gr_unittests.cpp
+++ b/gpu/src/gr_unittests.cpp
@@ -15,11 +15,12 @@
*/
-#include "GrClip.h"
+#include "GrDrawTarget.h"
#include "GrTDArray.h"
#include "GrTBSearch.h"
#include "GrMatrix.h"
#include "GrRedBlackTree.h"
+#include "GrPath.h"
static void dump(const GrTDArray<int>& array) {
#if 0
@@ -78,6 +79,8 @@
test_bsearch();
GrMatrix::UnitTest();
GrRedBlackTree<int>::UnitTest();
+ GrPath::ConvexUnitTest();
+ GrDrawTarget::VertexLayoutUnitTest();
}