fix path ops fuzz buster

Mark collapsed segments as done and remove collapsed
segment references from the coincidence array.

Also add test names to global debugging.

R=fmalita@chromium.org
BUG=512592

Review URL: https://codereview.chromium.org/1250293002
This commit is contained in:
caryclark 2015-07-23 12:40:22 -07:00 committed by Commit bot
parent a0170f10aa
commit d4349723fa
19 changed files with 260 additions and 15 deletions

View File

@ -44,7 +44,7 @@ void FixWinding(SkPath* path) {
}
SkChunkAlloc allocator(4096);
SkOpContourHead contourHead;
SkOpGlobalState globalState(NULL, &contourHead);
SkOpGlobalState globalState(NULL, &contourHead SkDEBUGPARAMS(NULL));
SkOpEdgeBuilder builder(*path, &contourHead, &allocator, &globalState);
builder.finish(&allocator);
SkASSERT(contourHead.next());

View File

@ -568,6 +568,16 @@ void SkOpCoincidence::fixAligned() {
coin->fOppPtTEnd = coin->fOppPtTEnd->doppelganger();
}
} while ((coin = coin->fNext));
coin = fHead;
SkCoincidentSpans** priorPtr = &fHead;
do {
if (coin->fCoinPtTStart->collapsed(coin->fCoinPtTEnd)
|| coin->fOppPtTStart->collapsed(coin->fOppPtTEnd)) {
*priorPtr = coin->fNext;
continue;
}
priorPtr = &coin->fNext;
} while ((coin = coin->fNext));
}
void SkOpCoincidence::fixUp(SkOpPtT* deleted, SkOpPtT* kept) {

View File

@ -192,6 +192,15 @@ public:
return fTail->pts()[SkPathOpsVerbToPoints(fTail->verb())];
}
bool findCollapsed() {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
do {
segment->findCollapsed();
} while ((segment = segment->next()));
return true;
}
SkOpSpan* findSortableTop(SkOpContour* );
SkOpSegment* first() {

View File

@ -615,6 +615,15 @@ double SkOpSegment::distSq(double t, SkOpAngle* oppAngle) {
return closestDistSq;
}
void SkOpSegment::findCollapsed() {
if (fHead.contains(&fTail)) {
markAllDone();
// move start and end to the same point
fHead.alignEnd(0, fHead.pt());
fTail.setAligned();
}
}
/*
The M and S variable name parts stand for the operators.
Mi stands for Minuend (see wiki subtraction, analogous to difference)

View File

@ -182,6 +182,7 @@ public:
void dumpPts() const;
void dumpPtsInner() const;
void findCollapsed();
SkOpSegment* findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op,
int xorMiMask, int xorSuMask);

View File

@ -13,6 +13,16 @@ bool SkOpPtT::alias() const {
return this->span()->ptT() != this;
}
bool SkOpPtT::collapsed(const SkOpPtT* check) const {
if (fPt != check->fPt) {
return false;
}
SkASSERT(this != check);
const SkOpSegment* segment = this->segment();
SkASSERT(segment == check->segment());
return segment->collapsed();
}
bool SkOpPtT::contains(const SkOpPtT* check) const {
SkASSERT(this != check);
const SkOpPtT* ptT = this;

View File

@ -48,6 +48,7 @@ public:
}
bool alias() const;
bool collapsed(const SkOpPtT* ) const;
bool contains(const SkOpPtT* ) const;
SkOpPtT* contains(const SkOpSegment* );
SkOpContour* contour() const;
@ -266,6 +267,10 @@ public:
return fSegment;
}
void setAligned() {
fAligned = true;
}
void setChased(bool chased) {
fChased = chased;
}

View File

@ -198,7 +198,7 @@ public:
void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
SkOpContourHead contour;
SkOpGlobalState globalState(NULL, &contour);
SkOpGlobalState globalState(NULL, &contour SkDEBUGPARAMS(NULL));
#if DEBUG_SHOW_TEST_NAME
SkDebugf("</div>\n");
#endif
@ -408,6 +408,13 @@ static void calcAngles(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
} while ((contour = contour->next()));
}
static void findCollapsed(SkOpContourHead* contourList) {
SkOpContour* contour = contourList;
do {
contour->findCollapsed();
} while ((contour = contour->next()));
}
static bool missingCoincidence(SkOpContourHead* contourList,
SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
SkOpContour* contour = contourList;
@ -444,6 +451,7 @@ bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidenc
SkOpGlobalState* globalState = contourList->globalState();
// combine t values when multiple intersections occur on some segments but not others
moveMultiples(contourList);
findCollapsed(contourList);
// move t values and points together to eliminate small/tiny gaps
moveNearby(contourList);
align(contourList); // give all span members common values

View File

@ -25,7 +25,8 @@ SkOpSegment* FindUndone(SkOpContourHead* , SkOpSpanBase** startPtr,
void FixWinding(SkPath* path);
bool SortContourList(SkOpContourHead** , bool evenOdd, bool oppEvenOdd);
bool HandleCoincidence(SkOpContourHead* , SkOpCoincidence* , SkChunkAlloc* );
bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result, bool expectSuccess);
bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result,
bool expectSuccess SkDEBUGPARAMS(const char* testName));
#if DEBUG_ACTIVE_SPANS
void DebugShowActiveSpans(SkOpContourHead* );
#endif

View File

@ -223,12 +223,12 @@ static void dump_op(const SkPath& one, const SkPath& two, SkPathOp op) {
#endif
bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result,
bool expectSuccess) {
bool expectSuccess SkDEBUGPARAMS(const char* testName)) {
SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tune
SkOpContour contour;
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
SkOpCoincidence coincidence;
SkOpGlobalState globalState(&coincidence, contourList);
SkOpGlobalState globalState(&coincidence, contourList SkDEBUGPARAMS(testName));
#if DEBUGGING_PATHOPS_FROM_HOST
dump_op(one, two, op);
#endif
@ -304,5 +304,5 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result,
}
bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
return OpDebug(one, two, op, result, true);
return OpDebug(one, two, op, result, true SkDEBUGPARAMS(NULL));
}

View File

@ -167,7 +167,7 @@ bool Simplify(const SkPath& path, SkPath* result) {
SkOpCoincidence coincidence;
SkOpContour contour;
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
SkOpGlobalState globalState(&coincidence, contourList);
SkOpGlobalState globalState(&coincidence, contourList SkDEBUGPARAMS(NULL));
#if DEBUG_SORT
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif

View File

@ -11,7 +11,7 @@ bool TightBounds(const SkPath& path, SkRect* result) {
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
SkOpContour contour;
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
SkOpGlobalState globalState(NULL, contourList);
SkOpGlobalState globalState(NULL, contourList SkDEBUGPARAMS(NULL));
// turn path into list of segments
SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
if (!builder.finish(&allocator)) {

View File

@ -200,13 +200,15 @@ double SkDCubeRoot(double x) {
return result;
}
SkOpGlobalState::SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head)
SkOpGlobalState::SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head
SkDEBUGPARAMS(const char* testName))
: fCoincidence(coincidence)
, fContourHead(head)
, fNested(0)
, fWindingFailed(false)
, fAngleCoincidence(false)
, fPhase(kIntersecting)
SkDEBUGPARAMS(fDebugTestName(testName))
SkDEBUGPARAMS(fAngleID(0))
SkDEBUGPARAMS(fContourID(0))
SkDEBUGPARAMS(fPtTID(0))

View File

@ -28,7 +28,8 @@ class SkOpContourHead;
class SkOpGlobalState {
public:
SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head);
SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head
SkDEBUGPARAMS(const char* testName));
enum Phase {
kIntersecting,
@ -67,6 +68,7 @@ public:
bool debugRunFail() const;
const class SkOpSegment* debugSegment(int id) const;
const class SkOpSpanBase* debugSpan(int id) const;
const char* debugTestName() const { return fDebugTestName; }
#endif
int nested() const {
@ -128,6 +130,7 @@ private:
bool fAngleCoincidence;
Phase fPhase;
#ifdef SK_DEBUG
const char* fDebugTestName;
int fAngleID;
int fContourID;
int fPtTID;

View File

@ -419,7 +419,7 @@ static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, c
SkPoint shortQuads[2][3];
SkOpContourHead contour;
SkOpGlobalState state(NULL, &contour);
SkOpGlobalState state(NULL, &contour SkDEBUGPARAMS(NULL));
contour.init(&state, false, false);
makeSegment(&contour, quad1, shortQuads[0], allocator);
makeSegment(&contour, quad1, shortQuads[1], allocator);

View File

@ -235,7 +235,7 @@ static const int circleDataSetSize = (int) SK_ARRAY_COUNT(circleDataSet);
DEF_TEST(PathOpsAngleCircle, reporter) {
SkChunkAlloc allocator(4096);
SkOpContourHead contour;
SkOpGlobalState state(NULL, &contour);
SkOpGlobalState state(NULL, &contour SkDEBUGPARAMS(NULL));
contour.init(&state, false, false);
for (int index = 0; index < circleDataSetSize; ++index) {
CircleData& data = circleDataSet[index];
@ -427,7 +427,7 @@ struct FourPoints {
DEF_TEST(PathOpsAngleAfter, reporter) {
SkChunkAlloc allocator(4096);
SkOpContourHead contour;
SkOpGlobalState state(NULL, &contour);
SkOpGlobalState state(NULL, &contour SkDEBUGPARAMS(NULL));
contour.init(&state, false, false);
for (int index = intersectDataSetsSize - 1; index >= 0; --index) {
IntersectData* dataArray = intersectDataSets[index];

View File

@ -150,3 +150,124 @@ DEF_TEST(BuilderIssue502792_2, reporter) {
SkPath result;
builder.resolve(&result);
}
DEF_TEST(Fuzz846, reporter) {
/*
<clipPath id="clip-circle">
<circle id="circle" cx="60" cy="60" r="50" />
</clipPath>
<clipPath id="clip-rect">
<clipPath id="clip-rect">
<clipPath id="clip-rect">
<clipPath id="clip-rect">
<rect x="10" y="30" width="0" height="60" />
<rect x="10" y="30" width="0" height="60" />
<rect x="10" y="30" width="100" height="60" />
<rect x="10" y="30" width="32668" />
<rect x="10" y="30" width="100" height="18446744073709551615" />
<rect x="10" y="255" width="100" height="60" />
<rect width="100" height="60" />
<rect x="10" y="30" width="100" height="60" />
<rect x="10" y="30" width="100" height="4294967236" />
<rect x="10" y="30" width="100" height="60" />
</clipPath>
<rect x="10" y="30" width="0" height="60" />
<rect x="10" y="30" width="0" height="0.18093252719929986369568203" />
<rect x="10" y="30" width="100" height="60" />
<rect x="10" y="30" width="32668" height="60" />
<rect x="10" y="30" width="100" height="18446744073709551615" />
<rect x="10" y="255" width="100" height="60" />
<rect x="2147483649" y="30" width="100" height="60" />
<rect x="10" y="30" width="100" height="60" />
<rect x="10" y="30" width="100" height="60" />
<rect x="10" y="30" width="100" height="60" />
</clipPath>
<rect x="10" y="30" width="0" height="60" />
<rect x="10" y="30" width="0" height="60" />
<rect x="10" y="30" width="100" height="60" />
<rect x="10" y="30" width="32668" height="60" />
<rect x="10" y="30" width="100" height="18446744073709551615" />
<rect x="10" y="255" width="100" height="60" />
<rect x="2147483649" y="30" width="100" height="60" />
<rect x="10" y="30" width="100" height="60" />
<rect x="10" y="2879753595" width="100" height="60" />
<rect x="10" y="30" width="100" height="60" />
</clipPath>
<rect x="10" y="30" width="100" height="60" />
<rect x="10" y="30" width="0" height="60" />
<rect x="10" y="30" width="100" height="60" />
<rect x="10" y="30" width="32668" height="60" />
<rect x="10" y="30" width="100" height="18446744073709551615" />
<rect x="10" y="255" width="100" height="60" />
<rect x="2147483649" y="30" width="100" height="60" />
<rect x="10" y="30" width="100" height="60" />
<rect x="10" y="30" width="100" height="4294967236" />
<rect x="10" y="30" width="100" height="4294967236" />
<rect x="10" y="30" width="100" height="4294967236" />
<rect x="10" y="30" width="100" height="4294967236" />
<rect x="10" y="30" width="100" height="60" />
<rect x="757798030" y="30" width="100" height="60" />
*/
SkPath clipCircle, clipRect;
SkPath inner;
clipCircle.addCircle(60, 60, 50); // <circle id="circle" cx="60" cy="60" r="50" />
inner.addRect(10, 30, 10+0, 30+60); // <rect x="10" y="30" width="0" height="60" />
inner.addRect(10, 30, 10+0, 30+60); // <rect x="10" y="30" width="0" height="60" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
inner.addRect(10, 30, 10+32668, 30+0); // <rect x="10" y="30" width="32668" />
inner.addRect(10, 30, 10+100, 30+18446744073709551615.f); // <rect x="10" y="30" width="100" height="18446744073709551615" />
inner.addRect(10, 255, 10+100, 255+60); // <rect x="10" y="255" width="100" height="60" />
inner.addRect(0, 0, 0+100, 0+60); // <rect width="100" height="60" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
clipRect.addPath(inner);
inner.reset();
inner.addRect(10, 30, 10+0, 30+60); // <rect x="10" y="30" width="0" height="60" />
inner.addRect(10, 30, 10+0, 30+0.18093252719929986369568203f); // <rect x="10" y="30" width="0" height="0.18093252719929986369568203" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
inner.addRect(10, 30, 10+32668, 30+60); // <rect x="10" y="30" width="32668" height="60" />
inner.addRect(10, 30, 10+100, 30+18446744073709551615.f); // <rect x="10" y="30" width="100" height="18446744073709551615" />
inner.addRect(10, 255, 10+100, 255+60); // <rect x="10" y="255" width="100" height="60" />
inner.addRect(2147483649.f, 30, 2147483649.f+100, 30+60); // <rect x="2147483649" y="30" width="100" height="60" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
clipRect.addPath(inner);
inner.reset();
inner.addRect(10, 30, 10+0, 30+60); // <rect x="10" y="30" width="0" height="60" />
inner.addRect(10, 30, 10+0, 30+60); // <rect x="10" y="30" width="0" height="60" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
inner.addRect(10, 30, 10+32668, 30+60); // <rect x="10" y="30" width="32668" height="60" />
inner.addRect(10, 30, 10+100, 30+18446744073709551615.f); // <rect x="10" y="30" width="100" height="18446744073709551615" />
inner.addRect(10, 255, 10+100, 255+60); // <rect x="10" y="255" width="100" height="60" />
inner.addRect(2147483649.f, 30, 2147483649.f+100, 30+60); // <rect x="2147483649" y="30" width="100" height="60" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
inner.addRect(10, 2879753595.f, 10+100, 30+2879753595.f); // <rect x="10" y="2879753595" width="100" height="60" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
clipRect.addPath(inner);
inner.reset();
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
inner.addRect(10, 30, 10+0, 30+60); // <rect x="10" y="30" width="0" height="60" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
inner.addRect(10, 30, 10+32668, 30+60); // <rect x="10" y="30" width="32668" height="60" />
inner.addRect(10, 30, 10+100, 30+18446744073709551615.f); // <rect x="10" y="30" width="100" height="18446744073709551615" />
inner.addRect(10, 255, 10+100, 255+60); // <rect x="10" y="255" width="100" height="60" />
inner.addRect(2147483649.f, 30, 2147483649.f+100, 30+60); // <rect x="2147483649" y="30" width="100" height="60" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" />
inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" />
inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" />
inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" />
inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" />
inner.addRect(757798030.f, 30, 757798030.f+100, 30+60); // <rect x="757798030" y="30" width="100" height="60" />
clipRect.addPath(inner);
SkOpBuilder builder;
builder.add(clipCircle, kUnion_SkPathOp);
builder.add(clipRect, kDifference_SkPathOp);
SkPath result;
builder.resolve(&result);
}

