diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp index cbf1c36f73d41..62bce964882a7 100644 --- a/src/pathops/SkOpSegment.cpp +++ b/src/pathops/SkOpSegment.cpp @@ -165,7 +165,9 @@ bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sum bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path) const { - FAIL_IF(start->starter(end)->alreadyAdded()); + const SkOpSpan* spanStart = start->starter(end); + FAIL_IF(spanStart->alreadyAdded()); + const_cast(spanStart)->markAdded(); SkDCurveSweep curvePart; start->segment()->subDivide(start, end, &curvePart.fCurve); curvePart.setCurveHullSweep(fVerb); @@ -951,6 +953,7 @@ bool SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* ang return false; } #if DEBUG_WINDING + SkOpSpanBase* last = *result; if (last) { SkDebugf("%s last seg=%d span=%d", __FUNCTION__, last->segment()->debugID(), last->debugID()); @@ -978,6 +981,7 @@ bool SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding, return false; } #if DEBUG_WINDING + SkOpSpanBase* last = *result; if (last) { SkDebugf("%s last segment=%d span=%d", __FUNCTION__, last->segment()->debugID(), last->debugID()); diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h index 0b23e500c3af9..bd40fab6ccd93 100644 --- a/src/pathops/SkOpSegment.h +++ b/src/pathops/SkOpSegment.h @@ -251,7 +251,7 @@ class SkOpSegment { return fBounds.fTop == fBounds.fBottom; } - SkOpSegment* isSimple(SkOpSpanBase** end, int* step) { + SkOpSegment* isSimple(SkOpSpanBase** end, int* step) const { return nextChase(end, step, nullptr, nullptr); } diff --git a/src/pathops/SkOpSpan.h b/src/pathops/SkOpSpan.h index f547040fb2c7c..529eb2b808977 100644 --- a/src/pathops/SkOpSpan.h +++ b/src/pathops/SkOpSpan.h @@ -421,7 +421,6 @@ class SkOpSpan : public SkOpSpanBase { if (fAlreadyAdded) { return true; } - fAlreadyAdded = true; return false; } @@ -489,6 +488,10 @@ class SkOpSpan : public SkOpSpanBase { return fCoincident != this; } + void markAdded() { + fAlreadyAdded = true; + } + SkOpSpanBase* next() const { SkASSERT(!final()); return fNext; @@ -569,7 +572,7 @@ class SkOpSpan : public SkOpSpanBase { int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here int fTopTTry; // specifies direction and t value to try next bool fDone; // if set, this span to next higher T has been processed - mutable bool fAlreadyAdded; + bool fAlreadyAdded; }; #endif diff --git a/src/pathops/SkPathWriter.cpp b/src/pathops/SkPathWriter.cpp index 950f8fbce5212..9548c3b4f7795 100644 --- a/src/pathops/SkPathWriter.cpp +++ b/src/pathops/SkPathWriter.cpp @@ -4,6 +4,7 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +#include "SkOpSegment.h" #include "SkOpSpan.h" #include "SkPathOpsPoint.h" #include "SkPathWriter.h" @@ -214,6 +215,49 @@ void SkPathWriter::assemble() { eStart->fPt.fX, eStart->fPt.fY, eEnd->fPt.fX, eEnd->fPt.fY); } #endif + // lengthen any partial contour adjacent to a simple segment + for (int pIndex = 0; pIndex < endCount; pIndex++) { + SkOpPtT* opPtT = const_cast(runs[pIndex]); + SkPath dummy; + SkPathWriter partWriter(dummy); + do { + if (!zero_or_one(opPtT->fT)) { + break; + } + SkOpSpanBase* opSpanBase = opPtT->span(); + SkOpSpanBase* start = opPtT->fT ? opSpanBase->prev() : opSpanBase->upCast()->next(); + int step = opPtT->fT ? 1 : -1; + const SkOpSegment* opSegment = opSpanBase->segment(); + const SkOpSegment* nextSegment = opSegment->isSimple(&start, &step); + if (!nextSegment) { + break; + } + SkOpSpanBase* opSpanEnd = start->t() ? start->prev() : start->upCast()->next(); + if (start->starter(opSpanEnd)->alreadyAdded()) { + break; + } + nextSegment->addCurveTo(start, opSpanEnd, &partWriter); + opPtT = opSpanEnd->ptT(); + SkOpPtT** runsPtr = const_cast(&runs[pIndex]); + *runsPtr = opPtT; + } while (true); + partWriter.finishContour(); + const SkTArray& partPartials = partWriter.partials(); + if (!partPartials.count()) { + continue; + } + // if pIndex is even, reverse and prepend to fPartials; otherwise, append + SkPath& partial = const_cast(fPartials[pIndex >> 1]); + const SkPath& part = partPartials[0]; + if (pIndex & 1) { + partial.addPath(part, SkPath::kExtend_AddPathMode); + } else { + SkPath reverse; + reverse.reverseAddPath(part); + reverse.addPath(partial, SkPath::kExtend_AddPathMode); + partial = reverse; + } + } SkTDArray sLink, eLink; int linkCount = endCount / 2; // number of partial contours sLink.append(linkCount); diff --git a/tests/PathOpsOpTest.cpp b/tests/PathOpsOpTest.cpp index 4aca7e1763f3e..2f847e42f2509 100644 --- a/tests/PathOpsOpTest.cpp +++ b/tests/PathOpsOpTest.cpp @@ -8943,6 +8943,96 @@ path.close(); testPathOpFail(reporter, path, path1, kXOR_SkPathOp, filename); } +static void op_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.setFillType(SkPath::kWinding_FillType); +path.moveTo(SkBits2Float(0x15e80300), SkBits2Float(0x400004dc)); // 9.37088e-26f, 2.0003f +path.quadTo(SkBits2Float(0xe56c206c), SkBits2Float(0x646c5f40), SkBits2Float(0x6c80885e), SkBits2Float(0xb4bc576c)); // -6.96923e+22f, 1.74412e+22f, 1.24309e+27f, -3.50813e-07f + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.setFillType(SkPath::kWinding_FillType); +path.moveTo(SkBits2Float(0x1b000010), SkBits2Float(0x6e5a5a1b)); // 1.05879e-22f, 1.68942e+28f +path.quadTo(SkBits2Float(0xef646464), SkBits2Float(0xefefefef), SkBits2Float(0x000000ef), SkBits2Float(0x1bb4bc00)); // -7.06839e+28f, -1.48514e+29f, 3.3491e-43f, 2.99e-22f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); +} + + +static void op_2(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(SkBits2Float(0xeee3ef57), SkBits2Float(0xef6300f8)); // -3.52712e+28f, -7.02543e+28f +path.quadTo(SkBits2Float(0xeeee9c6e), SkBits2Float(0xef609993), SkBits2Float(0x00000000), SkBits2Float(0x6e5a5a1b)); // -3.69233e+28f, -6.95103e+28f, 0, 1.68942e+28f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0xe56c206c), SkBits2Float(0x646c5f40), SkBits2Float(0x6c80885e), SkBits2Float(0x00000000)); // -6.96923e+22f, 1.74412e+22f, 1.24309e+27f, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0xeeda2c5a), SkBits2Float(0xef6533a7), SkBits2Float(0xeee3ef57), SkBits2Float(0xef6300f8)); // -3.37607e+28f, -7.09345e+28f, -3.52712e+28f, -7.02543e+28f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.setFillType(SkPath::kWinding_FillType); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x1b1b1b00), SkBits2Float(0x1b5a5a1b)); // 1.283e-22f, 1.80617e-22f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + + +static void op_3(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x6e5a5a1b)); // 0, 1.68942e+28f +path.quadTo(SkBits2Float(0xeeee9c6e), SkBits2Float(0xef609993), SkBits2Float(0xeee3ef57), SkBits2Float(0xef6300f8)); // -3.69233e+28f, -6.95103e+28f, -3.52712e+28f, -7.02543e+28f +path.quadTo(SkBits2Float(0xeeda2c5a), SkBits2Float(0xef6533a7), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // -3.37607e+28f, -7.09345e+28f, 0, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6e5a5a1b)); // 0, 1.68942e+28f +path.close(); +path.moveTo(SkBits2Float(0x6c80885e), SkBits2Float(0x00000000)); // 1.24309e+27f, 0 +path.quadTo(SkBits2Float(0xe56c206c), SkBits2Float(0x646c5f40), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // -6.96923e+22f, 1.74412e+22f, 0, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x6c80885e), SkBits2Float(0x00000000)); // 1.24309e+27f, 0 +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.setFillType(SkPath::kWinding_FillType); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void op_4(skiatest::Reporter* reporter, const char* filename) { + SkPath patha, pathb; + + patha.setFillType(SkPath::kEvenOdd_FillType); + patha.moveTo(SkBits2Float(0x40d7ea90), SkBits2Float(0x3fa58930)); // 6.74738f, 1.29325f + patha.lineTo(SkBits2Float(0x40ad3d93), SkBits2Float(0x3fa58930)); // 5.41377f, 1.29325f + patha.lineTo(SkBits2Float(0x40ad3d93), SkBits2Float(0x3edba819)); // 5.41377f, 0.429017f + patha.lineTo(SkBits2Float(0x40fc41e0), SkBits2Float(0x3edba819)); // 7.88304f, 0.429017f + patha.lineTo(SkBits2Float(0x40fc41e0), SkBits2Float(0x3f3b7c94)); // 7.88304f, 0.73237f + patha.lineTo(SkBits2Float(0x40d7ea90), SkBits2Float(0x3f3b7c94)); // 6.74738f, 0.73237f + patha.lineTo(SkBits2Float(0x40d7ea90), SkBits2Float(0x3fa58930)); // 6.74738f, 1.29325f + patha.close(); + + pathb.setFillType(SkPath::kEvenOdd_FillType); + pathb.moveTo(SkBits2Float(0x40d7ea89), SkBits2Float(0x409a721d)); // 6.74738f, 4.82643f + pathb.lineTo(SkBits2Float(0x411a9d73), SkBits2Float(0x409a721d)); // 9.66344f, 4.82643f + pathb.lineTo(SkBits2Float(0x411a9d73), SkBits2Float(0x3f3b7c9a)); // 9.66344f, 0.73237f + pathb.lineTo(SkBits2Float(0x40d7ea89), SkBits2Float(0x3f3b7c9a)); // 6.74738f, 0.73237f + pathb.lineTo(SkBits2Float(0x40d7ea89), SkBits2Float(0x409a721d)); // 6.74738f, 4.82643f + pathb.close(); + testPathOp(reporter, patha, pathb, kDifference_SkPathOp, 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; @@ -8950,7 +9040,10 @@ static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; #define TEST(name) { name, #name } static struct TestDesc tests[] = { - TEST(cubics41d), + TEST(op_4), + TEST(op_1), + TEST(op_2), + TEST(op_3), TEST(grshapearcs1), TEST(filinmangust14), TEST(testRect1_u), diff --git a/tools/pathops_visualizer.htm b/tools/pathops_visualizer.htm index 4c8d52f3019cd..ca520cd363fa3 100644 --- a/tools/pathops_visualizer.htm +++ b/tools/pathops_visualizer.htm @@ -2,535 +2,179 @@