double conicT = roots[index];
SkDPoint pt = fConic.ptAtT(conicT);
SkDEBUGCODE_(double conicVals[] = { fConic[0].fX, fConic[1].fX, fConic[2].fX });
- SkASSERT(close_to(pt.fX, axisIntercept, conicVals));
+ SkASSERT((fIntersections->debugGlobalState() &&
+ fIntersections->debugGlobalState()->debugSkipAssert()) ||
+ close_to(pt.fX, axisIntercept, conicVals));
double lineT = (pt.fY - top) / (bottom - top);
if (this->pinTs(&conicT, &lineT, &pt, kPointInitialized)
&& this->uniqueAnswer(conicT, pt)) {
return added;
}
-void SkOpCoincidence::addOverlap(SkOpSegment* seg1, SkOpSegment* seg1o, SkOpSegment* seg2,
+bool SkOpCoincidence::addOverlap(SkOpSegment* seg1, SkOpSegment* seg1o, SkOpSegment* seg2,
SkOpSegment* seg2o, SkOpPtT* overS, SkOpPtT* overE, SkChunkAlloc* allocator) {
SkOpPtT* s1 = overS->find(seg1);
SkOpPtT* e1 = overE->find(seg1);
+ if (approximately_equal_half(s1->fT, e1->fT)) {
+ return false;
+ }
if (!s1->starter(e1)->span()->upCast()->windValue()) {
s1 = overS->find(seg1o);
e1 = overE->find(seg1o);
if (!s1->starter(e1)->span()->upCast()->windValue()) {
- return;
+ return true;
}
}
SkOpPtT* s2 = overS->find(seg2);
SkOpPtT* e2 = overE->find(seg2);
+ if (approximately_equal_half(s2->fT, e2->fT)) {
+ return false;
+ }
if (!s2->starter(e2)->span()->upCast()->windValue()) {
s2 = overS->find(seg2o);
e2 = overE->find(seg2o);
if (!s2->starter(e2)->span()->upCast()->windValue()) {
- return;
+ return true;
}
}
if (s1->segment() == s2->segment()) {
- return;
+ return true;
}
if (s1->fT > e1->fT) {
SkTSwap(s1, e1);
SkTSwap(s2, e2);
}
this->add(s1, e1, s2, e2, allocator);
+ return true;
}
bool SkOpCoincidence::contains(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
return expanded;
}
-void SkOpCoincidence::findOverlaps(SkOpCoincidence* overlaps, SkChunkAlloc* allocator) const {
+bool SkOpCoincidence::findOverlaps(SkOpCoincidence* overlaps, SkChunkAlloc* allocator) const {
overlaps->fHead = overlaps->fTop = nullptr;
SkDEBUGCODE_(overlaps->debugSetGlobalState(fDebugState));
SkCoincidentSpans* outer = fHead;
|| (outerOpp == innerOpp && SkOpPtT::Overlaps(outer->fOppPtTStart,
outer->fOppPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd,
&overlapS, &overlapE))) {
- overlaps->addOverlap(outerCoin, outerOpp, innerCoin, innerOpp,
- overlapS, overlapE, allocator);
+ if (!overlaps->addOverlap(outerCoin, outerOpp, innerCoin, innerOpp,
+ overlapS, overlapE, allocator)) {
+ return false;
+ }
}
}
outer = outer->fNext;
}
+ return true;
}
-void SkOpCoincidence::fixAligned() {
+bool SkOpCoincidence::fixAligned() {
SkCoincidentSpans* coin = fHead;
if (!coin) {
- return;
+ return true;
}
do {
if (coin->fCoinPtTStart->deleted()) {
coin = fHead;
SkCoincidentSpans** priorPtr = &fHead;
do {
+ if (coin->fCoinPtTStart == coin->fCoinPtTEnd) {
+ return false;
+ }
+ if (coin->fOppPtTStart == coin->fOppPtTEnd) {
+ return false;
+ }
if (coin->fCoinPtTStart->collapsed(coin->fCoinPtTEnd)
|| coin->fOppPtTStart->collapsed(coin->fOppPtTEnd)) {
*priorPtr = coin->fNext;
}
priorPtr = &coin->fNext;
} while ((coin = coin->fNext));
+ return true;
}
void SkOpCoincidence::fixUp(SkOpPtT* deleted, SkOpPtT* kept) {
bool expand();
bool extend(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
SkOpPtT* oppPtTEnd);
- void findOverlaps(SkOpCoincidence* , SkChunkAlloc* allocator) const;
- void fixAligned();
+ bool findOverlaps(SkOpCoincidence* , SkChunkAlloc* allocator) const;
+ bool fixAligned();
void fixUp(SkOpPtT* deleted, SkOpPtT* kept);
bool isEmpty() const {
SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd,
SkChunkAlloc* );
- void addOverlap(SkOpSegment* seg1, SkOpSegment* seg1o, SkOpSegment* seg2, SkOpSegment* seg2o,
+ bool addOverlap(SkOpSegment* seg1, SkOpSegment* seg1o, SkOpSegment* seg2, SkOpSegment* seg2o,
SkOpPtT* overS, SkOpPtT* overE, SkChunkAlloc* );
bool debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
const SkOpPtT* over1e) const;
DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby");
align(contourList); // give all span members common values
DEBUG_COINCIDENCE_HEALTH(contourList, "align");
- coincidence->fixAligned(); // aligning may have marked a coincidence pt-t deleted
+ if (!coincidence->fixAligned()) { // aligning may have marked a coincidence pt-t deleted
+ return false;
+ }
DEBUG_COINCIDENCE_HEALTH(contourList, "fixAligned");
#if DEBUG_VALIDATE
globalState->setPhase(SkOpGlobalState::kIntersecting);
DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby2");
align(contourList); // give all span members common values
DEBUG_COINCIDENCE_HEALTH(contourList, "align2");
- coincidence->fixAligned(); // aligning may have marked a coincidence pt-t deleted
+ if (!coincidence->fixAligned()) { // aligning may have marked a coincidence pt-t deleted
+ return false;
+ }
DEBUG_COINCIDENCE_HEALTH(contourList, "fixAligned2");
}
#if DEBUG_VALIDATE
DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->apply");
// For each coincident pair that overlaps another, when the receivers (the 1st of the pair)
// are different, construct a new pair to resolve their mutual span
- pairs->findOverlaps(&overlaps, allocator);
+ if (!pairs->findOverlaps(&overlaps, allocator)) {
+ return false;
+ }
DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->findOverlaps");
} while (!overlaps.isEmpty());
calcAngles(contourList, allocator);
bool 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 SkDEBUGPARAMS(bool skipAssert)
+bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
+ SkDEBUGPARAMS(bool skipAssert)
SkDEBUGPARAMS(const char* testName));
#if DEBUG_ACTIVE_SPANS
void DebugShowActiveSpans(SkOpContourHead* );
double tValues[2];
int roots = SkDQuad::RootsValidT(coeff[0], coeff[1], coeff[2], tValues);
- SkASSERT(0 == roots || 1 == roots);
+ // In extreme cases, the number of roots returned can be 2. Pathops
+ // will fail later on, so there's no advantage to plumbing in an error
+ // return here.
+ // SkASSERT(0 == roots || 1 == roots);
if (1 == roots) {
t[0] = tValues[0];
#endif
-bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result,
- bool expectSuccess SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
+bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
+ SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tune
SkOpContour contour;
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
#if DEBUG_VERIFY
- if (!OpDebug(one, two, op, result, true SkDEBUGPARAMS(nullptr))) {
+ if (!OpDebug(one, two, op, result SkDEBUGPARAMS(nullptr))) {
SkDebugf("%s did not expect failure\none: fill=%d\n", __FUNCTION__, one.getFillType());
one.dumpHex();
SkDebugf("two: fill=%d\n", two.getFillType());
}
return true;
#else
- return OpDebug(one, two, op, result, true SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
+ return OpDebug(one, two, op, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
#endif
}
}
#endif
-bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result,
- bool expectSuccess SkDEBUGPARAMS(bool skipAssert)
+bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
+ SkDEBUGPARAMS(bool skipAssert)
SkDEBUGPARAMS(const char* testName));
static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
showName(a, b, shapeOp);
#endif
SkPath out;
- if (!OpDebug(a, b, shapeOp, &out, expectSuccess SkDEBUGPARAMS(skipAssert)
+ if (!OpDebug(a, b, shapeOp, &out SkDEBUGPARAMS(skipAssert)
SkDEBUGPARAMS(testName))) {
- SkDebugf("%s did not expect failure\n", __FUNCTION__);
- REPORTER_ASSERT(reporter, 0);
+ if (expectSuccess) {
+ SkDebugf("%s did not expect failure\n", __FUNCTION__);
+ REPORTER_ASSERT(reporter, 0);
+ }
return false;
}
if (!reporter->verbose()) {
return innerPathOp(reporter, a, b, shapeOp, testName, true, true);
}
+bool testPathFailSkipAssertOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+ const SkPathOp shapeOp, const char* testName) {
+ return innerPathOp(reporter, a, b, shapeOp, testName, false, true);
+}
+
bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp shapeOp, const char* testName) {
#if DEBUG_SHOW_TEST_NAME
const SkPathOp , const char* testName);
extern bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp , const char* testName);
+extern bool testPathFailSkipAssertOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+ const SkPathOp , const char* testName);
extern bool testPathSkipAssertOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
const SkPathOp , const char* testName);
extern bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
testPathFailOp(reporter, path1, path2, kUnion_SkPathOp, filename);
}
+static void fuzz763_9(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 1);
+
+ SkPath path1(path);
+ path.reset();
+ path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321), SkBits2Float(0xed7a6a4b), SkBits2Float(0x1f212a8c)); // 2.49282e-13f, 4.78968e-34f, 1.99397e+36f, -4.84373e+27f, 3.41283e-20f
+path.lineTo(SkBits2Float(0x7bc00321), SkBits2Float(0xed7a6a4b)); // 1.99397e+36f, -4.84373e+27f
+path.lineTo(SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28)); // 9.4495e-15f, 0.000617492f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+path.quadTo(SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3), SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6)); // -8.10388e-33f, 0.00148185f, 2.25206e-21f, 5.54035e+21f
+path.moveTo(SkBits2Float(0x29272a81), SkBits2Float(0x2ab03a55)); // 3.71183e-14f, 3.13044e-13f
+path.quadTo(SkBits2Float(0x2720213b), SkBits2Float(0x3a214729), SkBits2Float(0xdf28282a), SkBits2Float(0x8a2f2121)); // 2.22225e-15f, 0.000615227f, -1.2117e+19f, -8.43217e-33f
+path.quadTo(SkBits2Float(0x373b3a27), SkBits2Float(0x201fc4c1), SkBits2Float(0x27576c2a), SkBits2Float(0x5921c25d)); // 1.11596e-05f, 1.35329e-19f, 2.98959e-15f, 2.8457e+15f
+path.quadTo(SkBits2Float(0x2720213b), SkBits2Float(0x3a214729), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21)); // 2.22225e-15f, 0.000615227f, -1.2117e+19f, 0.00105459f
+path.cubicTo(SkBits2Float(0x373b3ac5), SkBits2Float(0x201fc422), SkBits2Float(0x523a702a), SkBits2Float(0x27576c51), SkBits2Float(0x5921c25d), SkBits2Float(0x51523a70)); // 1.11598e-05f, 1.35327e-19f, 2.00186e+11f, 2.9896e-15f, 2.8457e+15f, 5.64327e+10f
+path.quadTo(SkBits2Float(0xd912102a), SkBits2Float(0x284f9a28), SkBits2Float(0xb38a1f30), SkBits2Float(0x3a3ac23a)); // -2.56957e+15f, 1.15242e-14f, -6.4318e-08f, 0.000712428f
+path.lineTo(SkBits2Float(0xc809272a), SkBits2Float(0x29b02829)); // -140445, 7.82294e-14f
+
+ SkPath path2(path);
+ testPathFailOp(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+
+static void fuzz763_4(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 1);
+
+ SkPath path1(path);
+ path.reset();
+ path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+path.lineTo(SkBits2Float(0x555b3a2d), SkBits2Float(0x2a212a8c)); // 1.50652e+13f, 1.43144e-13f
+path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0)); // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f
+path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac2b33a)); // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148544f
+path.cubicTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6), SkBits2Float(0x295b2d2a), SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c275b)); // 2.25206e-21f, 5.54035e+21f, 4.86669e-14f, 3.19905e+24f, 9.6297e-12f, 2.48963e-13f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0x55685b1f), SkBits2Float(0x5b2d2968)); // 1.59674e+13f, 4.87407e+16f
+path.lineTo(SkBits2Float(0x2a212a8c), SkBits2Float(0x2a21081f)); // 1.43144e-13f, 1.43025e-13f
+path.conicTo(SkBits2Float(0xde6a4b7b), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0x3a7bc003), SkBits2Float(0x47ed7a6a)); // -4.22068e+18f, 2.50338e-13f, 4.61198e-19f, 0.00096035f, 121589
+path.lineTo(SkBits2Float(0x55685b1f), SkBits2Float(0x5b2d2968)); // 1.59674e+13f, 4.87407e+16f
+path.close();
+path.moveTo(SkBits2Float(0x55685b1f), SkBits2Float(0x5b2d2968)); // 1.59674e+13f, 4.87407e+16f
+path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3)); // -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f
+path.lineTo(SkBits2Float(0x2928088c), SkBits2Float(0x2be61d2a)); // 3.73109e-14f, 1.63506e-12f
+path.conicTo(SkBits2Float(0x2a812a63), SkBits2Float(0x2d292a27), SkBits2Float(0x5568295b), SkBits2Float(0x5b2d2968), SkBits2Float(0x552d6829)); // 2.29444e-13f, 9.6159e-12f, 1.5954e+13f, 4.87407e+16f, 1.19164e+13f
+path.conicTo(SkBits2Float(0x395b2d5b), SkBits2Float(0x68552768), SkBits2Float(0x555b2df0), SkBits2Float(0x1f722a8c), SkBits2Float(0x082a212a)); // 0.000209024f, 4.02636e+24f, 1.50619e+13f, 5.12807e-20f, 5.11965e-34f
+path.lineTo(SkBits2Float(0x55685b1f), SkBits2Float(0x5b2d2968)); // 1.59674e+13f, 4.87407e+16f
+path.close();
+path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2147ed7a), SkBits2Float(0x28282a3a), SkBits2Float(0x21df212a), SkBits2Float(0x033a8a3a)); // 6.14991e+25f, 6.77381e-19f, 9.33503e-15f, 1.51198e-18f, 5.48192e-37f
+
+ SkPath path2(path);
+ testPathFailSkipAssertOp(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
static struct TestDesc failTests[] = {
+ TEST(fuzz763_4),
+ TEST(fuzz763_9),
TEST(fuzz1450_1),
TEST(fuzz1450_0),
TEST(bug597926_0),