View File

@ -488,7 +488,8 @@ static void showName(const SkPath& a, const SkPath& b, const SkPathOp shapeOp) {
}
#endif
bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result, bool expectSuccess);
bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result,
bool expectSuccess SkDEBUGPARAMS(const char* testName));
static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp shapeOp, const char* testName, bool expectSuccess) {
@ -496,7 +497,7 @@ static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkP
showName(a, b, shapeOp);
#endif
SkPath out;
if (!OpDebug(a, b, shapeOp, &out, expectSuccess)) {
if (!OpDebug(a, b, shapeOp, &out, expectSuccess SkDEBUGPARAMS(testName))) {
SkDebugf("%s did not expect failure\n", __FUNCTION__);
REPORTER_ASSERT(reporter, 0);
return false;

View File

@ -4810,11 +4810,76 @@ static void testQuads65(skiatest::Reporter* reporter,const char* filename) {
testSimplify(reporter, path, filename);
}
static void fuzz864a(skiatest::Reporter* reporter,const char* filename) {
SkPath path;
path.moveTo(10, 90);
path.lineTo(10, 90);
path.lineTo(10, 30);
path.lineTo(10, 30);
path.lineTo(10, 90);
path.close();
path.moveTo(10, 90);
path.lineTo(10, 90);
path.lineTo(10, 30);
path.lineTo(10, 30);
path.lineTo(10, 90);
path.close();
path.moveTo(10, 90);
path.lineTo(110, 90);
path.lineTo(110, 30);
path.lineTo(10, 30);
path.lineTo(10, 90);
path.close();
path.moveTo(10, 30);
path.lineTo(32678, 30);
path.lineTo(32678, 30);
path.lineTo(10, 30);
path.close();
path.moveTo(10, 3.35545e+07f);
path.lineTo(110, 3.35545e+07f);
path.lineTo(110, 30);
path.lineTo(10, 30);
path.lineTo(10, 3.35545e+07f);
path.close();
path.moveTo(10, 315);
path.lineTo(110, 315);
path.lineTo(110, 255);
path.lineTo(10, 255);
path.lineTo(10, 315);
path.close();
path.moveTo(0, 60);
path.lineTo(100, 60);
path.lineTo(100, 0);
path.lineTo(0, 0);
path.lineTo(0, 60);
path.close();
path.moveTo(10, 90);
path.lineTo(110, 90);
path.lineTo(110, 30);
path.lineTo(10, 30);
path.lineTo(10, 90);
path.close();
path.moveTo(10, 3.35545e+07f);
path.lineTo(110, 3.35545e+07f);
path.lineTo(110, 30);
path.lineTo(10, 30);
path.lineTo(10, 3.35545e+07f);
path.close();
path.moveTo(10, 90);
path.lineTo(110, 90);
path.lineTo(110, 30);
path.lineTo(10, 30);
path.lineTo(10, 90);
path.close();
testSimplify(reporter, path, filename);
}
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static TestDesc tests[] = {
TEST(fuzz864a),
TEST(testQuads65),
TEST(testIssue3838_3),
TEST(testIssue3838),