path ops -- handle non-finite numbers
Op() and Simplify() do nothing if the input
is non-finite. Add code and tests.
Review URL: https://codereview.chromium.org/14407006
git-svn-id: http://skia.googlecode.com/svn/trunk@8882 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp
index d1d7af8..57e3b41 100644
--- a/src/pathops/SkOpEdgeBuilder.cpp
+++ b/src/pathops/SkOpEdgeBuilder.cpp
@@ -16,6 +16,7 @@
gContourID = 0;
gSegmentID = 0;
#endif
+ fUnparseable = false;
fSecondHalf = preFetch();
}
@@ -28,8 +29,10 @@
preFetch();
}
-void SkOpEdgeBuilder::finish() {
- walk();
+bool SkOpEdgeBuilder::finish() {
+ if (fUnparseable || !walk()) {
+ return false;
+ }
complete();
if (fCurrentContour && !fCurrentContour->segments().count()) {
fContours.pop_back();
@@ -51,6 +54,7 @@
&fReducePts[rIndex]);
}
fExtra.reset(); // we're done with this
+ return true;
}
// Note that copying the points here avoids copying the resulting path later.
@@ -59,6 +63,10 @@
// OPTIMIZATION: This copies both sets of input points every time. If the input data was read
// directly, the output path would only need to be copied if it was also one of the input paths.
int SkOpEdgeBuilder::preFetch() {
+ if (!fPath->isFinite()) {
+ fUnparseable = true;
+ return 0;
+ }
SkPath::RawIter iter(*fPath);
SkPoint pts[4];
SkPath::Verb verb;
@@ -74,14 +82,25 @@
return fPathVerbs.count() - 1;
}
-void SkOpEdgeBuilder::walk() {
+bool SkOpEdgeBuilder::close() {
+ if (fFinalCurveStart && fFinalCurveEnd && *fFinalCurveStart != *fFinalCurveEnd) {
+ *fReducePts.append() = *fFinalCurveStart;
+ *fReducePts.append() = *fFinalCurveEnd;
+ const SkPoint* lineStart = fReducePts.end() - 2;
+ *fExtra.append() = fCurrentContour->addLine(lineStart);
+ }
+ complete();
+ return true;
+}
+
+bool SkOpEdgeBuilder::walk() {
SkPath::Verb reducedVerb;
uint8_t* verbPtr = fPathVerbs.begin();
uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
const SkPoint* pointsPtr = fPathPts.begin();
- const SkPoint* finalCurveStart = NULL;
- const SkPoint* finalCurveEnd = NULL;
SkPath::Verb verb;
+ fFinalCurveStart = NULL;
+ fFinalCurveEnd = NULL;
while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
if (verbPtr == endOfFirstHalf) {
fOperand = true;
@@ -89,64 +108,76 @@
verbPtr++;
switch (verb) {
case SkPath::kMove_Verb:
- complete();
+ if (fCurrentContour) {
+ if (fAllowOpenContours) {
+ complete();
+ } else if (!close()) {
+ return false;
+ }
+ }
if (!fCurrentContour) {
fCurrentContour = fContours.push_back_n(1);
fCurrentContour->setOperand(fOperand);
fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_PathOpsMask);
*fExtra.append() = -1; // start new contour
}
- finalCurveEnd = pointsPtr++;
+ fFinalCurveEnd = pointsPtr++;
continue;
- case SkPath::kLine_Verb:
+ case SkPath::kLine_Verb: {
+ const SkPoint& lineEnd = pointsPtr[0];
+ const SkPoint& lineStart = pointsPtr[-1];
// skip degenerate points
- if (pointsPtr[-1].fX != pointsPtr[0].fX || pointsPtr[-1].fY != pointsPtr[0].fY) {
- fCurrentContour->addLine(&pointsPtr[-1]);
+ if (lineStart.fX != lineEnd.fX || lineStart.fY != lineEnd.fY) {
+ fCurrentContour->addLine(&lineStart);
}
- break;
- case SkPath::kQuad_Verb:
- reducedVerb = SkReduceOrder::Quad(&pointsPtr[-1], &fReducePts);
+ } break;
+ case SkPath::kQuad_Verb: {
+ const SkPoint* quadStart = &pointsPtr[-1];
+ reducedVerb = SkReduceOrder::Quad(quadStart, &fReducePts);
if (reducedVerb == 0) {
break; // skip degenerate points
}
if (reducedVerb == 1) {
- *fExtra.append() =
- fCurrentContour->addLine(fReducePts.end() - 2);
+ const SkPoint* lineStart = fReducePts.end() - 2;
+ *fExtra.append() = fCurrentContour->addLine(lineStart);
break;
}
- fCurrentContour->addQuad(&pointsPtr[-1]);
- break;
- case SkPath::kCubic_Verb:
- reducedVerb = SkReduceOrder::Cubic(&pointsPtr[-1], &fReducePts);
+ fCurrentContour->addQuad(quadStart);
+ } break;
+ case SkPath::kCubic_Verb: {
+ const SkPoint* cubicStart = &pointsPtr[-1];
+ reducedVerb = SkReduceOrder::Cubic(cubicStart, &fReducePts);
if (reducedVerb == 0) {
break; // skip degenerate points
}
if (reducedVerb == 1) {
- *fExtra.append() = fCurrentContour->addLine(fReducePts.end() - 2);
+ const SkPoint* lineStart = fReducePts.end() - 2;
+ *fExtra.append() = fCurrentContour->addLine(lineStart);
break;
}
if (reducedVerb == 2) {
- *fExtra.append() = fCurrentContour->addQuad(fReducePts.end() - 3);
+ const SkPoint* quadStart = fReducePts.end() - 3;
+ *fExtra.append() = fCurrentContour->addQuad(quadStart);
break;
}
- fCurrentContour->addCubic(&pointsPtr[-1]);
- break;
+ fCurrentContour->addCubic(cubicStart);
+ } break;
case SkPath::kClose_Verb:
SkASSERT(fCurrentContour);
- if (finalCurveStart && finalCurveEnd
- && *finalCurveStart != *finalCurveEnd) {
- *fReducePts.append() = *finalCurveStart;
- *fReducePts.append() = *finalCurveEnd;
- *fExtra.append() = fCurrentContour->addLine(fReducePts.end() - 2);
+ if (!close()) {
+ return false;
}
- complete();
continue;
default:
SkDEBUGFAIL("bad verb");
- return;
+ return false;
}
- finalCurveStart = &pointsPtr[verb - 1];
+ fFinalCurveStart = &pointsPtr[verb - 1];
pointsPtr += verb;
SkASSERT(fCurrentContour);
}
+ if (fCurrentContour && !fAllowOpenContours && !close()) {
+ return false;
+ }
+ return true;
}
diff --git a/src/pathops/SkOpEdgeBuilder.h b/src/pathops/SkOpEdgeBuilder.h
index 68afc93..b827a2a 100644
--- a/src/pathops/SkOpEdgeBuilder.h
+++ b/src/pathops/SkOpEdgeBuilder.h
@@ -16,13 +16,15 @@
public:
SkOpEdgeBuilder(const SkPathWriter& path, SkTArray<SkOpContour>& contours)
: fPath(path.nativePath())
- , fContours(contours) {
+ , fContours(contours)
+ , fAllowOpenContours(true) {
init();
}
SkOpEdgeBuilder(const SkPath& path, SkTArray<SkOpContour>& contours)
: fPath(&path)
- , fContours(contours) {
+ , fContours(contours)
+ , fAllowOpenContours(false) {
init();
}
@@ -38,23 +40,28 @@
}
void addOperand(const SkPath& path);
- void finish();
+ bool finish();
void init();
private:
+ bool close();
int preFetch();
- void walk();
+ bool walk();
const SkPath* fPath;
- SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
- SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
+ SkTDArray<SkPoint> fPathPts;
+ SkTDArray<uint8_t> fPathVerbs;
SkOpContour* fCurrentContour;
SkTArray<SkOpContour>& fContours;
SkTDArray<SkPoint> fReducePts; // segments created on the fly
SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
SkPathOpsMask fXorMask[2];
+ const SkPoint* fFinalCurveStart;
+ const SkPoint* fFinalCurveEnd;
int fSecondHalf;
bool fOperand;
+ bool fAllowOpenContours;
+ bool fUnparseable;
};
#endif
diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp
index a0071a0..80e698c 100644
--- a/src/pathops/SkPathOpsOp.cpp
+++ b/src/pathops/SkPathOpsOp.cpp
@@ -226,7 +226,7 @@
{{ false, true }, { false, false }}, // rev diff
};
-void Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
+bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]
? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
@@ -246,7 +246,9 @@
SkOpEdgeBuilder builder(*minuend, contours);
const int xorMask = builder.xorMask();
builder.addOperand(*subtrahend);
- builder.finish();
+ if (!builder.finish()) {
+ return false;
+ }
result->reset();
result->setFillType(fillType);
const int xorOpMask = builder.xorMask();
@@ -255,7 +257,7 @@
xorOpMask == kEvenOdd_PathOpsMask);
SkOpContour** currentPtr = contourList.begin();
if (!currentPtr) {
- return;
+ return true;
}
SkOpContour** listEnd = contourList.end();
// find all intersections between segments
@@ -298,5 +300,7 @@
SkPathWriter assembled(temp);
Assemble(wrapper, &assembled);
*result = *assembled.nativePath();
+ result->setFillType(fillType);
}
+ return true;
}
diff --git a/src/pathops/SkPathOpsSimplify.cpp b/src/pathops/SkPathOpsSimplify.cpp
index 5c9d683..cb00aff 100644
--- a/src/pathops/SkPathOpsSimplify.cpp
+++ b/src/pathops/SkPathOpsSimplify.cpp
@@ -143,26 +143,27 @@
}
// FIXME : add this as a member of SkPath
-void Simplify(const SkPath& path, SkPath* result) {
+bool Simplify(const SkPath& path, SkPath* result) {
#if DEBUG_SORT || DEBUG_SWAP_TOP
gDebugSortCount = gDebugSortCountDefault;
#endif
// returns 1 for evenodd, -1 for winding, regardless of inverse-ness
- result->reset();
SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
: SkPath::kEvenOdd_FillType;
- result->setFillType(fillType);
- SkPathWriter simple(*result);
// turn path into list of segments
SkTArray<SkOpContour> contours;
SkOpEdgeBuilder builder(path, contours);
- builder.finish();
+ if (!builder.finish()) {
+ return false;
+ }
SkTDArray<SkOpContour*> contourList;
MakeContourList(contours, contourList, false, false);
SkOpContour** currentPtr = contourList.begin();
+ result->setFillType(fillType);
+ result->reset();
if (!currentPtr) {
- return;
+ return true;
}
SkOpContour** listEnd = contourList.end();
// find all intersections between segments
@@ -185,6 +186,7 @@
DebugShowActiveSpans(contourList);
#endif
// construct closed contours
+ SkPathWriter simple(*result);
if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
: !bridgeXor(contourList, &simple))
{ // if some edges could not be resolved, assemble remaining fragments
@@ -193,5 +195,7 @@
SkPathWriter assembled(temp);
Assemble(simple, &assembled);
*result = *assembled.nativePath();
+ result->setFillType(fillType);
}
+ return true;
}