aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/pathops
diff options
context:
space:
mode:
Diffstat (limited to 'src/pathops')
-rw-r--r--src/pathops/SkAddIntersections.cpp17
-rw-r--r--src/pathops/SkAddIntersections.h3
-rw-r--r--src/pathops/SkDConicLineIntersection.cpp22
-rw-r--r--src/pathops/SkDCubicLineIntersection.cpp21
-rw-r--r--src/pathops/SkDQuadLineIntersection.cpp26
-rw-r--r--src/pathops/SkIntersections.h8
-rw-r--r--src/pathops/SkOpAngle.cpp56
-rw-r--r--src/pathops/SkOpAngle.h67
-rw-r--r--src/pathops/SkOpBuilder.cpp12
-rwxr-xr-xsrc/pathops/SkOpCoincidence.cpp1495
-rw-r--r--src/pathops/SkOpCoincidence.h270
-rw-r--r--src/pathops/SkOpContour.cpp10
-rw-r--r--src/pathops/SkOpContour.h82
-rw-r--r--src/pathops/SkOpEdgeBuilder.cpp22
-rw-r--r--src/pathops/SkOpEdgeBuilder.h17
-rw-r--r--src/pathops/SkOpSegment.cpp519
-rw-r--r--src/pathops/SkOpSegment.h124
-rwxr-xr-xsrc/pathops/SkOpSpan.cpp301
-rw-r--r--src/pathops/SkOpSpan.h119
-rw-r--r--src/pathops/SkPathOpsBounds.h14
-rw-r--r--src/pathops/SkPathOpsCommon.cpp192
-rw-r--r--src/pathops/SkPathOpsCommon.h7
-rw-r--r--src/pathops/SkPathOpsCubic.cpp2
-rw-r--r--src/pathops/SkPathOpsCurve.cpp53
-rw-r--r--src/pathops/SkPathOpsCurve.h79
-rw-r--r--src/pathops/SkPathOpsDebug.cpp1704
-rw-r--r--src/pathops/SkPathOpsDebug.h29
-rw-r--r--src/pathops/SkPathOpsOp.cpp57
-rw-r--r--src/pathops/SkPathOpsPoint.h16
-rw-r--r--src/pathops/SkPathOpsQuad.cpp13
-rw-r--r--src/pathops/SkPathOpsSimplify.cpp64
-rw-r--r--src/pathops/SkPathOpsTSect.h19
-rw-r--r--src/pathops/SkPathOpsTightBounds.cpp15
-rw-r--r--src/pathops/SkPathOpsTypes.cpp26
-rw-r--r--src/pathops/SkPathOpsTypes.h37
-rw-r--r--src/pathops/SkPathOpsWinding.cpp15
-rw-r--r--src/pathops/SkReduceOrder.cpp8
37 files changed, 3756 insertions, 1785 deletions
diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp
index 07666e796d..1ca56b8f23 100644
--- a/src/pathops/SkAddIntersections.cpp
+++ b/src/pathops/SkAddIntersections.cpp
@@ -254,8 +254,7 @@ static void debugShowCubicIntersection(int , const SkIntersectionHelper& ,
}
#endif
-bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
- SkChunkAlloc* allocator) {
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence) {
if (test != next) {
if (AlmostLessUlps(test->bounds().fBottom, next->bounds().fTop)) {
return false;
@@ -506,12 +505,14 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
SkASSERT(ts[0][pt] >= 0 && ts[0][pt] <= 1);
SkASSERT(ts[1][pt] >= 0 && ts[1][pt] <= 1);
wt.segment()->debugValidate();
- SkOpPtT* testTAt = wt.segment()->addT(ts[swap][pt], SkOpSegment::kAllowAlias,
- allocator);
+ SkOpPtT* testTAt = wt.segment()->addT(ts[swap][pt], SkOpSegment::kAllowAliasMatch,
+ nullptr);
wn.segment()->debugValidate();
- SkOpPtT* nextTAt = wn.segment()->addT(ts[!swap][pt], SkOpSegment::kAllowAlias,
- allocator);
- testTAt->addOpp(nextTAt);
+ SkOpPtT* nextTAt = wn.segment()->addT(ts[!swap][pt], SkOpSegment::kAllowAliasMatch,
+ nullptr);
+ if (testTAt->addOpp(nextTAt)) {
+ testTAt->span()->checkForCollapsedCoincidence();
+ }
if (testTAt->fPt != nextTAt->fPt) {
testTAt->span()->unaligned();
nextTAt->span()->unaligned();
@@ -540,7 +541,7 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
SkTSwap(testTAt, nextTAt);
}
SkASSERT(coinPtT[0]->span()->t() < testTAt->span()->t());
- coincidence->add(coinPtT[0], testTAt, coinPtT[1], nextTAt, allocator);
+ coincidence->add(coinPtT[0], testTAt, coinPtT[1], nextTAt);
wt.segment()->debugValidate();
wn.segment()->debugValidate();
coinIndex = -1;
diff --git a/src/pathops/SkAddIntersections.h b/src/pathops/SkAddIntersections.h
index 23654e5e91..ca409800cd 100644
--- a/src/pathops/SkAddIntersections.h
+++ b/src/pathops/SkAddIntersections.h
@@ -12,7 +12,6 @@
class SkOpCoincidence;
-bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
- SkChunkAlloc* allocator);
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence);
#endif
diff --git a/src/pathops/SkDConicLineIntersection.cpp b/src/pathops/SkDConicLineIntersection.cpp
index 710647236c..620e99c8ae 100644
--- a/src/pathops/SkDConicLineIntersection.cpp
+++ b/src/pathops/SkDConicLineIntersection.cpp
@@ -6,6 +6,7 @@
*/
#include "SkIntersections.h"
#include "SkPathOpsConic.h"
+#include "SkPathOpsCurve.h"
#include "SkPathOpsLine.h"
class LineConicIntersections {
@@ -199,7 +200,22 @@ protected:
}
fIntersections->insert(conicT, lineT, fConic[cIndex]);
}
- // FIXME: see if line end is nearly on conic
+ this->addLineNearEndPoints();
+ }
+
+ void addLineNearEndPoints() {
+ for (int lIndex = 0; lIndex < 2; ++lIndex) {
+ double lineT = (double) lIndex;
+ if (fIntersections->hasOppT(lineT)) {
+ continue;
+ }
+ double conicT = ((SkDCurve*) &fConic)->nearPoint(SkPath::kConic_Verb,
+ (*fLine)[lIndex], (*fLine)[!lIndex]);
+ if (conicT < 0) {
+ continue;
+ }
+ fIntersections->insert(conicT, lineT, (*fLine)[lIndex]);
+ }
}
void addExactHorizontalEndPoints(double left, double right, double y) {
@@ -225,7 +241,7 @@ protected:
}
fIntersections->insert(conicT, lineT, fConic[cIndex]);
}
- // FIXME: see if line end is nearly on conic
+ this->addLineNearEndPoints();
}
void addExactVerticalEndPoints(double top, double bottom, double x) {
@@ -251,7 +267,7 @@ protected:
}
fIntersections->insert(conicT, lineT, fConic[cIndex]);
}
- // FIXME: see if line end is nearly on conic
+ this->addLineNearEndPoints();
}
double findLineT(double t) {
diff --git a/src/pathops/SkDCubicLineIntersection.cpp b/src/pathops/SkDCubicLineIntersection.cpp
index cbdce7789a..045acd9e4f 100644
--- a/src/pathops/SkDCubicLineIntersection.cpp
+++ b/src/pathops/SkDCubicLineIntersection.cpp
@@ -6,6 +6,7 @@
*/
#include "SkIntersections.h"
#include "SkPathOpsCubic.h"
+#include "SkPathOpsCurve.h"
#include "SkPathOpsLine.h"
/*
@@ -291,6 +292,22 @@ public:
}
fIntersections->insert(cubicT, lineT, fCubic[cIndex]);
}
+ this->addLineNearEndPoints();
+ }
+
+ void addLineNearEndPoints() {
+ for (int lIndex = 0; lIndex < 2; ++lIndex) {
+ double lineT = (double) lIndex;
+ if (fIntersections->hasOppT(lineT)) {
+ continue;
+ }
+ double cubicT = ((SkDCurve*) &fCubic)->nearPoint(SkPath::kCubic_Verb,
+ fLine[lIndex], fLine[!lIndex]);
+ if (cubicT < 0) {
+ continue;
+ }
+ fIntersections->insert(cubicT, lineT, fLine[lIndex]);
+ }
}
void addExactHorizontalEndPoints(double left, double right, double y) {
@@ -316,7 +333,7 @@ public:
}
fIntersections->insert(cubicT, lineT, fCubic[cIndex]);
}
- // FIXME: see if line end is nearly on cubic
+ this->addLineNearEndPoints();
}
void addExactVerticalEndPoints(double top, double bottom, double x) {
@@ -342,7 +359,7 @@ public:
}
fIntersections->insert(cubicT, lineT, fCubic[cIndex]);
}
- // FIXME: see if line end is nearly on cubic
+ this->addLineNearEndPoints();
}
double findLineT(double t) {
diff --git a/src/pathops/SkDQuadLineIntersection.cpp b/src/pathops/SkDQuadLineIntersection.cpp
index a3d10bc9ff..dc237f57a5 100644
--- a/src/pathops/SkDQuadLineIntersection.cpp
+++ b/src/pathops/SkDQuadLineIntersection.cpp
@@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "SkIntersections.h"
+#include "SkPathOpsCurve.h"
#include "SkPathOpsLine.h"
#include "SkPathOpsQuad.h"
@@ -98,11 +99,11 @@ public:
, fLine(&l)
, fIntersections(i)
, fAllowNear(true) {
- i->setMax(3); // allow short partial coincidence plus discrete intersection
+ i->setMax(4); // allow short partial coincidence plus discrete intersections
}
LineQuadraticIntersections(const SkDQuad& q)
- : fQuad(q)
+ : fQuad(q)
SkDEBUGPARAMS(fLine(nullptr))
SkDEBUGPARAMS(fIntersections(nullptr))
SkDEBUGPARAMS(fAllowNear(false)) {
@@ -297,7 +298,22 @@ protected:
}
fIntersections->insert(quadT, lineT, fQuad[qIndex]);
}
- // FIXME: see if line end is nearly on quad
+ this->addLineNearEndPoints();
+ }
+
+ void addLineNearEndPoints() {
+ for (int lIndex = 0; lIndex < 2; ++lIndex) {
+ double lineT = (double) lIndex;
+ if (fIntersections->hasOppT(lineT)) {
+ continue;
+ }
+ double quadT = ((SkDCurve*) &fQuad)->nearPoint(SkPath::kQuad_Verb,
+ (*fLine)[lIndex], (*fLine)[!lIndex]);
+ if (quadT < 0) {
+ continue;
+ }
+ fIntersections->insert(quadT, lineT, (*fLine)[lIndex]);
+ }
}
void addExactHorizontalEndPoints(double left, double right, double y) {
@@ -323,7 +339,7 @@ protected:
}
fIntersections->insert(quadT, lineT, fQuad[qIndex]);
}
- // FIXME: see if line end is nearly on quad
+ this->addLineNearEndPoints();
}
void addExactVerticalEndPoints(double top, double bottom, double x) {
@@ -349,7 +365,7 @@ protected:
}
fIntersections->insert(quadT, lineT, fQuad[qIndex]);
}
- // FIXME: see if line end is nearly on quad
+ this->addLineNearEndPoints();
}
double findLineT(double t) {
diff --git a/src/pathops/SkIntersections.h b/src/pathops/SkIntersections.h
index 474142b269..abc10e19dd 100644
--- a/src/pathops/SkIntersections.h
+++ b/src/pathops/SkIntersections.h
@@ -112,6 +112,11 @@ public:
return fUsed > 0 && (t == 0 ? fT[0][0] == 0 : fT[0][fUsed - 1] == 1);
}
+ bool hasOppT(double t) const {
+ SkASSERT(t == 0 || t == 1);
+ return fUsed > 0 && (fT[1][0] == t || fT[1][fUsed - 1] == t);
+ }
+
int insertSwap(double one, double two, const SkDPoint& pt) {
if (fSwap) {
return insert(two, one, pt);
@@ -180,7 +185,6 @@ public:
quad.set(a);
SkDLine line;
line.set(b);
- fMax = 3; // 2; permit small coincident segment + non-coincident intersection
return intersect(quad, line);
}
@@ -207,7 +211,7 @@ public:
bool swapped() const {
return fSwap;
}
-
+
int used() const {
return fUsed;
}
diff --git a/src/pathops/SkOpAngle.cpp b/src/pathops/SkOpAngle.cpp
index c2eb0c9326..479a6f673d 100644
--- a/src/pathops/SkOpAngle.cpp
+++ b/src/pathops/SkOpAngle.cpp
@@ -62,6 +62,12 @@ bool SkOpAngle::after(SkOpAngle* test) {
SkOpAngle* lh = test;
SkOpAngle* rh = lh->fNext;
SkASSERT(lh != rh);
+ fCurvePart = fOriginalCurvePart;
+ lh->fCurvePart = lh->fOriginalCurvePart;
+ lh->fCurvePart.offset(lh->segment()->verb(), fCurvePart[0] - lh->fCurvePart[0]);
+ rh->fCurvePart = rh->fOriginalCurvePart;
+ rh->fCurvePart.offset(rh->segment()->verb(), fCurvePart[0] - rh->fCurvePart[0]);
+
#if DEBUG_ANGLE
SkString bugOut;
bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
@@ -150,9 +156,7 @@ bool SkOpAngle::after(SkOpAngle* test) {
return COMPARE_RESULT(8, ltOpposite);
} else if (ltOrder == 1 && trOrder == 0) {
SkASSERT(lrOrder < 0);
- SkDEBUGCODE(bool ltOpposite = lh->oppositePlanes(this));
bool trOpposite = oppositePlanes(rh);
- SkASSERT(ltOpposite != trOpposite);
return COMPARE_RESULT(9, trOpposite);
} else if (lrOrder == 1 && trOrder == 1) {
SkASSERT(ltOrder < 0);
@@ -175,15 +179,8 @@ bool SkOpAngle::after(SkOpAngle* test) {
int SkOpAngle::allOnOneSide(const SkOpAngle* test) {
SkASSERT(!fIsCurve);
SkASSERT(test->fIsCurve);
- const SkDPoint& origin = test->fCurvePart[0];
- SkVector line;
- if (segment()->verb() == SkPath::kLine_Verb) {
- const SkPoint* linePts = segment()->pts();
- int lineStart = fStart->t() < fEnd->t() ? 0 : 1;
- line = linePts[lineStart ^ 1] - linePts[lineStart];
- } else {
- line = (fCurvePart[1] - fCurvePart[0]).asSkVector();
- }
+ SkDPoint origin = fCurvePart[0];
+ SkDVector line = fCurvePart[1] - origin;
float crosses[3];
SkPath::Verb testVerb = test->segment()->verb();
int iMax = SkPathOpsVerbToPoints(testVerb);
@@ -244,8 +241,7 @@ bool SkOpAngle::checkParallel(SkOpAngle* rh) {
// compute the perpendicular to the endpoints and see where it intersects the opposite curve
// if the intersections within the t range, do a cross check on those
bool inside;
- if (!fCurvePart[SkPathOpsVerbToPoints(this->segment()->verb())].approximatelyEqual(
- rh->fCurvePart[SkPathOpsVerbToPoints(rh->segment()->verb())])) {
+ if (!fEnd->contains(rh->fEnd)) {
if (this->endToSide(rh, &inside)) {
return inside;
}
@@ -280,7 +276,7 @@ bool SkOpAngle::computeSector() {
}
fComputedSector = true;
bool stepUp = fStart->t() < fEnd->t();
- const SkOpSpanBase* checkEnd = fEnd;
+ SkOpSpanBase* checkEnd = fEnd;
if (checkEnd->final() && stepUp) {
fUnorderable = true;
return false;
@@ -306,7 +302,7 @@ bool SkOpAngle::computeSector() {
: checkEnd->prev();
} while (checkEnd);
recomputeSector:
- SkOpSpanBase* computedEnd = stepUp ? checkEnd ? checkEnd->prev() : fEnd->segment()->head()
+ SkOpSpanBase* computedEnd = stepUp ? checkEnd ? checkEnd->prev() : fEnd->segment()->head()
: checkEnd ? checkEnd->upCast()->next() : fEnd->segment()->tail();
if (checkEnd == fEnd || computedEnd == fEnd || computedEnd == fStart) {
fUnorderable = true;
@@ -398,7 +394,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
int rPts = SkPathOpsVerbToPoints(rVerb);
SkDLine rays[] = {{{this->fCurvePart[0], rh->fCurvePart[rPts]}},
{{this->fCurvePart[0], this->fCurvePart[lPts]}}};
- if (rays[0][1] == rays[1][1]) {
+ if (this->fEnd->contains(rh->fEnd)) {
return checkParallel(rh);
}
double smallTs[2] = {-1, -1};
@@ -538,14 +534,14 @@ bool SkOpAngle::endToSide(const SkOpAngle* rh, bool* inside) const {
}
double maxWidth = SkTMax(maxX - minX, maxY - minY);
endDist /= maxWidth;
- if (endDist < 5e-11) { // empirically found
+ if (endDist < 5e-12) { // empirically found
return false;
}
const SkDPoint* endPt = &rayEnd[0];
SkDPoint oppPt = iEnd.pt(closestEnd);
SkDVector vLeft = *endPt - start;
SkDVector vRight = oppPt - start;
- double dir = vLeft.crossCheck(vRight);
+ double dir = vLeft.crossNoNormalCheck(vRight);
if (!dir) {
return false;
}
@@ -785,7 +781,7 @@ bool SkOpAngle::orderable(SkOpAngle* rh) {
SkASSERT(x_ry != rx_y); // indicates an undetected coincidence -- worth finding earlier
return x_ry < rx_y;
}
- if ((result = allOnOneSide(rh)) >= 0) {
+ if ((result = this->allOnOneSide(rh)) >= 0) {
return result;
}
if (fUnorderable || approximately_zero(rh->fSide)) {
@@ -798,11 +794,10 @@ bool SkOpAngle::orderable(SkOpAngle* rh) {
if (rh->fUnorderable || approximately_zero(fSide)) {
goto unorderable;
}
- }
- if ((result = convexHullOverlaps(rh)) >= 0) {
+ } else if ((result = this->convexHullOverlaps(rh)) >= 0) {
return result;
}
- return endsIntersect(rh);
+ return this->endsIntersect(rh);
unorderable:
fUnorderable = true;
rh->fUnorderable = true;
@@ -846,8 +841,17 @@ void SkOpAngle::setCurveHullSweep() {
return;
}
fSweep[1] = fCurvePart[2] - fCurvePart[0];
+ // OPTIMIZE: I do the following float check a lot -- probably need a
+ // central place for this val-is-small-compared-to-curve check
+ double maxVal = 0;
+ for (int index = 0; index < SkPathOpsVerbToPoints(segment->verb()); ++index) {
+ maxVal = SkTMax(maxVal, SkTMax(SkTAbs(fCurvePart[index].fX),
+ SkTAbs(fCurvePart[index].fY)));
+ }
+
if (SkPath::kCubic_Verb != segment->verb()) {
- if (!fSweep[0].fX && !fSweep[0].fY) {
+ if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
+ && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
fSweep[0] = fSweep[1];
}
return;
@@ -856,7 +860,8 @@ void SkOpAngle::setCurveHullSweep() {
if (fSweep[0].fX == 0 && fSweep[0].fY == 0) {
fSweep[0] = fSweep[1];
fSweep[1] = thirdSweep;
- if (fSweep[0].fX == 0 && fSweep[0].fY == 0) {
+ if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
+ && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
fSweep[0] = fSweep[1];
fCurvePart[1] = fCurvePart[3];
fIsCurve = false;
@@ -894,6 +899,7 @@ void SkOpAngle::setSpans() {
= SK_ScalarNaN);
SkDEBUGCODE(fCurvePart.fVerb = segment->verb());
segment->subDivide(fStart, fEnd, &fCurvePart);
+ fOriginalCurvePart = fCurvePart;
setCurveHullSweep();
const SkPath::Verb verb = segment->verb();
if (verb != SkPath::kLine_Verb
@@ -1049,5 +1055,5 @@ bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
double tDist = tweep[0].length() * m;
bool useS = fabs(sDist) < fabs(tDist);
double mFactor = fabs(useS ? this->distEndRatio(sDist) : rh->distEndRatio(tDist));
- return mFactor < 2400; // empirically found limit
+ return mFactor < 50; // empirically found limit
}
diff --git a/src/pathops/SkOpAngle.h b/src/pathops/SkOpAngle.h
index 4b209de32d..a1ead1ecfd 100644
--- a/src/pathops/SkOpAngle.h
+++ b/src/pathops/SkOpAngle.h
@@ -19,7 +19,8 @@ class SkOpSegment;
class SkOpSpanBase;
class SkOpSpan;
-struct SkOpAngle {
+class SkOpAngle {
+public:
enum IncludeType {
kUnaryWinding,
kUnaryXor,
@@ -27,14 +28,8 @@ struct SkOpAngle {
kBinaryOpp,
};
- bool after(SkOpAngle* test);
- int allOnOneSide(const SkOpAngle* test);
- bool checkCrossesZero() const;
- bool checkParallel(SkOpAngle* );
- bool computeSector();
- int convexHullOverlaps(const SkOpAngle* ) const;
-
const SkOpAngle* debugAngle(int id) const;
+ const SkOpCoincidence* debugCoincidence() const;
SkOpContour* debugContour(int id);
int debugID() const {
@@ -46,6 +41,7 @@ struct SkOpAngle {
#endif
#if DEBUG_ANGLE
+ bool debugCheckCoincidence() const { return fCheckCoincidence; }
void debugCheckNearCoincidence() const;
SkString debugPart() const;
#endif
@@ -53,7 +49,7 @@ struct SkOpAngle {
const SkOpSegment* debugSegment(int id) const;
int debugSign() const;
const SkOpSpanBase* debugSpan(int id) const;
- void debugValidate() const;
+ void debugValidate() const;
void debugValidateNext() const; // in debug builds, verify that angle loop is uncorrupted
double distEndRatio(double dist) const;
// available to testing only
@@ -68,62 +64,56 @@ struct SkOpAngle {
return fEnd;
}
- bool endsIntersect(SkOpAngle* );
- bool endToSide(const SkOpAngle* rh, bool* inside) const;
- int findSector(SkPath::Verb verb, double x, double y) const;
- SkOpGlobalState* globalState() const;
void insert(SkOpAngle* );
SkOpSpanBase* lastMarked() const;
bool loopContains(const SkOpAngle* ) const;
int loopCount() const;
- bool merge(SkOpAngle* );
- double midT() const;
- bool midToSide(const SkOpAngle* rh, bool* inside) const;
SkOpAngle* next() const {
return fNext;
}
- bool oppositePlanes(const SkOpAngle* rh) const;
- bool orderable(SkOpAngle* rh); // false == this < rh ; true == this > rh
SkOpAngle* previous() const;
-
- int sectorEnd() const {
- return fSectorEnd;
- }
-
- int sectorStart() const {
- return fSectorStart;
- }
-
SkOpSegment* segment() const;
-
void set(SkOpSpanBase* start, SkOpSpanBase* end);
- void setCurveHullSweep();
-
- void setID(int id) {
- SkDEBUGCODE(fID = id);
- }
void setLastMarked(SkOpSpanBase* marked) {
fLastMarked = marked;
}
- void setSector();
- void setSpans();
-
SkOpSpanBase* start() const {
return fStart;
}
SkOpSpan* starter();
- bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
bool unorderable() const {
return fUnorderable;
}
- SkDCurve fCurvePart; // the curve from start to end
+private:
+ bool after(SkOpAngle* test);
+ int allOnOneSide(const SkOpAngle* test);
+ bool checkCrossesZero() const;
+ bool checkParallel(SkOpAngle* );
+ bool computeSector();
+ int convexHullOverlaps(const SkOpAngle* ) const;
+ bool endToSide(const SkOpAngle* rh, bool* inside) const;
+ bool endsIntersect(SkOpAngle* );
+ int findSector(SkPath::Verb verb, double x, double y) const;
+ SkOpGlobalState* globalState() const;
+ bool merge(SkOpAngle* );
+ double midT() const;
+ bool midToSide(const SkOpAngle* rh, bool* inside) const;
+ bool oppositePlanes(const SkOpAngle* rh) const;
+ bool orderable(SkOpAngle* rh); // false == this < rh ; true == this > rh
+ void setCurveHullSweep();
+ void setSector();
+ void setSpans();
+ bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
+
+ SkDCurve fOriginalCurvePart; // the curve from start to end
+ SkDCurve fCurvePart; // the curve from start to end offset as needed
double fSide;
SkLineParameters fTangentHalf; // used only to sort a pair of lines or line-like sections
SkOpAngle* fNext;
@@ -143,6 +133,7 @@ struct SkOpAngle {
bool fCheckCoincidence;
SkDEBUGCODE(int fID);
+ friend class PathOpsAngleTester;
};
diff --git a/src/pathops/SkOpBuilder.cpp b/src/pathops/SkOpBuilder.cpp
index 67aa92fe26..6652c10d52 100644
--- a/src/pathops/SkOpBuilder.cpp
+++ b/src/pathops/SkOpBuilder.cpp
@@ -44,13 +44,11 @@ bool FixWinding(SkPath* path) {
}
SkChunkAlloc allocator(4096);
SkOpContourHead contourHead;
- SkOpGlobalState globalState(nullptr, &contourHead SkDEBUGPARAMS(false)
+ SkOpGlobalState globalState(&contourHead, &allocator SkDEBUGPARAMS(false)
SkDEBUGPARAMS(nullptr));
- SkOpEdgeBuilder builder(*path, &contourHead, &allocator, &globalState);
- builder.finish(&allocator);
- if (!contourHead.next()) {
- return false;
- }
+ SkOpEdgeBuilder builder(*path, &contourHead, &globalState);
+ builder.finish();
+ SkASSERT(contourHead.next());
contourHead.resetReverse();
bool writePath = false;
SkOpSpan* topSpan;
@@ -67,7 +65,7 @@ bool FixWinding(SkPath* path) {
topContour->setReverse();
writePath = true;
}
- topContour->markDone();
+ topContour->markAllDone();
globalState.clearNested();
}
if (!writePath) {
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
index 4b46e685ee..af330dfe1e 100755
--- a/src/pathops/SkOpCoincidence.cpp
+++ b/src/pathops/SkOpCoincidence.cpp
@@ -8,237 +8,725 @@
#include "SkOpSegment.h"
#include "SkPathOpsTSect.h"
-bool SkOpCoincidence::extend(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
- SkOpPtT* oppPtTEnd) {
- // if there is an existing pair that overlaps the addition, extend it
+#if DEBUG_COINCIDENCE
+#define FAIL_IF(cond) SkASSERT(!(cond))
+#else
+#define FAIL_IF(cond) do { if (cond) return false; } while (false)
+#endif
+
+// returns true if coincident span's start and end are the same
+bool SkCoincidentSpans::collapsed(const SkOpPtT* test) const {
+ return (fCoinPtTStart == test && fCoinPtTEnd->contains(test))
+ || (fCoinPtTEnd == test && fCoinPtTStart->contains(test))
+ || (fOppPtTStart == test && fOppPtTEnd->contains(test))
+ || (fOppPtTEnd == test && fOppPtTStart->contains(test));
+}
+
+// sets the span's end to the ptT referenced by the previous-next
+void SkCoincidentSpans::correctOneEnd(
+ const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
+ void (SkCoincidentSpans::*setEnd)(const SkOpPtT* ptT) ) {
+ const SkOpPtT* origPtT = (this->*getEnd)();
+ const SkOpSpanBase* origSpan = origPtT->span();
+ const SkOpSpan* prev = origSpan->prev();
+ const SkOpPtT* testPtT = prev ? prev->next()->ptT()
+ : origSpan->upCast()->next()->prev()->ptT();
+ if (origPtT != testPtT) {
+ (this->*setEnd)(testPtT);
+ }
+}
+
+// FIXME: member pointers have fallen out of favor and can be replaced with
+// an alternative approach.
+// makes all span ends agree with the segment's spans that define them
+void SkCoincidentSpans::correctEnds() {
+ this->correctOneEnd(&SkCoincidentSpans::coinPtTStart, &SkCoincidentSpans::setCoinPtTStart);
+ this->correctOneEnd(&SkCoincidentSpans::coinPtTEnd, &SkCoincidentSpans::setCoinPtTEnd);
+ this->correctOneEnd(&SkCoincidentSpans::oppPtTStart, &SkCoincidentSpans::setOppPtTStart);
+ this->correctOneEnd(&SkCoincidentSpans::oppPtTEnd, &SkCoincidentSpans::setOppPtTEnd);
+}
+
+/* Please keep this in sync with debugExpand */
+// expand the range by checking adjacent spans for coincidence
+bool SkCoincidentSpans::expand() {
+ bool expanded = false;
+ const SkOpSegment* segment = coinPtTStart()->segment();
+ const SkOpSegment* oppSegment = oppPtTStart()->segment();
+ do {
+ const SkOpSpan* start = coinPtTStart()->span()->upCast();
+ const SkOpSpan* prev = start->prev();
+ const SkOpPtT* oppPtT;
+ if (!prev || !(oppPtT = prev->contains(oppSegment))) {
+ break;
+ }
+ double midT = (prev->t() + start->t()) / 2;
+ if (!segment->isClose(midT, oppSegment)) {
+ break;
+ }
+ setStarts(prev->ptT(), oppPtT);
+ expanded = true;
+ } while (true);
+ do {
+ const SkOpSpanBase* end = coinPtTEnd()->span();
+ SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
+ const SkOpPtT* oppPtT;
+ if (!next || !(oppPtT = next->contains(oppSegment))) {
+ break;
+ }
+ double midT = (end->t() + next->t()) / 2;
+ if (!segment->isClose(midT, oppSegment)) {
+ break;
+ }
+ setEnds(next->ptT(), oppPtT);
+ expanded = true;
+ } while (true);
+ return expanded;
+}
+
+// increase the range of this span
+bool SkCoincidentSpans::extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
+ bool result = false;
+ if (fCoinPtTStart->fT > coinPtTStart->fT || (this->flipped()
+ ? fOppPtTStart->fT < oppPtTStart->fT : fOppPtTStart->fT > oppPtTStart->fT)) {
+ this->setStarts(coinPtTStart, oppPtTStart);
+ result = true;
+ }
+ if (fCoinPtTEnd->fT < coinPtTEnd->fT || (this->flipped()
+ ? fOppPtTEnd->fT > oppPtTEnd->fT : fOppPtTEnd->fT < oppPtTEnd->fT)) {
+ this->setEnds(coinPtTEnd, oppPtTEnd);
+ result = true;
+ }
+ return result;
+}
+
+// set the range of this span
+void SkCoincidentSpans::set(SkCoincidentSpans* next, const SkOpPtT* coinPtTStart,
+ const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd
+ SkDEBUGPARAMS(int id)) {
+ SkASSERT(SkOpCoincidence::Ordered(coinPtTStart, oppPtTStart));
+ fNext = next;
+ this->setStarts(coinPtTStart, oppPtTStart);
+ this->setEnds(coinPtTEnd, oppPtTEnd);
+ SkDEBUGCODE(fID = id);
+}
+
+// returns true if both points are inside this
+bool SkCoincidentSpans::contains(const SkOpPtT* s, const SkOpPtT* e) const {
+ if (s->fT > e->fT) {
+ SkTSwap(s, e);
+ }
+ if (s->segment() == fCoinPtTStart->segment()) {
+ return fCoinPtTStart->fT <= s->fT && e->fT <= fCoinPtTEnd->fT;
+ } else {
+ SkASSERT(s->segment() == fOppPtTStart->segment());
+ double oppTs = fOppPtTStart->fT;
+ double oppTe = fOppPtTEnd->fT;
+ if (oppTs > oppTe) {
+ SkTSwap(oppTs, oppTe);
+ }
+ return oppTs <= s->fT && e->fT <= oppTe;
+ }
+}
+
+// returns the number of segment span's contained by this, or -1 if inconsistent
+int SkCoincidentSpans::spanCount() const {
+ // most commonly, concidence are one span long; check for that first
+ const SkOpSpanBase* start = coinPtTStart()->span();
+ const SkOpSpanBase* end = coinPtTEnd()->span();
+ int coinIntervals = 0;
+ while (start != end) {
+ coinIntervals++;
+ start = start->upCast()->next();
+ }
+ const SkOpSpanBase* oppStart = (flipped() ? oppPtTEnd() : oppPtTStart())->span();
+ const SkOpSpanBase* oppEnd = (flipped() ? oppPtTStart() : oppPtTEnd())->span();
+ int oppIntervals = 0;
+ while (oppStart != oppEnd) {
+ oppIntervals++;
+ oppStart = oppStart->upCast()->next();
+ }
+ return coinIntervals == oppIntervals ? coinIntervals : -1;
+}
+
+// returns true if the point is on a coincident edge, and if it is the start of that edge
+bool SkOpCoincidence::edge(const SkOpPtT* test, bool* start) const {
SkCoincidentSpans* coinRec = fHead;
- if (coinRec) {
- do {
- if (coinRec->fCoinPtTStart->segment() != coinPtTStart->segment()) {
- continue;
- }
- if (coinRec->fOppPtTStart->segment() != oppPtTStart->segment()) {
+ if (!coinRec) {
+ return false;
+ }
+ do {
+ if (coinRec->coinPtTStart() == test) {
+ *start = true;
+ return true;
+ }
+ if (coinRec->coinPtTEnd() == test) {
+ *start = false;
+ return true;
+ }
+ if (coinRec->oppPtTStart() == test) {
+ *start = !coinRec->flipped();
+ return true;
+ }
+ if (coinRec->coinPtTEnd() == test) {
+ *start = coinRec->flipped();
+ return true;
+ }
+ } while ((coinRec = coinRec->next()));
+ return false;
+}
+
+// if there is an existing pair that overlaps the addition, extend it
+bool SkOpCoincidence::extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
+ SkCoincidentSpans* test = fHead;
+ if (!test) {
+ return false;
+ }
+ const SkOpSegment* coinSeg = coinPtTStart->segment();
+ const SkOpSegment* oppSeg = oppPtTStart->segment();
+ if (!Ordered(coinPtTStart, oppPtTStart)) {
+ SkTSwap(coinSeg, oppSeg);
+ SkTSwap(coinPtTStart, oppPtTStart);
+ SkTSwap(coinPtTEnd, oppPtTEnd);
+ if (coinPtTStart->fT > coinPtTEnd->fT) {
+ SkTSwap(coinPtTStart, coinPtTEnd);
+ SkTSwap(oppPtTStart, oppPtTEnd);
+ }
+ }
+ double oppMinT = SkTMin(oppPtTStart->fT, oppPtTEnd->fT);
+ SkDEBUGCODE(double oppMaxT = SkTMax(oppPtTStart->fT, oppPtTEnd->fT));
+ do {
+ if (coinSeg != test->coinPtTStart()->segment()) {
+ continue;
+ }
+ if (oppSeg != test->oppPtTStart()->segment()) {
+ continue;
+ }
+ double oTestMinT = SkTMin(test->oppPtTStart()->fT, test->oppPtTEnd()->fT);
+ double oTestMaxT = SkTMax(test->oppPtTStart()->fT, test->oppPtTEnd()->fT);
+ // if debug check triggers, caller failed to check if extended already exists
+ SkASSERT(test->coinPtTStart()->fT > coinPtTStart->fT
+ || coinPtTEnd->fT > test->coinPtTEnd()->fT
+ || oTestMinT > oppMinT || oppMaxT > oTestMaxT);
+ if ((test->coinPtTStart()->fT <= coinPtTEnd->fT
+ && coinPtTStart->fT <= test->coinPtTEnd()->fT)
+ || (oTestMinT <= oTestMaxT && oppMinT <= oTestMaxT)) {
+ test->extend(coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
+ return true;
+ }
+ } while ((test = test->next()));
+ return false;
+}
+
+// verifies that the coincidence hasn't already been added
+static void DebugCheckAdd(const SkCoincidentSpans* check, const SkOpPtT* coinPtTStart,
+ const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
+#if DEBUG_COINCIDENCE
+ while (check) {
+ SkASSERT(check->coinPtTStart() != coinPtTStart || check->coinPtTEnd() != coinPtTEnd
+ || check->oppPtTStart() != oppPtTStart || check->oppPtTEnd() != oppPtTEnd);
+ SkASSERT(check->coinPtTStart() != oppPtTStart || check->coinPtTEnd() != oppPtTEnd
+ || check->oppPtTStart() != coinPtTStart || check->oppPtTEnd() != coinPtTEnd);
+ check = check->next();
+ }
+#endif
+}
+
+// adds a new coincident pair
+void SkOpCoincidence::add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
+ SkOpPtT* oppPtTEnd) {
+ // OPTIMIZE: caller should have already sorted
+ if (!Ordered(coinPtTStart, oppPtTStart)) {
+ if (oppPtTStart->fT < oppPtTEnd->fT) {
+ this->add(oppPtTStart, oppPtTEnd, coinPtTStart, coinPtTEnd);
+ } else {
+ this->add(oppPtTEnd, oppPtTStart, coinPtTEnd, coinPtTStart);
+ }
+ return;
+ }
+ SkASSERT(Ordered(coinPtTStart, oppPtTStart));
+ // choose the ptT at the front of the list to track
+ coinPtTStart = coinPtTStart->span()->ptT();
+ coinPtTEnd = coinPtTEnd->span()->ptT();
+ oppPtTStart = oppPtTStart->span()->ptT();
+ oppPtTEnd = oppPtTEnd->span()->ptT();
+ SkASSERT(coinPtTStart->fT < coinPtTEnd->fT);
+ SkASSERT(oppPtTStart->fT != oppPtTEnd->fT);
+ SkASSERT(!coinPtTStart->deleted());
+ SkASSERT(!coinPtTEnd->deleted());
+ SkASSERT(!oppPtTStart->deleted());
+ SkASSERT(!oppPtTEnd->deleted());
+ DebugCheckAdd(fHead, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
+ DebugCheckAdd(fTop, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd);
+ SkCoincidentSpans* coinRec = SkOpTAllocator<SkCoincidentSpans>::Allocate(
+ this->globalState()->allocator());
+ coinRec->init();
+ coinRec->set(this->fHead, coinPtTStart, coinPtTEnd, oppPtTStart, oppPtTEnd
+ SkDEBUGPARAMS(fGlobalState->nextCoinID()));
+ fHead = coinRec;
+}
+
+// description below
+void SkOpCoincidence::addEndMovedSpans(const SkOpSpan* base, const SkOpSpanBase* testSpan) {
+ const SkOpPtT* testPtT = testSpan->ptT();
+ const SkOpPtT* stopPtT = testPtT;
+ const SkOpSegment* baseSeg = base->segment();
+ while ((testPtT = testPtT->next()) != stopPtT) {
+ const SkOpSegment* testSeg = testPtT->segment();
+ if (testPtT->deleted()) {
+ continue;
+ }
+ if (testSeg == baseSeg) {
+ continue;
+ }
+ if (this->contains(baseSeg, testSeg, testPtT->fT)) {
+ continue;
+ }
+ // intersect perp with base->ptT() with testPtT->segment()
+ SkDVector dxdy = baseSeg->dSlopeAtT(base->t());
+ const SkPoint& pt = base->pt();
+ SkDLine ray = {{{pt.fX, pt.fY}, {pt.fX + dxdy.fY, pt.fY - dxdy.fX}}};
+ SkIntersections i;
+ (*CurveIntersectRay[testSeg->verb()])(testSeg->pts(), testSeg->weight(), ray, &i);
+ for (int index = 0; index < i.used(); ++index) {
+ double t = i[0][index];
+ if (!between(0, t, 1)) {
continue;
}
- if (coinRec->fCoinPtTStart->fT > coinPtTEnd->fT) {
+ SkDPoint oppPt = i.pt(index);
+ if (!oppPt.approximatelyEqual(pt)) {
continue;
}
- if (coinRec->fCoinPtTEnd->fT < coinPtTStart->fT) {
+ SkOpSegment* writableSeg = const_cast<SkOpSegment*>(testSeg);
+ SkOpPtT* oppStart = writableSeg->addT(t, SkOpSegment::kAllowAliasMatch, nullptr);
+ SkOpSpan* writableBase = const_cast<SkOpSpan*>(base);
+ oppStart->span()->addOppAndMerge(writableBase);
+ if (oppStart->deleted()) {
continue;
}
- if (coinRec->fCoinPtTStart->fT > coinPtTStart->fT) {
- coinRec->fCoinPtTStart = coinPtTStart;
- coinRec->fOppPtTStart = oppPtTStart;
+ SkOpSegment* coinSeg = base->segment();
+ SkOpSegment* oppSeg = oppStart->segment();
+ double coinTs, coinTe, oppTs, oppTe;
+ if (coinSeg < oppSeg) {
+ coinTs = base->t();
+ coinTe = testSpan->t();
+ oppTs = oppStart->fT;
+ oppTe = testPtT->fT;
+ } else {
+ SkTSwap(coinSeg, oppSeg);
+ coinTs = oppStart->fT;
+ coinTe = testPtT->fT;
+ oppTs = base->t();
+ oppTe = testSpan->t();
}
- if (coinRec->fCoinPtTEnd->fT < coinPtTEnd->fT) {
- coinRec->fCoinPtTEnd = coinPtTEnd;
- coinRec->fOppPtTEnd = oppPtTEnd;
+ if (coinTs > coinTe) {
+ SkTSwap(coinTs, coinTe);
+ SkTSwap(oppTs, oppTe);
}
- return true;
- } while ((coinRec = coinRec->fNext));
+ (void) this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe);
+ }
}
- return false;
}
-void SkOpCoincidence::add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
- SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator) {
- SkASSERT(coinPtTStart->fT < coinPtTEnd->fT);
- bool flipped = oppPtTStart->fT > oppPtTEnd->fT;
- SkCoincidentSpans* coinRec = SkOpTAllocator<SkCoincidentSpans>::Allocate(allocator);
- coinRec->fNext = this->fHead;
- coinRec->fCoinPtTStart = coinPtTStart;
- coinRec->fCoinPtTEnd = coinPtTEnd;
- coinRec->fOppPtTStart = oppPtTStart;
- coinRec->fOppPtTEnd = oppPtTEnd;
- coinRec->fFlipped = flipped;
- SkDEBUGCODE(coinRec->fID = fDebugState->nextCoinID());
-
- this->fHead = coinRec;
+// description below
+bool SkOpCoincidence::addEndMovedSpans(const SkOpPtT* ptT) {
+ const SkOpSpan* base = ptT->span()->upCast();
+ const SkOpSpan* prev = base->prev();
+ if (!prev) {
+ return false;
+ }
+ if (!prev->isCanceled()) {
+ this->addEndMovedSpans(base, base->prev());
+ }
+ if (!base->isCanceled()) {
+ this->addEndMovedSpans(base, base->next());
+ }
+ return true;
}
-static void t_range(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, double tEnd,
- const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs, double* coinTe) {
- double denom = overE->fT - overS->fT;
- double start = 0 < denom ? tStart : tEnd;
- double end = 0 < denom ? tEnd : tStart;
- double sRatio = (start - overS->fT) / denom;
- double eRatio = (end - overS->fT) / denom;
- *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio;
- *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
+/* If A is coincident with B and B includes an endpoint, and A's matching point
+ is not the endpoint (i.e., there's an implied line connecting B-end and A)
+ then assume that the same implied line may intersect another curve close to B.
+ Since we only care about coincidence that was undetected, look at the
+ ptT list on B-segment adjacent to the B-end/A ptT loop (not in the loop, but
+ next door) and see if the A matching point is close enough to form another
+ coincident pair. If so, check for a new coincident span between B-end/A ptT loop
+ and the adjacent ptT loop.
+*/
+bool SkOpCoincidence::addEndMovedSpans() {
+ SkCoincidentSpans* span = fHead;
+ if (!span) {
+ return true;
+ }
+ fTop = span;
+ fHead = nullptr;
+ do {
+ if (span->coinPtTStart()->fPt != span->oppPtTStart()->fPt) {
+ if (1 == span->coinPtTStart()->fT) {
+ return false;
+ }
+ bool onEnd = span->coinPtTStart()->fT == 0;
+ bool oOnEnd = zero_or_one(span->oppPtTStart()->fT);
+ if (onEnd) {
+ if (!oOnEnd) { // if both are on end, any nearby intersect was already found
+ if (!this->addEndMovedSpans(span->oppPtTStart())) {
+ return false;
+ }
+ }
+ } else if (oOnEnd) {
+ if (!this->addEndMovedSpans(span->coinPtTStart())) {
+ return false;
+ }
+ }
+ }
+ if (span->coinPtTEnd()->fPt != span->oppPtTEnd()->fPt) {
+ bool onEnd = span->coinPtTEnd()->fT == 1;
+ bool oOnEnd = zero_or_one(span->oppPtTEnd()->fT);
+ if (onEnd) {
+ if (!oOnEnd) {
+ if (!this->addEndMovedSpans(span->oppPtTEnd())) {
+ return false;
+ }
+ }
+ } else if (oOnEnd) {
+ if (!this->addEndMovedSpans(span->coinPtTEnd())) {
+ return false;
+ }
+ }
+ }
+ } while ((span = span->next()));
+ this->restoreHead();
+ return true;
}
-bool SkOpCoincidence::addExpanded(SkChunkAlloc* allocator
- PATH_OPS_DEBUG_VALIDATE_PARAMS(SkOpGlobalState* globalState)) {
-#if DEBUG_VALIDATE
- globalState->setPhase(SkOpGlobalState::kIntersecting);
-#endif
- // for each coincident pair, match the spans
- // if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
+/* Please keep this in sync with debugAddExpanded */
+// for each coincident pair, match the spans
+// if the spans don't match, add the missing pt to the segment and loop it in the opposite span
+bool SkOpCoincidence::addExpanded() {
SkCoincidentSpans* coin = this->fHead;
- SkASSERT(coin);
+ if (!coin) {
+ return true;
+ }
do {
- SkOpPtT* startPtT = coin->fCoinPtTStart;
- SkOpPtT* oStartPtT = coin->fOppPtTStart;
+ const SkOpPtT* startPtT = coin->coinPtTStart();
+ const SkOpPtT* oStartPtT = coin->oppPtTStart();
SkASSERT(startPtT->contains(oStartPtT));
- SkASSERT(coin->fCoinPtTEnd->contains(coin->fOppPtTEnd));
- SkOpSpanBase* start = startPtT->span();
- SkOpSpanBase* oStart = oStartPtT->span();
- const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
- if (oEnd->deleted()) {
- return false;
- }
- SkOpSpanBase* test = start->upCast()->next();
- SkOpSpanBase* oTest = coin->fFlipped ? oStart->prev() : oStart->upCast()->next();
+ SkASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
+ const SkOpSpanBase* start = startPtT->span();
+ const SkOpSpanBase* oStart = oStartPtT->span();
+ const SkOpSpanBase* end = coin->coinPtTEnd()->span();
+ const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
+ FAIL_IF(oEnd->deleted());
+ const SkOpSpanBase* test = start->upCast()->next();
+ const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
if (!oTest) {
return false;
}
while (test != end || oTest != oEnd) {
- if (!test->ptT()->contains(oTest->ptT())) {
+ if (!test->ptT()->contains(oStart->segment())
+ || !oTest->ptT()->contains(start->segment())) {
// use t ranges to guess which one is missing
- double startRange = coin->fCoinPtTEnd->fT - startPtT->fT;
- if (!startRange) {
- return false;
- }
+ double startRange = coin->coinPtTEnd()->fT - startPtT->fT;
+ FAIL_IF(!startRange);
double startPart = (test->t() - startPtT->fT) / startRange;
- double oStartRange = coin->fOppPtTEnd->fT - oStartPtT->fT;
- if (!oStartRange) {
- return false;
- }
+ double oStartRange = coin->oppPtTEnd()->fT - oStartPtT->fT;
+ FAIL_IF(!oStartRange);
double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
- if (startPart == oStartPart) {
+ FAIL_IF(startPart == oStartPart);
+ bool startOver = false;
+ bool success = startPart < oStartPart
+ ? oStart->segment()->addExpanded(
+ oStartPtT->fT + oStartRange * startPart, test, &startOver)
+ : start->segment()->addExpanded(
+ startPtT->fT + startRange * oStartPart, oTest, &startOver);
+ if (!success) {
+ SkASSERT(0);
return false;
}
- SkOpPtT* newPt;
- if (startPart < oStartPart) {
- double newT = oStartPtT->fT + oStartRange * startPart;
- newPt = oStart->segment()->addT(newT, SkOpSegment::kAllowAlias, allocator);
- if (!newPt) {
- return false;
- }
- newPt->fPt = test->pt();
- test->ptT()->addOpp(newPt);
- } else {
- double newT = startPtT->fT + startRange * oStartPart;
- newPt = start->segment()->addT(newT, SkOpSegment::kAllowAlias, allocator);
- if (!newPt) {
- return false;
- }
- newPt->fPt = oTest->pt();
- oTest->ptT()->addOpp(newPt);
+ if (startOver) {
+ test = start;
+ oTest = oStart;
}
- // start over
- test = start;
- oTest = oStart;
}
if (test != end) {
test = test->upCast()->next();
}
if (oTest != oEnd) {
- oTest = coin->fFlipped ? oTest->prev() : oTest->upCast()->next();
+ oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
if (!oTest) {
return false;
}
}
}
- } while ((coin = coin->fNext));
-#if DEBUG_VALIDATE
- globalState->setPhase(SkOpGlobalState::kWalking);
-#endif
+ } while ((coin = coin->next()));
return true;
}
+// checks to see if coincidence has already been found
+bool SkOpCoincidence::alreadyAdded(const SkCoincidentSpans* check, const SkCoincidentSpans* outer,
+ const SkOpPtT* over1s, const SkOpPtT* over1e) const {
+ do {
+ if (check->oppPtTStart() == outer->coinPtTStart() && check->coinPtTStart() == over1s
+ && check->oppPtTEnd() == outer->coinPtTEnd() && check->coinPtTEnd() == over1e) {
+ return true;
+ }
+ if (check->coinPtTStart() == outer->coinPtTStart() && check->oppPtTStart() == over1s
+ && check->coinPtTEnd() == outer->coinPtTEnd() && check->oppPtTEnd() == over1e) {
+ return true;
+ }
+ if (check->startEquals(outer->oppPtTStart()->span(), over1s->span())) {
+ SkDEBUGCODE(check->debugStartCheck(outer->oppPtTEnd()->span(), over1e->span(),
+ fGlobalState));
+ return true;
+ }
+ if (check->startEquals(over1s->span(), outer->coinPtTStart()->span())) {
+ SkDEBUGCODE(check->debugStartCheck(over1e->span(), outer->oppPtTEnd()->span(),
+ fGlobalState));
+ return true;
+ }
+ } while ((check = check->next()));
+ return false;
+}
+
+ /* Please keep this in sync with debugAddIfMissing() */
bool SkOpCoincidence::addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over1s,
- SkOpPtT* over1e, SkChunkAlloc* allocator) {
- SkCoincidentSpans* check = this->fTop;
+ SkOpPtT* over1e) {
+ SkASSERT(fTop);
+ if (this->alreadyAdded(fTop, outer, over1s, over1e)) {
+ return false;
+ }
+ if (fHead && this->alreadyAdded(fHead, outer, over1s, over1e)) {
+ return false;
+ }
+ this->add(outer->coinPtTStart(), outer->coinPtTEnd(), over1s, over1e);
+ this->debugValidate();
+ return true;
+}
+
+// given a t span, map the same range on the coincident span
+void SkOpCoincidence::TRange(const SkOpPtT* overS, const SkOpPtT* overE, double tStart,
+ double tEnd, const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs,
+ double* coinTe) {
+ double denom = overE->fT - overS->fT;
+ double start = 0 < denom ? tStart : tEnd;
+ double end = 0 < denom ? tEnd : tStart;
+ double sRatio = (start - overS->fT) / denom;
+ double eRatio = (end - overS->fT) / denom;
+ *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio;
+ *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
+}
+
+// return true if span overlaps existing and needs to adjust the coincident list
+bool SkOpCoincidence::checkOverlap(SkCoincidentSpans* check,
+ const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
+ double coinTs, double coinTe, double oppTs, double oppTe,
+ SkTDArray<SkCoincidentSpans*>* overlaps) const {
+ if (!Ordered(coinSeg, oppSeg)) {
+ if (oppTs < oppTe) {
+ return this->checkOverlap(check, oppSeg, coinSeg, oppTs, oppTe, coinTs, coinTe,
+ overlaps);
+ }
+ return this->checkOverlap(check, oppSeg, coinSeg, oppTe, oppTs, coinTe, coinTs, overlaps);
+ }
+ bool swapOpp = oppTs > oppTe;
+ if (swapOpp) {
+ SkTSwap(oppTs, oppTe);
+ }
do {
- if (check->fCoinPtTStart->span() == over1s->span()
- && check->fOppPtTStart->span() == outer->fOppPtTStart->span()) {
- SkASSERT(check->fCoinPtTEnd->span() == over1e->span()
- || !fDebugState->debugRunFail());
- SkASSERT(check->fOppPtTEnd->span() == outer->fOppPtTEnd->span()
- || !fDebugState->debugRunFail());
- return false;
+ if (check->coinPtTStart()->segment() != coinSeg) {
+ continue;
}
- if (check->fCoinPtTStart->span() == outer->fCoinPtTStart->span()
- && check->fOppPtTStart->span() == over1s->span()) {
- SkASSERT(check->fCoinPtTEnd->span() == outer->fCoinPtTEnd->span()
- || !fDebugState->debugRunFail());
- SkASSERT(check->fOppPtTEnd->span() == over1e->span()
- || !fDebugState->debugRunFail());
- return false;
+ if (check->oppPtTStart()->segment() != oppSeg) {
+ continue;
}
- } while ((check = check->fNext));
- this->add(outer->fCoinPtTStart, outer->fCoinPtTEnd, over1s, over1e, allocator);
-#if 0
- // FIXME: up to four flavors could be added -- do we need only one?
-#endif
+ double checkTs = check->coinPtTStart()->fT;
+ double checkTe = check->coinPtTEnd()->fT;
+ bool coinOutside = coinTe < checkTs || coinTs > checkTe;
+ double oCheckTs = check->oppPtTStart()->fT;
+ double oCheckTe = check->oppPtTEnd()->fT;
+ if (swapOpp) {
+ if (oCheckTs <= oCheckTe) {
+ return false;
+ }
+ SkTSwap(oCheckTs, oCheckTe);
+ }
+ bool oppOutside = oppTe < oCheckTs || oppTs > oCheckTe;
+ if (coinOutside && oppOutside) {
+ continue;
+ }
+ bool coinInside = coinTe <= checkTe && coinTs >= checkTs;
+ bool oppInside = oppTe <= oCheckTe && oppTs >= oCheckTs;
+ if (coinInside && oppInside) {
+ return false; // complete overlap, already included, do nothing
+ }
+ *overlaps->append() = check; // partial overlap, extend existing entry
+ } while ((check = check->next()));
return true;
}
+/* Please keep this in sync with debugAddIfMissing() */
bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
- const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
+ const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
- SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator) {
+ SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
double coinTs, coinTe, oppTs, oppTe;
- t_range(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
- t_range(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
+ TRange(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
+ TRange(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
+ bool swap = coinTs > coinTe;
+ if (swap) {
+ SkTSwap(coinTs, coinTe);
+ }
+ if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
+ SkTSwap(oppTs, oppTe);
+ }
+ if (swap) {
+ SkTSwap(oppTs, oppTe);
+ }
SkOpSegment* coinSeg = coinPtTStart->segment();
SkOpSegment* oppSeg = oppPtTStart->segment();
- SkASSERT(coinSeg != oppSeg);
- SkCoincidentSpans* check = this->fTop;
- do {
- const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment();
- if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) {
- continue;
+ if (coinSeg == oppSeg) {
+ return false;
+ }
+ return this->addOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe);
+}
+
+/* Please keep this in sync with debugAddOrOverlap() */
+bool SkOpCoincidence::addOrOverlap(SkOpSegment* coinSeg, SkOpSegment* oppSeg,
+ double coinTs, double coinTe, double oppTs, double oppTe) {
+ SkTDArray<SkCoincidentSpans*> overlaps;
+ SkASSERT(fTop);
+ if (!this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &overlaps)) {
+ return false;
+ }
+ if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
+ coinTe, oppTs, oppTe, &overlaps)) {
+ return false;
+ }
+ SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
+ for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
+ SkCoincidentSpans* test = overlaps[index];
+ if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
+ overlap->setCoinPtTStart(test->coinPtTStart());
}
- const SkOpSegment* checkOppSeg = check->fOppPtTStart->segment();
- if (checkOppSeg != coinSeg && checkOppSeg != oppSeg) {
- continue;
+ if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
+ overlap->setCoinPtTEnd(test->coinPtTEnd());
+ }
+ if (overlap->flipped()
+ ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
+ : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
+ overlap->setOppPtTStart(test->oppPtTStart());
}
- int cTs = coinTs;
- int cTe = coinTe;
- int oTs = oppTs;
- int oTe = oppTe;
- if (checkCoinSeg != coinSeg) {
- SkASSERT(checkOppSeg != oppSeg);
- SkTSwap(cTs, oTs);
- SkTSwap(cTe, oTe);
- }
- int tweenCount = (int) between(check->fCoinPtTStart->fT, cTs, check->fCoinPtTEnd->fT)
- + (int) between(check->fCoinPtTStart->fT, cTe, check->fCoinPtTEnd->fT)
- + (int) between(check->fOppPtTStart->fT, oTs, check->fOppPtTEnd->fT)
- + (int) between(check->fOppPtTStart->fT, oTe, check->fOppPtTEnd->fT);
-// SkASSERT(tweenCount == 0 || tweenCount == 4);
- if (tweenCount) {
+ if (overlap->flipped()
+ ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
+ : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
+ overlap->setOppPtTEnd(test->oppPtTEnd());
+ }
+ if (!fHead || !this->release(fHead, test)) {
+ SkAssertResult(this->release(fTop, test));
+ }
+ }
+ const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
+ const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
+ if (overlap && cs && ce && overlap->contains(cs, ce)) {
+ return false;
+ }
+ SkASSERT(cs != ce || !cs);
+ const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
+ const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
+ if (overlap && os && oe && overlap->contains(os, oe)) {
+ return false;
+ }
+ SkASSERT(!cs || !cs->deleted());
+ SkASSERT(!os || !os->deleted());
+ SkASSERT(!ce || !ce->deleted());
+ SkASSERT(!oe || !oe->deleted());
+ const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
+ const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
+ if (csExisting && csExisting == ceExisting) {
+ return false;
+ }
+ if (csExisting && (csExisting == ce || csExisting->contains(ceExisting ? ceExisting : ce))) {
+ return false;
+ }
+ if (ceExisting && (ceExisting == cs || ceExisting->contains(csExisting ? csExisting : cs))) {
+ return false;
+ }
+ const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
+ const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
+ if (osExisting && osExisting == oeExisting) {
+ return false;
+ }
+ if (osExisting && (osExisting == oe || osExisting->contains(oeExisting ? oeExisting : oe))) {
+ return false;
+ }
+ if (oeExisting && (oeExisting == os || oeExisting->contains(osExisting ? osExisting : os))) {
+ return false;
+ }
+ // extra line in debug code
+ this->debugValidate();
+ if (!cs || !os) {
+ SkOpPtT* csWritable = cs ? const_cast<SkOpPtT*>(cs)
+ : coinSeg->addT(coinTs, SkOpSegment::kNoAliasMatch, nullptr);
+ SkOpPtT* osWritable = os ? const_cast<SkOpPtT*>(os)
+ : oppSeg->addT(oppTs, SkOpSegment::kNoAliasMatch, nullptr);
+ csWritable->span()->addOppAndMerge(osWritable->span());
+ cs = csWritable;
+ os = osWritable;
+ if ((ce && ce->deleted()) || (oe && oe->deleted())) {
return false;
}
- } while ((check = check->fNext));
- if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
- SkTSwap(oppTs, oppTe);
}
- if (coinTs > coinTe) {
- SkTSwap(coinTs, coinTe);
- SkTSwap(oppTs, oppTe);
+ if (!ce || !oe) {
+ SkOpPtT* ceWritable = ce ? const_cast<SkOpPtT*>(ce)
+ : coinSeg->addT(coinTe, SkOpSegment::kNoAliasMatch, nullptr);
+ SkOpPtT* oeWritable = oe ? const_cast<SkOpPtT*>(oe)
+ : oppSeg->addT(oppTe, SkOpSegment::kNoAliasMatch, nullptr);
+ ceWritable->span()->addOppAndMerge(oeWritable->span());
+ ce = ceWritable;
+ oe = oeWritable;
}
- SkOpPtT* cs = coinSeg->addMissing(coinTs, oppSeg, allocator);
- SkOpPtT* ce = coinSeg->addMissing(coinTe, oppSeg, allocator);
- SkASSERT(cs != ce);
- SkOpPtT* os = oppSeg->addMissing(oppTs, coinSeg, allocator);
- SkOpPtT* oe = oppSeg->addMissing(oppTe, coinSeg, allocator);
-// SkASSERT(os != oe);
- cs->addOpp(os);
- ce->addOpp(oe);
- this->add(cs, ce, os, oe, allocator);
- return true;
+ this->debugValidate();
+ if (cs->deleted() || os->deleted() || ce->deleted() || oe->deleted()) {
+ return false;
+ }
+ if (cs->contains(ce) || os->contains(oe)) {
+ return false;
+ }
+ bool result = true;
+ if (overlap) {
+ if (overlap->coinPtTStart()->segment() == coinSeg) {
+ result = overlap->extend(cs, ce, os, oe);
+ } else {
+ if (os->fT > oe->fT) {
+ SkTSwap(cs, ce);
+ SkTSwap(os, oe);
+ }
+ result = overlap->extend(os, oe, cs, ce);
+ }
+#if DEBUG_COINCIDENCE_VERBOSE
+ if (result) {
+ overlaps[0]->debugShow();
+ }
+#endif
+ } else {
+ this->add(cs, ce, os, oe);
+#if DEBUG_COINCIDENCE_VERBOSE
+ fHead->debugShow();
+#endif
+ }
+ this->debugValidate();
+ return result;
}
+// Please keep this in sync with debugAddMissing()
/* detects overlaps of different coincident runs on same segment */
/* does not detect overlaps for pairs without any segments in common */
-bool SkOpCoincidence::addMissing(SkChunkAlloc* allocator) {
+// returns true if caller should loop again
+bool SkOpCoincidence::addMissing() {
SkCoincidentSpans* outer = fHead;
if (!outer) {
- return true;
+ return false;
}
bool added = false;
fTop = outer;
@@ -247,107 +735,116 @@ bool SkOpCoincidence::addMissing(SkChunkAlloc* allocator) {
// addifmissing can modify the list that this is walking
// save head so that walker can iterate over old data unperturbed
// addifmissing adds to head freely then add saved head in the end
- const SkOpSegment* outerCoin = outer->fCoinPtTStart->segment();
- SkASSERT(outerCoin == outer->fCoinPtTEnd->segment());
- const SkOpSegment* outerOpp = outer->fOppPtTStart->segment();
- SkASSERT(outerOpp == outer->fOppPtTEnd->segment());
+ const SkOpSegment* outerCoin = outer->coinPtTStart()->segment();
+ const SkOpSegment* outerOpp = outer->oppPtTStart()->segment();
+ if (outerCoin->done() || outerOpp->done()) {
+ continue;
+ }
SkCoincidentSpans* inner = outer;
- while ((inner = inner->fNext)) {
+ while ((inner = inner->next())) {
+ this->debugValidate();
double overS, overE;
- const SkOpSegment* innerCoin = inner->fCoinPtTStart->segment();
- SkASSERT(innerCoin == inner->fCoinPtTEnd->segment());
- const SkOpSegment* innerOpp = inner->fOppPtTStart->segment();
- SkASSERT(innerOpp == inner->fOppPtTEnd->segment());
- if (outerCoin == innerCoin
- && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
- added |= this->addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
- outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, allocator);
- } else if (outerCoin == innerOpp
- && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
- added |= this->addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
- outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator);
- } else if (outerOpp == innerCoin
- && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
- added |= this->addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
- outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, allocator);
- } else if (outerOpp == innerOpp
- && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
- added |= this->addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
- outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator);
- } else if (outerCoin != innerCoin) {
- // check to see if outer span overlaps the inner span
- // look for inner segment in pt-t list
- // if present, and if t values are in coincident range
- // add two pairs of new coincidence
- SkOpPtT* testS = outer->fCoinPtTStart->contains(innerCoin);
- SkOpPtT* testE = outer->fCoinPtTEnd->contains(innerCoin);
- if (testS && testS->fT >= inner->fCoinPtTStart->fT
- && testE && testE->fT <= inner->fCoinPtTEnd->fT
- && this->testForCoincidence(outer, testS, testE)) {
- added |= this->addIfMissing(outer, testS, testE, allocator);
- } else {
- testS = inner->fCoinPtTStart->contains(outerCoin);
- testE = inner->fCoinPtTEnd->contains(outerCoin);
- if (testS && testS->fT >= outer->fCoinPtTStart->fT
- && testE && testE->fT <= outer->fCoinPtTEnd->fT
- && this->testForCoincidence(inner, testS, testE)) {
- added |= this->addIfMissing(inner, testS, testE, allocator);
- }
+ const SkOpSegment* innerCoin = inner->coinPtTStart()->segment();
+ const SkOpSegment* innerOpp = inner->oppPtTStart()->segment();
+ if (innerCoin->done() || innerOpp->done()) {
+ continue;
+ }
+ if (outerCoin == innerCoin) {
+ if (outerOpp != innerOpp
+ && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) {
+ added |= this->addIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE,
+ outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd());
+ }
+ } else if (outerCoin == innerOpp) {
+ if (outerOpp != innerCoin
+ && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) {
+ added |= this->addIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE,
+ outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd());
+ }
+ } else if (outerOpp == innerCoin) {
+ SkASSERT(outerCoin != innerOpp);
+ if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) {
+ added |= this->addIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE,
+ outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd());
+ }
+ } else if (outerOpp == innerOpp) {
+ SkASSERT(outerCoin != innerCoin);
+ if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) {
+ added |= this->addIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE,
+ outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd());
}
}
-#if 0 && DEBUG_COINCIDENCE
- SkString miss;
- miss.printf("addMissing inner=%d outer=%d", inner->debugID(), outer->debugID());
- DEBUG_COINCIDENCE_HEALTH(fDebugState->contourHead(), miss.c_str());
-#endif
+ this->debugValidate();
}
- } while ((outer = outer->fNext));
- SkCoincidentSpans** headPtr = &fHead;
- while (*headPtr) {
- SkCoincidentSpans** headNext = &(*headPtr)->fNext;
- if (*headNext) {
- break;
- }
- headPtr = headNext;
- }
- *headPtr = fTop;
+ } while ((outer = outer->next()));
+ this->restoreHead();
return added;
}
-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);
+bool SkOpCoincidence::addOverlap(const SkOpSegment* seg1, const SkOpSegment* seg1o,
+ const SkOpSegment* seg2, const SkOpSegment* seg2o,
+ const SkOpPtT* overS, const SkOpPtT* overE) {
+ const SkOpPtT* s1, * e1, * s2, * e2;
+ if (!(s1 = overS->find(seg1))) {
+ return true;
+ }
+ if (!(e1 = overE->find(seg1))) {
+ return true;
+ }
+ if (s1 == e1) {
+ return true;
+ }
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 = overS->find(seg1o))) {
+ return true;
+ }
+ if (!(e1 = overE->find(seg1o))) {
+ return true;
+ }
+ if (s1 == e1) {
+ return true;
+ }
if (!s1->starter(e1)->span()->upCast()->windValue()) {
return true;
}
}
- SkOpPtT* s2 = overS->find(seg2);
- SkOpPtT* e2 = overE->find(seg2);
+ if (!(s2 = overS->find(seg2))) {
+ return true;
+ }
+ if (!(e2 = overE->find(seg2))) {
+ return true;
+ }
+ if (s2 == e2) {
+ return true;
+ }
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 = overS->find(seg2o))) {
+ return true;
+ }
+ if (!(e2 = overE->find(seg2o))) {
+ return true;
+ }
+ if (s2 == e2) {
+ return true;
+ }
if (!s2->starter(e2)->span()->upCast()->windValue()) {
return true;
}
@@ -359,26 +856,144 @@ bool SkOpCoincidence::addOverlap(SkOpSegment* seg1, SkOpSegment* seg1o, SkOpSegm
SkTSwap(s1, e1);
SkTSwap(s2, e2);
}
- this->add(s1, e1, s2, e2, allocator);
+ this->add(s1, e1, s2, e2);
return true;
}
-bool SkOpCoincidence::contains(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
- const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, bool flipped) const {
- const SkCoincidentSpans* coin = fHead;
- if (!coin) {
+/* look for pairs of coincidence with no common segments
+ if there's no existing coincidence found that matches up the segments, and
+ if the pt-t list for one contains the other, create coincident pairs for what's left */
+bool SkOpCoincidence::addUncommon() {
+ SkCoincidentSpans* outer = fHead;
+ if (!outer) {
return false;
}
+ bool added = false;
+ fTop = outer;
+ fHead = nullptr;
do {
- if (coin->fCoinPtTStart == coinPtTStart && coin->fCoinPtTEnd == coinPtTEnd
- && coin->fOppPtTStart == oppPtTStart && coin->fOppPtTEnd == oppPtTEnd
- && coin->fFlipped == flipped) {
+ // addifmissing can modify the list that this is walking
+ // save head so that walker can iterate over old data unperturbed
+ // addifmissing adds to head freely then add saved head in the end
+ const SkOpSegment* outerCoin = outer->coinPtTStart()->segment();
+ const SkOpSegment* outerOpp = outer->oppPtTStart()->segment();
+ if (outerCoin->done() || outerOpp->done()) {
+ continue;
+ }
+ SkCoincidentSpans* inner = outer;
+ while ((inner = inner->next())) {
+ this->debugValidate();
+ const SkOpSegment* innerCoin = inner->coinPtTStart()->segment();
+ const SkOpSegment* innerOpp = inner->oppPtTStart()->segment();
+ if (innerCoin->done() || innerOpp->done()) {
+ continue;
+ }
+ // check to see if outer span overlaps the inner span
+ // look for inner segment in pt-t list
+ // if present, and if t values are in coincident range
+ // add two pairs of new coincidence
+ const SkOpPtT* testS = outer->coinPtTStart()->contains(innerCoin);
+ const SkOpPtT* testE = outer->coinPtTEnd()->contains(innerCoin);
+ if (testS && testS->fT >= inner->coinPtTStart()->fT
+ && testE && testE->fT <= inner->coinPtTEnd()->fT
+ && this->testForCoincidence(outer, testS, testE)) {
+ added |= this->addIfMissing(outer, testS, testE);
+ } else {
+ testS = inner->coinPtTStart()->contains(outerCoin);
+ testE = inner->coinPtTEnd()->contains(outerCoin);
+ if (testS && testS->fT >= outer->coinPtTStart()->fT
+ && testE && testE->fT <= outer->coinPtTEnd()->fT
+ && this->testForCoincidence(inner, testS, testE)) {
+ added |= this->addIfMissing(inner, testS, testE);
+ }
+ }
+ }
+ } while ((outer = outer->next()));
+ this->restoreHead();
+ return added;
+}
+
+bool SkOpCoincidence::contains(const SkOpSegment* seg, const SkOpSegment* opp, double oppT) const {
+ if (this->contains(fHead, seg, opp, oppT)) {
+ return true;
+ }
+ if (this->contains(fTop, seg, opp, oppT)) {
+ return true;
+ }
+ return false;
+}
+
+bool SkOpCoincidence::contains(const SkCoincidentSpans* coin, const SkOpSegment* seg,
+ const SkOpSegment* opp, double oppT) const {
+ if (!coin) {
+ return false;
+ }
+ do {
+ if (coin->coinPtTStart()->segment() == seg && coin->oppPtTStart()->segment() == opp
+ && between(coin->oppPtTStart()->fT, oppT, coin->oppPtTEnd()->fT)) {
return true;
}
- } while ((coin = coin->fNext));
+ if (coin->oppPtTStart()->segment() == seg && coin->coinPtTStart()->segment() == opp
+ && between(coin->coinPtTStart()->fT, oppT, coin->coinPtTEnd()->fT)) {
+ return true;
+ }
+ } while ((coin = coin->next()));
+ return false;
+}
+
+bool SkOpCoincidence::contains(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const {
+ const SkCoincidentSpans* test = fHead;
+ if (!test) {
+ return false;
+ }
+ const SkOpSegment* coinSeg = coinPtTStart->segment();
+ const SkOpSegment* oppSeg = oppPtTStart->segment();
+ if (!Ordered(coinPtTStart, oppPtTStart)) {
+ SkTSwap(coinSeg, oppSeg);
+ SkTSwap(coinPtTStart, oppPtTStart);
+ SkTSwap(coinPtTEnd, oppPtTEnd);
+ if (coinPtTStart->fT > coinPtTEnd->fT) {
+ SkTSwap(coinPtTStart, coinPtTEnd);
+ SkTSwap(oppPtTStart, oppPtTEnd);
+ }
+ }
+ double oppMinT = SkTMin(oppPtTStart->fT, oppPtTEnd->fT);
+ double oppMaxT = SkTMax(oppPtTStart->fT, oppPtTEnd->fT);
+ do {
+ if (coinSeg != test->coinPtTStart()->segment()) {
+ continue;
+ }
+ if (coinPtTStart->fT < test->coinPtTStart()->fT) {
+ continue;
+ }
+ if (coinPtTEnd->fT > test->coinPtTEnd()->fT) {
+ continue;
+ }
+ if (oppSeg != test->oppPtTStart()->segment()) {
+ continue;
+ }
+ if (oppMinT < SkTMin(test->oppPtTStart()->fT, test->oppPtTEnd()->fT)) {
+ continue;
+ }
+ if (oppMaxT > SkTMax(test->oppPtTStart()->fT, test->oppPtTEnd()->fT)) {
+ continue;
+ }
+ return true;
+ } while ((test = test->next()));
return false;
}
+void SkOpCoincidence::correctEnds() {
+ SkCoincidentSpans* coin = fHead;
+ if (!coin) {
+ return;
+ }
+ do {
+ coin->correctEnds();
+ } while ((coin = coin->next()));
+}
+
// walk span sets in parallel, moving winding from one to the other
bool SkOpCoincidence::apply() {
SkCoincidentSpans* coin = fHead;
@@ -386,18 +1001,19 @@ bool SkOpCoincidence::apply() {
return true;
}
do {
- SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
+ SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
if (start->deleted()) {
continue;
}
- SkOpSpanBase* end = coin->fCoinPtTEnd->span();
+ const SkOpSpanBase* end = coin->coinPtTEnd()->span();
SkASSERT(start == start->starter(end));
- bool flipped = coin->fFlipped;
- SkOpSpan* oStart = (flipped ? coin->fOppPtTEnd : coin->fOppPtTStart)->span()->upCast();
+ bool flipped = coin->flipped();
+ SkOpSpan* oStart = (flipped ? coin->oppPtTEndWritable()
+ : coin->oppPtTStartWritable())->span()->upCast();
if (oStart->deleted()) {
continue;
}
- SkOpSpanBase* oEnd = (flipped ? coin->fOppPtTStart : coin->fOppPtTEnd)->span();
+ const SkOpSpanBase* oEnd = (flipped ? coin->oppPtTStart() : coin->oppPtTEnd())->span();
SkASSERT(oStart == oStart->starter(oEnd));
SkOpSegment* segment = start->segment();
SkOpSegment* oSegment = oStart->segment();
@@ -427,8 +1043,12 @@ bool SkOpCoincidence::apply() {
windDiff = -windDiff;
oWindDiff = -oWindDiff;
}
- if (windValue && (windValue > windDiff || (windValue == windDiff
- && oWindValue <= oWindDiff))) {
+ bool addToStart = windValue && (windValue > windDiff || (windValue == windDiff
+ && oWindValue <= oWindDiff));
+ if (addToStart ? start->done() : oStart->done()) {
+ addToStart ^= true;
+ }
+ if (addToStart) {
if (operandSwap) {
SkTSwap(oWindValue, oOppValue);
}
@@ -465,6 +1085,12 @@ bool SkOpCoincidence::apply() {
}
windValue = oppValue = 0;
}
+#if DEBUG_COINCIDENCE
+ SkDebugf("seg=%d span=%d windValue=%d oppValue=%d\n", segment->debugID(),
+ start->debugID(), windValue, oppValue);
+ SkDebugf("seg=%d span=%d windValue=%d oppValue=%d\n", oSegment->debugID(),
+ oStart->debugID(), oWindValue, oOppValue);
+#endif
start->setWindValue(windValue);
start->setOppValue(oppValue);
oStart->setWindValue(oWindValue);
@@ -490,29 +1116,87 @@ bool SkOpCoincidence::apply() {
}
oStart = oNext->upCast();
} while (true);
- } while ((coin = coin->fNext));
+ } while ((coin = coin->next()));
return true;
}
-void SkOpCoincidence::release(SkCoincidentSpans* remove) {
- SkCoincidentSpans* coin = fHead;
+// Please keep this in sync with debugRelease()
+bool SkOpCoincidence::release(SkCoincidentSpans* coin, SkCoincidentSpans* remove) {
+ SkCoincidentSpans* head = coin;
SkCoincidentSpans* prev = nullptr;
SkCoincidentSpans* next;
do {
- next = coin->fNext;
+ next = coin->next();
if (coin == remove) {
if (prev) {
- prev->fNext = next;
- } else {
+ prev->setNext(next);
+ } else if (head == fHead) {
fHead = next;
+ } else {
+ fTop = next;
}
break;
}
prev = coin;
} while ((coin = next));
- SkASSERT(coin);
+ return coin != nullptr;
+}
+
+// Please keep this in sync with debugReorder()
+// iterate through all coincident pairs, looking for ranges greater than 1
+// if found, see if the opposite pair can match it -- which may require
+// reordering the ptT pairs
+bool SkOpCoincidence::reorder() {
+ SkCoincidentSpans* coin = fHead;
+ if (!coin) {
+ return true;
+ }
+ do {
+ // most commonly, concidence are one span long; check for that first
+ int intervals = coin->spanCount();
+ if (intervals <= 0) {
+ return false;
+ }
+ if (1 == intervals) {
+#if DEBUG_COINCIDENCE_VERBOSE
+ SkASSERT(!coin->debugExpand(nullptr, nullptr));
+#endif
+ continue;
+ }
+ coin->expand(); // be all that you can be
+ if (coin->spanCount() <= 0) {
+ return false;
+ }
+ // check to see if every span in coin has a mate in opp
+ const SkOpSpan* start = coin->coinPtTStart()->span()->upCast();
+ bool flipped = coin->flipped();
+ const SkOpSpanBase* oppStartBase = coin->oppPtTStart()->span();
+ const SkOpSpan* oppStart = flipped ? oppStartBase->prev() : oppStartBase->upCast();
+ SkDebugf("", start, oppStart);
+ } while ((coin = coin->next()));
+ return true;
}
+void SkOpCoincidence::restoreHead() {
+ SkCoincidentSpans** headPtr = &fHead;
+ while (*headPtr) {
+ headPtr = (*headPtr)->nextPtr();
+ }
+ *headPtr = fTop;
+ fTop = nullptr;
+ // segments may have collapsed in the meantime; remove empty referenced segments
+ headPtr = &fHead;
+ while (*headPtr) {
+ SkCoincidentSpans* test = *headPtr;
+ if (test->coinPtTStart()->segment()->done() || test->oppPtTStart()->segment()->done()) {
+ *headPtr = test->next();
+ continue;
+ }
+ headPtr = (*headPtr)->nextPtr();
+ }
+}
+
+// Please keep this in sync with debugExpand()
bool SkOpCoincidence::expand() {
SkCoincidentSpans* coin = fHead;
if (!coin) {
@@ -520,142 +1204,132 @@ bool SkOpCoincidence::expand() {
}
bool expanded = false;
do {
- SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
- SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- SkOpSegment* segment = coin->fCoinPtTStart->segment();
- SkOpSegment* oppSegment = coin->fOppPtTStart->segment();
- SkOpSpan* prev = start->prev();
- SkOpPtT* oppPtT;
- if (prev && (oppPtT = prev->contains(oppSegment))) {
- double midT = (prev->t() + start->t()) / 2;
- if (segment->isClose(midT, oppSegment)) {
- coin->fCoinPtTStart = prev->ptT();
- coin->fOppPtTStart = oppPtT;
- expanded = true;
- }
- }
- SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
- if (next && (oppPtT = next->contains(oppSegment))) {
- double midT = (end->t() + next->t()) / 2;
- if (segment->isClose(midT, oppSegment)) {
- coin->fCoinPtTEnd = next->ptT();
- coin->fOppPtTEnd = oppPtT;
- expanded = true;
- }
+ if (coin->expand()) {
+ // check to see if multiple spans expanded so they are now identical
+ SkCoincidentSpans* test = fHead;
+ do {
+ if (coin == test) {
+ continue;
+ }
+ if (coin->coinPtTStart() == test->coinPtTStart()
+ && coin->oppPtTStart() == test->oppPtTStart()) {
+ this->release(fHead, test);
+ break;
+ }
+ } while ((test = test->next()));
+ expanded = true;
}
- } while ((coin = coin->fNext));
+ } while ((coin = coin->next()));
return expanded;
}
-bool SkOpCoincidence::findOverlaps(SkOpCoincidence* overlaps, SkChunkAlloc* allocator) const {
+bool SkOpCoincidence::findOverlaps(SkOpCoincidence* overlaps) const {
overlaps->fHead = overlaps->fTop = nullptr;
- SkDEBUGCODE_(overlaps->debugSetGlobalState(fDebugState));
SkCoincidentSpans* outer = fHead;
while (outer) {
- SkOpSegment* outerCoin = outer->fCoinPtTStart->segment();
- SkOpSegment* outerOpp = outer->fOppPtTStart->segment();
+ const SkOpSegment* outerCoin = outer->coinPtTStart()->segment();
+ const SkOpSegment* outerOpp = outer->oppPtTStart()->segment();
SkCoincidentSpans* inner = outer;
- while ((inner = inner->fNext)) {
- SkOpSegment* innerCoin = inner->fCoinPtTStart->segment();
+ while ((inner = inner->next())) {
+ const SkOpSegment* innerCoin = inner->coinPtTStart()->segment();
if (outerCoin == innerCoin) {
continue; // both winners are the same segment, so there's no additional overlap
}
- SkOpSegment* innerOpp = inner->fOppPtTStart->segment();
- SkOpPtT* overlapS, * overlapE;
- if ((outerOpp == innerCoin && SkOpPtT::Overlaps(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, &overlapS, &overlapE))
- || (outerCoin == innerOpp && SkOpPtT::Overlaps(outer->fCoinPtTStart,
- outer->fCoinPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd,
+ const SkOpSegment* innerOpp = inner->oppPtTStart()->segment();
+ const SkOpPtT* overlapS;
+ const SkOpPtT* overlapE;
+ if ((outerOpp == innerCoin && SkOpPtT::Overlaps(outer->oppPtTStart(),
+ outer->oppPtTEnd(),inner->coinPtTStart(), inner->coinPtTEnd(), &overlapS,
+ &overlapE))
+ || (outerCoin == innerOpp && SkOpPtT::Overlaps(outer->coinPtTStart(),
+ outer->coinPtTEnd(), inner->oppPtTStart(), inner->oppPtTEnd(),
&overlapS, &overlapE))
- || (outerOpp == innerOpp && SkOpPtT::Overlaps(outer->fOppPtTStart,
- outer->fOppPtTEnd, inner->fOppPtTStart, inner->fOppPtTEnd,
+ || (outerOpp == innerOpp && SkOpPtT::Overlaps(outer->oppPtTStart(),
+ outer->oppPtTEnd(), inner->oppPtTStart(), inner->oppPtTEnd(),
&overlapS, &overlapE))) {
if (!overlaps->addOverlap(outerCoin, outerOpp, innerCoin, innerOpp,
- overlapS, overlapE, allocator)) {
+ overlapS, overlapE)) {
return false;
}
}
}
- outer = outer->fNext;
+ outer = outer->next();
}
return true;
}
-bool SkOpCoincidence::fixAligned() {
+// Please keep this in sync with debugRemoveCollapsed()
+bool SkOpCoincidence::removeCollapsed() {
SkCoincidentSpans* coin = fHead;
if (!coin) {
return true;
}
- do {
- if (coin->fCoinPtTStart->deleted()) {
- coin->fCoinPtTStart = coin->fCoinPtTStart->doppelganger();
- }
- if (coin->fCoinPtTEnd->deleted()) {
- coin->fCoinPtTEnd = coin->fCoinPtTEnd->doppelganger();
- }
- if (coin->fOppPtTStart->deleted()) {
- coin->fOppPtTStart = coin->fOppPtTStart->doppelganger();
- }
- if (coin->fOppPtTEnd->deleted()) {
- coin->fOppPtTEnd = coin->fOppPtTEnd->doppelganger();
- }
- } while ((coin = coin->fNext));
- coin = fHead;
SkCoincidentSpans** priorPtr = &fHead;
do {
- if (coin->fCoinPtTStart == coin->fCoinPtTEnd) {
+ if (coin->coinPtTStart() == coin->coinPtTEnd()) {
return false;
}
- if (coin->fOppPtTStart == coin->fOppPtTEnd) {
+ if (coin->oppPtTStart() == coin->oppPtTEnd()) {
return false;
}
- if (coin->fCoinPtTStart->collapsed(coin->fCoinPtTEnd)
- || coin->fOppPtTStart->collapsed(coin->fOppPtTEnd)) {
- *priorPtr = coin->fNext;
+ if (coin->coinPtTStart()->collapsed(coin->coinPtTEnd())) {
+ *priorPtr = coin->next();
continue;
}
- priorPtr = &coin->fNext;
- } while ((coin = coin->fNext));
+ if (coin->oppPtTStart()->collapsed(coin->oppPtTEnd())) {
+ *priorPtr = coin->next();
+ continue;
+ }
+ priorPtr = coin->nextPtr();
+ } while ((coin = coin->next()));
return true;
}
-void SkOpCoincidence::fixUp(SkOpPtT* deleted, SkOpPtT* kept) {
- SkCoincidentSpans* coin = fHead;
- if (!coin) {
- return;
+void SkOpCoincidence::fixUp(SkOpPtT* deleted, const SkOpPtT* kept) {
+ SkASSERT(deleted != kept);
+ if (fHead) {
+ this->fixUp(fHead, deleted, kept);
+ }
+ if (fTop) {
+ this->fixUp(fTop, deleted, kept);
}
+}
+
+void SkOpCoincidence::fixUp(SkCoincidentSpans* coin, SkOpPtT* deleted, const SkOpPtT* kept) {
+ SkCoincidentSpans* head = coin;
do {
- if (coin->fCoinPtTStart == deleted) {
- if (coin->fCoinPtTEnd->span() == kept->span()) {
- this->release(coin);
+ if (coin->coinPtTStart() == deleted) {
+ if (coin->coinPtTEnd()->span() == kept->span()) {
+ this->release(head, coin);
continue;
}
- coin->fCoinPtTStart = kept;
+ coin->setCoinPtTStart(kept);
}
- if (coin->fCoinPtTEnd == deleted) {
- if (coin->fCoinPtTStart->span() == kept->span()) {
- this->release(coin);
+ if (coin->coinPtTEnd() == deleted) {
+ if (coin->coinPtTStart()->span() == kept->span()) {
+ this->release(head, coin);
continue;
}
- coin->fCoinPtTEnd = kept;
- }
- if (coin->fOppPtTStart == deleted) {
- if (coin->fOppPtTEnd->span() == kept->span()) {
- this->release(coin);
+ coin->setCoinPtTEnd(kept);
+ }
+ if (coin->oppPtTStart() == deleted) {
+ if (coin->oppPtTEnd()->span() == kept->span()) {
+ this->release(head, coin);
continue;
}
- coin->fOppPtTStart = kept;
+ coin->setOppPtTStart(kept);
}
- if (coin->fOppPtTEnd == deleted) {
- if (coin->fOppPtTStart->span() == kept->span()) {
- this->release(coin);
+ if (coin->oppPtTEnd() == deleted) {
+ if (coin->oppPtTStart()->span() == kept->span()) {
+ this->release(head, coin);
continue;
}
- coin->fOppPtTEnd = kept;
+ coin->setOppPtTEnd(kept);
}
- } while ((coin = coin->fNext));
+ } while ((coin = coin->next()));
}
+// Please keep this in sync with debugMark()
/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
bool SkOpCoincidence::mark() {
SkCoincidentSpans* coin = fHead;
@@ -663,44 +1337,87 @@ bool SkOpCoincidence::mark() {
return true;
}
do {
- SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- if (end->deleted()) {
- return false;
- }
- SkOpSpanBase* oldEnd = end;
- SkOpSpan* start = coin->fCoinPtTStart->span()->starter(&end);
- SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
- if (oEnd->deleted()) {
- return false;
- }
- SkOpSpanBase* oOldEnd = oEnd;
- SkOpSpanBase* oStart = coin->fOppPtTStart->span()->starter(&oEnd);
- bool flipped = (end == oldEnd) != (oEnd == oOldEnd);
+ SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
+ SkASSERT(!start->deleted());
+ SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
+ SkASSERT(!end->deleted());
+ SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
+ SkASSERT(!oStart->deleted());
+ SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
+ SkASSERT(!oEnd->deleted());
+ bool flipped = coin->flipped();
if (flipped) {
SkTSwap(oStart, oEnd);
}
+ /* coin and opp spans may not match up. Mark the ends, and then let the interior
+ get marked as many times as the spans allow */
+ start->insertCoincidence(oStart->upCast());
+ end->insertCoinEnd(oEnd);
+ const SkOpSegment* segment = start->segment();
+ const SkOpSegment* oSegment = oStart->segment();
SkOpSpanBase* next = start;
SkOpSpanBase* oNext = oStart;
- do {
- next = next->upCast()->next();
- oNext = flipped ? oNext->prev() : oNext->upCast()->next();
- if (next == end || oNext == oEnd) {
- break;
+ while ((next = next->upCast()->next()) != end) {
+ if (!next->upCast()->insertCoincidence(oSegment, flipped)) {
+ return false;
}
- if (!next->containsCoinEnd(oNext)) {
- next->insertCoinEnd(oNext);
+ }
+ while ((oNext = oNext->upCast()->next()) != oEnd) {
+ if (!oNext->upCast()->insertCoincidence(segment, flipped)) {
+ return false;
}
- SkOpSpan* nextSpan = next->upCast();
- SkOpSpan* oNextSpan = oNext->upCast();
- if (!nextSpan->containsCoincidence(oNextSpan)) {
- nextSpan->insertCoincidence(oNextSpan);
+ }
+ } while ((coin = coin->next()));
+ return true;
+}
+
+// Please keep in sync with debugMarkCollapsed()
+void SkOpCoincidence::markCollapsed(SkCoincidentSpans* coin, SkOpPtT* test) {
+ SkCoincidentSpans* head = coin;
+ while (coin) {
+ if (coin->collapsed(test)) {
+ if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
+ coin->coinPtTStartWritable()->segment()->markAllDone();
}
- } while (true);
- } while ((coin = coin->fNext));
+ if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
+ coin->oppPtTStartWritable()->segment()->markAllDone();
+ }
+ this->release(head, coin);
+ }
+ coin = coin->next();
+ }
+}
+
+// Please keep in sync with debugMarkCollapsed()
+void SkOpCoincidence::markCollapsed(SkOpPtT* test) {
+ markCollapsed(fHead, test);
+ markCollapsed(fTop, test);
+}
+
+bool SkOpCoincidence::Ordered(const SkOpSegment* coinSeg, const SkOpSegment* oppSeg) {
+ if (coinSeg->verb() < oppSeg->verb()) {
+ return true;
+ }
+ if (coinSeg->verb() > oppSeg->verb()) {
+ return false;
+ }
+ int count = (SkPathOpsVerbToPoints(coinSeg->verb()) + 1) * 2;
+ const SkScalar* cPt = &coinSeg->pts()[0].fX;
+ const SkScalar* oPt = &oppSeg->pts()[0].fX;
+ for (int index = 0; index < count; ++index) {
+ if (*cPt < *oPt) {
+ return true;
+ }
+ if (*cPt > *oPt) {
+ return false;
+ }
+ ++cPt;
+ ++oPt;
+ }
return true;
}
-bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e,
+bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e,
const SkOpPtT* coin2s, const SkOpPtT* coin2e, double* overS, double* overE) const {
SkASSERT(coin1s->segment() == coin2s->segment());
*overS = SkTMax(SkTMin(coin1s->fT, coin1e->fT), SkTMin(coin2s->fT, coin2e->fT));
@@ -708,8 +1425,24 @@ bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e,
return *overS < *overE;
}
+// Commented-out lines keep this in sync with debugRelease()
+void SkOpCoincidence::release(const SkOpSegment* deleted) {
+ SkCoincidentSpans* coin = fHead;
+ if (!coin) {
+ return;
+ }
+ do {
+ if (coin->coinPtTStart()->segment() == deleted
+ || coin->coinPtTEnd()->segment() == deleted
+ || coin->oppPtTStart()->segment() == deleted
+ || coin->oppPtTEnd()->segment() == deleted) {
+ this->release(fHead, coin);
+ }
+ } while ((coin = coin->next()));
+}
+
bool SkOpCoincidence::testForCoincidence(const SkCoincidentSpans* outer, const SkOpPtT* testS,
const SkOpPtT* testE) const {
return testS->segment()->testForCoincidence(testS, testE, testS->span(),
- testE->span(), outer->fCoinPtTStart->segment(), 120000); // FIXME: replace with tuned
+ testE->span(), outer->coinPtTStart()->segment());
}
diff --git a/src/pathops/SkOpCoincidence.h b/src/pathops/SkOpCoincidence.h
index 1efe6c0613..c64d148c24 100644
--- a/src/pathops/SkOpCoincidence.h
+++ b/src/pathops/SkOpCoincidence.h
@@ -7,120 +7,300 @@
#ifndef SkOpCoincidence_DEFINED
#define SkOpCoincidence_DEFINED
+#include "SkTDArray.h"
#include "SkOpTAllocator.h"
#include "SkOpSpan.h"
#include "SkPathOpsTypes.h"
class SkOpPtT;
+class SkOpSpanBase;
-struct SkCoincidentSpans {
- SkCoincidentSpans* fNext;
- SkOpPtT* fCoinPtTStart;
- SkOpPtT* fCoinPtTEnd;
- SkOpPtT* fOppPtTStart;
- SkOpPtT* fOppPtTEnd;
- bool fFlipped;
- SkDEBUGCODE(int fID);
+class SkCoincidentSpans {
+public:
+ const SkOpPtT* coinPtTEnd() const { return fCoinPtTEnd; }
+ const SkOpPtT* coinPtTStart() const { return fCoinPtTStart; }
+
+ // These return non-const pointers so that, as copies, they can be added
+ // to a new span pair
+ SkOpPtT* coinPtTEndWritable() const { return const_cast<SkOpPtT*>(fCoinPtTEnd); }
+ SkOpPtT* coinPtTStartWritable() const { return const_cast<SkOpPtT*>(fCoinPtTStart); }
+
+ bool collapsed(const SkOpPtT* ) const;
+ bool contains(const SkOpPtT* s, const SkOpPtT* e) const;
+ void correctEnds();
+ void correctOneEnd(const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
+ void (SkCoincidentSpans::* setEnd)(const SkOpPtT* ptT) );
+
+#if DEBUG_COINCIDENCE_VERBOSE
+ bool debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log) const;
+#endif
int debugID() const {
return SkDEBUGRELEASE(fID, -1);
}
+ void debugShow() const;
+#ifdef SK_DEBUG
+ void debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
+ const SkOpGlobalState* debugState) const;
+#endif
void dump() const;
+ bool expand();
+ bool extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd);
+ bool flipped() const { return fOppPtTStart->fT > fOppPtTEnd->fT; }
+ void init() { sk_bzero(this, sizeof(*this)); }
+ const SkOpPtT* oppPtTStart() const { return fOppPtTStart; }
+ const SkOpPtT* oppPtTEnd() const { return fOppPtTEnd; }
+ // These return non-const pointers so that, as copies, they can be added
+ // to a new span pair
+ SkOpPtT* oppPtTStartWritable() const { return const_cast<SkOpPtT*>(fOppPtTStart); }
+ SkOpPtT* oppPtTEndWritable() const { return const_cast<SkOpPtT*>(fOppPtTEnd); }
+ SkCoincidentSpans* next() { return fNext; }
+ const SkCoincidentSpans* next() const { return fNext; }
+ SkCoincidentSpans** nextPtr() { return &fNext; }
+ int spanCount() const;
+
+ void set(SkCoincidentSpans* next, const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd
+ SkDEBUGPARAMS(int id));
+
+ void setCoinPtTEnd(const SkOpPtT* ptT) {
+ SkASSERT(ptT == ptT->span()->ptT())
+ SkASSERT(!fCoinPtTStart || ptT->fT != fCoinPtTStart->fT);
+ SkASSERT(!fCoinPtTStart || fCoinPtTStart->segment() == ptT->segment());
+ fCoinPtTEnd = ptT;
+ ptT->setCoincident();
+ }
+
+ void setCoinPtTStart(const SkOpPtT* ptT) {
+ SkASSERT(ptT == ptT->span()->ptT())
+ SkASSERT(!fCoinPtTEnd || ptT->fT != fCoinPtTEnd->fT);
+ SkASSERT(!fCoinPtTEnd || fCoinPtTEnd->segment() == ptT->segment());
+ fCoinPtTStart = ptT;
+ ptT->setCoincident();
+ }
+
+ void setEnds(const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTEnd) {
+ this->setCoinPtTEnd(coinPtTEnd);
+ this->setOppPtTEnd(oppPtTEnd);
+ }
+
+ void setOppPtTEnd(const SkOpPtT* ptT) {
+ SkASSERT(ptT == ptT->span()->ptT())
+ SkASSERT(!fOppPtTStart || ptT->fT != fOppPtTStart->fT);
+ SkASSERT(!fOppPtTStart || fOppPtTStart->segment() == ptT->segment());
+ fOppPtTEnd = ptT;
+ ptT->setCoincident();
+ }
+
+ void setOppPtTStart(const SkOpPtT* ptT) {
+ SkASSERT(ptT == ptT->span()->ptT())
+ SkASSERT(!fOppPtTEnd || ptT->fT != fOppPtTEnd->fT);
+ SkASSERT(!fOppPtTEnd || fOppPtTEnd->segment() == ptT->segment());
+ fOppPtTStart = ptT;
+ ptT->setCoincident();
+ }
+
+ void setStarts(const SkOpPtT* coinPtTStart, const SkOpPtT* oppPtTStart) {
+ this->setCoinPtTStart(coinPtTStart);
+ this->setOppPtTStart(oppPtTStart);
+ }
+
+ void setNext(SkCoincidentSpans* next) { fNext = next; }
+
+ bool startEquals(const SkOpSpanBase* outer, const SkOpSpanBase* over) const {
+ return fCoinPtTStart->span() == over && fOppPtTStart->span() == outer;
+ }
+private:
+ SkCoincidentSpans* fNext;
+ const SkOpPtT* fCoinPtTStart;
+ const SkOpPtT* fCoinPtTEnd;
+ const SkOpPtT* fOppPtTStart;
+ const SkOpPtT* fOppPtTEnd;
+ SkDEBUGCODE(int fID);
};
class SkOpCoincidence {
public:
- SkOpCoincidence()
+ SkOpCoincidence(SkOpGlobalState* globalState)
: fHead(nullptr)
, fTop(nullptr)
- SkDEBUGPARAMS(fDebugState(nullptr))
- {
+ , fGlobalState(globalState)
+ , fContinue(false)
+ , fSpanDeleted(false)
+ , fPtAllocated(false)
+ , fCoinExtended(false)
+ , fSpanMerged(false) {
+ globalState->setCoincidence(this);
}
void add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
- SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator);
- bool addExpanded(SkChunkAlloc* allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(SkOpGlobalState* ));
- bool addMissing(SkChunkAlloc* allocator);
+ SkOpPtT* oppPtTEnd);
+ bool addEndMovedSpans();
+ bool addExpanded();
+ bool addMissing();
+ bool addUncommon();
bool apply();
bool contains(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
- const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, bool flipped) const;
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const;
+ void correctEnds();
+#if DEBUG_COINCIDENCE_VERBOSE
void debugAddExpanded(const char* id, SkPathOpsDebug::GlitchLog* ) const;
void debugAddMissing(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+ void debugAddOrOverlap(const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
+ double coinTs, double coinTe, double oppTs, double oppTe,
+ const char* id, SkPathOpsDebug::GlitchLog* log) const;
+#endif
const SkOpAngle* debugAngle(int id) const {
- return SkDEBUGRELEASE(fDebugState->debugAngle(id), nullptr);
+ return SkDEBUGRELEASE(fGlobalState->debugAngle(id), nullptr);
}
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugCheckOverlap(const char* id, SkPathOpsDebug::GlitchLog* log) const;
+ void debugCheckValid(const char* id, SkPathOpsDebug::GlitchLog* log) const;
+#endif
+
SkOpContour* debugContour(int id) {
- return SkDEBUGRELEASE(fDebugState->debugContour(id), nullptr);
+ return SkDEBUGRELEASE(fGlobalState->debugContour(id), nullptr);
}
+#if DEBUG_COINCIDENCE_VERBOSE
bool debugExpand(const char* id, SkPathOpsDebug::GlitchLog* ) const;
void debugMark(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+ void debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* ,
+ const SkCoincidentSpans* coin, const SkOpPtT* test) const;
+ void debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* , const SkOpPtT* test) const;
+#endif
const SkOpPtT* debugPtT(int id) const {
- return SkDEBUGRELEASE(fDebugState->debugPtT(id), nullptr);
+ return SkDEBUGRELEASE(fGlobalState->debugPtT(id), nullptr);
}
const SkOpSegment* debugSegment(int id) const {
- return SkDEBUGRELEASE(fDebugState->debugSegment(id), nullptr);
+ return SkDEBUGRELEASE(fGlobalState->debugSegment(id), nullptr);
}
- void debugSetGlobalState(SkOpGlobalState* debugState) {
- SkDEBUGCODE(fDebugState = debugState);
- }
-
- void debugFixAligned(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugRemoveCollapsed(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+ void debugReorder(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+ void debugRelease(const char* id, SkPathOpsDebug::GlitchLog* , const SkOpSegment* ) const;
+#endif
void debugShowCoincidence() const;
const SkOpSpanBase* debugSpan(int id) const {
- return SkDEBUGRELEASE(fDebugState->debugSpan(id), nullptr);
+ return SkDEBUGRELEASE(fGlobalState->debugSpan(id), nullptr);
}
- void release(SkCoincidentSpans* );
+ void debugValidate() const;
void dump() const;
+ bool edge(const SkOpPtT* , bool* start) const;
bool expand();
- bool extend(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
- SkOpPtT* oppPtTEnd);
- bool findOverlaps(SkOpCoincidence* , SkChunkAlloc* allocator) const;
- bool fixAligned();
- void fixUp(SkOpPtT* deleted, SkOpPtT* kept);
+ bool extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart,
+ const SkOpPtT* oppPtTEnd);
+ bool findOverlaps(SkOpCoincidence* ) const;
+ void fixUp(SkOpPtT* deleted, const SkOpPtT* kept);
+
+ SkOpGlobalState* globalState() {
+ return fGlobalState;
+ }
bool isEmpty() const {
- return !fHead;
+ return !fHead && !fTop;
}
bool mark();
+ void markCollapsed(SkOpPtT* );
+
+ static bool Ordered(const SkOpPtT* coinPtTStart, const SkOpPtT* oppPtTStart) {
+ return Ordered(coinPtTStart->segment(), oppPtTStart->segment());
+ }
+
+ static bool Ordered(const SkOpSegment* coin, const SkOpSegment* opp);
+ void release(const SkOpSegment* );
+ bool removeCollapsed();
+ bool reorder();
private:
- bool addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over1s, SkOpPtT* over1e,
- SkChunkAlloc* );
+ void add(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart,
+ const SkOpPtT* oppPtTEnd) {
+ this->add(const_cast<SkOpPtT*>(coinPtTStart), const_cast<SkOpPtT*>(coinPtTEnd),
+ const_cast<SkOpPtT*>(oppPtTStart), const_cast<SkOpPtT*>(oppPtTEnd));
+ }
+
+ void addEndMovedSpans(const SkOpSpan* base, const SkOpSpanBase* testSpan);
+ bool addEndMovedSpans(const SkOpPtT* ptT);
+
+ bool addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over1s, SkOpPtT* over1e);
+
+ bool addIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
+ const SkOpPtT* over1e) {
+ return addIfMissing(outer, const_cast<SkOpPtT*>(over1s), const_cast<SkOpPtT*>(over1e));
+ }
+
bool addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
const SkOpPtT* over2s, const SkOpPtT* over2e,
double tStart, double tEnd,
SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
- SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd,
- SkChunkAlloc* );
- 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;
- bool debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
+ SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd);
+
+ bool addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
+ const SkOpPtT* over2s, const SkOpPtT* over2e,
+ double tStart, double tEnd,
+ const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
+ return addIfMissing(over1s, over1e, over2s, over2e, tStart, tEnd,
+ const_cast<SkOpPtT*>(coinPtTStart), coinPtTEnd,
+ const_cast<SkOpPtT*>(oppPtTStart), oppPtTEnd);
+ }
+
+ bool addOrOverlap(SkOpSegment* coinSeg, SkOpSegment* oppSeg,
+ double coinTs, double coinTe, double oppTs, double oppTe);
+ bool addOverlap(const SkOpSegment* seg1, const SkOpSegment* seg1o,
+ const SkOpSegment* seg2, const SkOpSegment* seg2o,
+ const SkOpPtT* overS, const SkOpPtT* overE);
+ bool alreadyAdded(const SkCoincidentSpans* check, const SkCoincidentSpans* outer,
+ const SkOpPtT* over1s, const SkOpPtT* over1e) const;
+ bool checkOverlap(SkCoincidentSpans* check,
+ const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
+ double coinTs, double coinTe, double oppTs, double oppTe,
+ SkTDArray<SkCoincidentSpans*>* overlaps) const;
+ bool contains(const SkOpSegment* seg, const SkOpSegment* opp, double oppT) const;
+ bool contains(const SkCoincidentSpans* coin, const SkOpSegment* seg,
+ const SkOpSegment* opp, double oppT) const;
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
+ const SkOpPtT* over1e, const char* id, SkPathOpsDebug::GlitchLog*) const;
+ void debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
const SkOpPtT* over2s, const SkOpPtT* over2e,
double tStart, double tEnd,
- SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
- SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const;
+ const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd,
+ const char* id, SkPathOpsDebug::GlitchLog*) const;
+#endif
+ void fixUp(SkCoincidentSpans* coin, SkOpPtT* deleted, const SkOpPtT* kept);
+ void markCollapsed(SkCoincidentSpans* head, SkOpPtT* test);
bool overlap(const SkOpPtT* coinStart1, const SkOpPtT* coinEnd1,
const SkOpPtT* coinStart2, const SkOpPtT* coinEnd2,
double* overS, double* overE) const;
-
+ bool release(SkCoincidentSpans* coin, SkCoincidentSpans* );
+ void restoreHead();
bool testForCoincidence(const SkCoincidentSpans* outer, const SkOpPtT* testS,
const SkOpPtT* testE) const;
+ static void TRange(const SkOpPtT* overS, const SkOpPtT* overE, double tStart,
+ double tEnd, const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ double* coinTs, double* coinTe);
+
SkCoincidentSpans* fHead;
SkCoincidentSpans* fTop;
- SkDEBUGCODE_(SkOpGlobalState* fDebugState);
+ SkOpGlobalState* fGlobalState;
+ bool fContinue;
+ bool fSpanDeleted;
+ bool fPtAllocated;
+ bool fCoinExtended;
+ bool fSpanMerged;
};
#endif
diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp
index df65437c97..ed533887e5 100644
--- a/src/pathops/SkOpContour.cpp
+++ b/src/pathops/SkOpContour.cpp
@@ -10,18 +10,18 @@
#include "SkReduceOrder.h"
#include "SkTSort.h"
-SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4],
- SkChunkAlloc* allocator) {
+SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4]) {
+ SkChunkAlloc* allocator = this->globalState()->allocator();
switch (verb) {
case SkPath::kLine_Verb: {
SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 2);
memcpy(ptStorage, pts, sizeof(SkPoint) * 2);
- return appendSegment(allocator).addLine(ptStorage, this);
+ return appendSegment().addLine(ptStorage, this);
} break;
case SkPath::kQuad_Verb: {
SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 3);
memcpy(ptStorage, pts, sizeof(SkPoint) * 3);
- return appendSegment(allocator).addQuad(ptStorage, this);
+ return appendSegment().addQuad(ptStorage, this);
} break;
case SkPath::kConic_Verb: {
SkASSERT(0); // the original curve is a cubic, which will never reduce to a conic
@@ -29,7 +29,7 @@ SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4],
case SkPath::kCubic_Verb: {
SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 4);
memcpy(ptStorage, pts, sizeof(SkPoint) * 4);
- return appendSegment(allocator).addCubic(ptStorage, this);
+ return appendSegment().addCubic(ptStorage, this);
} break;
default:
SkASSERT(0);
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index b9f0b85af5..0db6eabdfe 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -11,7 +11,6 @@
#include "SkTDArray.h"
#include "SkTSort.h"
-class SkChunkAlloc;
enum class SkOpRayDir;
struct SkOpRayHit;
class SkPathWriter;
@@ -30,47 +29,31 @@ public:
bool operator<(const SkOpContour& rh) const {
return fBounds.fTop == rh.fBounds.fTop
- ? fBounds.fLeft < rh.fBounds.fLeft
- : fBounds.fTop < rh.fBounds.fTop;
+ ? fBounds.fLeft < rh.fBounds.fLeft
+ : fBounds.fTop < rh.fBounds.fTop;
}
- void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
- SkASSERT(fCount > 0);
- SkOpSegment* segment = &fHead;
- do {
- segment->addAlignIntersections(contourList, allocator);
- } while ((segment = segment->next()));
- }
-
- void addConic(SkPoint pts[3], SkScalar weight, SkChunkAlloc* allocator) {
- appendSegment(allocator).addConic(pts, weight, this);
+ void addConic(SkPoint pts[3], SkScalar weight) {
+ appendSegment().addConic(pts, weight, this);
}
- void addCubic(SkPoint pts[4], SkChunkAlloc* allocator) {
- appendSegment(allocator).addCubic(pts, this);
+ void addCubic(SkPoint pts[4]) {
+ appendSegment().addCubic(pts, this);
}
- SkOpSegment* addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator);
+ SkOpSegment* addCurve(SkPath::Verb verb, const SkPoint pts[4]);
- void addLine(SkPoint pts[2], SkChunkAlloc* allocator) {
- appendSegment(allocator).addLine(pts, this);
+ SkOpSegment* addLine(SkPoint pts[2]) {
+ return appendSegment().addLine(pts, this);
}
- void addQuad(SkPoint pts[3], SkChunkAlloc* allocator) {
- appendSegment(allocator).addQuad(pts, this);
- }
-
- void align() {
- SkASSERT(fCount > 0);
- SkOpSegment* segment = &fHead;
- do {
- segment->align();
- } while ((segment = segment->next()));
+ void addQuad(SkPoint pts[3]) {
+ appendSegment().addQuad(pts, this);
}
- SkOpSegment& appendSegment(SkChunkAlloc* allocator) {
+ SkOpSegment& appendSegment() {
SkOpSegment* result = fCount++
- ? SkOpTAllocator<SkOpSegment>::Allocate(allocator) : &fHead;
+ ? SkOpTAllocator<SkOpSegment>::Allocate(this->globalState()->allocator()) : &fHead;
result->setPrev(fTail);
if (fTail) {
fTail->setNext(result);
@@ -79,8 +62,8 @@ public:
return *result;
}
- SkOpContour* appendContour(SkChunkAlloc* allocator) {
- SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(allocator);
+ SkOpContour* appendContour() {
+ SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(this->globalState()->allocator());
contour->setNext(nullptr);
SkOpContour* prev = this;
SkOpContour* next;
@@ -90,16 +73,16 @@ public:
prev->setNext(contour);
return contour;
}
-
+
const SkPathOpsBounds& bounds() const {
return fBounds;
}
- void calcAngles(SkChunkAlloc* allocator) {
+ void calcAngles() {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
do {
- segment->calcAngles(allocator);
+ segment->calcAngles();
} while ((segment = segment->next()));
}
@@ -132,14 +115,21 @@ public:
return SkDEBUGRELEASE(this->globalState()->debugAngle(id), nullptr);
}
+ const SkOpCoincidence* debugCoincidence() const {
+ return this->globalState()->coincidence();
+ }
+
+#if DEBUG_COINCIDENCE_VERBOSE
void debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+#endif
SkOpContour* debugContour(int id) {
return SkDEBUGRELEASE(this->globalState()->debugContour(id), nullptr);
}
- void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log,
- const SkOpCoincidence* coincidence) const;
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const;
+#endif
const SkOpPtT* debugPtT(int id) const {
return SkDEBUGRELEASE(this->globalState()->debugPtT(id), nullptr);
@@ -154,7 +144,7 @@ public:
}
SkOpGlobalState* globalState() const {
- return fState;
+ return fState;
}
void debugValidate() const {
@@ -197,15 +187,6 @@ 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() {
@@ -237,14 +218,15 @@ public:
return fXor;
}
- void markDone() {
+ void markAllDone() {
SkOpSegment* segment = &fHead;
do {
segment->markAllDone();
} while ((segment = segment->next()));
}
- bool missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
+ // Please keep this aligned with debugMissingCoincidence()
+ bool missingCoincidence() {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
bool result = false;
@@ -253,7 +235,7 @@ public:
#if DEBUG_ANGLE
segment->debugCheckAngleCoin();
#endif
- } else if (segment->missingCoincidence(coincidences, allocator)) {
+ } else if (segment->missingCoincidence()) {
result = true;
// FIXME: trying again loops forever in issue3651_6
// The continue below is speculative -- once there's an actual case that requires it,
diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp
index 617ca76c82..95152a7ffa 100644
--- a/src/pathops/SkOpEdgeBuilder.cpp
+++ b/src/pathops/SkOpEdgeBuilder.cpp
@@ -36,9 +36,9 @@ int SkOpEdgeBuilder::count() const {
return count;
}
-bool SkOpEdgeBuilder::finish(SkChunkAlloc* allocator) {
+bool SkOpEdgeBuilder::finish() {
fOperand = false;
- if (fUnparseable || !walk(allocator)) {
+ if (fUnparseable || !walk()) {
return false;
}
complete();
@@ -162,7 +162,7 @@ bool SkOpEdgeBuilder::close() {
return true;
}
-bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
+bool SkOpEdgeBuilder::walk() {
uint8_t* verbPtr = fPathVerbs.begin();
uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
SkPoint* pointsPtr = fPathPts.begin() - 1;
@@ -183,20 +183,20 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
}
}
if (!fCurrentContour) {
- fCurrentContour = fContoursHead->appendContour(allocator);
+ fCurrentContour = fContoursHead->appendContour();
}
fCurrentContour->init(fGlobalState, fOperand,
fXorMask[fOperand] == kEvenOdd_PathOpsMask);
pointsPtr += 1;
continue;
case SkPath::kLine_Verb:
- fCurrentContour->addLine(pointsPtr, fAllocator);
+ fCurrentContour->addLine(pointsPtr);
break;
case SkPath::kQuad_Verb:
- fCurrentContour->addQuad(pointsPtr, fAllocator);
+ fCurrentContour->addQuad(pointsPtr);
break;
case SkPath::kConic_Verb:
- fCurrentContour->addConic(pointsPtr, *weightPtr++, fAllocator);
+ fCurrentContour->addConic(pointsPtr, *weightPtr++);
break;
case SkPath::kCubic_Verb: {
// Split complex cubics (such as self-intersecting curves or
@@ -221,13 +221,13 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
for (int index = 0; index < SkPathOpsVerbToPoints(v2); ++index) {
force_small_to_zero(&curve2[index]);
}
- fCurrentContour->addCurve(v1, curve1, fAllocator);
- fCurrentContour->addCurve(v2, curve2, fAllocator);
+ fCurrentContour->addCurve(v1, curve1);
+ fCurrentContour->addCurve(v2, curve2);
} else {
- fCurrentContour->addCubic(pointsPtr, fAllocator);
+ fCurrentContour->addCubic(pointsPtr);
}
} else {
- fCurrentContour->addCubic(pointsPtr, fAllocator);
+ fCurrentContour->addCubic(pointsPtr);
}
} break;
case SkPath::kClose_Verb:
diff --git a/src/pathops/SkOpEdgeBuilder.h b/src/pathops/SkOpEdgeBuilder.h
index f11e0e4c89..1a78a2137e 100644
--- a/src/pathops/SkOpEdgeBuilder.h
+++ b/src/pathops/SkOpEdgeBuilder.h
@@ -12,20 +12,16 @@
class SkOpEdgeBuilder {
public:
- SkOpEdgeBuilder(const SkPathWriter& path, SkOpContour* contours2, SkChunkAlloc* allocator,
- SkOpGlobalState* globalState)
- : fAllocator(allocator) // FIXME: replace with const, tune this
- , fGlobalState(globalState)
+ SkOpEdgeBuilder(const SkPathWriter& path, SkOpContour* contours2, SkOpGlobalState* globalState)
+ : fGlobalState(globalState)
, fPath(path.nativePath())
, fContoursHead(contours2)
, fAllowOpenContours(true) {
init();
}
- SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkChunkAlloc* allocator,
- SkOpGlobalState* globalState)
- : fAllocator(allocator)
- , fGlobalState(globalState)
+ SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkOpGlobalState* globalState)
+ : fGlobalState(globalState)
, fPath(&path)
, fContoursHead(contours2)
, fAllowOpenContours(false) {
@@ -42,7 +38,7 @@ public:
}
int count() const;
- bool finish(SkChunkAlloc* );
+ bool finish();
const SkOpContour* head() const {
return fContoursHead;
@@ -56,9 +52,8 @@ private:
void closeContour(const SkPoint& curveEnd, const SkPoint& curveStart);
bool close();
int preFetch();
- bool walk(SkChunkAlloc* );
+ bool walk();
- SkChunkAlloc* fAllocator;
SkOpGlobalState* fGlobalState;
const SkPath* fPath;
SkTDArray<SkPoint> fPathPts;
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 41d62369b6..e4e00bbfab 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -9,6 +9,8 @@
#include "SkOpSegment.h"
#include "SkPathWriter.h"
+#define FAIL_IF(cond) do { if (cond) return false; } while (false)
+
/*
After computing raw intersections, post process all segments to:
- find small collections of points that can be collapsed to a single point
@@ -159,90 +161,10 @@ bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sum
return result;
}
-void SkOpSegment::addAlignIntersection(SkOpPtT& endPtT, SkPoint& oldPt,
- SkOpContourHead* contourList, SkChunkAlloc* allocator) {
- const SkPoint& newPt = endPtT.fPt;
- if (newPt == oldPt) {
- return;
- }
- SkPoint line[2] = { newPt, oldPt };
- SkPathOpsBounds lineBounds;
- lineBounds.setBounds(line, 2);
- SkDLine aLine;
- aLine.set(line);
- SkOpContour* current = contourList;
- do {
- if (!SkPathOpsBounds::Intersects(current->bounds(), lineBounds)) {
- continue;
- }
- SkOpSegment* segment = current->first();
- do {
- if (!SkPathOpsBounds::Intersects(segment->bounds(), lineBounds)) {
- continue;
- }
- if (newPt == segment->fPts[0]) {
- continue;
- }
- if (newPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
- continue;
- }
- if (oldPt == segment->fPts[0]) {
- continue;
- }
- if (oldPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
- continue;
- }
- if (endPtT.contains(segment)) {
- continue;
- }
- SkIntersections i;
- switch (segment->fVerb) {
- case SkPath::kLine_Verb: {
- SkDLine bLine;
- bLine.set(segment->fPts);
- i.intersect(bLine, aLine);
- } break;
- case SkPath::kQuad_Verb: {
- SkDQuad bQuad;
- bQuad.set(segment->fPts);
- i.intersect(bQuad, aLine);
- } break;
- case SkPath::kConic_Verb: {
- SkDConic bConic;
- bConic.set(segment->fPts, segment->fWeight);
- i.intersect(bConic, aLine);
- } break;
- case SkPath::kCubic_Verb: {
- SkDCubic bCubic;
- bCubic.set(segment->fPts);
- i.intersect(bCubic, aLine);
- } break;
- default:
- SkASSERT(0);
- }
- if (i.used()) {
- SkASSERT(i.used() == 1);
- SkASSERT(!zero_or_one(i[0][0]));
- SkOpSpanBase* checkSpan = fHead.next();
- while (!checkSpan->final()) {
- if (checkSpan->contains(segment)) {
- goto nextSegment;
- }
- checkSpan = checkSpan->upCast()->next();
- }
- SkOpPtT* ptT = segment->addT(i[0][0], SkOpSegment::kAllowAlias, allocator);
- ptT->fPt = newPt;
- endPtT.addOpp(ptT);
- }
- nextSegment:
- ;
- } while ((segment = segment->next()));
- } while ((current = current->next()));
-}
-
bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
SkPathWriter* path) const {
if (start->starter(end)->alreadyAdded()) {
+ SkDEBUGF(("same curve added twice aborted pathops\n"));
return false;
}
SkOpCurve edge;
@@ -298,29 +220,57 @@ bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
return true;
}
-SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) {
- SkOpSpanBase* existing = nullptr;
- SkOpSpanBase* test = &fHead;
- double testT;
+const SkOpPtT* SkOpSegment::existing(double t, const SkOpSegment* opp) const {
+ const SkOpSpanBase* test = &fHead;
+ const SkOpPtT* testPtT;
+ SkPoint pt = this->ptAtT(t);
do {
- if ((testT = test->ptT()->fT) >= t) {
- if (testT == t) {
- existing = test;
- }
+ testPtT = test->ptT();
+ if (testPtT->fT == t) {
break;
}
+ if (!this->match(testPtT, this, t, pt, opp ? kAllowAliasMatch : kNoAliasMatch)) {
+ if (t < testPtT->fT) {
+ return nullptr;
+ }
+ continue;
+ }
+ if (!opp) {
+ return testPtT;
+ }
+ const SkOpPtT* loop = testPtT->next();
+ while (loop != testPtT) {
+ if (loop->segment() == this && loop->fT == t && loop->fPt == pt) {
+ goto foundMatch;
+ }
+ loop = loop->next();
+ }
+ return nullptr;
} while ((test = test->upCast()->next()));
- SkOpPtT* result;
- if (existing && existing->contains(opp)) {
- result = existing->ptT();
- } else {
- result = this->addT(t, SkOpSegment::kNoAlias, allocator);
+foundMatch:
+ return opp && !test->contains(opp) ? nullptr : testPtT;
+}
+
+// break the span so that the coincident part does not change the angle of the remainder
+bool SkOpSegment::addExpanded(double newT, const SkOpSpanBase* test, bool* startOver) {
+ if (this->contains(newT)) {
+ return true;
}
- SkASSERT(result);
- return result;
+ SkOpPtT* newPtT = this->addT(newT, kAllowAliasMatch, startOver);
+ if (!newPtT) {
+ return false;
+ }
+ newPtT->fPt = this->ptAtT(newT);
+ // const cast away to change linked list; pt/t values stays unchanged
+ SkOpSpanBase* writableTest = const_cast<SkOpSpanBase*>(test);
+ if (writableTest->ptT()->addOpp(newPtT)) {
+ writableTest->checkForCollapsedCoincidence();
+ }
+ return true;
}
-SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
+// Please keep this in sync with debugAddT()
+SkOpPtT* SkOpSegment::addT(double t, AliasMatch allowAlias, bool* allocated) {
debugValidate();
SkPoint pt = this->ptAtT(t);
SkOpSpanBase* span = &fHead;
@@ -331,9 +281,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
if (t == result->fT) {
goto bumpSpan;
}
- if (this->match(result, this, t, pt)) {
+ if (this->match(result, this, t, pt, allowAlias)) {
// see if any existing alias matches segment, pt, and t
- loop = result->next();
+ loop = result->next();
duplicatePt = false;
while (loop != result) {
bool ptMatch = loop->fPt == pt;
@@ -343,12 +293,12 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
duplicatePt |= ptMatch;
loop = loop->next();
}
- if (kNoAlias == allowAlias) {
+ if (kNoAliasMatch == allowAlias) {
bumpSpan:
span->bumpSpanAdds();
return result;
}
- SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator);
+ SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(this->globalState()->allocator());
alias->init(result->span(), t, pt, duplicatePt);
result->insert(alias);
result->span()->unaligned();
@@ -358,6 +308,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
alias->segment()->debugID(), alias->span()->debugID());
#endif
span->bumpSpanAdds();
+ if (allocated) {
+ *allocated = true;
+ }
return alias;
}
if (t < result->fT) {
@@ -365,7 +318,7 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
if (!prev) {
return nullptr;
}
- SkOpSpan* span = insert(prev, allocator);
+ SkOpSpan* span = insert(prev);
span->init(this, prev, t, pt);
this->debugValidate();
#if DEBUG_ADD_T
@@ -373,6 +326,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
span->segment()->debugID(), span->debugID());
#endif
span->bumpSpanAdds();
+ if (allocated) {
+ *allocated = true;
+ }
return span->ptT();
}
SkASSERT(span != &fTail);
@@ -381,43 +337,17 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
return nullptr;
}
-// choose a solitary t and pt value; remove aliases; align the opposite ends
-void SkOpSegment::align() {
- debugValidate();
- SkOpSpanBase* span = &fHead;
- if (!span->aligned()) {
- span->alignEnd(0, fPts[0]);
- }
- while ((span = span->upCast()->next())) {
- if (span == &fTail) {
- break;
- }
- span->align();
- }
- if (!span->aligned()) {
- span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
- }
- if (this->collapsed()) {
- SkOpSpan* span = &fHead;
- do {
- span->setWindValue(0);
- span->setOppValue(0);
- this->markDone(span);
- } while ((span = span->next()->upCastable()));
- }
- debugValidate();
-}
-
-void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
+void SkOpSegment::calcAngles() {
bool activePrior = !fHead.isCanceled();
if (activePrior && !fHead.simple()) {
- addStartSpan(allocator);
+ addStartSpan();
}
SkOpSpan* prior = &fHead;
SkOpSpanBase* spanBase = fHead.next();
while (spanBase != &fTail) {
if (activePrior) {
- SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(
+ this->globalState()->allocator());
priorAngle->set(spanBase, prior);
spanBase->setFromAngle(priorAngle);
}
@@ -425,7 +355,8 @@ void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
bool active = !span->isCanceled();
SkOpSpanBase* next = span->next();
if (active) {
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(
+ this->globalState()->allocator());
angle->set(span, next);
span->setToAngle(angle);
}
@@ -434,12 +365,32 @@ void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
spanBase = next;
}
if (activePrior && !fTail.simple()) {
- addEndSpan(allocator);
+ addEndSpan();
}
}
+// Please keep this in sync with debugClearAll()
+void SkOpSegment::clearAll() {
+ SkOpSpan* span = &fHead;
+ do {
+ this->clearOne(span);
+ } while ((span = span->next()->upCastable()));
+ this->globalState()->coincidence()->release(this);
+}
+
+// Please keep this in sync with debugClearOne()
+void SkOpSegment::clearOne(SkOpSpan* span) {
+ span->setWindValue(0);
+ span->setOppValue(0);
+ this->markDone(span);
+}
+
+// Quads and conics collapse if the end points are the same, because
+// the curve doesn't enclose an area.
bool SkOpSegment::collapsed() const {
- return fVerb == SkPath::kLine_Verb && fHead.pt() == fTail.pt();
+ // FIXME: cubics can have also collapsed -- need to check if the
+ // control points are on a line with the end points
+ return fVerb < SkPath::kCubic_Verb && fHead.pt() == fTail.pt();
}
void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
@@ -571,12 +522,26 @@ int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
return start->starter(end)->windSum();
}
+bool SkOpSegment::contains(double newT) const {
+ const SkOpSpanBase* spanBase = &fHead;
+ do {
+ if (spanBase->ptT()->contains(this, newT)) {
+ return true;
+ }
+ if (spanBase == &fTail) {
+ break;
+ }
+ spanBase = spanBase->upCast()->next();
+ } while (true);
+ return false;
+}
+
void SkOpSegment::release(const SkOpSpan* span) {
if (span->done()) {
--fDoneCount;
}
--fCount;
- SkASSERT(fCount >= fDoneCount);
+ SkASSERT(this->globalState()->debugSkipAssert() || fCount >= fDoneCount);
}
double SkOpSegment::distSq(double t, const SkOpAngle* oppAngle) const {
@@ -601,15 +566,6 @@ double SkOpSegment::distSq(double t, const SkOpAngle* oppAngle) const {
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)
@@ -887,14 +843,12 @@ SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** n
}
SkOpGlobalState* SkOpSegment::globalState() const {
- return contour()->globalState();
+ return contour()->globalState();
}
void SkOpSegment::init(SkPoint pts[], SkScalar weight, SkOpContour* contour, SkPath::Verb verb) {
fContour = contour;
fNext = nullptr;
- fOriginal[0] = pts[0];
- fOriginal[1] = pts[SkPathOpsVerbToPoints(verb)];
fPts = pts;
fWeight = weight;
fVerb = verb;
@@ -1095,15 +1049,17 @@ bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
}
bool SkOpSegment::match(const SkOpPtT* base, const SkOpSegment* testParent, double testT,
- const SkPoint& testPt) const {
- const SkOpSegment* baseParent = base->segment();
- if (this == baseParent && this == testParent && precisely_equal(base->fT, testT)) {
- return true;
+ const SkPoint& testPt, AliasMatch aliasMatch) const {
+ SkASSERT(this == base->segment());
+ if (this == testParent) {
+ if (precisely_equal(base->fT, testT)) {
+ return true;
+ }
}
if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
return false;
}
- return !this->ptsDisjoint(base->fT, base->fPt, testT, testPt);
+ return this != testParent || !this->ptsDisjoint(base->fT, base->fPt, testT, testPt);
}
static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
@@ -1178,7 +1134,8 @@ SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpS
return other;
}
-static void clear_visited(SkOpSpanBase* span) {
+// Please keep this in sync with DebugClearVisited()
+void SkOpSegment::ClearVisited(SkOpSpanBase* span) {
// reset visited flag back to false
do {
SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
@@ -1189,20 +1146,23 @@ static void clear_visited(SkOpSpanBase* span) {
} while (!span->final() && (span = span->upCast()->next()));
}
+// Please keep this in sync with debugMissingCoincidence()
// look for pairs of undetected coincident curves
// assumes that segments going in have visited flag clear
-// curve/curve intersection should now do a pretty good job of finding coincident runs so
-// this may be only be necessary for line/curve pairs -- so skip unless this is a line and the
-// the opp is not a line
-bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
- if (this->verb() != SkPath::kLine_Verb) {
- return false;
- }
+// Even though pairs of curves correct detect coincident runs, a run may be missed
+// if the coincidence is a product of multiple intersections. For instance, given
+// curves A, B, and C:
+// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
+// the end of C that the intersection is replaced with the end of C.
+// Even though A-B correctly do not detect an intersection at point 2,
+// the resulting run from point 1 to point 2 is coincident on A and B.
+bool SkOpSegment::missingCoincidence() {
if (this->done()) {
return false;
}
SkOpSpan* prior = nullptr;
SkOpSpanBase* spanBase = &fHead;
+ bool result = false;
do {
SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
SkASSERT(ptT->span() == spanBase);
@@ -1211,9 +1171,6 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
continue;
}
SkOpSegment* opp = ptT->span()->segment();
-// if (opp->verb() == SkPath::kLine_Verb) {
-// continue;
-// }
if (opp->done()) {
continue;
}
@@ -1224,18 +1181,18 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
if (spanBase == &fHead) {
continue;
}
+ if (ptT->segment() == this) {
+ continue;
+ }
SkOpSpan* span = spanBase->upCastable();
// FIXME?: this assumes that if the opposite segment is coincident then no more
// coincidence needs to be detected. This may not be true.
- if (span && span->containsCoincidence(opp)) {
- continue;
- }
- if (spanBase->segment() == opp) {
+ if (span && span->containsCoincidence(opp)) {
continue;
}
if (spanBase->containsCoinEnd(opp)) {
continue;
- }
+ }
SkOpPtT* priorPtT = nullptr, * priorStopPtT;
// find prior span containing opp segment
SkOpSegment* priorOpp = nullptr;
@@ -1268,28 +1225,28 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
SkTSwap(priorPtT, ptT);
SkTSwap(oppStart, oppEnd);
}
- bool flipped = oppStart->fT > oppEnd->fT;
- bool coincident = false;
- if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
+ SkOpCoincidence* coincidences = this->globalState()->coincidence();
+ SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
+ SkOpPtT* rootPtT = ptT->span()->ptT();
+ SkOpPtT* rootOppStart = oppStart->span()->ptT();
+ SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
+ if (coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
goto swapBack;
}
- if (opp->verb() == SkPath::kLine_Verb) {
- coincident = (SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppStart->fPt) ||
- SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppEnd->fPt)) &&
- (SkDPoint::ApproximatelyEqual(ptT->fPt, oppStart->fPt) ||
- SkDPoint::ApproximatelyEqual(ptT->fPt, oppEnd->fPt));
- }
- if (!coincident) {
- coincident = testForCoincidence(priorPtT, ptT, prior, spanBase, opp, 5000);
- }
- if (coincident) {
+ if (this->testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
// mark coincidence
- if (!coincidences->extend(priorPtT, ptT, oppStart, oppEnd)
- && !coincidences->extend(oppStart, oppEnd, priorPtT, ptT)) {
- coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator);
+#if DEBUG_COINCIDENCE_VERBOSE
+ SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
+ rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
+ rootOppEnd->debugID());
+#endif
+ if (!coincidences->extend(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
+ coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
}
- clear_visited(&fHead);
- return true;
+#if DEBUG_COINCIDENCE
+ SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd));
+#endif
+ result = true;
}
swapBack:
if (swapped) {
@@ -1297,19 +1254,18 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
}
}
} while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
- clear_visited(&fHead);
- return false;
+ ClearVisited(&fHead);
+ return result;
}
+// please keep this in sync with debugMoveMultiples()
// if a span has more than one intersection, merge the other segments' span as needed
bool SkOpSegment::moveMultiples() {
debugValidate();
SkOpSpanBase* test = &fHead;
do {
int addCount = test->spanAddsCount();
- if (addCount < 1) {
- return false;
- }
+ FAIL_IF(addCount < 1);
if (addCount == 1) {
continue;
}
@@ -1392,66 +1348,126 @@ bool SkOpSegment::moveMultiples() {
oppSegment->debugValidate();
goto checkNextSpan;
}
- tryNextSpan:
+ tryNextSpan:
;
} while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
} while ((testPtT = testPtT->next()) != startPtT);
-checkNextSpan:
+checkNextSpan:
;
} while ((test = test->final() ? nullptr : test->upCast()->next()));
debugValidate();
return true;
}
+// adjacent spans may have points close by
+bool SkOpSegment::spansNearby(const SkOpSpanBase* refSpan, const SkOpSpanBase* checkSpan) const {
+ const SkOpPtT* refHead = refSpan->ptT();
+ const SkOpPtT* checkHead = checkSpan->ptT();
+// if the first pt pair from adjacent spans are far apart, assume that all are far enough apart
+ if (!SkDPoint::RoughlyEqual(refHead->fPt, checkHead->fPt)) {
+#if DEBUG_COINCIDENCE
+ // verify that no combination of points are close
+ const SkOpPtT* dBugRef = refHead;
+ do {
+ const SkOpPtT* dBugCheck = checkHead;
+ do {
+ SkASSERT(!SkDPoint::ApproximatelyEqual(dBugRef->fPt, dBugCheck->fPt));
+ dBugCheck = dBugCheck->next();
+ } while (dBugCheck != checkHead);
+ dBugRef = dBugRef->next();
+ } while (dBugRef != refHead);
+#endif
+ return false;
+ }
+ // check only unique points
+ SkScalar distSqBest = SK_ScalarMax;
+ const SkOpPtT* refBest = nullptr;
+ const SkOpPtT* checkBest = nullptr;
+ const SkOpPtT* ref = refHead;
+ do {
+ if (ref->deleted()) {
+ continue;
+ }
+ while (ref->ptAlreadySeen(refHead)) {
+ ref = ref->next();
+ if (ref == refHead) {
+ goto doneCheckingDistance;
+ }
+ }
+ const SkOpPtT* check = checkHead;
+ const SkOpSegment* refSeg = ref->segment();
+ do {
+ if (check->deleted()) {
+ continue;
+ }
+ while (check->ptAlreadySeen(checkHead)) {
+ check = check->next();
+ if (check == checkHead) {
+ goto nextRef;
+ }
+ }
+ SkScalar distSq = ref->fPt.distanceToSqd(check->fPt);
+ if (distSqBest > distSq && (refSeg != check->segment()
+ || !refSeg->ptsDisjoint(*ref, *check))) {
+ distSqBest = distSq;
+ refBest = ref;
+ checkBest = check;
+ }
+ } while ((check = check->next()) != checkHead);
+nextRef:
+ ;
+ } while ((ref = ref->next()) != refHead);
+doneCheckingDistance:
+ return checkBest && refBest->segment()->match(refBest, checkBest->segment(), checkBest->fT,
+ checkBest->fPt, kAllowAliasMatch);
+}
+
+// Please keep this function in sync with debugMoveNearby()
// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
void SkOpSegment::moveNearby() {
debugValidate();
- SkOpSpanBase* spanS = &fHead;
+ // release undeleted spans pointing to this seg that are linked to the primary span
+ SkOpSpanBase* spanBase = &fHead;
do {
- SkOpSpanBase* test = spanS->upCast()->next();
- SkOpSpanBase* next;
- if (spanS->contains(test)) {
- if (!test->final()) {
- test->upCast()->release(spanS->ptT());
- continue;
- } else if (spanS != &fHead) {
- spanS->upCast()->release(test->ptT());
- spanS = test;
- continue;
+ SkOpPtT* ptT = spanBase->ptT();
+ const SkOpPtT* headPtT = ptT;
+ while ((ptT = ptT->next()) != headPtT) {
+ SkOpSpanBase* test = ptT->span();
+ if (ptT->segment() == this && !ptT->deleted() && test != spanBase
+ && test->ptT() == ptT) {
+ if (test->final()) {
+ if (spanBase == &fHead) {
+ this->clearAll();
+ return;
+ }
+ spanBase->upCast()->release(ptT);
+ } else if (test->prev()) {
+ test->upCast()->release(headPtT);
+ }
+ break;
}
}
- do { // iterate through all spans associated with start
- SkOpPtT* startBase = spanS->ptT();
- next = test->final() ? nullptr : test->upCast()->next();
- do {
- SkOpPtT* testBase = test->ptT();
- do {
- if (startBase == testBase) {
- goto checkNextSpan;
- }
- if (testBase->duplicate()) {
- continue;
- }
- if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) {
- if (test == &this->fTail) {
- if (spanS == &fHead) {
- debugValidate();
- return; // if this span has collapsed, remove it from parent
- }
- this->fTail.merge(spanS->upCast());
- debugValidate();
- return;
- }
- spanS->merge(test->upCast());
- goto checkNextSpan;
- }
- } while ((testBase = testBase->next()) != test->ptT());
- } while ((startBase = startBase->next()) != spanS->ptT());
- checkNextSpan:
- ;
- } while ((test = next));
- spanS = spanS->upCast()->next();
- } while (!spanS->final());
+ spanBase = spanBase->upCast()->next();
+ } while (!spanBase->final());
+
+ // This loop looks for adjacent spans which are near by
+ spanBase = &fHead;
+ do { // iterate through all spans associated with start
+ SkOpSpanBase* test = spanBase->upCast()->next();
+ if (this->spansNearby(spanBase, test)) {
+ if (test->final()) {
+ if (spanBase->prev()) {
+ test->merge(spanBase->upCast());
+ } else {
+ this->clearAll();
+ return;
+ }
+ } else {
+ spanBase->merge(test->upCast());
+ }
+ }
+ spanBase = test;
+ } while (!spanBase->final());
debugValidate();
}
@@ -1679,8 +1695,7 @@ bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
}
bool SkOpSegment::testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT,
- const SkOpSpanBase* prior, const SkOpSpanBase* spanBase, const SkOpSegment* opp,
- SkScalar flatnessLimit) const {
+ const SkOpSpanBase* prior, const SkOpSpanBase* spanBase, const SkOpSegment* opp) const {
// average t, find mid pt
double midT = (prior->t() + spanBase->t()) / 2;
SkPoint midPt = this->ptAtT(midT);
@@ -1688,22 +1703,28 @@ bool SkOpSegment::testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT
// if the mid pt is not near either end pt, project perpendicular through opp seg
if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt)
&& !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) {
+ if (priorPtT->span() == ptT->span()) {
+ return false;
+ }
coincident = false;
SkIntersections i;
- SkVector dxdy = (*CurveSlopeAtT[fVerb])(this->pts(), this->weight(), midT);
- SkDLine ray = {{{midPt.fX, midPt.fY},
- {(double) midPt.fX + dxdy.fY, (double) midPt.fY - dxdy.fX}}};
- (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), ray, &i);
+ SkDCurve curvePart;
+ this->subDivide(prior, spanBase, &curvePart);
+ SkDVector dxdy = (*CurveDDSlopeAtT[fVerb])(curvePart, 0.5f);
+ SkDPoint partMidPt = (*CurveDDPointAtT[fVerb])(curvePart, 0.5f);
+ SkDLine ray = {{{midPt.fX, midPt.fY}, {partMidPt.fX + dxdy.fY, partMidPt.fY - dxdy.fX}}};
+ SkDCurve oppPart;
+ opp->subDivide(priorPtT->span(), ptT->span(), &oppPart);
+ (*CurveDIntersectRay[opp->verb()])(oppPart, ray, &i);
// measure distance and see if it's small enough to denote coincidence
for (int index = 0; index < i.used(); ++index) {
+ if (!between(0, i[0][index], 1)) {
+ continue;
+ }
SkDPoint oppPt = i.pt(index);
if (oppPt.approximatelyEqual(midPt)) {
- SkVector oppDxdy = (*CurveSlopeAtT[opp->verb()])(opp->pts(),
- opp->weight(), i[index][0]);
- oppDxdy.normalize();
- dxdy.normalize();
- SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON);
- coincident |= flatness < flatnessLimit;
+ // the coincidence can occur at almost any angle
+ coincident = true;
}
}
}
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index 1d67b1c131..55e67a5d80 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -23,9 +23,9 @@ class SkPathWriter;
class SkOpSegment {
public:
- enum AllowAlias {
- kAllowAlias,
- kNoAlias
+ enum AliasMatch {
+ kNoAliasMatch,
+ kAllowAliasMatch,
};
bool operator<(const SkOpSegment& rh) const {
@@ -45,13 +45,6 @@ public:
bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end);
bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding);
- void addAlignIntersection(SkOpPtT& endPtT, SkPoint& oldPt,
- SkOpContourHead* contourList, SkChunkAlloc* allocator);
-
- void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
- this->addAlignIntersection(*fHead.ptT(), fOriginal[0], contourList, allocator);
- this->addAlignIntersection(*fTail.ptT(), fOriginal[1], contourList, allocator);
- }
SkOpSegment* addConic(SkPoint pts[3], SkScalar weight, SkOpContour* parent) {
init(pts, weight, parent, SkPath::kConic_Verb);
@@ -71,23 +64,25 @@ public:
bool addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path) const;
- SkOpAngle* addEndSpan(SkChunkAlloc* allocator) {
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ SkOpAngle* addEndSpan() {
+ SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
angle->set(&fTail, fTail.prev());
fTail.setFromAngle(angle);
return angle;
}
+ bool addExpanded(double newT, const SkOpSpanBase* test, bool* startOver);
+
SkOpSegment* addLine(SkPoint pts[2], SkOpContour* parent) {
init(pts, 1, parent, SkPath::kLine_Verb);
fBounds.set(pts, 2);
return this;
}
- SkOpPtT* addMissing(double t, SkOpSegment* opp, SkChunkAlloc* );
+ SkOpPtT* addMissing(double t, SkOpSegment* opp, bool* allExist);
- SkOpAngle* addStartSpan(SkChunkAlloc* allocator) {
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ SkOpAngle* addStartSpan() {
+ SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
angle->set(&fHead, fHead.next());
fHead.setToAngle(angle);
return angle;
@@ -101,9 +96,11 @@ public:
return this;
}
- SkOpPtT* addT(double t, AllowAlias , SkChunkAlloc* );
+ SkOpPtT* addT(double t, AliasMatch, bool* allocated);
- void align();
+ template<typename T> T* allocateArray(int count) {
+ return SkOpTAllocator<T>::AllocateArray(this->globalState()->allocator(), count);
+ }
const SkPathOpsBounds& bounds() const {
return fBounds;
@@ -113,7 +110,7 @@ public:
++fCount;
}
- void calcAngles(SkChunkAlloc*);
+ void calcAngles();
bool collapsed() const;
static void ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
SkOpAngle::IncludeType );
@@ -121,6 +118,11 @@ public:
SkOpAngle::IncludeType );
int computeSum(SkOpSpanBase* start, SkOpSpanBase* end, SkOpAngle::IncludeType includeType);
+ void clearAll();
+ void clearOne(SkOpSpan* span);
+ static void ClearVisited(SkOpSpanBase* span);
+ bool contains(double t) const;
+
SkOpContour* contour() const {
return fContour;
}
@@ -129,36 +131,30 @@ public:
return fCount;
}
- void debugAddAngle(double startT, double endT, SkChunkAlloc*);
- void debugAddAlignIntersection(const char* id, SkPathOpsDebug::GlitchLog* glitches,
- const SkOpPtT& endPtT, const SkPoint& oldPt,
- const SkOpContourHead* ) const;
-
- void debugAddAlignIntersections(const char* id, SkPathOpsDebug::GlitchLog* glitches,
- SkOpContourHead* contourList) const {
- this->debugAddAlignIntersection(id, glitches, *fHead.ptT(), fOriginal[0], contourList);
- this->debugAddAlignIntersection(id, glitches, *fTail.ptT(), fOriginal[1], contourList);
- }
-
- bool debugAddMissing(double t, const SkOpSegment* opp) const;
- void debugAlign(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
+ void debugAddAngle(double startT, double endT);
+ const SkOpPtT* debugAddT(double t, AliasMatch , bool* allocated) const;
const SkOpAngle* debugAngle(int id) const;
#if DEBUG_ANGLE
void debugCheckAngleCoin() const;
#endif
+#if DEBUG_COINCIDENCE_VERBOSE
void debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+ void debugClearAll(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
+ void debugClearOne(const SkOpSpan* span, const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
+#endif
+ const SkOpCoincidence* debugCoincidence() const;
SkOpContour* debugContour(int id);
- void debugFindCollapsed(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
int debugID() const {
return SkDEBUGRELEASE(fID, -1);
}
SkOpAngle* debugLastAngle();
- void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* glitches,
- const SkOpCoincidence* coincidences) const;
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
void debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
void debugMoveNearby(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
+#endif
const SkOpPtT* debugPtT(int id) const;
void debugReset();
const SkOpSegment* debugSegment(int id) const;
@@ -173,11 +169,24 @@ public:
const SkOpSpanBase* debugSpan(int id) const;
void debugValidate() const;
+
+#if DEBUG_COINCIDENCE
+ static void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span);
+
+ bool debugVisited() const {
+ if (!fDebugVisited) {
+ fDebugVisited = true;
+ return false;
+ }
+ return true;
+ }
+#endif
+
void release(const SkOpSpan* );
double distSq(double t, const SkOpAngle* opp) const;
bool done() const {
- SkASSERT(fDoneCount <= fCount);
+ SkASSERT(this->globalState()->debugSkipAssert() || fDoneCount <= fCount);
return fDoneCount == fCount;
}
@@ -200,7 +209,7 @@ public:
void dumpPts(const char* prefix = "seg") const;
void dumpPtsInner(const char* prefix = "seg") const;
- void findCollapsed();
+ const SkOpPtT* existing(double t, const SkOpSegment* opp) const;
SkOpSegment* findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op,
int xorMiMask, int xorSuMask);
@@ -220,8 +229,8 @@ public:
void init(SkPoint pts[], SkScalar weight, SkOpContour* parent, SkPath::Verb verb);
- SkOpSpan* insert(SkOpSpan* prev, SkChunkAlloc* allocator) {
- SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(allocator);
+ SkOpSpan* insert(SkOpSpan* prev) {
+ SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(this->globalState()->allocator());
SkOpSpanBase* next = prev->next();
result->setPrev(prev);
prev->setNext(result);
@@ -269,8 +278,9 @@ public:
void markDone(SkOpSpan* );
bool markWinding(SkOpSpan* , int winding);
bool markWinding(SkOpSpan* , int winding, int oppWinding);
- bool match(const SkOpPtT* span, const SkOpSegment* parent, double t, const SkPoint& pt) const;
- bool missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
+ bool match(const SkOpPtT* span, const SkOpSegment* parent, double t, const SkPoint& pt,
+ AliasMatch ) const;
+ bool missingCoincidence();
bool moveMultiples();
void moveNearby();
@@ -302,17 +312,31 @@ public:
}
bool ptsDisjoint(const SkOpPtT& span, const SkOpPtT& test) const {
+ SkASSERT(this == span.segment());
+ SkASSERT(this == test.segment());
return ptsDisjoint(span.fT, span.fPt, test.fT, test.fPt);
}
bool ptsDisjoint(const SkOpPtT& span, double t, const SkPoint& pt) const {
+ SkASSERT(this == span.segment());
return ptsDisjoint(span.fT, span.fPt, t, pt);
}
+ bool ptsDisjoint(const SkOpSpanBase* span, const SkOpSpanBase* test) const {
+ SkASSERT(this == span->segment());
+ SkASSERT(this == test->segment());
+ return ptsDisjoint(span->t(), span->pt(), test->t(), test->pt());
+ }
+
bool ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const;
- void rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits,
- SkChunkAlloc* allocator);
+ void rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits, SkChunkAlloc*);
+
+#if DEBUG_COINCIDENCE
+ void resetDebugVisited() const {
+ fDebugVisited = false;
+ }
+#endif
void resetVisited() {
fVisited = false;
@@ -330,10 +354,6 @@ public:
fPrev = prev;
}
- void setVisited() {
- fVisited = true;
- }
-
void setUpWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* maxWinding, int* sumWinding) {
int deltaSum = SpanSign(start, end);
*maxWinding = *sumWinding;
@@ -348,6 +368,7 @@ public:
void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding, int* sumSuWinding,
int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding);
void sortAngles();
+ bool spansNearby(const SkOpSpanBase* ref, const SkOpSpanBase* check) const;
static int SpanSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
int result = start->t() < end->t() ? -start->upCast()->windValue()
@@ -372,9 +393,10 @@ public:
}
bool testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT, const SkOpSpanBase* prior,
- const SkOpSpanBase* spanBase, const SkOpSegment* opp, SkScalar flatnessLimit) const;
+ const SkOpSpanBase* spanBase, const SkOpSegment* opp) const;
void undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end);
+ bool uniqueT(double t, AliasMatch allowAlias) const;
int updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
int updateOppWinding(const SkOpAngle* angle) const;
int updateOppWindingReverse(const SkOpAngle* angle) const;
@@ -404,17 +426,12 @@ public:
SkOpSpan* windingSpanAtT(double tHit);
int windSum(const SkOpAngle* angle) const;
- SkPoint* writablePt(bool end) {
- return &fPts[end ? SkPathOpsVerbToPoints(fVerb) : 0];
- }
-
private:
SkOpSpan fHead; // the head span always has its t set to zero
SkOpSpanBase fTail; // the tail span always has its t set to one
SkOpContour* fContour;
SkOpSegment* fNext; // forward-only linked list used by contour to walk the segments
const SkOpSegment* fPrev;
- SkPoint fOriginal[2]; // if aligned, the original unaligned points are here
SkPoint* fPts; // pointer into array of points owned by edge builder that may be tweaked
SkPathOpsBounds fBounds; // tight bounds
SkScalar fWeight;
@@ -422,6 +439,9 @@ private:
int fDoneCount; // number of processed spans (zero initially)
SkPath::Verb fVerb;
bool fVisited; // used by missing coincidence check
+#if DEBUG_COINCIDENCE
+ mutable bool fDebugVisited; // used by debug missing coincidence check
+#endif
SkDEBUGCODE(int fID);
};
diff --git a/src/pathops/SkOpSpan.cpp b/src/pathops/SkOpSpan.cpp
index f3362235d8..577a9db326 100755
--- a/src/pathops/SkOpSpan.cpp
+++ b/src/pathops/SkOpSpan.cpp
@@ -35,54 +35,60 @@ bool SkOpPtT::contains(const SkOpPtT* check) const {
return false;
}
-SkOpPtT* SkOpPtT::contains(const SkOpSegment* check) {
- SkASSERT(this->segment() != check);
- SkOpPtT* ptT = this;
+bool SkOpPtT::contains(const SkOpSegment* segment, const SkPoint& pt) const {
+ SkASSERT(this->segment() != segment);
+ const SkOpPtT* ptT = this;
const SkOpPtT* stopPtT = ptT;
while ((ptT = ptT->next()) != stopPtT) {
- if (ptT->segment() == check) {
- return ptT;
+ if (ptT->fPt == pt && ptT->segment() == segment) {
+ return true;
}
}
- return nullptr;
+ return false;
}
-SkOpContour* SkOpPtT::contour() const {
- return segment()->contour();
+bool SkOpPtT::contains(const SkOpSegment* segment, double t) const {
+ const SkOpPtT* ptT = this;
+ const SkOpPtT* stopPtT = ptT;
+ while ((ptT = ptT->next()) != stopPtT) {
+ if (ptT->fT == t && ptT->segment() == segment) {
+ return true;
+ }
+ }
+ return false;
}
-SkOpPtT* SkOpPtT::doppelganger() {
- SkASSERT(fDeleted);
- SkOpPtT* ptT = fNext;
- while (ptT->fDeleted) {
- ptT = ptT->fNext;
- }
+const SkOpPtT* SkOpPtT::contains(const SkOpSegment* check) const {
+ SkASSERT(this->segment() != check);
+ const SkOpPtT* ptT = this;
const SkOpPtT* stopPtT = ptT;
- do {
- if (ptT->fSpan == fSpan) {
+ while ((ptT = ptT->next()) != stopPtT) {
+ if (ptT->segment() == check && !ptT->deleted()) {
return ptT;
}
- ptT = ptT->fNext;
- } while (stopPtT != ptT);
- SkASSERT(0);
+ }
return nullptr;
}
-SkOpPtT* SkOpPtT::find(SkOpSegment* segment) {
- SkOpPtT* ptT = this;
+SkOpContour* SkOpPtT::contour() const {
+ return segment()->contour();
+}
+
+const SkOpPtT* SkOpPtT::find(const SkOpSegment* segment) const {
+ const SkOpPtT* ptT = this;
const SkOpPtT* stopPtT = ptT;
do {
- if (ptT->segment() == segment) {
+ if (ptT->segment() == segment && !ptT->deleted()) {
return ptT;
}
ptT = ptT->fNext;
} while (stopPtT != ptT);
- SkASSERT(0);
+// SkASSERT(0);
return nullptr;
}
SkOpGlobalState* SkOpPtT::globalState() const {
- return contour()->globalState();
+ return contour()->globalState();
}
void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
@@ -92,6 +98,7 @@ void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplica
fNext = this;
fDuplicatePt = duplicate;
fDeleted = false;
+ fCoincident = false;
SkDEBUGCODE(fID = span->globalState()->nextPtTID());
}
@@ -104,6 +111,16 @@ bool SkOpPtT::onEnd() const {
return span == segment->head() || span == segment->tail();
}
+bool SkOpPtT::ptAlreadySeen(const SkOpPtT* check) const {
+ while (this != check) {
+ if (this->fPt == check->fPt) {
+ return true;
+ }
+ check = check->fNext;
+ }
+ return false;
+}
+
SkOpPtT* SkOpPtT::prev() {
SkOpPtT* result = this;
SkOpPtT* next = this;
@@ -114,12 +131,12 @@ SkOpPtT* SkOpPtT::prev() {
return result;
}
-SkOpPtT* SkOpPtT::remove() {
+SkOpPtT* SkOpPtT::remove(const SkOpPtT* kept) {
SkOpPtT* prev = this;
do {
SkOpPtT* next = prev->fNext;
if (next == this) {
- prev->removeNext(this);
+ prev->removeNext(kept);
SkASSERT(prev->fNext != prev);
fDeleted = true;
return prev;
@@ -130,12 +147,16 @@ SkOpPtT* SkOpPtT::remove() {
return nullptr;
}
-void SkOpPtT::removeNext(SkOpPtT* kept) {
+void SkOpPtT::removeNext(const SkOpPtT* kept) {
SkASSERT(this->fNext);
SkOpPtT* next = this->fNext;
SkASSERT(this != next->fNext);
this->fNext = next->fNext;
SkOpSpanBase* span = next->span();
+ SkOpCoincidence* coincidence = span->globalState()->coincidence();
+ if (coincidence) {
+ coincidence->fixUp(next, kept);
+ }
next->setDeleted();
if (span->ptT() == next) {
span->upCast()->release(kept);
@@ -150,85 +171,68 @@ SkOpSegment* SkOpPtT::segment() {
return span()->segment();
}
-void SkOpSpanBase::align() {
- if (this->fAligned) {
+void SkOpPtT::setDeleted() {
+ SkASSERT(this->span()->debugDeleted() || this->span()->ptT() != this);
+ SkASSERT(this->globalState()->debugSkipAssert() || !fDeleted);
+ fDeleted = true;
+}
+
+// please keep this in sync with debugAddOppAndMerge
+// If the added points envelop adjacent spans, merge them in.
+void SkOpSpanBase::addOppAndMerge(SkOpSpanBase* opp) {
+ if (this->ptT()->addOpp(opp->ptT())) {
+ this->checkForCollapsedCoincidence();
+ }
+ // compute bounds of points in span
+ SkPathOpsBounds bounds;
+ bounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin);
+ const SkOpPtT* head = this->ptT();
+ const SkOpPtT* nextPt = head;
+ do {
+ bounds.add(nextPt->fPt);
+ } while ((nextPt = nextPt->next()) != head);
+ if (!bounds.width() && !bounds.height()) {
return;
}
- SkASSERT(!zero_or_one(this->fPtT.fT));
- SkASSERT(this->fPtT.next());
- // if a linked pt/t pair has a t of zero or one, use it as the base for alignment
- SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
- while ((ptT = ptT->next()) != stopPtT) {
- if (zero_or_one(ptT->fT)) {
- SkOpSegment* segment = ptT->segment();
- SkASSERT(this->segment() != segment);
- SkASSERT(segment->head()->ptT() == ptT || segment->tail()->ptT() == ptT);
- if (ptT->fT) {
- segment->tail()->alignEnd(1, segment->lastPt());
- } else {
- segment->head()->alignEnd(0, segment->pts()[0]);
- }
+ this->mergeContained(bounds);
+ opp->mergeContained(bounds);
+}
+
+// Please keep this in sync with debugMergeContained()
+void SkOpSpanBase::mergeContained(const SkPathOpsBounds& bounds) {
+ // while adjacent spans' points are contained by the bounds, merge them
+ SkOpSpanBase* prev = this;
+ SkOpSegment* seg = this->segment();
+ while ((prev = prev->prev()) && bounds.contains(prev->pt()) && !seg->ptsDisjoint(prev, this)) {
+ if (prev->prev()) {
+ this->merge(prev->upCast());
+ prev = this;
+ } else if (this->final()) {
+ seg->clearAll();
return;
+ } else {
+ prev->merge(this->upCast());
}
}
- alignInner();
- this->fAligned = true;
-}
-
-
-// FIXME: delete spans that collapse
-// delete segments that collapse
-// delete contours that collapse
-void SkOpSpanBase::alignEnd(double t, const SkPoint& pt) {
- SkASSERT(zero_or_one(t));
- SkOpSegment* segment = this->segment();
- SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
- alignInner();
- *segment->writablePt(!!t) = pt;
- SkOpPtT* ptT = &this->fPtT;
- SkASSERT(t == ptT->fT);
- SkASSERT(pt == ptT->fPt);
- SkOpPtT* test = ptT, * stopPtT = ptT;
- while ((test = test->next()) != stopPtT) {
- SkOpSegment* other = test->segment();
- if (other == this->segment()) {
- continue;
+ SkOpSpanBase* current = this;
+ SkOpSpanBase* next = this;
+ while (next->upCastable() && (next = next->upCast()->next())
+ && bounds.contains(next->pt()) && !seg->ptsDisjoint(this, next)) {
+ if (!current->prev() && next->final()) {
+ seg->clearAll();
+ return;
}
- if (!zero_or_one(test->fT)) {
- continue;
+ if (current->prev()) {
+ next->merge(current->upCast());
+ current = next;
+ } else {
+ current->merge(next->upCast());
+ // extra line in debug version
}
- *other->writablePt(!!test->fT) = pt;
}
- this->fAligned = true;
-}
-
-void SkOpSpanBase::alignInner() {
- // force the spans to share points and t values
- SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
- const SkPoint& pt = ptT->fPt;
- do {
- ptT->fPt = pt;
- const SkOpSpanBase* span = ptT->span();
- SkOpPtT* test = ptT;
- do {
- SkOpPtT* prev = test;
- if ((test = test->next()) == stopPtT) {
- break;
- }
- if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) {
- // omit aliases that alignment makes redundant
- if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
- SkASSERT(test->alias());
- prev->removeNext(ptT);
- test = prev;
- } else {
- SkASSERT(ptT->alias());
- stopPtT = ptT = ptT->remove();
- break;
- }
- }
- } while (true);
- } while ((ptT = ptT->next()) != stopPtT);
+#if DEBUG_COINCIDENCE
+ this->globalState()->coincidence()->debugValidate();
+#endif
}
bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
@@ -244,11 +248,14 @@ bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
return false;
}
-SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) {
- SkOpPtT* start = &fPtT;
- SkOpPtT* walk = start;
+const SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) const {
+ const SkOpPtT* start = &fPtT;
+ const SkOpPtT* walk = start;
while ((walk = walk->next()) != start) {
- if (walk->segment() == segment) {
+ if (walk->deleted()) {
+ continue;
+ }
+ if (walk->segment() == segment && walk->span()->ptT() == walk) {
return walk;
}
}
@@ -271,7 +278,7 @@ SkOpContour* SkOpSpanBase::contour() const {
}
SkOpGlobalState* SkOpSpanBase::globalState() const {
- return contour()->globalState();
+ return contour()->globalState();
}
void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
@@ -285,6 +292,7 @@ void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, cons
fChased = false;
SkDEBUGCODE(fCount = 1);
SkDEBUGCODE(fID = globalState()->nextSpanID());
+ SkDEBUGCODE(fDeleted = false);
}
// this pair of spans share a common t value or point; merge them and eliminate duplicates
@@ -294,8 +302,11 @@ void SkOpSpanBase::merge(SkOpSpan* span) {
SkASSERT(this->t() != spanPtT->fT);
SkASSERT(!zero_or_one(spanPtT->fT));
span->release(this->ptT());
+ if (this->contains(span)) {
+ return; // merge is already in the ptT loop
+ }
SkOpPtT* remainder = spanPtT->next();
- ptT()->insert(spanPtT);
+ this->ptT()->insert(spanPtT);
while (remainder != spanPtT) {
SkOpPtT* next = remainder->next();
SkOpPtT* compare = spanPtT->next();
@@ -311,6 +322,26 @@ tryNextRemainder:
remainder = next;
}
fSpanAdds += span->fSpanAdds;
+ this->checkForCollapsedCoincidence();
+}
+
+// please keep in sync with debugCheckForCollapsedCoincidence()
+void SkOpSpanBase::checkForCollapsedCoincidence() {
+ SkOpCoincidence* coins = this->globalState()->coincidence();
+ if (coins->isEmpty()) {
+ return;
+ }
+// the insert above may have put both ends of a coincident run in the same span
+// for each coincident ptT in loop; see if its opposite in is also in the loop
+// this implementation is the motivation for marking that a ptT is referenced by a coincident span
+ SkOpPtT* head = this->ptT();
+ SkOpPtT* test = head;
+ do {
+ if (!test->coincident()) {
+ continue;
+ }
+ coins->markCollapsed(test);
+ } while ((test = test->next()) != head);
}
int SkOpSpan::computeWindSum() {
@@ -334,7 +365,45 @@ bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
return false;
}
-void SkOpSpan::release(SkOpPtT* kept) {
+void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
+ SkASSERT(t != 1);
+ initBase(segment, prev, t, pt);
+ fCoincident = this;
+ fToAngle = nullptr;
+ fWindSum = fOppSum = SK_MinS32;
+ fWindValue = 1;
+ fOppValue = 0;
+ fTopTTry = 0;
+ fChased = fDone = false;
+ segment->bumpCount();
+ fAlreadyAdded = false;
+}
+
+// Please keep this in sync with debugInsertCoincidence()
+bool SkOpSpan::insertCoincidence(const SkOpSegment* segment, bool flipped) {
+ if (this->containsCoincidence(segment)) {
+ return true;
+ }
+ SkOpPtT* next = &fPtT;
+ while ((next = next->next()) != &fPtT) {
+ if (next->segment() == segment) {
+ SkOpSpan* span = flipped ? next->span()->prev() : next->span()->upCast();
+ if (!span) {
+ return false;
+ }
+ this->insertCoincidence(span);
+ return true;
+ }
+ }
+#if DEBUG_COINCIDENCE
+ SkASSERT(0); // FIXME? if we get here, the span is missing its opposite segment...
+#endif
+ return true;
+}
+
+void SkOpSpan::release(const SkOpPtT* kept) {
+ SkDEBUGCODE(fDeleted = true);
+ SkASSERT(kept->span() != this);
SkASSERT(!final());
SkOpSpan* prev = this->prev();
SkASSERT(prev);
@@ -348,20 +417,14 @@ void SkOpSpan::release(SkOpPtT* kept) {
coincidence->fixUp(this->ptT(), kept);
}
this->ptT()->setDeleted();
-}
-
-void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
- SkASSERT(t != 1);
- initBase(segment, prev, t, pt);
- fCoincident = this;
- fToAngle = nullptr;
- fWindSum = fOppSum = SK_MinS32;
- fWindValue = 1;
- fOppValue = 0;
- fTopTTry = 0;
- fChased = fDone = false;
- segment->bumpCount();
- fAlreadyAdded = false;
+ SkOpPtT* stopPtT = this->ptT();
+ SkOpPtT* testPtT = stopPtT;
+ const SkOpSpanBase* keptSpan = kept->span();
+ do {
+ if (this == testPtT->span()) {
+ testPtT->setSpan(keptSpan);
+ }
+ } while ((testPtT = testPtT->next()) != stopPtT);
}
void SkOpSpan::setOppSum(int oppSum) {
diff --git a/src/pathops/SkOpSpan.h b/src/pathops/SkOpSpan.h
index c6fc4b138f..63fc1e9a1e 100644
--- a/src/pathops/SkOpSpan.h
+++ b/src/pathops/SkOpSpan.h
@@ -12,12 +12,13 @@
#include "SkPoint.h"
class SkChunkAlloc;
-struct SkOpAngle;
+class SkOpAngle;
class SkOpContour;
class SkOpGlobalState;
class SkOpSegment;
class SkOpSpanBase;
class SkOpSpan;
+struct SkPathOpsBounds;
// subset of op span used by terminal span (when t is equal to one)
class SkOpPtT {
@@ -27,37 +28,43 @@ public:
kIsDuplicate = 1
};
- void addOpp(SkOpPtT* opp) {
+ // please keep in sync with debugAddOpp()
+ bool addOpp(SkOpPtT* opp) {
// find the fOpp ptr to opp
SkOpPtT* oppPrev = opp->fNext;
if (oppPrev == this) {
- return;
+ return false;
}
while (oppPrev->fNext != opp) {
oppPrev = oppPrev->fNext;
- if (oppPrev == this) {
- return;
- }
+ if (oppPrev == this) {
+ return false;
+ }
}
-
SkOpPtT* oldNext = this->fNext;
SkASSERT(this != opp);
this->fNext = opp;
SkASSERT(oppPrev != oldNext);
oppPrev->fNext = oldNext;
+ return true;
}
bool alias() const;
+ bool coincident() const { return fCoincident; }
bool collapsed(const SkOpPtT* ) const;
bool contains(const SkOpPtT* ) const;
- SkOpPtT* contains(const SkOpSegment* );
+ bool contains(const SkOpSegment*, const SkPoint& ) const;
+ bool contains(const SkOpSegment*, double t) const;
+ const SkOpPtT* contains(const SkOpSegment* ) const;
SkOpContour* contour() const;
int debugID() const {
return SkDEBUGRELEASE(fID, -1);
}
+ bool debugAddOpp(const SkOpPtT* opp) const;
const SkOpAngle* debugAngle(int id) const;
+ const SkOpCoincidence* debugCoincidence() const;
bool debugContains(const SkOpPtT* ) const;
const SkOpPtT* debugContains(const SkOpSegment* check) const;
SkOpContour* debugContour(int id);
@@ -72,8 +79,6 @@ public:
return fDeleted;
}
- SkOpPtT* doppelganger();
-
bool duplicate() const {
return fDuplicatePt;
}
@@ -82,7 +87,7 @@ public:
void dumpAll() const;
void dumpBase() const;
- SkOpPtT* find(SkOpSegment* );
+ const SkOpPtT* find(const SkOpSegment* ) const;
SkOpGlobalState* globalState() const;
void init(SkOpSpanBase* , double t, const SkPoint& , bool dup);
@@ -102,14 +107,14 @@ public:
bool onEnd() const;
- static bool Overlaps(SkOpPtT* s1, SkOpPtT* e1, SkOpPtT* s2, SkOpPtT* e2,
- SkOpPtT** sOut, SkOpPtT** eOut) {
- SkOpPtT* start1 = s1->fT < e1->fT ? s1 : e1;
- SkOpPtT* start2 = s2->fT < e2->fT ? s2 : e2;
+ static bool Overlaps(const SkOpPtT* s1, const SkOpPtT* e1, const SkOpPtT* s2,
+ const SkOpPtT* e2, const SkOpPtT** sOut, const SkOpPtT** eOut) {
+ const SkOpPtT* start1 = s1->fT < e1->fT ? s1 : e1;
+ const SkOpPtT* start2 = s2->fT < e2->fT ? s2 : e2;
*sOut = between(s1->fT, start2->fT, e1->fT) ? start2
: between(s2->fT, start1->fT, e2->fT) ? start1 : nullptr;
- SkOpPtT* end1 = s1->fT < e1->fT ? e1 : s1;
- SkOpPtT* end2 = s2->fT < e2->fT ? e2 : s2;
+ const SkOpPtT* end1 = s1->fT < e1->fT ? e1 : s1;
+ const SkOpPtT* end2 = s2->fT < e2->fT ? e2 : s2;
*eOut = between(s1->fT, end2->fT, e1->fT) ? end2
: between(s2->fT, end1->fT, e2->fT) ? end1 : nullptr;
if (*sOut == *eOut) {
@@ -120,16 +125,23 @@ public:
return *sOut && *eOut;
}
+ bool ptAlreadySeen(const SkOpPtT* head) const;
SkOpPtT* prev();
- SkOpPtT* remove();
- void removeNext(SkOpPtT* kept);
+ SkOpPtT* remove(const SkOpPtT* kept);
+ void removeNext(const SkOpPtT* kept);
const SkOpSegment* segment() const;
SkOpSegment* segment();
- void setDeleted() {
+ void setCoincident() const {
SkASSERT(!fDeleted);
- fDeleted = true;
+ fCoincident = true;
+ }
+
+ void setDeleted();
+
+ void setSpan(const SkOpSpanBase* span) {
+ fSpan = const_cast<SkOpSpanBase*>(span);
}
const SkOpSpanBase* span() const {
@@ -144,25 +156,21 @@ public:
return fT < end->fT ? this : end;
}
- double fT;
+ double fT;
SkPoint fPt; // cache of point value at this t
protected:
SkOpSpanBase* fSpan; // contains winding data
SkOpPtT* fNext; // intersection on opposite curve or alias on this curve
- bool fDeleted; // set if removed from span list
+ bool fDeleted; // set if removed from span list
bool fDuplicatePt; // set if identical pt is somewhere in the next loop
+ // below mutable since referrer is otherwise always const
+ mutable bool fCoincident; // set if at some point a coincident span pointed here
SkDEBUGCODE(int fID);
};
class SkOpSpanBase {
public:
- void align();
-
- bool aligned() const {
- return fAligned;
- }
-
- void alignEnd(double t, const SkPoint& pt);
+ void addOppAndMerge(SkOpSpanBase* );
void bumpSpanAdds() {
++fSpanAdds;
@@ -172,17 +180,14 @@ public:
return fChased;
}
- void clearCoinEnd() {
- SkASSERT(fCoinEnd != this);
- fCoinEnd = this;
- }
+ void checkForCollapsedCoincidence();
const SkOpSpanBase* coinEnd() const {
return fCoinEnd;
}
bool contains(const SkOpSpanBase* ) const;
- SkOpPtT* contains(const SkOpSegment* );
+ const SkOpPtT* contains(const SkOpSegment* ) const;
bool containsCoinEnd(const SkOpSpanBase* coin) const {
SkASSERT(this != coin);
@@ -198,6 +203,11 @@ public:
bool containsCoinEnd(const SkOpSegment* ) const;
SkOpContour* contour() const;
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugAddOppAndMerge(const char* id, SkPathOpsDebug::GlitchLog* , const SkOpSpanBase* ,
+ bool* del1, bool* del2) const;
+#endif
+
int debugBumpCount() {
return SkDEBUGRELEASE(++fCount, -1);
}
@@ -209,9 +219,21 @@ public:
bool debugAlignedEnd(double t, const SkPoint& pt) const;
bool debugAlignedInner() const;
const SkOpAngle* debugAngle(int id) const;
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugCheckForCollapsedCoincidence(const char* id, SkPathOpsDebug::GlitchLog* ) const;
+#endif
+ const SkOpCoincidence* debugCoincidence() const;
bool debugCoinEndLoopCheck() const;
- bool debugContains(const SkOpSegment* ) const;
SkOpContour* debugContour(int id);
+#ifdef SK_DEBUG
+ bool debugDeleted() const { return fDeleted; }
+#endif
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugInsertCoinEnd(const char* id, SkPathOpsDebug::GlitchLog* ,
+ const SkOpSpanBase* ) const;
+ void debugMergeContained(const char* id, SkPathOpsDebug::GlitchLog* ,
+ const SkPathOpsBounds& bounds, bool* deleted) const;
+#endif
const SkOpPtT* debugPtT(int id) const;
const SkOpSegment* debugSegment(int id) const;
const SkOpSpanBase* debugSpan(int id) const;
@@ -227,6 +249,7 @@ public:
void dumpCoin() const;
void dumpAll() const;
void dumpBase() const;
+ void dumpHead() const;
bool final() const {
return fPtT.fT == 1;
@@ -238,6 +261,7 @@ public:
void initBase(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
+ // Please keep this in sync with debugInsertCoinEnd()
void insertCoinEnd(SkOpSpanBase* coin) {
if (containsCoinEnd(coin)) {
SkASSERT(coin->containsCoinEnd(this));
@@ -252,8 +276,13 @@ public:
}
void merge(SkOpSpan* span);
+ void mergeContained(const SkPathOpsBounds& bounds);
- SkOpSpan* prev() const {
+ const SkOpSpan* prev() const {
+ return fPrev;
+ }
+
+ SkOpSpan* prev() {
return fPrev;
}
@@ -281,8 +310,6 @@ public:
fChased = chased;
}
- SkOpPtT* setCoinEnd(SkOpSpanBase* oldCoinEnd, SkOpSegment* oppSegment);
-
void setFromAngle(SkOpAngle* angle) {
fFromAngle = angle;
}
@@ -293,7 +320,7 @@ public:
bool simple() const {
fPtT.debugValidate();
- return fPtT.next()->next() == &fPtT;
+ return fPtT.next()->next() == &fPtT;
}
int spanAddsCount() const {
@@ -368,6 +395,7 @@ protected: // no direct access to internals to avoid treating a span base as a
bool fChased; // set after span has been added to chase array
SkDEBUGCODE(int fCount); // number of pt/t pairs added
SkDEBUGCODE(int fID);
+ SkDEBUGCODE(bool fDeleted); // set when span was merged with another span
};
class SkOpSpan : public SkOpSpanBase {
@@ -404,7 +432,12 @@ public:
}
bool debugCoinLoopCheck() const;
- void release(SkOpPtT* );
+#if DEBUG_COINCIDENCE_VERBOSE
+ void debugInsertCoincidence(const char* , SkPathOpsDebug::GlitchLog* , const SkOpSpan* ) const;
+ void debugInsertCoincidence(const char* , SkPathOpsDebug::GlitchLog* ,
+ const SkOpSegment* , bool flipped) const;
+#endif
+ void release(const SkOpPtT* );
bool done() const {
SkASSERT(!final());
@@ -414,7 +447,9 @@ public:
void dumpCoin() const;
bool dumpSpan() const;
void init(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
+ bool insertCoincidence(const SkOpSegment* , bool flipped);
+ // Please keep this in sync with debugInsertCoincidence()
void insertCoincidence(SkOpSpan* coin) {
if (containsCoincidence(coin)) {
SkASSERT(coin->containsCoincidence(this));
@@ -470,6 +505,7 @@ public:
void setOppValue(int oppValue) {
SkASSERT(!final());
SkASSERT(fOppSum == SK_MinS32);
+ SkASSERT(!oppValue || !fDone);
fOppValue = oppValue;
}
@@ -484,6 +520,7 @@ public:
SkASSERT(!final());
SkASSERT(windValue >= 0);
SkASSERT(fWindSum == SK_MinS32);
+ SkASSERT(!windValue || !fDone);
fWindValue = windValue;
}
diff --git a/src/pathops/SkPathOpsBounds.h b/src/pathops/SkPathOpsBounds.h
index b99f36f5d9..610d7233a3 100644
--- a/src/pathops/SkPathOpsBounds.h
+++ b/src/pathops/SkPathOpsBounds.h
@@ -33,6 +33,13 @@ struct SkPathOpsBounds : public SkRect {
add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
}
+ void add(const SkPoint& pt) {
+ if (pt.fX < fLeft) fLeft = pt.fX;
+ if (pt.fY < fTop) fTop = pt.fY;
+ if (pt.fX > fRight) fRight = pt.fX;
+ if (pt.fY > fBottom) fBottom = pt.fY;
+ }
+
void add(const SkDPoint& pt) {
if (pt.fX < fLeft) fLeft = SkDoubleToScalar(pt.fX);
if (pt.fY < fTop) fTop = SkDoubleToScalar(pt.fY);
@@ -40,13 +47,18 @@ struct SkPathOpsBounds : public SkRect {
if (pt.fY > fBottom) fBottom = SkDoubleToScalar(pt.fY);
}
- bool almostContains(const SkPoint& pt) {
+ bool almostContains(const SkPoint& pt) const {
return AlmostLessOrEqualUlps(fLeft, pt.fX)
&& AlmostLessOrEqualUlps(pt.fX, fRight)
&& AlmostLessOrEqualUlps(fTop, pt.fY)
&& AlmostLessOrEqualUlps(pt.fY, fBottom);
}
+ bool contains(const SkPoint& pt) const {
+ return fLeft <= pt.fX && fTop <= pt.fY &&
+ fRight >= pt.fX && fBottom >= pt.fY;
+ }
+
typedef SkRect INHERITED;
};
diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp
index 24ef6f1129..fd4c027ddb 100644
--- a/src/pathops/SkPathOpsCommon.cpp
+++ b/src/pathops/SkPathOpsCommon.cpp
@@ -11,6 +11,28 @@
#include "SkPathWriter.h"
#include "SkTSort.h"
+SkScalar ScaleFactor(const SkPath& path) {
+ static const SkScalar twoTo10 = 1024.f;
+ SkScalar largest = 0;
+ const SkScalar* oneBounds = &path.getBounds().fLeft;
+ for (int index = 0; index < 4; ++index) {
+ largest = SkTMax(largest, SkScalarAbs(oneBounds[index]));
+ }
+ SkScalar scale = twoTo10;
+ SkScalar next;
+ while ((next = scale * twoTo10) < largest) {
+ scale = next;
+ }
+ return scale == twoTo10 ? SK_Scalar1 : scale;
+}
+
+void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled) {
+ SkMatrix matrix;
+ matrix.setScale(scale, scale);
+ *scaled = path;
+ scaled->transform(matrix);
+}
+
const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
bool* sortablePtr) {
// find first angle, initialize winding to computed fWindSum
@@ -144,15 +166,6 @@ SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
return nullptr;
}
-#if DEBUG_ACTIVE_SPANS
-void DebugShowActiveSpans(SkOpContourHead* contourList) {
- SkOpContour* contour = contourList;
- do {
- contour->debugShowActiveSpans();
- } while ((contour = contour->next()));
-}
-#endif
-
bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) {
SkTDArray<SkOpContour* > list;
SkOpContour* contour = *contourList;
@@ -201,7 +214,7 @@ public:
void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
SkOpContourHead contour;
- SkOpGlobalState globalState(nullptr, &contour SkDEBUGPARAMS(false)
+ SkOpGlobalState globalState(&contour, &allocator SkDEBUGPARAMS(false)
SkDEBUGPARAMS(nullptr));
#if DEBUG_SHOW_TEST_NAME
SkDebugf("</div>\n");
@@ -209,8 +222,8 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("%s\n", __FUNCTION__);
#endif
- SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
- builder.finish(&allocator);
+ SkOpEdgeBuilder builder(path, &contour, &globalState);
+ builder.finish();
SkTDArray<const SkOpContour* > runs; // indices of partial contours
const SkOpContour* eContour = builder.head();
do {
@@ -391,40 +404,18 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
#endif
}
-static void align(SkOpContourHead* contourList) {
- SkOpContour* contour = contourList;
- do {
- contour->align();
- } while ((contour = contour->next()));
-}
-
-static void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
- SkOpContour* contour = contourList;
- do {
- contour->addAlignIntersections(contourList, allocator);
- } while ((contour = contour->next()));
-}
-
-static void calcAngles(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
- SkOpContour* contour = contourList;
- do {
- contour->calcAngles(allocator);
- } while ((contour = contour->next()));
-}
-
-static void findCollapsed(SkOpContourHead* contourList) {
+static void calcAngles(SkOpContourHead* contourList) {
SkOpContour* contour = contourList;
do {
- contour->findCollapsed();
+ contour->calcAngles();
} while ((contour = contour->next()));
}
-static bool missingCoincidence(SkOpContourHead* contourList,
- SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
+static bool missingCoincidence(SkOpContourHead* contourList) {
SkOpContour* contour = contourList;
bool result = false;
do {
- result |= contour->missingCoincidence(coincidence, allocator);
+ result |= contour->missingCoincidence();
} while ((contour = contour->next()));
return result;
}
@@ -453,72 +444,116 @@ static void sortAngles(SkOpContourHead* contourList) {
} while ((contour = contour->next()));
}
-bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence,
- SkChunkAlloc* allocator) {
+bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence) {
SkOpGlobalState* globalState = contourList->globalState();
- // combine t values when multiple intersections occur on some segments but not others
DEBUG_COINCIDENCE_HEALTH(contourList, "start");
+#if DEBUG_VALIDATE
+ globalState->setPhase(SkOpGlobalState::kIntersecting);
+#endif
+
+ // match up points within the coincident runs
+ if (!coincidence->addExpanded()) {
+ return false;
+ }
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded");
+#if DEBUG_VALIDATE
+ globalState->setPhase(SkOpGlobalState::kWalking);
+#endif
+ // combine t values when multiple intersections occur on some segments but not others
if (!moveMultiples(contourList)) {
return false;
}
DEBUG_COINCIDENCE_HEALTH(contourList, "moveMultiples");
- findCollapsed(contourList);
- DEBUG_COINCIDENCE_HEALTH(contourList, "findCollapsed");
// move t values and points together to eliminate small/tiny gaps
- moveNearby(contourList);
+ (void) moveNearby(contourList);
DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby");
- align(contourList); // give all span members common values
- DEBUG_COINCIDENCE_HEALTH(contourList, "align");
- 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);
#endif
- // look for intersections on line segments formed by moving end points
- addAlignIntersections(contourList, allocator);
- DEBUG_COINCIDENCE_HEALTH(contourList, "addAlignIntersections");
- if (coincidence->addMissing(allocator)) {
- DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing");
- moveNearby(contourList);
- DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby2");
- align(contourList); // give all span members common values
- DEBUG_COINCIDENCE_HEALTH(contourList, "align2");
- if (!coincidence->fixAligned()) { // aligning may have marked a coincidence pt-t deleted
+ // add coincidence formed by pairing on curve points and endpoints
+ coincidence->correctEnds();
+ if (!coincidence->addEndMovedSpans()) {
+ return false;
+ }
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addEndMovedSpans");
+
+ const int SAFETY_COUNT = 100; // FIXME: tune
+ int safetyHatch = SAFETY_COUNT;
+ // look for coincidence present in A-B and A-C but missing in B-C
+ while (coincidence->addMissing()) {
+ if (!--safetyHatch) {
+ SkASSERT(0); // FIXME: take this out after verifying std tests don't trigger
return false;
}
- DEBUG_COINCIDENCE_HEALTH(contourList, "fixAligned2");
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing");
+ moveNearby(contourList);
+ DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby");
}
-#if DEBUG_VALIDATE
- globalState->setPhase(SkOpGlobalState::kWalking);
-#endif
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing2");
+ // FIXME: only call this if addMissing modified something when returning false
+ moveNearby(contourList);
+ DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby2");
// check to see if, loosely, coincident ranges may be expanded
if (coincidence->expand()) {
DEBUG_COINCIDENCE_HEALTH(contourList, "expand1");
- if (!coincidence->addExpanded(allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(globalState))) {
+ coincidence->addMissing();
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing2");
+ if (!coincidence->addExpanded()) {
+ return false;
+ }
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded2");
+ if (!moveMultiples(contourList)) {
return false;
}
+ DEBUG_COINCIDENCE_HEALTH(contourList, "moveMultiples2");
+ moveNearby(contourList);
}
+#if DEBUG_VALIDATE
+ globalState->setPhase(SkOpGlobalState::kWalking);
+#endif
DEBUG_COINCIDENCE_HEALTH(contourList, "expand2");
// the expanded ranges may not align -- add the missing spans
+ SkAssertResult(coincidence->addExpanded());
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded3");
+ coincidence->correctEnds();
if (!coincidence->mark()) { // mark spans of coincident segments as coincident
return false;
}
DEBUG_COINCIDENCE_HEALTH(contourList, "mark1");
- // look for coincidence missed earlier
- if (missingCoincidence(contourList, coincidence, allocator)) {
+ // look for coincidence lines and curves undetected by intersection
+ if (missingCoincidence(contourList)) {
+#if DEBUG_VALIDATE
+ globalState->setPhase(SkOpGlobalState::kIntersecting);
+#endif
DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence1");
(void) coincidence->expand();
DEBUG_COINCIDENCE_HEALTH(contourList, "expand3");
- if (!coincidence->addExpanded(allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(globalState))) {
+ if (!coincidence->addExpanded()) {
return false;
}
- DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded2");
- coincidence->mark();
+#if DEBUG_VALIDATE
+ globalState->setPhase(SkOpGlobalState::kWalking);
+#endif
+ DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded3");
+ if (!coincidence->mark()) {
+ return false;
+ }
+ } else {
+ DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence2");
+ (void) coincidence->expand();
}
- DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence2");
- SkOpCoincidence overlaps;
+ DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence3");
+
+ (void) coincidence->expand();
+
+#if 0 // under development
+ // coincident runs may cross two or more spans, but the opposite spans may be out of order
+ if (!coincidence->reorder()) {
+ return false;
+ }
+#endif
+ DEBUG_COINCIDENCE_HEALTH(contourList, "coincidence.reorder");
+ SkOpCoincidence overlaps(globalState);
do {
SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
if (!pairs->apply()) { // adjust the winding value to account for coincident edges
@@ -527,22 +562,25 @@ bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidenc
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
- if (!pairs->findOverlaps(&overlaps, allocator)) {
+ if (!pairs->findOverlaps(&overlaps)) {
return false;
}
DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->findOverlaps");
} while (!overlaps.isEmpty());
- calcAngles(contourList, allocator);
+ calcAngles(contourList);
sortAngles(contourList);
if (globalState->angleCoincidence()) {
- (void) missingCoincidence(contourList, coincidence, allocator);
+ (void) missingCoincidence(contourList);
if (!coincidence->apply()) {
return false;
}
}
-#if DEBUG_ACTIVE_SPANS
+#if DEBUG_COINCIDENCE_VERBOSE
coincidence->debugShowCoincidence();
- DebugShowActiveSpans(contourList);
#endif
+#if DEBUG_COINCIDENCE
+ coincidence->debugValidate();
+#endif
+ SkPathOpsDebug::ShowActiveSpans(contourList);
return true;
}
diff --git a/src/pathops/SkPathOpsCommon.h b/src/pathops/SkPathOpsCommon.h
index 5b4902f53b..ed80318160 100644
--- a/src/pathops/SkPathOpsCommon.h
+++ b/src/pathops/SkPathOpsCommon.h
@@ -24,12 +24,11 @@ SkOpSegment* FindUndone(SkOpContourHead* , SkOpSpanBase** startPtr,
SkOpSpanBase** endPtr);
bool FixWinding(SkPath* path);
bool SortContourList(SkOpContourHead** , bool evenOdd, bool oppEvenOdd);
-bool HandleCoincidence(SkOpContourHead* , SkOpCoincidence* , SkChunkAlloc* );
+bool HandleCoincidence(SkOpContourHead* , SkOpCoincidence* );
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* );
-#endif
+SkScalar ScaleFactor(const SkPath& path);
+void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled);
#endif
diff --git a/src/pathops/SkPathOpsCubic.cpp b/src/pathops/SkPathOpsCubic.cpp
index a161e368e7..7fd3dd235d 100644
--- a/src/pathops/SkPathOpsCubic.cpp
+++ b/src/pathops/SkPathOpsCubic.cpp
@@ -277,7 +277,7 @@ bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
for (int index = 0; index < roots; ++index) {
if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
*t = maxCurvature[index];
- return true;
+ return *t > 0 && *t < 1;
}
}
} else if (infTCount == 1) {
diff --git a/src/pathops/SkPathOpsCurve.cpp b/src/pathops/SkPathOpsCurve.cpp
index bf44d25e17..df67efaca5 100644
--- a/src/pathops/SkPathOpsCurve.cpp
+++ b/src/pathops/SkPathOpsCurve.cpp
@@ -8,6 +8,57 @@
#include "SkPathOpsRect.h"
#include "SkPathOpsCurve.h"
+ // this cheats and assumes that the perpendicular to the point is the closest ray to the curve
+ // this case (where the line and the curve are nearly coincident) may be the only case that counts
+double SkDCurve::nearPoint(SkPath::Verb verb, const SkDPoint& xy, const SkDPoint& opp) const {
+ int count = SkPathOpsVerbToPoints(verb);
+ double minX = fCubic.fPts[0].fX;
+ double maxX = minX;
+ for (int index = 0; index < count; ++index) {
+ minX = SkTMin(minX, fCubic.fPts[index].fX);
+ maxX = SkTMax(maxX, fCubic.fPts[index].fX);
+ }
+ if (!AlmostBetweenUlps(minX, xy.fX, maxX)) {
+ return -1;
+ }
+ double minY = fCubic.fPts[0].fY;
+ double maxY = minY;
+ for (int index = 0; index < count; ++index) {
+ minY = SkTMin(minY, fCubic.fPts[index].fY);
+ maxY = SkTMax(maxY, fCubic.fPts[index].fY);
+ }
+ if (!AlmostBetweenUlps(minY, xy.fY, maxY)) {
+ return -1;
+ }
+ SkIntersections i;
+ SkDLine perp = {{ xy, { xy.fX + opp.fY - xy.fY, xy.fY + xy.fX - opp.fX }}};
+ (*CurveDIntersectRay[verb])(*this, perp, &i);
+ int minIndex = -1;
+ double minDist = FLT_MAX;
+ for (int index = 0; index < i.used(); ++index) {
+ double dist = xy.distance(i.pt(index));
+ if (minDist > dist) {
+ minDist = dist;
+ minIndex = index;
+ }
+ }
+ if (minIndex < 0) {
+ return -1;
+ }
+ double largest = SkTMax(SkTMax(maxX, maxY), -SkTMin(minX, minY));
+ if (!AlmostEqualUlps_Pin(largest, largest + minDist)) { // is distance within ULPS tolerance?
+ return -1;
+ }
+ return SkPinT(i[0][minIndex]);
+}
+
+void SkDCurve::offset(SkPath::Verb verb, const SkDVector& off) {
+ int count = SkPathOpsVerbToPoints(verb);
+ for (int index = 0; index < count; ++index) {
+ fCubic.fPts[index] += off;
+ }
+}
+
void SkDCurve::setConicBounds(const SkPoint curve[3], SkScalar curveWeight,
double tStart, double tEnd, SkPathOpsBounds* bounds) {
SkDConic dCurve;
@@ -18,7 +69,7 @@ void SkDCurve::setConicBounds(const SkPoint curve[3], SkScalar curveWeight,
SkDoubleToScalar(dRect.fRight), SkDoubleToScalar(dRect.fBottom));
}
-void SkDCurve::setCubicBounds(const SkPoint curve[4], SkScalar ,
+void SkDCurve::setCubicBounds(const SkPoint curve[4], SkScalar ,
double tStart, double tEnd, SkPathOpsBounds* bounds) {
SkDCubic dCurve;
dCurve.set(curve);
diff --git a/src/pathops/SkPathOpsCurve.h b/src/pathops/SkPathOpsCurve.h
index 97e20be2b1..dc9cec97e4 100644
--- a/src/pathops/SkPathOpsCurve.h
+++ b/src/pathops/SkPathOpsCurve.h
@@ -64,14 +64,17 @@ struct SkDCurve {
return fCubic[n];
}
- SkDPoint conicTop(const SkPoint curve[3], SkScalar curveWeight,
+ SkDPoint conicTop(const SkPoint curve[3], SkScalar curveWeight,
double s, double e, double* topT);
SkDPoint cubicTop(const SkPoint curve[4], SkScalar , double s, double e, double* topT);
+ void dump() const;
void dumpID(int ) const;
SkDPoint lineTop(const SkPoint[2], SkScalar , double , double , double* topT);
+ double nearPoint(SkPath::Verb verb, const SkDPoint& xy, const SkDPoint& opp) const;
+ void offset(SkPath::Verb verb, const SkDVector& );
SkDPoint quadTop(const SkPoint curve[3], SkScalar , double s, double e, double* topT);
- void setConicBounds(const SkPoint curve[3], SkScalar curveWeight,
+ void setConicBounds(const SkPoint curve[3], SkScalar curveWeight,
double s, double e, SkPathOpsBounds* );
void setCubicBounds(const SkPoint curve[4], SkScalar ,
double s, double e, SkPathOpsBounds* );
@@ -115,6 +118,30 @@ static SkDPoint (* const CurveDPointAtT[])(const SkPoint[], SkScalar , double )
dcubic_xy_at_t
};
+static SkDPoint ddline_xy_at_t(const SkDCurve& c, double t) {
+ return c.fLine.ptAtT(t);
+}
+
+static SkDPoint ddquad_xy_at_t(const SkDCurve& c, double t) {
+ return c.fQuad.ptAtT(t);
+}
+
+static SkDPoint ddconic_xy_at_t(const SkDCurve& c, double t) {
+ return c.fConic.ptAtT(t);
+}
+
+static SkDPoint ddcubic_xy_at_t(const SkDCurve& c, double t) {
+ return c.fCubic.ptAtT(t);
+}
+
+static SkDPoint (* const CurveDDPointAtT[])(const SkDCurve& , double ) = {
+ nullptr,
+ ddline_xy_at_t,
+ ddquad_xy_at_t,
+ ddconic_xy_at_t,
+ ddcubic_xy_at_t
+};
+
static SkPoint fline_xy_at_t(const SkPoint a[2], SkScalar weight, double t) {
return dline_xy_at_t(a, weight, t).asSkPoint();
}
@@ -171,6 +198,30 @@ static SkDVector (* const CurveDSlopeAtT[])(const SkPoint[], SkScalar , double )
dcubic_dxdy_at_t
};
+static SkDVector ddline_dxdy_at_t(const SkDCurve& c, double ) {
+ return c.fLine.fPts[1] - c.fLine.fPts[0];
+}
+
+static SkDVector ddquad_dxdy_at_t(const SkDCurve& c, double t) {
+ return c.fQuad.dxdyAtT(t);
+}
+
+static SkDVector ddconic_dxdy_at_t(const SkDCurve& c, double t) {
+ return c.fConic.dxdyAtT(t);
+}
+
+static SkDVector ddcubic_dxdy_at_t(const SkDCurve& c, double t) {
+ return c.fCubic.dxdyAtT(t);
+}
+
+static SkDVector (* const CurveDDSlopeAtT[])(const SkDCurve& , double ) = {
+ nullptr,
+ ddline_dxdy_at_t,
+ ddquad_dxdy_at_t,
+ ddconic_dxdy_at_t,
+ ddcubic_dxdy_at_t
+};
+
static SkVector fline_dxdy_at_t(const SkPoint a[2], SkScalar , double ) {
return a[1] - a[0];
}
@@ -269,6 +320,30 @@ static void (* const CurveIntersectRay[])(const SkPoint[] , SkScalar , const SkD
cubic_intersect_ray
};
+static void dline_intersect_ray(const SkDCurve& c, const SkDLine& ray, SkIntersections* i) {
+ i->intersectRay(c.fLine, ray);
+}
+
+static void dquad_intersect_ray(const SkDCurve& c, const SkDLine& ray, SkIntersections* i) {
+ i->intersectRay(c.fQuad, ray);
+}
+
+static void dconic_intersect_ray(const SkDCurve& c, const SkDLine& ray, SkIntersections* i) {
+ i->intersectRay(c.fConic, ray);
+}
+
+static void dcubic_intersect_ray(const SkDCurve& c, const SkDLine& ray, SkIntersections* i) {
+ i->intersectRay(c.fCubic, ray);
+}
+
+static void (* const CurveDIntersectRay[])(const SkDCurve& , const SkDLine& , SkIntersections* ) = {
+ nullptr,
+ dline_intersect_ray,
+ dquad_intersect_ray,
+ dconic_intersect_ray,
+ dcubic_intersect_ray
+};
+
static int line_intercept_h(const SkPoint a[2], SkScalar , SkScalar y, double* roots) {
SkDLine line;
roots[0] = SkIntersections::HorizontalIntercept(line.set(a), y);
diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp
index 546771e84c..93743f3d6c 100644
--- a/src/pathops/SkPathOpsDebug.cpp
+++ b/src/pathops/SkPathOpsDebug.cpp
@@ -12,7 +12,7 @@
#include "SkPathOpsDebug.h"
#include "SkString.h"
-struct SkCoincidentSpans;
+class SkCoincidentSpans;
#if DEBUG_VALIDATE
extern bool FLAGS_runFail;
@@ -46,11 +46,15 @@ bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
}
#endif
-#if DEBUG_COINCIDENCE
+#if DEBUG_COINCIDENCE_VERBOSE
enum GlitchType {
kAddCorruptCoin_Glitch,
kAddExpandedCoin_Glitch,
+ kAddExpandedFail_Glitch,
+ kAddIfMissingCoin_Glitch,
kAddMissingCoin_Glitch,
+ kAddMissingExtend_Glitch,
+ kAddOrOverlap_Glitch,
kCollapsedCoin_Glitch,
kCollapsedDone_Glitch,
kCollapsedOppValue_Glitch,
@@ -60,30 +64,41 @@ enum GlitchType {
kExpandCoin_Glitch,
kMarkCoinEnd_Glitch,
kMarkCoinInsert_Glitch,
+ kMarkCoinMissing_Glitch,
+ kMarkCoinStart_Glitch,
+ kMergeContained_Glitch,
kMissingCoin_Glitch,
kMissingDone_Glitch,
kMissingIntersection_Glitch,
kMoveMultiple_Glitch,
+ kMoveNearbyClearAll_Glitch,
+ kMoveNearbyClearAll2_Glitch,
+ kMoveNearbyMerge_Glitch,
+ kMoveNearbyMergeFinal_Glitch,
+ kMoveNearbyRelease_Glitch,
+ kMoveNearbyReleaseFinal_Glitch,
+ kReleasedSpan_Glitch,
kUnaligned_Glitch,
kUnalignedHead_Glitch,
kUnalignedTail_Glitch,
- kUndetachedSpan_Glitch,
- kUnmergedSpan_Glitch,
};
-static const int kGlitchType_Count = kUnmergedSpan_Glitch + 1;
+static const int kGlitchType_Count = kUnalignedTail_Glitch + 1;
struct SpanGlitch {
const char* fStage;
const SkOpSpanBase* fBase;
const SkOpSpanBase* fSuspect;
- const SkCoincidentSpans* fCoin;
const SkOpSegment* fSegment;
+ const SkOpSegment* fOppSegment;
const SkOpPtT* fCoinSpan;
const SkOpPtT* fEndSpan;
const SkOpPtT* fOppSpan;
const SkOpPtT* fOppEndSpan;
- double fT;
+ double fStartT;
+ double fEndT;
+ double fOppStartT;
+ double fOppEndT;
SkPoint fPt;
GlitchType fType;
};
@@ -94,13 +109,16 @@ struct SkPathOpsDebug::GlitchLog {
glitch->fStage = stage;
glitch->fBase = nullptr;
glitch->fSuspect = nullptr;
- glitch->fCoin = nullptr;
glitch->fSegment = nullptr;
+ glitch->fOppSegment = nullptr;
glitch->fCoinSpan = nullptr;
glitch->fEndSpan = nullptr;
glitch->fOppSpan = nullptr;
glitch->fOppEndSpan = nullptr;
- glitch->fT = SK_ScalarNaN;
+ glitch->fStartT = SK_ScalarNaN;
+ glitch->fEndT = SK_ScalarNaN;
+ glitch->fOppStartT = SK_ScalarNaN;
+ glitch->fOppEndT = SK_ScalarNaN;
glitch->fPt = { SK_ScalarNaN, SK_ScalarNaN };
glitch->fType = type;
return glitch;
@@ -113,11 +131,22 @@ struct SkPathOpsDebug::GlitchLog {
glitch->fSuspect = suspect;
}
+ void record(GlitchType type, const char* stage, const SkOpSpanBase* base,
+ const SkOpPtT* ptT) {
+ SpanGlitch* glitch = recordCommon(type, stage);
+ glitch->fBase = base;
+ glitch->fCoinSpan = ptT;
+ }
+
void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
- const SkOpPtT* coinSpan) {
+ const SkCoincidentSpans* opp = NULL) {
SpanGlitch* glitch = recordCommon(type, stage);
- glitch->fCoin = coin;
- glitch->fCoinSpan = coinSpan;
+ glitch->fCoinSpan = coin->coinPtTStart();
+ glitch->fEndSpan = coin->coinPtTEnd();
+ if (opp) {
+ glitch->fOppSpan = opp->coinPtTStart();
+ glitch->fOppEndSpan = opp->coinPtTEnd();
+ }
}
void record(GlitchType type, const char* stage, const SkOpSpanBase* base,
@@ -125,7 +154,7 @@ struct SkPathOpsDebug::GlitchLog {
SpanGlitch* glitch = recordCommon(type, stage);
glitch->fBase = base;
glitch->fSegment = seg;
- glitch->fT = t;
+ glitch->fStartT = t;
glitch->fPt = pt;
}
@@ -133,23 +162,26 @@ struct SkPathOpsDebug::GlitchLog {
SkPoint pt) {
SpanGlitch* glitch = recordCommon(type, stage);
glitch->fBase = base;
- glitch->fT = t;
+ glitch->fStartT = t;
glitch->fPt = pt;
}
void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
const SkOpPtT* coinSpan, const SkOpPtT* endSpan) {
SpanGlitch* glitch = recordCommon(type, stage);
- glitch->fCoin = coin;
- glitch->fCoinSpan = coinSpan;
+ glitch->fCoinSpan = coin->coinPtTStart();
+ glitch->fEndSpan = coin->coinPtTEnd();
glitch->fEndSpan = endSpan;
+ glitch->fOppSpan = coinSpan;
+ glitch->fOppEndSpan = endSpan;
}
void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
- const SkOpSpanBase* suspect) {
+ const SkOpSpanBase* base) {
SpanGlitch* glitch = recordCommon(type, stage);
- glitch->fSuspect = suspect;
- glitch->fCoin = coin;
+ glitch->fBase = base;
+ glitch->fCoinSpan = coin->coinPtTStart();
+ glitch->fEndSpan = coin->coinPtTEnd();
}
void record(GlitchType type, const char* stage, const SkOpPtT* ptTS, const SkOpPtT* ptTE,
@@ -161,22 +193,73 @@ struct SkPathOpsDebug::GlitchLog {
glitch->fOppEndSpan = oPtTE;
}
+ void record(GlitchType type, const char* stage, const SkOpSegment* seg, double startT,
+ double endT, const SkOpSegment* oppSeg, double oppStartT, double oppEndT) {
+ SpanGlitch* glitch = recordCommon(type, stage);
+ glitch->fSegment = seg;
+ glitch->fStartT = startT;
+ glitch->fEndT = endT;
+ glitch->fOppSegment = oppSeg;
+ glitch->fOppStartT = oppStartT;
+ glitch->fOppEndT = oppEndT;
+ }
+
+ void record(GlitchType type, const char* stage, const SkOpSegment* seg,
+ const SkOpSpan* span) {
+ SpanGlitch* glitch = recordCommon(type, stage);
+ glitch->fSegment = seg;
+ glitch->fBase = span;
+ }
+
+ void record(GlitchType type, const char* stage, double t, const SkOpSpanBase* span) {
+ SpanGlitch* glitch = recordCommon(type, stage);
+ glitch->fStartT = t;
+ glitch->fBase = span;
+ }
+
+ void record(GlitchType type, const char* stage, const SkOpSegment* seg) {
+ SpanGlitch* glitch = recordCommon(type, stage);
+ glitch->fSegment = seg;
+ }
+
+ void record(GlitchType type, const char* stage, const SkCoincidentSpans* coin,
+ const SkOpPtT* ptT) {
+ SpanGlitch* glitch = recordCommon(type, stage);
+ glitch->fCoinSpan = coin->coinPtTStart();
+ glitch->fEndSpan = ptT;
+ }
+
SkTDArray<SpanGlitch> fGlitches;
};
+#endif
+void SkPathOpsDebug::ShowActiveSpans(SkOpContourHead* contourList) {
+#if DEBUG_ACTIVE_SPANS
+ SkOpContour* contour = contourList;
+ do {
+ contour->debugShowActiveSpans();
+ } while ((contour = contour->next()));
+#endif
+}
+
+#if DEBUG_COINCIDENCE
void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList, const char* id) {
+ contourList->globalState()->debugSetCheckHealth(true);
+#if DEBUG_COINCIDENCE_VERBOSE
GlitchLog glitches;
const SkOpContour* contour = contourList;
const SkOpCoincidence* coincidence = contour->globalState()->coincidence();
+ coincidence->debugCheckValid(id, &glitches); // don't call validate; spans may be inconsistent
do {
contour->debugCheckHealth(id, &glitches);
- contour->debugMissingCoincidence(id, &glitches, coincidence);
+ contour->debugMissingCoincidence(id, &glitches);
} while ((contour = contour->next()));
- coincidence->debugFixAligned(id, &glitches);
+ coincidence->debugRemoveCollapsed(id, &glitches);
coincidence->debugAddMissing(id, &glitches);
coincidence->debugExpand(id, &glitches);
coincidence->debugAddExpanded(id, &glitches);
coincidence->debugMark(id, &glitches);
+ coincidence->debugReorder(id, &glitches);
unsigned mask = 0;
for (int index = 0; index < glitches.fGlitches.count(); ++index) {
const SpanGlitch& glitch = glitches.fGlitches[index];
@@ -186,6 +269,92 @@ void SkPathOpsDebug::CheckHealth(SkOpContourHead* contourList, const char* id) {
SkDebugf(mask & (1 << index) ? "x" : "-");
}
SkDebugf(" %s\n", id);
+ for (int index = 0; index < glitches.fGlitches.count(); ++index) {
+ const SpanGlitch& glitch = glitches.fGlitches[index];
+ SkDebugf("%02d: ", index);
+ if (glitch.fBase) {
+ SkDebugf(" base=%d", glitch.fBase->debugID());
+ }
+ if (glitch.fSuspect) {
+ SkDebugf(" base=%d", glitch.fSuspect->debugID());
+ }
+ if (glitch.fSegment) {
+ SkDebugf(" segment=%d", glitch.fSegment->debugID());
+ }
+ if (glitch.fCoinSpan) {
+ SkDebugf(" coinSpan=%d", glitch.fCoinSpan->debugID());
+ }
+ if (glitch.fEndSpan) {
+ SkDebugf(" endSpan=%d", glitch.fEndSpan->debugID());
+ }
+ if (glitch.fOppSpan) {
+ SkDebugf(" oppSpan=%d", glitch.fOppSpan->debugID());
+ }
+ if (glitch.fOppEndSpan) {
+ SkDebugf(" oppEndSpan=%d", glitch.fOppEndSpan->debugID());
+ }
+ if (!SkScalarIsNaN(glitch.fStartT)) {
+ SkDebugf(" startT=%g", glitch.fStartT);
+ }
+ if (!SkScalarIsNaN(glitch.fEndT)) {
+ SkDebugf(" endT=%g", glitch.fEndT);
+ }
+ if (glitch.fOppSegment) {
+ SkDebugf(" segment=%d", glitch.fOppSegment->debugID());
+ }
+ if (!SkScalarIsNaN(glitch.fOppStartT)) {
+ SkDebugf(" oppStartT=%g", glitch.fOppStartT);
+ }
+ if (!SkScalarIsNaN(glitch.fOppEndT)) {
+ SkDebugf(" oppEndT=%g", glitch.fOppEndT);
+ }
+ if (!SkScalarIsNaN(glitch.fPt.fX) || !SkScalarIsNaN(glitch.fPt.fY)) {
+ SkDebugf(" pt=%g,%g", glitch.fPt.fX, glitch.fPt.fY);
+ }
+ switch (glitch.fType) {
+ case kAddCorruptCoin_Glitch: SkDebugf(" AddCorruptCoin"); break;
+ case kAddExpandedCoin_Glitch: SkDebugf(" AddExpandedCoin"); break;
+ case kAddExpandedFail_Glitch: SkDebugf(" AddExpandedFail"); break;
+ case kAddIfMissingCoin_Glitch: SkDebugf(" AddIfMissingCoin"); break;
+ case kAddMissingCoin_Glitch: SkDebugf(" AddMissingCoin"); break;
+ case kAddMissingExtend_Glitch: SkDebugf(" AddMissingExtend"); break;
+ case kAddOrOverlap_Glitch: SkDebugf(" AAddOrOverlap"); break;
+ case kCollapsedCoin_Glitch: SkDebugf(" CollapsedCoin"); break;
+ case kCollapsedDone_Glitch: SkDebugf(" CollapsedDone"); break;
+ case kCollapsedOppValue_Glitch: SkDebugf(" CollapsedOppValue"); break;
+ case kCollapsedSpan_Glitch: SkDebugf(" CollapsedSpan"); break;
+ case kCollapsedWindValue_Glitch: SkDebugf(" CollapsedWindValue"); break;
+ case kDeletedCoin_Glitch: SkDebugf(" DeletedCoin"); break;
+ case kExpandCoin_Glitch: SkDebugf(" ExpandCoin"); break;
+ case kMarkCoinEnd_Glitch: SkDebugf(" MarkCoinEnd"); break;
+ case kMarkCoinInsert_Glitch: SkDebugf(" MarkCoinInsert"); break;
+ case kMarkCoinMissing_Glitch: SkDebugf(" MarkCoinMissing"); break;
+ case kMarkCoinStart_Glitch: SkDebugf(" MarkCoinStart"); break;
+ case kMergeContained_Glitch: SkDebugf(" MergeContained"); break;
+ case kMissingCoin_Glitch: SkDebugf(" MissingCoin"); break;
+ case kMissingDone_Glitch: SkDebugf(" MissingDone"); break;
+ case kMissingIntersection_Glitch: SkDebugf(" MissingIntersection"); break;
+ case kMoveMultiple_Glitch: SkDebugf(" MoveMultiple"); break;
+ case kMoveNearbyClearAll_Glitch: SkDebugf(" MoveNearbyClearAll"); break;
+ case kMoveNearbyClearAll2_Glitch: SkDebugf(" MoveNearbyClearAll2"); break;
+ case kMoveNearbyMerge_Glitch: SkDebugf(" MoveNearbyMerge"); break;
+ case kMoveNearbyMergeFinal_Glitch: SkDebugf(" MoveNearbyMergeFinal"); break;
+ case kMoveNearbyRelease_Glitch: SkDebugf(" MoveNearbyRelease"); break;
+ case kMoveNearbyReleaseFinal_Glitch: SkDebugf(" MoveNearbyReleaseFinal"); break;
+ case kReleasedSpan_Glitch: SkDebugf(" ReleasedSpan"); break;
+ case kUnaligned_Glitch: SkDebugf(" Unaligned"); break;
+ case kUnalignedHead_Glitch: SkDebugf(" UnalignedHead"); break;
+ case kUnalignedTail_Glitch: SkDebugf(" UnalignedTail"); break;
+ default: SkASSERT(0);
+ }
+ SkDebugf("\n");
+ }
+ contourList->globalState()->debugSetCheckHealth(false);
+#if DEBUG_ACTIVE_SPANS
+ SkDebugf("active after %s:\n", id);
+ ShowActiveSpans(contourList);
+#endif
+#endif
}
#endif
@@ -254,7 +423,7 @@ static const char* gOpStrs[] = {
"kDifference_SkPathOp",
"kIntersect_SkPathOp",
"kUnion_SkPathOp",
- "kXor_PathOp",
+ "kXOR_PathOp",
"kReverseDifference_SkPathOp",
};
@@ -412,132 +581,72 @@ void SkDRect::debugInit() {
#include "SkOpSegment.h"
#if DEBUG_COINCIDENCE
-void SkOpSegment::debugAddAlignIntersection(const char* id, SkPathOpsDebug::GlitchLog* log,
- const SkOpPtT& endPtT, const SkPoint& oldPt, const SkOpContourHead* contourList) const {
- const SkPoint& newPt = endPtT.fPt;
- if (newPt == oldPt) {
- return;
- }
- SkPoint line[2] = { newPt, oldPt };
- SkPathOpsBounds lineBounds;
- lineBounds.setBounds(line, 2);
- SkDLine aLine;
- aLine.set(line);
- const SkOpContour* current = contourList;
+// commented-out lines keep this in sync with addT()
+ const SkOpPtT* SkOpSegment::debugAddT(double t, AliasMatch allowAlias, bool* allocated) const {
+ debugValidate();
+ SkPoint pt = this->ptAtT(t);
+ const SkOpSpanBase* span = &fHead;
do {
- if (!SkPathOpsBounds::Intersects(current->bounds(), lineBounds)) {
- continue;
- }
- const SkOpSegment* segment = current->first();
- do {
- if (!SkPathOpsBounds::Intersects(segment->bounds(), lineBounds)) {
- continue;
- }
- if (newPt == segment->fPts[0]) {
- continue;
- }
- if (newPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
- continue;
- }
- if (oldPt == segment->fPts[0]) {
- continue;
- }
- if (oldPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
- continue;
- }
- if (endPtT.debugContains(segment)) {
- continue;
- }
- SkIntersections i;
- switch (segment->fVerb) {
- case SkPath::kLine_Verb: {
- SkDLine bLine;
- bLine.set(segment->fPts);
- i.intersect(bLine, aLine);
- } break;
- case SkPath::kQuad_Verb: {
- SkDQuad bQuad;
- bQuad.set(segment->fPts);
- i.intersect(bQuad, aLine);
- } break;
- case SkPath::kConic_Verb: {
- SkDConic bConic;
- bConic.set(segment->fPts, segment->fWeight);
- i.intersect(bConic, aLine);
- } break;
- case SkPath::kCubic_Verb: {
- SkDCubic bCubic;
- bCubic.set(segment->fPts);
- i.intersect(bCubic, aLine);
- } break;
- default:
- SkASSERT(0);
- }
- if (i.used()) {
- SkASSERT(i.used() == 1);
- SkASSERT(!zero_or_one(i[0][0]));
- SkOpSpanBase* checkSpan = fHead.next();
- while (!checkSpan->final()) {
- if (checkSpan->contains(segment)) {
- goto nextSegment;
- }
- checkSpan = checkSpan->upCast()->next();
+ const SkOpPtT* result = span->ptT();
+ const SkOpPtT* loop;
+ bool duplicatePt;
+ if (t == result->fT) {
+ goto bumpSpan;
+ }
+ if (this->match(result, this, t, pt, allowAlias)) {
+ // see if any existing alias matches segment, pt, and t
+ loop = result->next();
+ duplicatePt = false;
+ while (loop != result) {
+ bool ptMatch = loop->fPt == pt;
+ if (loop->segment() == this && loop->fT == t && ptMatch) {
+ goto bumpSpan;
}
- log->record(kMissingIntersection_Glitch, id, checkSpan, segment, i[0][0], newPt);
+ duplicatePt |= ptMatch;
+ loop = loop->next();
}
- nextSegment:
- ;
- } while ((segment = segment->next()));
- } while ((current = current->next()));
-}
-
-bool SkOpSegment::debugAddMissing(double t, const SkOpSegment* opp) const {
- const SkOpSpanBase* existing = nullptr;
- const SkOpSpanBase* test = &fHead;
- double testT;
- do {
- if ((testT = test->ptT()->fT) >= t) {
- if (testT == t) {
- existing = test;
+ if (kNoAliasMatch == allowAlias) {
+ bumpSpan:
+// span->bumpSpanAdds();
+ return result;
}
- break;
- }
- } while ((test = test->upCast()->next()));
- return !existing || !existing->debugContains(opp);
-}
-
-void SkOpSegment::debugAlign(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
- const SkOpSpanBase* span = &fHead;
- if (!span->aligned()) {
- if (!span->debugAlignedEnd(0, fPts[0])) {
- glitches->record(kUnalignedHead_Glitch, id, span);
- }
- }
- while ((span = span->upCast()->next())) {
- if (span == &fTail) {
- break;
- }
- if (!span->aligned()) {
- glitches->record(kUnaligned_Glitch, id, span);
- }
- }
- if (!span->aligned()) {
- span->debugAlignedEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
- }
- if (this->collapsed()) {
- const SkOpSpan* span = &fHead;
- do {
- if (span->windValue()) {
- glitches->record(kCollapsedWindValue_Glitch, id, span);
+// SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator);
+// alias->init(result->span(), t, pt, duplicatePt);
+// result->insert(alias);
+// result->span()->unaligned();
+ this->debugValidate();
+// #if DEBUG_ADD_T
+// SkDebugf("%s alias t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
+// alias->segment()->debugID(), alias->span()->debugID());
+// #endif
+// span->bumpSpanAdds();
+ if (allocated) {
+ *allocated = true;
}
- if (span->oppValue()) {
- glitches->record(kCollapsedOppValue_Glitch, id, span);
+ return nullptr;
+ }
+ if (t < result->fT) {
+ const SkOpSpan* prev = result->span()->prev();
+ if (!prev) {
+ return nullptr; // FIXME: this is a fail case; nullptr return elsewhere means result was allocated in non-const version
}
- if (!span->done()) {
- glitches->record(kCollapsedDone_Glitch, id, span);
+// SkOpSpan* span = insert(prev, allocator);
+// span->init(this, prev, t, pt);
+ this->debugValidate();
+// #if DEBUG_ADD_T
+// SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
+// span->segment()->debugID(), span->debugID());
+// #endif
+// span->bumpSpanAdds();
+ if (allocated) {
+ *allocated = true;
}
- } while ((span = span->next()->upCastable()));
- }
+ return nullptr;
+ }
+ SkASSERT(span != &fTail);
+ } while ((span = span->upCast()->next()));
+ SkASSERT(0);
+ return nullptr;
}
#endif
@@ -547,7 +656,7 @@ void SkOpSegment::debugCheckAngleCoin() const {
const SkOpSpan* span;
do {
const SkOpAngle* angle = base->fromAngle();
- if (angle && angle->fCheckCoincidence) {
+ if (angle && angle->debugCheckCoincidence()) {
angle->debugCheckNearCoincidence();
}
if (base->final()) {
@@ -555,41 +664,35 @@ void SkOpSegment::debugCheckAngleCoin() const {
}
span = base->upCast();
angle = span->toAngle();
- if (angle && angle->fCheckCoincidence) {
+ if (angle && angle->debugCheckCoincidence()) {
angle->debugCheckNearCoincidence();
}
} while ((base = span->next()));
}
#endif
-#if DEBUG_COINCIDENCE
+#if DEBUG_COINCIDENCE_VERBOSE
// this mimics the order of the checks in handle coincidence
void SkOpSegment::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
debugMoveMultiples(id, glitches);
- debugFindCollapsed(id, glitches);
debugMoveNearby(id, glitches);
- debugAlign(id, glitches);
- debugAddAlignIntersections(id, glitches, this->globalState()->contourHead());
+ debugMissingCoincidence(id, glitches);
+}
+// commented-out lines keep this in sync with clearAll()
+void SkOpSegment::debugClearAll(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
+ const SkOpSpan* span = &fHead;
+ do {
+ this->debugClearOne(span, id, glitches);
+ } while ((span = span->next()->upCastable()));
+ this->globalState()->coincidence()->debugRelease(id, glitches, this);
}
-void SkOpSegment::debugFindCollapsed(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
- if (fHead.contains(&fTail)) {
- const SkOpSpan* span = this->head();
- bool missingDone = false;
- do {
- missingDone |= !span->done();
- } while ((span = span->next()->upCastable()));
- if (missingDone) {
- glitches->record(kMissingDone_Glitch, id, &fHead);
- }
- if (!fHead.debugAlignedEnd(0, fHead.pt())) {
- glitches->record(kUnalignedHead_Glitch, id, &fHead);
- }
- if (!fTail.aligned()) {
- glitches->record(kUnalignedTail_Glitch, id, &fTail);
- }
- }
+// commented-out lines keep this in sync with clearOne()
+void SkOpSegment::debugClearOne(const SkOpSpan* span, const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
+ if (span->windValue()) glitches->record(kCollapsedWindValue_Glitch, id, span);
+ if (span->oppValue()) glitches->record(kCollapsedOppValue_Glitch, id, span);
+ if (!span->done()) glitches->record(kCollapsedDone_Glitch, id, span);
}
#endif
@@ -607,16 +710,37 @@ SkOpAngle* SkOpSegment::debugLastAngle() {
}
#if DEBUG_COINCIDENCE
-void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log,
- const SkOpCoincidence* coincidences) const {
- if (this->verb() != SkPath::kLine_Verb) {
- return;
- }
+// commented-out lines keep this in sync with ClearVisited
+void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span) {
+ // reset visited flag back to false
+ do {
+ const SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
+ while ((ptT = ptT->next()) != stopPtT) {
+ const SkOpSegment* opp = ptT->segment();
+ opp->resetDebugVisited();
+ }
+ } while (!span->final() && (span = span->upCast()->next()));
+}
+#endif
+
+#if DEBUG_COINCIDENCE_VERBOSE
+// commented-out lines keep this in sync with missingCoincidence()
+// look for pairs of undetected coincident curves
+// assumes that segments going in have visited flag clear
+// Even though pairs of curves correct detect coincident runs, a run may be missed
+// if the coincidence is a product of multiple intersections. For instance, given
+// curves A, B, and C:
+// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
+// the end of C that the intersection is replaced with the end of C.
+// Even though A-B correctly do not detect an intersection at point 2,
+// the resulting run from point 1 to point 2 is coincident on A and B.
+void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const {
if (this->done()) {
return;
}
const SkOpSpan* prior = nullptr;
const SkOpSpanBase* spanBase = &fHead;
+// bool result = false;
do {
const SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
SkASSERT(ptT->span() == spanBase);
@@ -624,29 +748,29 @@ void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch
if (ptT->deleted()) {
continue;
}
- SkOpSegment* opp = ptT->span()->segment();
-// if (opp->verb() == SkPath::kLine_Verb) {
-// continue;
-// }
+ const SkOpSegment* opp = ptT->span()->segment();
if (opp->done()) {
continue;
}
// when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
- if (!opp->visited()) {
+ if (!opp->debugVisited()) {
continue;
}
if (spanBase == &fHead) {
continue;
}
+ if (ptT->segment() == this) {
+ continue;
+ }
const SkOpSpan* span = spanBase->upCastable();
// FIXME?: this assumes that if the opposite segment is coincident then no more
// coincidence needs to be detected. This may not be true.
- if (span && span->segment() != opp && span->containsCoincidence(opp)) {
+ if (span && span->segment() != opp && span->containsCoincidence(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted
continue;
}
- if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) {
+ if (spanBase->segment() != opp && spanBase->containsCoinEnd(opp)) { // debug has additional condition since it may be called before inner duplicate points have been deleted
continue;
- }
+ }
const SkOpPtT* priorPtT = nullptr, * priorStopPtT;
// find prior span containing opp segment
const SkOpSegment* priorOpp = nullptr;
@@ -669,6 +793,9 @@ void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch
if (!priorOpp) {
continue;
}
+ if (priorPtT == ptT) {
+ continue;
+ }
const SkOpPtT* oppStart = prior->ptT();
const SkOpPtT* oppEnd = spanBase->ptT();
bool swapped = priorPtT->fT > ptT->fT;
@@ -676,22 +803,28 @@ void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch
SkTSwap(priorPtT, ptT);
SkTSwap(oppStart, oppEnd);
}
- bool flipped = oppStart->fT > oppEnd->fT;
- bool coincident = false;
- if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
+ const SkOpCoincidence* coincidence = this->globalState()->coincidence();
+ const SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
+ const SkOpPtT* rootPtT = ptT->span()->ptT();
+ const SkOpPtT* rootOppStart = oppStart->span()->ptT();
+ const SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
+ if (coincidence->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
goto swapBack;
}
- if (opp->verb() == SkPath::kLine_Verb) {
- coincident = (SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppStart->fPt) ||
- SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppEnd->fPt)) &&
- (SkDPoint::ApproximatelyEqual(ptT->fPt, oppStart->fPt) ||
- SkDPoint::ApproximatelyEqual(ptT->fPt, oppEnd->fPt));
- }
- if (!coincident) {
- coincident = testForCoincidence(priorPtT, ptT, prior, spanBase, opp, 5000);
- }
- if (coincident) {
+ if (testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
+ // mark coincidence
+#if DEBUG_COINCIDENCE
+// SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
+// rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
+// rootOppEnd->debugID());
+#endif
log->record(kMissingCoin_Glitch, id, priorPtT, ptT, oppStart, oppEnd);
+ // coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
+ // }
+#if DEBUG_COINCIDENCE
+// SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)
+#endif
+ // result = true;
}
swapBack:
if (swapped) {
@@ -699,9 +832,14 @@ void SkOpSegment::debugMissingCoincidence(const char* id, SkPathOpsDebug::Glitch
}
}
} while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
+ DebugClearVisited(&fHead);
+ return;
}
+// commented-out lines keep this in sync with moveMultiples()
+// if a span has more than one intersection, merge the other segments' span as needed
void SkOpSegment::debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
+ debugValidate();
const SkOpSpanBase* test = &fHead;
do {
int addCount = test->spanAddsCount();
@@ -777,6 +915,7 @@ void SkOpSegment::debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog*
} while ((matchPtT = matchPtT->next()) != startPtT);
goto tryNextSpan;
foundMatch: // merge oppTest and oppSpan
+ oppSegment->debugValidate();
if (oppTest == &oppSegment->fTail || oppTest == &oppSegment->fHead) {
SkASSERT(oppSpan != &oppSegment->fHead); // don't expect collapse
SkASSERT(oppSpan != &oppSegment->fTail);
@@ -784,60 +923,67 @@ void SkOpSegment::debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog*
} else {
glitches->record(kMoveMultiple_Glitch, id, oppSpan, oppTest);
}
+ oppSegment->debugValidate();
goto checkNextSpan;
}
- tryNextSpan:
+ tryNextSpan:
;
} while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
} while ((testPtT = testPtT->next()) != startPtT);
-checkNextSpan:
+checkNextSpan:
;
} while ((test = test->final() ? nullptr : test->upCast()->next()));
+ debugValidate();
+ return;
}
+// commented-out lines keep this in sync with moveNearby()
+// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
void SkOpSegment::debugMoveNearby(const char* id, SkPathOpsDebug::GlitchLog* glitches) const {
- const SkOpSpanBase* spanS = &fHead;
+ debugValidate();
+ // release undeleted spans pointing to this seg that are linked to the primary span
+ const SkOpSpanBase* spanBase = &fHead;
do {
- const SkOpSpanBase* test = spanS->upCast()->next();
- const SkOpSpanBase* next;
- if (spanS->contains(test)) {
- if (!test->final()) {
- glitches->record(kUndetachedSpan_Glitch, id, test, spanS);
- } else if (spanS != &fHead) {
- glitches->record(kUndetachedSpan_Glitch, id, spanS, test);
+ const SkOpPtT* ptT = spanBase->ptT();
+ const SkOpPtT* headPtT = ptT;
+ while ((ptT = ptT->next()) != headPtT) {
+ const SkOpSpanBase* test = ptT->span();
+ if (ptT->segment() == this && !ptT->deleted() && test != spanBase
+ && test->ptT() == ptT) {
+ if (test->final()) {
+ if (spanBase == &fHead) {
+ glitches->record(kMoveNearbyClearAll_Glitch, id, this);
+// return;
+ }
+ glitches->record(kMoveNearbyReleaseFinal_Glitch, id, spanBase, ptT);
+ } else if (test->prev()) {
+ glitches->record(kMoveNearbyRelease_Glitch, id, test, headPtT);
+ }
+// break;
}
}
- do { // iterate through all spans associated with start
- const SkOpPtT* startBase = spanS->ptT();
- next = test->final() ? nullptr : test->upCast()->next();
- do {
- const SkOpPtT* testBase = test->ptT();
- do {
- if (startBase == testBase) {
- goto checkNextSpan;
- }
- if (testBase->duplicate()) {
- continue;
- }
- if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) {
- if (test == &this->fTail) {
- if (spanS == &fHead) {
- glitches->record(kCollapsedSpan_Glitch, id, spanS);
- } else {
- glitches->record(kUnmergedSpan_Glitch, id, &this->fTail, spanS);
- }
- } else {
- glitches->record(kUnmergedSpan_Glitch, id, spanS, test);
- goto checkNextSpan;
- }
- }
- } while ((testBase = testBase->next()) != test->ptT());
- } while ((startBase = startBase->next()) != spanS->ptT());
- checkNextSpan:
- ;
- } while ((test = next));
- spanS = spanS->upCast()->next();
- } while (!spanS->final());
+ spanBase = spanBase->upCast()->next();
+ } while (!spanBase->final());
+
+ // This loop looks for adjacent spans which are near by
+ spanBase = &fHead;
+ do { // iterate through all spans associated with start
+ const SkOpSpanBase* test = spanBase->upCast()->next();
+ if (this->spansNearby(spanBase, test)) {
+ if (test->final()) {
+ if (spanBase->prev()) {
+ glitches->record(kMoveNearbyMergeFinal_Glitch, id, test);
+ } else {
+ glitches->record(kMoveNearbyClearAll2_Glitch, id, this);
+ // return
+ }
+ } else {
+ glitches->record(kMoveNearbyMerge_Glitch, id, spanBase);
+ }
+ }
+ spanBase = test;
+ } while (!spanBase->final());
+ debugValidate();
}
#endif
@@ -864,16 +1010,18 @@ void SkOpSegment::debugShowActiveSpans() const {
lastId = this->debugID();
lastT = span->t();
SkDebugf("%s id=%d", __FUNCTION__, this->debugID());
- SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
+ // since endpoints may have be adjusted, show actual computed curves
+ SkDCurve curvePart;
+ this->subDivide(span, span->next(), &curvePart);
+ const SkDPoint* pts = curvePart.fCubic.fPts;
+ SkDebugf(" (%1.9g,%1.9g", pts[0].fX, pts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
- SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
+ SkDebugf(" %1.9g,%1.9g", pts[vIndex].fX, pts[vIndex].fY);
}
if (SkPath::kConic_Verb == fVerb) {
- SkDebugf(" %1.9gf", fWeight);
+ SkDebugf(" %1.9gf", curvePart.fConic.fWeight);
}
- const SkOpPtT* ptT = span->ptT();
- SkDebugf(") t=%1.9g (%1.9g,%1.9g)", ptT->fT, ptT->fPt.fX, ptT->fPt.fY);
- SkDebugf(" tEnd=%1.9g", span->next()->t());
+ SkDebugf(") t=%1.9g tEnd=%1.9g", span->t(), span->next()->t());
if (span->windSum() == SK_MinS32) {
SkDebugf(" windSum=?");
} else {
@@ -958,7 +1106,7 @@ void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int
// loop looking for a pair of angle parts that are too close to be sorted
/* This is called after other more simple intersection and angle sorting tests have been exhausted.
This should be rarely called -- the test below is thorough and time consuming.
- This checks the distance between start points; the distance between
+ This checks the distance between start points; the distance between
*/
#if DEBUG_ANGLE
void SkOpAngle::debugCheckNearCoincidence() const {
@@ -996,7 +1144,7 @@ void SkOpAngle::debugCheckNearCoincidence() const {
SkDebugf("\n");
}
test = test->fNext;
- } while (test->fNext != this);
+ } while (test->fNext != this);
}
#endif
@@ -1046,6 +1194,11 @@ void SkOpAngle::debugLoop() const {
#endif
void SkOpAngle::debugValidate() const {
+#if DEBUG_COINCIDENCE
+ if (this->globalState()->debugCheckHealth()) {
+ return;
+ }
+#endif
#if DEBUG_VALIDATE
const SkOpAngle* first = this;
const SkOpAngle* next = this;
@@ -1108,243 +1261,409 @@ void SkOpAngle::debugValidateNext() const {
#endif
}
+#ifdef SK_DEBUG
+void SkCoincidentSpans::debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
+ const SkOpGlobalState* debugState) const {
+ SkASSERT(coinPtTEnd()->span() == over || !debugState->debugRunFail());
+ SkASSERT(oppPtTEnd()->span() == outer || !debugState->debugRunFail());
+}
+#endif
-#if DEBUG_COINCIDENCE
+#if DEBUG_COINCIDENCE_VERBOSE
+/* Commented-out lines keep this in sync with expand */
+bool SkCoincidentSpans::debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+ bool expanded = false;
+ const SkOpSegment* segment = coinPtTStart()->segment();
+ const SkOpSegment* oppSegment = oppPtTStart()->segment();
+ do {
+ const SkOpSpan* start = coinPtTStart()->span()->upCast();
+ const SkOpSpan* prev = start->prev();
+ const SkOpPtT* oppPtT;
+ if (!prev || !(oppPtT = prev->contains(oppSegment))) {
+ break;
+ }
+ double midT = (prev->t() + start->t()) / 2;
+ if (!segment->isClose(midT, oppSegment)) {
+ break;
+ }
+ if (log) log->record(kExpandCoin_Glitch, id, this, prev->ptT(), oppPtT);
+ expanded = true;
+ } while (false); // actual continues while expansion is possible
+ do {
+ const SkOpSpanBase* end = coinPtTEnd()->span();
+ SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
+ const SkOpPtT* oppPtT;
+ if (!next || !(oppPtT = next->contains(oppSegment))) {
+ break;
+ }
+ double midT = (end->t() + next->t()) / 2;
+ if (!segment->isClose(midT, oppSegment)) {
+ break;
+ }
+ if (log) log->record(kExpandCoin_Glitch, id, this, next->ptT(), oppPtT);
+ expanded = true;
+ } while (false); // actual continues while expansion is possible
+ return expanded;
+}
+
+#define FAIL_IF(cond) do { if (cond) log->record(kAddExpandedFail_Glitch, id, coin); } while (false)
+
+/* Commented-out lines keep this in sync with addExpanded */
+// for each coincident pair, match the spans
+// if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
void SkOpCoincidence::debugAddExpanded(const char* id, SkPathOpsDebug::GlitchLog* log) const {
- // for each coincident pair, match the spans
- // if the spans don't match, add the mssing pt to the segment and loop it in the opposite span
const SkCoincidentSpans* coin = this->fHead;
if (!coin) {
- coin = this->fTop;
- }
- if (!coin) {
return;
}
do {
- const SkOpPtT* startPtT = coin->fCoinPtTStart;
- const SkOpPtT* oStartPtT = coin->fOppPtTStart;
+ const SkOpPtT* startPtT = coin->coinPtTStart();
+ const SkOpPtT* oStartPtT = coin->oppPtTStart();
SkASSERT(startPtT->contains(oStartPtT));
- SkASSERT(coin->fCoinPtTEnd->contains(coin->fOppPtTEnd));
+ SkASSERT(coin->coinPtTEnd()->contains(coin->oppPtTEnd()));
const SkOpSpanBase* start = startPtT->span();
const SkOpSpanBase* oStart = oStartPtT->span();
- const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
+ const SkOpSpanBase* end = coin->coinPtTEnd()->span();
+ const SkOpSpanBase* oEnd = coin->oppPtTEnd()->span();
+ FAIL_IF(oEnd->deleted());
const SkOpSpanBase* test = start->upCast()->next();
- const SkOpSpanBase* oTest = coin->fFlipped ? oStart->prev() : oStart->upCast()->next();
+ const SkOpSpanBase* oTest = coin->flipped() ? oStart->prev() : oStart->upCast()->next();
+ if (!oTest) {
+ return;
+ }
while (test != end || oTest != oEnd) {
- bool bumpTest = true;
- bool bumpOTest = true;
- if (!test->ptT()->contains(oTest->ptT())) {
+ if (!test->ptT()->contains(oTest->segment())
+ || !oTest->ptT()->contains(start->segment())) {
// use t ranges to guess which one is missing
- double startRange = coin->fCoinPtTEnd->fT - startPtT->fT;
+ double startRange = coin->coinPtTEnd()->fT - startPtT->fT;
+ FAIL_IF(!startRange);
double startPart = (test->t() - startPtT->fT) / startRange;
- double oStartRange = coin->fOppPtTEnd->fT - oStartPtT->fT;
+ double oStartRange = coin->oppPtTEnd()->fT - oStartPtT->fT;
+ FAIL_IF(!oStartRange);
double oStartPart = (oTest->t() - oStartPtT->fT) / oStartRange;
- if (startPart == oStartPart) {
- // data is corrupt
- log->record(kAddCorruptCoin_Glitch, id, start, oStart);
- break;
+ FAIL_IF(startPart == oStartPart);
+ bool startOver = false;
+ if (startPart < oStartPart)
+ log->record(kAddExpandedCoin_Glitch, id, // strange debug formatting lines up with original
+ oStartPtT->fT + oStartRange * startPart, test);
+ else log->record(kAddExpandedCoin_Glitch, id,
+ startPtT->fT + startRange * oStartPart, oTest);
+ if (false) {
+ SkASSERT(0);
+ return;
}
- if (startPart < oStartPart) {
- double newT = oStartPtT->fT + oStartRange * startPart;
- log->record(kAddExpandedCoin_Glitch, id, oStart, newT, test->pt());
- bumpOTest = false;
- } else {
- double newT = startPtT->fT + startRange * oStartPart;
- log->record(kAddExpandedCoin_Glitch, id, start, newT, oTest->pt());
- bumpTest = false;
+ if (startOver) {
+ test = start;
+ oTest = oStart;
}
}
- if (bumpTest && test != end) {
+ if (test != end) {
test = test->upCast()->next();
}
- if (bumpOTest && oTest != oEnd) {
- oTest = coin->fFlipped ? oTest->prev() : oTest->upCast()->next();
+ if (oTest != oEnd) {
+ oTest = coin->flipped() ? oTest->prev() : oTest->upCast()->next();
+ if (!oTest) {
+ return;
+ }
}
}
- } while ((coin = coin->fNext));
-}
-
-static void t_range(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, double tEnd,
- const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs, double* coinTe) {
- double denom = overE->fT - overS->fT;
- double start = 0 < denom ? tStart : tEnd;
- double end = 0 < denom ? tEnd : tStart;
- double sRatio = (start - overS->fT) / denom;
- double eRatio = (end - overS->fT) / denom;
- *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio;
- *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
-}
-
-bool SkOpCoincidence::debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
- const SkOpPtT* over1e) const {
- const SkCoincidentSpans* check = this->fTop;
- while (check) {
- if (check->fCoinPtTStart->span() == over1s->span()
- && check->fOppPtTStart->span() == outer->fOppPtTStart->span()) {
- SkASSERT(check->fCoinPtTEnd->span() == over1e->span()
- || !fDebugState->debugRunFail());
- SkASSERT(check->fOppPtTEnd->span() == outer->fOppPtTEnd->span()
- || !fDebugState->debugRunFail());
- return false;
- }
- if (check->fCoinPtTStart->span() == outer->fCoinPtTStart->span()
- && check->fOppPtTStart->span() == over1s->span()) {
- SkASSERT(check->fCoinPtTEnd->span() == outer->fCoinPtTEnd->span()
- || !fDebugState->debugRunFail());
- SkASSERT(check->fOppPtTEnd->span() == over1e->span()
- || !fDebugState->debugRunFail());
- return false;
- }
- check = check->fNext;
+ } while ((coin = coin->next()));
+ return;
+}
+
+/* Commented-out lines keep this in sync with addIfMissing() */
+void SkOpCoincidence::debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
+ const SkOpPtT* over1e, const char* id, SkPathOpsDebug::GlitchLog* log) const {
+// SkASSERT(fTop);
+ if (fTop && alreadyAdded(fTop, outer, over1s, over1e)) { // in debug, fTop may be null
+ return;
}
- return true;
+ if (fHead && alreadyAdded(fHead, outer, over1s, over1e)) {
+ return;
+ }
+ log->record(kAddIfMissingCoin_Glitch, id, outer->coinPtTStart(), outer->coinPtTEnd(), over1s, over1e);
+ this->debugValidate();
+ return;
}
-bool SkOpCoincidence::debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
- const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
- SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
- SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const {
+/* Commented-out lines keep this in sync addIfMissing() */
+void SkOpCoincidence::debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
+ const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
+ const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+ const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, const char* id, SkPathOpsDebug::GlitchLog* log) const {
double coinTs, coinTe, oppTs, oppTe;
- t_range(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
- t_range(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
- const SkOpSegment* coinSeg = coinPtTStart->segment();
- const SkOpSegment* oppSeg = oppPtTStart->segment();
- SkASSERT(coinSeg != oppSeg);
- const SkCoincidentSpans* check = this->fTop;
- ;
- while (check) {
- const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment();
- const SkOpSegment* checkOppSeg;
- if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) {
- goto next;
- }
- checkOppSeg = check->fOppPtTStart->segment();
- if (checkOppSeg != coinSeg && checkOppSeg != oppSeg) {
- goto next;
- }
- {
- int cTs = coinTs;
- int cTe = coinTe;
- int oTs = oppTs;
- int oTe = oppTe;
- if (checkCoinSeg != coinSeg) {
- SkASSERT(checkOppSeg != oppSeg);
- SkTSwap(cTs, oTs);
- SkTSwap(cTe, oTe);
- }
- int tweenCount = (int) between(check->fCoinPtTStart->fT, cTs, check->fCoinPtTEnd->fT)
- + (int) between(check->fCoinPtTStart->fT, cTe, check->fCoinPtTEnd->fT)
- + (int) between(check->fOppPtTStart->fT, oTs, check->fOppPtTEnd->fT)
- + (int) between(check->fOppPtTStart->fT, oTe, check->fOppPtTEnd->fT);
- // SkASSERT(tweenCount == 0 || tweenCount == 4);
- if (tweenCount) {
- return true;
- }
- }
-next:
- check = check->fNext;
+ TRange(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
+ TRange(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
+ bool swap = coinTs > coinTe;
+ if (swap) {
+ SkTSwap(coinTs, coinTe);
}
if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
SkTSwap(oppTs, oppTe);
}
- if (coinTs > coinTe) {
- SkTSwap(coinTs, coinTe);
+ if (swap) {
SkTSwap(oppTs, oppTe);
}
- bool cs = coinSeg->debugAddMissing(coinTs, oppSeg);
- bool ce = coinSeg->debugAddMissing(coinTe, oppSeg);
- if (cs == ce) {
- return false;
+ const SkOpSegment* coinSeg = coinPtTStart->segment();
+ const SkOpSegment* oppSeg = oppPtTStart->segment();
+ if (coinSeg == oppSeg) {
+ return;
}
- return true;
+ return this->debugAddOrOverlap(coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, id, log);
+}
+
+/* Commented-out lines keep this in sync addOrOverlap() */
+void SkOpCoincidence::debugAddOrOverlap(const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
+ double coinTs, double coinTe, double oppTs, double oppTe, const char* id, SkPathOpsDebug::GlitchLog* log) const {
+ SkTDArray<SkCoincidentSpans*> overlaps;
+ SkASSERT(!fTop); // this is (correctly) reversed in addifMissing()
+ if (fTop && !this->checkOverlap(fTop, coinSeg, oppSeg, coinTs, coinTe, oppTs, oppTe, &overlaps)) {
+ return;
+ }
+ if (fHead && !this->checkOverlap(fHead, coinSeg, oppSeg, coinTs,
+ coinTe, oppTs, oppTe, &overlaps)) {
+ return;
+ }
+ const SkCoincidentSpans* overlap = overlaps.count() ? overlaps[0] : nullptr;
+ for (int index = 1; index < overlaps.count(); ++index) { // combine overlaps before continuing
+ const SkCoincidentSpans* test = overlaps[index];
+ if (overlap->coinPtTStart()->fT > test->coinPtTStart()->fT) {
+ log->record(kAddOrOverlap_Glitch, id, overlap, test->coinPtTStart());
+ }
+ if (overlap->coinPtTEnd()->fT < test->coinPtTEnd()->fT) {
+ log->record(kAddOrOverlap_Glitch, id, overlap, test->coinPtTEnd());
+ }
+ if (overlap->flipped()
+ ? overlap->oppPtTStart()->fT < test->oppPtTStart()->fT
+ : overlap->oppPtTStart()->fT > test->oppPtTStart()->fT) {
+ log->record(kAddOrOverlap_Glitch, id, overlap, test->oppPtTStart());
+ }
+ if (overlap->flipped()
+ ? overlap->oppPtTEnd()->fT > test->oppPtTEnd()->fT
+ : overlap->oppPtTEnd()->fT < test->oppPtTEnd()->fT) {
+ log->record(kAddOrOverlap_Glitch, id, overlap, test->oppPtTEnd());
+ }
+ if (!fHead) {
+ SkAssertResult(true);
+ }
+ }
+ const SkOpPtT* cs = coinSeg->existing(coinTs, oppSeg);
+ const SkOpPtT* ce = coinSeg->existing(coinTe, oppSeg);
+ if (overlap && cs && ce && overlap->contains(cs, ce)) {
+ return;
+ }
+ SkASSERT(cs != ce || !cs);
+ const SkOpPtT* os = oppSeg->existing(oppTs, coinSeg);
+ const SkOpPtT* oe = oppSeg->existing(oppTe, coinSeg);
+ if (overlap && os && oe && overlap->contains(os, oe)) {
+ return;
+ }
+ SkASSERT(true || !cs || !cs->deleted());
+ SkASSERT(true || !os || !os->deleted());
+ SkASSERT(true || !ce || !ce->deleted());
+ SkASSERT(true || !oe || !oe->deleted());
+ const SkOpPtT* csExisting = !cs ? coinSeg->existing(coinTs, nullptr) : nullptr;
+ const SkOpPtT* ceExisting = !ce ? coinSeg->existing(coinTe, nullptr) : nullptr;
+ if (csExisting && csExisting == ceExisting) {
+ return;
+ }
+ if (csExisting && (csExisting == ce || csExisting->contains(ceExisting ? ceExisting : ce))) {
+ return;
+ }
+ if (ceExisting && (ceExisting == cs || ceExisting->contains(csExisting ? csExisting : cs))) {
+ return;
+ }
+ const SkOpPtT* osExisting = !os ? oppSeg->existing(oppTs, nullptr) : nullptr;
+ const SkOpPtT* oeExisting = !oe ? oppSeg->existing(oppTe, nullptr) : nullptr;
+ if (osExisting && osExisting == oeExisting) {
+ return;
+ }
+ if (osExisting && (osExisting == oe || osExisting->contains(oeExisting ? oeExisting : oe))) {
+ return;
+ }
+ if (oeExisting && (oeExisting == os || oeExisting->contains(osExisting ? osExisting : os))) {
+ return;
+ }
+ bool csDeleted = false, osDeleted = false, ceDeleted = false, oeDeleted = false;
+ this->debugValidate();
+ if (!cs || !os) {
+ if (!cs)
+ cs = coinSeg->debugAddT(coinTs, SkOpSegment::kNoAliasMatch, nullptr);
+ if (!os)
+ os = oppSeg->debugAddT(oppTs, SkOpSegment::kNoAliasMatch, nullptr);
+ if (cs && os) cs->span()->debugAddOppAndMerge(id, log, os->span(), &csDeleted, &osDeleted);
+// cs = csWritable;
+// os = osWritable;
+ if ((ce && ce->deleted()) || (oe && oe->deleted())) {
+ return;
+ }
+ }
+ if (!ce || !oe) {
+ if (!ce)
+ ce = coinSeg->debugAddT(coinTe, SkOpSegment::kNoAliasMatch, nullptr);
+ if (!oe)
+ oe = oppSeg->debugAddT(oppTe, SkOpSegment::kNoAliasMatch, nullptr);
+ if (ce && oe) ce->span()->debugAddOppAndMerge(id, log, oe->span(), &ceDeleted, &oeDeleted);
+// ce = ceWritable;
+// oe = oeWritable;
+ }
+ this->debugValidate();
+ if (csDeleted || osDeleted || ceDeleted || oeDeleted) {
+ return;
+ }
+ if (!cs || !ce || cs->contains(ce) || !os || !oe || os->contains(oe)) {
+ return;
+ }
+// bool result = true;
+ if (overlap) {
+ if (overlap->coinPtTStart()->segment() == coinSeg) {
+ log->record(kAddMissingExtend_Glitch, id, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
+ } else {
+ if (oppTs > oppTe) {
+ SkTSwap(coinTs, coinTe);
+ SkTSwap(oppTs, oppTe);
+ }
+ log->record(kAddMissingExtend_Glitch, id, oppSeg, oppTs, oppTe, coinSeg, coinTs, coinTe);
+ }
+#if DEBUG_COINCIDENCE_VERBOSE
+// if (result) {
+// overlap->debugShow();
+// }
+#endif
+ } else {
+ log->record(kAddMissingCoin_Glitch, id, coinSeg, coinTs, coinTe, oppSeg, oppTs, oppTe);
+#if DEBUG_COINCIDENCE_VERBOSE
+// fHead->debugShow();
+#endif
+ }
+ this->debugValidate();
+ return;
}
+// Extra commented-out lines keep this in sync with addMissing()
+/* detects overlaps of different coincident runs on same segment */
+/* does not detect overlaps for pairs without any segments in common */
+// returns true if caller should loop again
void SkOpCoincidence::debugAddMissing(const char* id, SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* outer = fHead;
if (!outer) {
return;
}
+ // bool added = false;
+ // fTop = outer;
+ // fHead = nullptr;
do {
// addifmissing can modify the list that this is walking
// save head so that walker can iterate over old data unperturbed
// addifmissing adds to head freely then add saved head in the end
- const SkOpSegment* outerCoin = outer->fCoinPtTStart->segment();
- SkASSERT(outerCoin == outer->fCoinPtTEnd->segment());
- const SkOpSegment* outerOpp = outer->fOppPtTStart->segment();
- SkASSERT(outerOpp == outer->fOppPtTEnd->segment());
+ const SkOpSegment* outerCoin = outer->coinPtTStart()->segment();
+ const SkOpSegment* outerOpp = outer->oppPtTStart()->segment();
+ if (outerCoin->done() || outerOpp->done()) {
+ continue;
+ }
const SkCoincidentSpans* inner = outer;
- while ((inner = inner->fNext)) {
+ while ((inner = inner->next())) {
+ this->debugValidate();
double overS, overE;
- const SkOpSegment* innerCoin = inner->fCoinPtTStart->segment();
- SkASSERT(innerCoin == inner->fCoinPtTEnd->segment());
- const SkOpSegment* innerOpp = inner->fOppPtTStart->segment();
- SkASSERT(innerOpp == inner->fOppPtTEnd->segment());
- if (outerCoin == innerCoin
- && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
- if (this->debugAddIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
- outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd)) {
- log->record(kAddMissingCoin_Glitch, id, outer, inner->fCoinPtTStart);
+ const SkOpSegment* innerCoin = inner->coinPtTStart()->segment();
+ const SkOpSegment* innerOpp = inner->oppPtTStart()->segment();
+ if (innerCoin->done() || innerOpp->done()) {
+ continue;
+ }
+ if (outerCoin == innerCoin) {
+ if (outerOpp != innerOpp
+ && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) {
+ this->debugAddIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE,
+ outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), id, log);
}
- } else if (outerCoin == innerOpp
- && this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
- if (this->debugAddIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
- outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd)) {
- log->record(kAddMissingCoin_Glitch, id, outer, inner->fOppPtTStart);
+ } else if (outerCoin == innerOpp) {
+ if (outerOpp != innerCoin
+ && this->overlap(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) {
+ this->debugAddIfMissing(outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE,
+ outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), id, log);
}
- } else if (outerOpp == innerCoin
- && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
- if (this->debugAddIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
- outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd)) {
- log->record(kAddMissingCoin_Glitch, id, outer, inner->fCoinPtTStart);
+ } else if (outerOpp == innerCoin) {
+ SkASSERT(outerCoin != innerOpp);
+ if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), &overS, &overE)) {
+ this->debugAddIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), overS, overE,
+ outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), id, log);
}
- } else if (outerOpp == innerOpp
- && this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
- if (this->debugAddIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
- outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd)) {
- log->record(kAddMissingCoin_Glitch, id, outer, inner->fOppPtTStart);
- }
- } else if (outerCoin != innerCoin) {
- // check to see if outer span overlaps the inner span
- // look for inner segment in pt-t list
- // if present, and if t values are in coincident range
- // add two pairs of new coincidence
- const SkOpPtT* testS = outer->fCoinPtTStart->debugContains(innerCoin);
- const SkOpPtT* testE = outer->fCoinPtTEnd->debugContains(innerCoin);
- if (testS && testS->fT >= inner->fCoinPtTStart->fT
- && testE && testE->fT <= inner->fCoinPtTEnd->fT
- && this->testForCoincidence(outer, testS, testE)) {
- if (this->debugAddIfMissing(outer, testS, testE)) {
- log->record(kAddMissingCoin_Glitch, id, outer, testS, testE);
- }
- } else {
- testS = inner->fCoinPtTStart->debugContains(outerCoin);
- testE = inner->fCoinPtTEnd->debugContains(outerCoin);
- if (testS && testS->fT >= outer->fCoinPtTStart->fT
- && testE && testE->fT <= outer->fCoinPtTEnd->fT
- && this->testForCoincidence(inner, testS, testE)) {
- if (this->debugAddIfMissing(inner, testS, testE)) {
- log->record(kAddMissingCoin_Glitch, id, inner, testS, testE);
- }
- }
+ } else if (outerOpp == innerOpp) {
+ SkASSERT(outerCoin != innerCoin);
+ if (this->overlap(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), &overS, &overE)) {
+ this->debugAddIfMissing(outer->oppPtTStart(), outer->oppPtTEnd(),
+ inner->oppPtTStart(), inner->oppPtTEnd(), overS, overE,
+ outer->coinPtTStart(), outer->coinPtTEnd(),
+ inner->coinPtTStart(), inner->coinPtTEnd(), id, log);
}
}
+ this->debugValidate();
+ }
+ } while ((outer = outer->next()));
+ // this->restoreHead();
+ return;
+}
+
+// Commented-out lines keep this in sync with release()
+void SkOpCoincidence::debugRelease(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSegment* deleted) const {
+ const SkCoincidentSpans* coin = fHead;
+ if (!coin) {
+ return;
+ }
+ do {
+ if (coin->coinPtTStart()->segment() == deleted
+ || coin->coinPtTEnd()->segment() == deleted
+ || coin->oppPtTStart()->segment() == deleted
+ || coin->oppPtTEnd()->segment() == deleted) {
+ log->record(kReleasedSpan_Glitch, id, coin);
+ }
+ } while ((coin = coin->next()));
+}
+
+// Commented-out lines keep this in sync with reorder()
+// iterate through all coincident pairs, looking for ranges greater than 1
+// if found, see if the opposite pair can match it -- which may require
+// reordering the ptT pairs
+void SkOpCoincidence::debugReorder(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+ const SkCoincidentSpans* coin = fHead;
+ if (!coin) {
+ return;
+ }
+ do {
+ // most commonly, concidence are one span long; check for that first
+ int intervals = coin->spanCount();
+ if (intervals = 1) {
+#if DEBUG_COINCIDENCE_VERBOSE
+ // SkASSERT(!coin->debugExpand(nullptr, nullptr));
+#endif
+ continue;
+ }
+ coin->debugExpand(id, log);
+ if (coin->spanCount() <= 0) {
+ return;
}
- } while ((outer = outer->fNext));
+ // check to see if every span in coin has a mate in opp
+ const SkOpSpan* start = coin->coinPtTStart()->span()->upCast();
+ bool flipped = coin->flipped();
+ const SkOpSpanBase* oppStartBase = coin->oppPtTStart()->span();
+ const SkOpSpan* oppStart = flipped ? oppStartBase->prev() : oppStartBase->upCast();
+ SkDebugf("", start, oppStart);
+ } while ((coin = coin->next()));
+ return;
}
+// Commented-out lines keep this in sync with expand()
+// expand the range by checking adjacent spans for coincidence
bool SkOpCoincidence::debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* coin = fHead;
if (!coin) {
@@ -1352,109 +1671,296 @@ bool SkOpCoincidence::debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log
}
bool expanded = false;
do {
- const SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
- const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- const SkOpSegment* segment = coin->fCoinPtTStart->segment();
- const SkOpSegment* oppSegment = coin->fOppPtTStart->segment();
- const SkOpSpan* prev = start->prev();
- if (prev && prev->debugContains(oppSegment)) {
- double midT = (prev->t() + start->t()) / 2;
- if (segment->isClose(midT, oppSegment)) {
- log->record(kExpandCoin_Glitch, id, coin, prev);
- }
- }
- SkOpSpanBase* next = end->final() ? nullptr : end->upCast()->next();
- if (next && next->debugContains(oppSegment)) {
- double midT = (end->t() + next->t()) / 2;
- if (segment->isClose(midT, oppSegment)) {
- log->record(kExpandCoin_Glitch, id, coin, next);
- }
+ if (coin->debugExpand(id, log)) {
+ // check to see if multiple spans expanded so they are now identical
+ const SkCoincidentSpans* test = fHead;
+ do {
+ if (coin == test) {
+ continue;
+ }
+ if (coin->coinPtTStart() == test->coinPtTStart()
+ && coin->oppPtTStart() == test->oppPtTStart()) {
+ if (log) log->record(kExpandCoin_Glitch, id, fHead, test->coinPtTStart());
+ break;
+ }
+ } while ((test = test->next()));
+ expanded = true;
}
- } while ((coin = coin->fNext));
+ } while ((coin = coin->next()));
return expanded;
}
-void SkOpCoincidence::debugFixAligned(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+// Commented-out lines keep this in sync with removeCollapsed()
+void SkOpCoincidence::debugRemoveCollapsed(const char* id, SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* coin = fHead;
if (!coin) {
return;
}
+ // SkCoincidentSpans** priorPtr = &fHead;
do {
- if (coin->fCoinPtTStart->deleted()) {
- log->record(kDeletedCoin_Glitch, id, coin, coin->fCoinPtTStart);
- }
- if (coin->fCoinPtTEnd->deleted()) {
- log->record(kDeletedCoin_Glitch, id, coin, coin->fCoinPtTEnd);
- }
- if (coin->fOppPtTStart->deleted()) {
- log->record(kDeletedCoin_Glitch, id, coin, coin->fOppPtTStart);
+ if (coin->coinPtTStart() == coin->coinPtTEnd()) {
+ return;
}
- if (coin->fOppPtTEnd->deleted()) {
- log->record(kDeletedCoin_Glitch, id, coin, coin->fOppPtTEnd);
+ if (coin->oppPtTStart() == coin->oppPtTEnd()) {
+ return;
}
- } while ((coin = coin->fNext));
- coin = fHead;
- do {
- if (coin->fCoinPtTStart->collapsed(coin->fCoinPtTEnd)) {
- log->record(kCollapsedCoin_Glitch, id, coin, coin->fCoinPtTStart);
+ if (coin->coinPtTStart()->collapsed(coin->coinPtTEnd())) {
+ log->record(kCollapsedCoin_Glitch, id, coin);
+// continue;
}
- if (coin->fOppPtTStart->collapsed(coin->fOppPtTEnd)) {
- log->record(kCollapsedCoin_Glitch, id, coin, coin->fOppPtTStart);
+ if (coin->oppPtTStart()->collapsed(coin->oppPtTEnd())) {
+ log->record(kCollapsedCoin_Glitch, id, coin, coin);
+// continue;
}
- } while ((coin = coin->fNext));
+ // priorPtr = &coin->nextPtr();
+ } while ((coin = coin->next()));
+ return;
}
+// Commented-out lines keep this in sync with mark()
+/* this sets up the coincidence links in the segments when the coincidence crosses multiple spans */
void SkOpCoincidence::debugMark(const char* id, SkPathOpsDebug::GlitchLog* log) const {
const SkCoincidentSpans* coin = fHead;
if (!coin) {
return;
}
do {
- const SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- const SkOpSpanBase* oldEnd = end;
- const SkOpSpan* start = coin->fCoinPtTStart->span()->debugStarter(&end);
- const SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
- const SkOpSpanBase* oOldEnd = oEnd;
- const SkOpSpanBase* oStart = coin->fOppPtTStart->span()->debugStarter(&oEnd);
- bool flipped = (end == oldEnd) != (oEnd == oOldEnd);
+ const SkOpSpan* start = coin->coinPtTStartWritable()->span()->upCast();
+// SkASSERT(start->deleted());
+ const SkOpSpanBase* end = coin->coinPtTEndWritable()->span();
+// SkASSERT(end->deleted());
+ const SkOpSpanBase* oStart = coin->oppPtTStartWritable()->span();
+// SkASSERT(oStart->deleted());
+ const SkOpSpanBase* oEnd = coin->oppPtTEndWritable()->span();
+// SkASSERT(oEnd->deleted());
+ bool flipped = coin->flipped();
if (flipped) {
SkTSwap(oStart, oEnd);
}
+ /* coin and opp spans may not match up. Mark the ends, and then let the interior
+ get marked as many times as the spans allow */
+ start->debugInsertCoincidence(id, log, oStart->upCast());
+ end->debugInsertCoinEnd(id, log, oEnd);
+ const SkOpSegment* segment = start->segment();
+ const SkOpSegment* oSegment = oStart->segment();
const SkOpSpanBase* next = start;
const SkOpSpanBase* oNext = oStart;
- do {
- next = next->upCast()->next();
- oNext = flipped ? oNext->prev() : oNext->upCast()->next();
- if (next == end || oNext == oEnd) {
- break;
+ while ((next = next->upCast()->next()) != end) {
+ if (next->upCast()->debugInsertCoincidence(id, log, oSegment, flipped), false) {
+ return;
}
- if (!next->containsCoinEnd(oNext)) {
- log->record(kMarkCoinEnd_Glitch, id, next, oNext);
+ }
+ while ((oNext = oNext->upCast()->next()) != oEnd) {
+ if (oNext->upCast()->debugInsertCoincidence(id, log, segment, flipped), false) {
+ return;
}
- const SkOpSpan* nextSpan = next->upCast();
- const SkOpSpan* oNextSpan = oNext->upCast();
- if (!nextSpan->containsCoincidence(oNextSpan)) {
- log->record(kMarkCoinInsert_Glitch, id, nextSpan, oNextSpan);
+ }
+ } while ((coin = coin->next()));
+ return;
+}
+#endif
+
+#if DEBUG_COINCIDENCE_VERBOSE
+// Commented-out lines keep this in sync with markCollapsed()
+void SkOpCoincidence::debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* log, const SkCoincidentSpans* coin, const SkOpPtT* test) const {
+ while (coin) {
+ if (coin->collapsed(test)) {
+ if (zero_or_one(coin->coinPtTStart()->fT) && zero_or_one(coin->coinPtTEnd()->fT)) {
+ log->record(kCollapsedCoin_Glitch, id, coin);
}
- } while (true);
- } while ((coin = coin->fNext));
+ if (zero_or_one(coin->oppPtTStart()->fT) && zero_or_one(coin->oppPtTEnd()->fT)) {
+ log->record(kCollapsedCoin_Glitch, id, coin);
+ }
+ }
+ coin = coin->next();
+ }
+}
+
+// Commented-out lines keep this in sync with markCollapsed()
+void SkOpCoincidence::debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpPtT* test) const {
+ this->debugMarkCollapsed(id, log, fHead, test);
+ this->debugMarkCollapsed(id, log, fTop, test);
}
#endif
+void SkCoincidentSpans::debugShow() const {
+ SkDebugf("%s - id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
+ coinPtTStart()->segment()->debugID(),
+ coinPtTStart()->fT, coinPtTEnd()->fT);
+ SkDebugf("%s + id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
+ oppPtTStart()->segment()->debugID(),
+ oppPtTStart()->fT, oppPtTEnd()->fT);
+}
+
void SkOpCoincidence::debugShowCoincidence() const {
- SkCoincidentSpans* span = fHead;
+#if DEBUG_COINCIDENCE
+ const SkCoincidentSpans* span = fHead;
while (span) {
- SkDebugf("%s - id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
- span->fCoinPtTStart->segment()->debugID(),
- span->fCoinPtTStart->fT, span->fCoinPtTEnd->fT);
- SkDebugf("%s + id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
- span->fOppPtTStart->segment()->debugID(),
- span->fOppPtTStart->fT, span->fOppPtTEnd->fT);
- span = span->fNext;
+ span->debugShow();
+ span = span->next();
}
+#endif
}
#if DEBUG_COINCIDENCE
+static void DebugValidate(const SkOpSpanBase* next, const SkOpSpanBase* end,
+ double oStart, double oEnd, const SkOpSegment* oSegment,
+ const char* id, SkPathOpsDebug::GlitchLog* log) {
+ SkASSERT(next != end);
+ SkASSERT(!next->contains(end) || log);
+ if (next->t() > end->t()) {
+ SkTSwap(next, end);
+ }
+ do {
+ const SkOpPtT* ptT = next->ptT();
+ int index = 0;
+ bool somethingBetween;
+ do {
+ ++index;
+ ptT = ptT->next();
+ const SkOpPtT* checkPtT = next->ptT();
+ if (ptT == checkPtT) {
+ break;
+ }
+ bool looped = false;
+ for (int check = 0; check < index; ++check) {
+ if ((looped = checkPtT == ptT)) {
+ break;
+ }
+ checkPtT = checkPtT->next();
+ }
+ if (looped) {
+ SkASSERT(0);
+ break;
+ }
+ if (ptT->deleted()) {
+ continue;
+ }
+ if (ptT->segment() != oSegment) {
+ continue;
+ }
+ somethingBetween |= between(oStart, ptT->fT, oEnd);
+ } while (true);
+ SkASSERT(somethingBetween);
+ } while (next != end && (next = next->upCast()->next()));
+}
+
+static void DebugCheckOverlap(const SkCoincidentSpans* test, const SkCoincidentSpans* list,
+ const char* id, SkPathOpsDebug::GlitchLog* log) {
+ if (!list) {
+ return;
+ }
+ const SkOpSegment* coinSeg = test->coinPtTStart()->segment();
+ SkASSERT(coinSeg == test->coinPtTEnd()->segment());
+ const SkOpSegment* oppSeg = test->oppPtTStart()->segment();
+ SkASSERT(oppSeg == test->oppPtTEnd()->segment());
+ SkASSERT(coinSeg != test->oppPtTStart()->segment());
+ SkDEBUGCODE(double tcs = test->coinPtTStart()->fT);
+ SkASSERT(between(0, tcs, 1));
+ SkDEBUGCODE(double tce = test->coinPtTEnd()->fT);
+ SkASSERT(between(0, tce, 1));
+ SkASSERT(tcs < tce);
+ double tos = test->oppPtTStart()->fT;
+ SkASSERT(between(0, tos, 1));
+ double toe = test->oppPtTEnd()->fT;
+ SkASSERT(between(0, toe, 1));
+ SkASSERT(tos != toe);
+ if (tos > toe) {
+ SkTSwap(tos, toe);
+ }
+ do {
+ double lcs, lce, los, loe;
+ if (coinSeg == list->coinPtTStart()->segment()) {
+ if (oppSeg != list->oppPtTStart()->segment()) {
+ continue;
+ }
+ lcs = list->coinPtTStart()->fT;
+ lce = list->coinPtTEnd()->fT;
+ los = list->oppPtTStart()->fT;
+ loe = list->oppPtTEnd()->fT;
+ if (los > loe) {
+ SkTSwap(los, loe);
+ }
+ } else if (coinSeg == list->oppPtTStart()->segment()) {
+ if (oppSeg != list->coinPtTStart()->segment()) {
+ continue;
+ }
+ lcs = list->oppPtTStart()->fT;
+ lce = list->oppPtTEnd()->fT;
+ if (lcs > lce) {
+ SkTSwap(lcs, lce);
+ }
+ los = list->coinPtTStart()->fT;
+ loe = list->coinPtTEnd()->fT;
+ } else {
+ continue;
+ }
+ SkASSERT(tce < lcs || lce < tcs);
+ SkASSERT(toe < los || loe < tos);
+ } while ((list = list->next()));
+}
+
+
+static void DebugCheckOverlapTop(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
+ const char* id, SkPathOpsDebug::GlitchLog* log) {
+ // check for overlapping coincident spans
+ const SkCoincidentSpans* test = head;
+ while (test) {
+ const SkCoincidentSpans* next = test->next();
+ DebugCheckOverlap(test, next, id, log);
+ DebugCheckOverlap(test, opt, id, log);
+ test = next;
+ }
+}
+
+#if DEBUG_COINCIDENCE_VERBOSE
+void SkOpCoincidence::debugCheckOverlap(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+ DebugCheckOverlapTop(fHead, fTop, id, log);
+ DebugCheckOverlapTop(fTop, nullptr, id, log);
+}
+#endif
+
+static void DebugValidate(const SkCoincidentSpans* head, const SkCoincidentSpans* opt,
+ const char* id, SkPathOpsDebug::GlitchLog* log) {
+ // look for pts inside coincident spans that are not inside the opposite spans
+ const SkCoincidentSpans* coin = head;
+ while (coin) {
+ SkASSERT(SkOpCoincidence::Ordered(coin->coinPtTStart()->segment(),
+ coin->oppPtTStart()->segment()));
+ SkASSERT(coin->coinPtTStart()->span()->ptT() == coin->coinPtTStart());
+ SkASSERT(coin->coinPtTEnd()->span()->ptT() == coin->coinPtTEnd());
+ SkASSERT(coin->oppPtTStart()->span()->ptT() == coin->oppPtTStart());
+ SkASSERT(coin->oppPtTEnd()->span()->ptT() == coin->oppPtTEnd());
+ DebugValidate(coin->coinPtTStart()->span(), coin->coinPtTEnd()->span(),
+ coin->oppPtTStart()->fT, coin->oppPtTEnd()->fT, coin->oppPtTStart()->segment(),
+ id, log);
+ DebugValidate(coin->oppPtTStart()->span(), coin->oppPtTEnd()->span(),
+ coin->coinPtTStart()->fT, coin->coinPtTEnd()->fT, coin->coinPtTStart()->segment(),
+ id, log);
+ coin = coin->next();
+ }
+ DebugCheckOverlapTop(head, opt, id, log);
+}
+#endif
+
+void SkOpCoincidence::debugValidate() const {
+#if DEBUG_COINCIDENCE
+ // if (fGlobalState->debugCheckHealth()) {
+// return;
+// }
+ DebugValidate(fHead, fTop, nullptr, nullptr);
+ DebugValidate(fTop, nullptr, nullptr, nullptr);
+#endif
+}
+
+#if DEBUG_COINCIDENCE_VERBOSE
+void SkOpCoincidence::debugCheckValid(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+ DebugValidate(fHead, fTop, id, log);
+ DebugValidate(fTop, nullptr, id, log);
+}
+#endif
+
+#if DEBUG_COINCIDENCE_VERBOSE
void SkOpContour::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* log) const {
const SkOpSegment* segment = &fHead;
do {
@@ -1462,16 +1968,36 @@ void SkOpContour::debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* lo
} while ((segment = segment->next()));
}
-void SkOpContour::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log,
- const SkOpCoincidence* coincidence) const {
+// commmented-out lines keep this aligned with missingCoincidence()
+void SkOpContour::debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+// SkASSERT(fCount > 0);
const SkOpSegment* segment = &fHead;
+// bool result = false;
do {
- segment->debugMissingCoincidence(id, log, coincidence);
- } while ((segment = segment->next()));
+ if (fState->angleCoincidence()) {
+// #if DEBUG_ANGLE
+// segment->debugCheckAngleCoin();
+// #endif
+ } else if (segment->debugMissingCoincidence(id, log), false) {
+// result = true;
+// see FIXME in missingCoincidence()
+//
+//
+//
+ // continue;
+ }
+ segment = segment->next();
+ } while (segment);
+ return;
}
#endif
void SkOpSegment::debugValidate() const {
+#if DEBUG_COINCIDENCE
+ if (this->globalState()->debugCheckHealth()) {
+ return;
+ }
+#endif
#if DEBUG_VALIDATE
const SkOpSpanBase* span = &fHead;
double lastT = -1;
@@ -1500,56 +2026,47 @@ void SkOpSegment::debugValidate() const {
#endif
}
-bool SkOpSpanBase::debugAlignedEnd(double t, const SkPoint& pt) const {
- SkASSERT(zero_or_one(t));
- const SkOpSegment* segment = this->segment();
- SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
- if (!debugAlignedInner()) {
- return false;
- }
- if ((t ? segment->lastPt() : segment->pts()[0]) != pt) {
- return false;
+#if DEBUG_COINCIDENCE_VERBOSE
+// Commented-out lines keep this in sync with addOppAndMerge()
+// If the added points envelop adjacent spans, merge them in.
+void SkOpSpanBase::debugAddOppAndMerge(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* opp, bool* spanDeleted, bool* oppDeleted) const {
+ if (this->ptT()->debugAddOpp(opp->ptT())) {
+ this->debugCheckForCollapsedCoincidence(id, log);
}
- const SkOpPtT* ptT = &this->fPtT;
- SkASSERT(t == ptT->fT);
- SkASSERT(pt == ptT->fPt);
- const SkOpPtT* test = ptT, * stopPtT = ptT;
- while ((test = test->next()) != stopPtT) {
- const SkOpSegment* other = test->segment();
- if (other == this->segment()) {
- continue;
- }
- if (!zero_or_one(test->fT)) {
- continue;
- }
- if ((test->fT ? other->lastPt() : other->pts()[0]) != pt) {
- return false;
- }
+ // compute bounds of points in span
+ SkPathOpsBounds bounds;
+ bounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin);
+ const SkOpPtT* head = this->ptT();
+ const SkOpPtT* nextPt = head;
+ do {
+ bounds.add(nextPt->fPt);
+ } while ((nextPt = nextPt->next()) != head);
+ if (!bounds.width() && !bounds.height()) {
+ return;
}
- return this->fAligned;
+ this->debugMergeContained(id, log, bounds, spanDeleted);
+ opp->debugMergeContained(id, log, bounds, oppDeleted);
}
-bool SkOpSpanBase::debugAlignedInner() const {
- // force the spans to share points and t values
- const SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
- const SkPoint& pt = ptT->fPt;
+// Commented-out lines keep this in sync with checkForCollapsedCoincidence()
+void SkOpSpanBase::debugCheckForCollapsedCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const {
+ const SkOpCoincidence* coins = this->globalState()->coincidence();
+ if (coins->isEmpty()) {
+ return;
+ }
+// the insert above may have put both ends of a coincident run in the same span
+// for each coincident ptT in loop; see if its opposite in is also in the loop
+// this implementation is the motivation for marking that a ptT is referenced by a coincident span
+ const SkOpPtT* head = this->ptT();
+ const SkOpPtT* test = head;
do {
- if (ptT->fPt != pt) {
- return false;
+ if (!test->coincident()) {
+ continue;
}
- const SkOpSpanBase* span = ptT->span();
- const SkOpPtT* test = ptT;
- do {
- if ((test = test->next()) == stopPtT) {
- break;
- }
- if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) {
- return false;
- }
- } while (true);
- } while ((ptT = ptT->next()) != stopPtT);
- return true;
+ coins->debugMarkCollapsed(id, log, test);
+ } while ((test = test->next()) != head);
}
+#endif
bool SkOpSpanBase::debugCoinEndLoopCheck() const {
int loop = 0;
@@ -1574,16 +2091,57 @@ bool SkOpSpanBase::debugCoinEndLoopCheck() const {
return true;
}
-bool SkOpSpanBase::debugContains(const SkOpSegment* segment) const {
- const SkOpPtT* start = &fPtT;
- const SkOpPtT* walk = start;
- while ((walk = walk->next()) != start) {
- if (walk->segment() == segment) {
- return true;
+#if DEBUG_COINCIDENCE_VERBOSE
+// Commented-out lines keep this in sync with insertCoinEnd()
+void SkOpSpanBase::debugInsertCoinEnd(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSpanBase* coin) const {
+ if (containsCoinEnd(coin)) {
+// SkASSERT(coin->containsCoinEnd(this));
+ return;
+ }
+ debugValidate();
+// SkASSERT(this != coin);
+ log->record(kMarkCoinEnd_Glitch, id, this, coin);
+// coin->fCoinEnd = this->fCoinEnd;
+// this->fCoinEnd = coinNext;
+ debugValidate();
+}
+
+// Commented-out lines keep this in sync with mergeContained()
+void SkOpSpanBase::debugMergeContained(const char* id, SkPathOpsDebug::GlitchLog* log, const SkPathOpsBounds& bounds, bool* deleted) const {
+ // while adjacent spans' points are contained by the bounds, merge them
+ const SkOpSpanBase* prev = this;
+ const SkOpSegment* seg = this->segment();
+ while ((prev = prev->prev()) && bounds.contains(prev->pt()) && !seg->ptsDisjoint(prev, this)) {
+ if (prev->prev()) {
+ log->record(kMergeContained_Glitch, id, this, prev);
+ } else if (this->final()) {
+ log->record(kMergeContained_Glitch, id, this, prev);
+ // return;
+ } else {
+ log->record(kMergeContained_Glitch, id, prev, this);
}
}
- return false;
+ const SkOpSpanBase* current = this;
+ const SkOpSpanBase* next = this;
+ while (next->upCastable() && (next = next->upCast()->next())
+ && bounds.contains(next->pt()) && !seg->ptsDisjoint(this, next)) {
+ if (!current->prev() && next->final()) {
+ log->record(kMergeContained_Glitch, id, next, current);
+ current = next;
+ }
+ if (current->prev()) {
+ log->record(kMergeContained_Glitch, id, next, current);
+ current = next;
+ } else {
+ log->record(kMergeContained_Glitch, id, next, current);
+ current = next;
+ }
+ }
+#if DEBUG_COINCIDENCE
+ // this->globalState()->coincidence()->debugValidate();
+#endif
}
+#endif
const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
const SkOpSpanBase* end = *endPtr;
@@ -1599,6 +2157,11 @@ const SkOpSpan* SkOpSpanBase::debugStarter(SkOpSpanBase const** endPtr) const {
}
void SkOpSpanBase::debugValidate() const {
+#if DEBUG_COINCIDENCE
+ if (this->globalState()->debugCheckHealth()) {
+ return;
+ }
+#endif
#if DEBUG_VALIDATE
const SkOpPtT* ptT = &fPtT;
SkASSERT(ptT->span() == this);
@@ -1643,6 +2206,39 @@ bool SkOpSpan::debugCoinLoopCheck() const {
return true;
}
+#if DEBUG_COINCIDENCE_VERBOSE
+// Commented-out lines keep this in sync with insertCoincidence() in header
+void SkOpSpan::debugInsertCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSpan* coin) const {
+ if (containsCoincidence(coin)) {
+// SkASSERT(coin->containsCoincidence(this));
+ return;
+ }
+ debugValidate();
+// SkASSERT(this != coin);
+ log->record(kMarkCoinStart_Glitch, id, this, coin);
+// coin->fCoincident = this->fCoincident;
+// this->fCoincident = coinNext;
+ debugValidate();
+}
+
+// Commented-out lines keep this in sync with insertCoincidence()
+void SkOpSpan::debugInsertCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpSegment* segment, bool flipped) const {
+ if (this->containsCoincidence(segment)) {
+ return;
+ }
+ const SkOpPtT* next = &fPtT;
+ while ((next = next->next()) != &fPtT) {
+ if (next->segment() == segment) {
+ log->record(kMarkCoinInsert_Glitch, id, flipped ? next->span()->prev() : next->span());
+ return;
+ }
+ }
+#if DEBUG_COINCIDENCE
+ log->record(kMarkCoinMissing_Glitch, id, segment, this);
+#endif
+}
+#endif
+
// called only by test code
int SkIntersections::debugCoincidentUsed() const {
if (!fIsCoincident[0]) {
@@ -1667,6 +2263,27 @@ int SkIntersections::debugCoincidentUsed() const {
#include "SkOpContour.h"
+// Commented-out lines keep this in sync with addOpp()
+bool SkOpPtT::debugAddOpp(const SkOpPtT* opp) const {
+ // find the fOpp ptr to opp
+ const SkOpPtT* oppPrev = opp->fNext;
+ if (oppPrev == this) {
+ return false;
+ }
+ while (oppPrev->fNext != opp) {
+ oppPrev = oppPrev->fNext;
+ if (oppPrev == this) {
+ return false;
+ }
+ }
+// const SkOpPtT* oldNext = this->fNext;
+ SkASSERT(this != opp);
+// this->fNext = opp;
+// SkASSERT(oppPrev != oldNext);
+// oppPrev->fNext = oldNext;
+ return true;
+}
+
bool SkOpPtT::debugContains(const SkOpPtT* check) const {
SkASSERT(this != check);
const SkOpPtT* ptT = this;
@@ -1736,6 +2353,11 @@ int SkOpPtT::debugLoopLimit(bool report) const {
}
void SkOpPtT::debugValidate() const {
+#if DEBUG_COINCIDENCE
+ if (this->globalState()->debugCheckHealth()) {
+ return;
+ }
+#endif
#if DEBUG_VALIDATE
SkOpGlobalState::Phase phase = contour()->globalState()->phase();
if (phase == SkOpGlobalState::kIntersecting
diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h
index bd27e7df2d..9e910686b7 100644
--- a/src/pathops/SkPathOpsDebug.h
+++ b/src/pathops/SkPathOpsDebug.h
@@ -37,6 +37,8 @@
if (!SkPathOpsDebug::ValidWind(x)) strcpy(x##Str, "?"); \
else SK_SNPRINTF(x##Str, sizeof(x##Str), "%d", x)
+#define DEBUG_UNDER_DEVELOPMENT 01
+
#if FORCE_RELEASE
#define DEBUG_ACTIVE_OP 0
@@ -47,6 +49,7 @@
#define DEBUG_ANGLE 0
#define DEBUG_ASSEMBLE 0
#define DEBUG_COINCIDENCE 0
+#define DEBUG_COINCIDENCE_VERBOSE 0
#define DEBUG_CUBIC_BINARY_SEARCH 0
#define DEBUG_CUBIC_SPLIT 0
#define DEBUG_DUMP_SEGMENTS 0
@@ -74,12 +77,13 @@
#define DEBUG_ALIGNMENT 0
#define DEBUG_ANGLE 1
#define DEBUG_ASSEMBLE 1
-#define DEBUG_COINCIDENCE 0
+#define DEBUG_COINCIDENCE 01
+#define DEBUG_COINCIDENCE_VERBOSE 01
#define DEBUG_CUBIC_BINARY_SEARCH 0
#define DEBUG_CUBIC_SPLIT 1
#define DEBUG_DUMP_SEGMENTS 1
#define DEBUG_FLOW 1
-#define DEBUG_LIMIT_WIND_SUM 5
+#define DEBUG_LIMIT_WIND_SUM 15
#define DEBUG_MARK_DONE 1
#define DEBUG_PATH_CONSTRUCTION 1
#define DEBUG_PERP 1
@@ -186,6 +190,7 @@ public:
static void BumpTestName(char* );
#endif
static const char* OpStr(SkPathOp );
+ static void ShowActiveSpans(SkOpContourHead* contourList);
static void ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration);
static void ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name);
@@ -193,37 +198,37 @@ public:
static void CheckHealth(class SkOpContourHead* contourList, const char* id);
- static const struct SkOpAngle* DebugAngleAngle(const struct SkOpAngle*, int id);
- static class SkOpContour* DebugAngleContour(struct SkOpAngle*, int id);
- static const class SkOpPtT* DebugAnglePtT(const struct SkOpAngle*, int id);
- static const class SkOpSegment* DebugAngleSegment(const struct SkOpAngle*, int id);
- static const class SkOpSpanBase* DebugAngleSpan(const struct SkOpAngle*, int id);
+ static const class SkOpAngle* DebugAngleAngle(const class SkOpAngle*, int id);
+ static class SkOpContour* DebugAngleContour(class SkOpAngle*, int id);
+ static const class SkOpPtT* DebugAnglePtT(const class SkOpAngle*, int id);
+ static const class SkOpSegment* DebugAngleSegment(const class SkOpAngle*, int id);
+ static const class SkOpSpanBase* DebugAngleSpan(const class SkOpAngle*, int id);
- static const struct SkOpAngle* DebugContourAngle(class SkOpContour*, int id);
+ static const class SkOpAngle* DebugContourAngle(class SkOpContour*, int id);
static class SkOpContour* DebugContourContour(class SkOpContour*, int id);
static const class SkOpPtT* DebugContourPtT(class SkOpContour*, int id);
static const class SkOpSegment* DebugContourSegment(class SkOpContour*, int id);
static const class SkOpSpanBase* DebugContourSpan(class SkOpContour*, int id);
- static const struct SkOpAngle* DebugCoincidenceAngle(class SkOpCoincidence*, int id);
+ static const class SkOpAngle* DebugCoincidenceAngle(class SkOpCoincidence*, int id);
static class SkOpContour* DebugCoincidenceContour(class SkOpCoincidence*, int id);
static const class SkOpPtT* DebugCoincidencePtT(class SkOpCoincidence*, int id);
static const class SkOpSegment* DebugCoincidenceSegment(class SkOpCoincidence*, int id);
static const class SkOpSpanBase* DebugCoincidenceSpan(class SkOpCoincidence*, int id);
- static const struct SkOpAngle* DebugPtTAngle(const class SkOpPtT*, int id);
+ static const class SkOpAngle* DebugPtTAngle(const class SkOpPtT*, int id);
static class SkOpContour* DebugPtTContour(class SkOpPtT*, int id);
static const class SkOpPtT* DebugPtTPtT(const class SkOpPtT*, int id);
static const class SkOpSegment* DebugPtTSegment(const class SkOpPtT*, int id);
static const class SkOpSpanBase* DebugPtTSpan(const class SkOpPtT*, int id);
- static const struct SkOpAngle* DebugSegmentAngle(const class SkOpSegment*, int id);
+ static const class SkOpAngle* DebugSegmentAngle(const class SkOpSegment*, int id);
static class SkOpContour* DebugSegmentContour(class SkOpSegment*, int id);
static const class SkOpPtT* DebugSegmentPtT(const class SkOpSegment*, int id);
static const class SkOpSegment* DebugSegmentSegment(const class SkOpSegment*, int id);
static const class SkOpSpanBase* DebugSegmentSpan(const class SkOpSegment*, int id);
- static const struct SkOpAngle* DebugSpanAngle(const class SkOpSpanBase*, int id);
+ static const class SkOpAngle* DebugSpanAngle(const class SkOpSpanBase*, int id);
static class SkOpContour* DebugSpanContour(class SkOpSpanBase*, int id);
static const class SkOpPtT* DebugSpanPtT(const class SkOpSpanBase*, int id);
static const class SkOpSegment* DebugSpanSegment(const class SkOpSpanBase*, int id);
diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp
index 1970eea124..188af5722b 100644
--- a/src/pathops/SkPathOpsOp.cpp
+++ b/src/pathops/SkPathOpsOp.cpp
@@ -88,7 +88,7 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase**
}
static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
- const int xorMask, const int xorOpMask, SkPathWriter* simple, SkChunkAlloc* allocator) {
+ const int xorMask, const int xorOpMask, SkPathWriter* simple) {
bool unsortable = false;
do {
SkOpSpan* span = FindSortableTop(contourList);
@@ -117,11 +117,9 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
if (!current->addCurveTo(start, end, simple)) {
return false;
}
- #if DEBUG_ACTIVE_SPANS
if (!simple->isClosed()) {
- DebugShowActiveSpans(contourList);
+ SkPathOpsDebug::ShowActiveSpans(contourList);
}
- #endif
}
break;
}
@@ -163,9 +161,7 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
}
}
current = findChaseOp(chase, &start, &end);
- #if DEBUG_ACTIVE_SPANS
- DebugShowActiveSpans(contourList);
- #endif
+ SkPathOpsDebug::ShowActiveSpans(contourList);
if (!current) {
break;
}
@@ -226,7 +222,7 @@ static void dump_op(const SkPath& one, const SkPath& two, SkPathOp op) {
dump_path(file, two, false, true);
fprintf(file, " SkPath path2(path);\n");
fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
- fprintf(file, "}\n");
+ fprintf(file, "}\n");
fclose(file);
}
#endif
@@ -253,40 +249,42 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tune
SkOpContour contour;
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
- SkOpCoincidence coincidence;
- SkOpGlobalState globalState(&coincidence, contourList
- SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
+ SkOpGlobalState globalState(contourList, &allocator
+ SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
+ SkOpCoincidence coincidence(&globalState);
#if DEBUGGING_PATHOPS_FROM_HOST
dump_op(one, two, op);
-#endif
-#if 0 && DEBUG_SHOW_TEST_NAME
- char* debugName = DEBUG_FILENAME_STRING;
- if (debugName && debugName[0]) {
- SkPathOpsDebug::BumpTestName(debugName);
- SkPathOpsDebug::ShowPath(one, two, op, debugName);
- }
#endif
op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]
? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
- const SkPath* minuend = &one;
- const SkPath* subtrahend = &two;
+ SkScalar scaleFactor = SkTMax(ScaleFactor(one), ScaleFactor(two));
+ SkPath scaledOne, scaledTwo;
+ const SkPath* minuend, * subtrahend;
+ if (scaleFactor > SK_Scalar1) {
+ ScalePath(one, 1.f / scaleFactor, &scaledOne);
+ minuend = &scaledOne;
+ ScalePath(two, 1.f / scaleFactor, &scaledTwo);
+ subtrahend = &scaledTwo;
+ } else {
+ minuend = &one;
+ subtrahend = &two;
+ }
if (op == kReverseDifference_SkPathOp) {
- minuend = &two;
- subtrahend = &one;
+ SkTSwap(minuend, subtrahend);
op = kDifference_SkPathOp;
}
#if DEBUG_SORT
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
// turn path into list of segments
- SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState);
+ SkOpEdgeBuilder builder(*minuend, &contour, &globalState);
if (builder.unparseable()) {
return false;
}
const int xorMask = builder.xorMask();
builder.addOperand(*subtrahend);
- if (!builder.finish(&allocator)) {
+ if (!builder.finish()) {
return false;
}
#if DEBUG_DUMP_SEGMENTS
@@ -304,14 +302,14 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
SkOpContour* current = contourList;
do {
SkOpContour* next = current;
- while (AddIntersectTs(current, next, &coincidence, &allocator)
+ while (AddIntersectTs(current, next, &coincidence)
&& (next = next->next()))
;
} while ((current = current->next()));
#if DEBUG_VALIDATE
globalState.setPhase(SkOpGlobalState::kWalking);
#endif
- if (!HandleCoincidence(contourList, &coincidence, &allocator)) {
+ if (!HandleCoincidence(contourList, &coincidence)) {
return false;
}
#if DEBUG_ALIGNMENT
@@ -321,7 +319,7 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
result->reset();
result->setFillType(fillType);
SkPathWriter wrapper(*result);
- bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator);
+ bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
{ // if some edges could not be resolved, assemble remaining fragments
SkPath temp;
temp.setFillType(fillType);
@@ -339,6 +337,9 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
debugWorstState.debugDoYourWorst(&globalState);
}
#endif
+ if (scaleFactor > 1) {
+ ScalePath(*result, scaleFactor, result);
+ }
return true;
}
@@ -458,6 +459,6 @@ bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
}
return true;
#else
- return OpDebug(one, two, op, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
+ return OpDebug(one, two, op, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
#endif
}
diff --git a/src/pathops/SkPathOpsPoint.h b/src/pathops/SkPathOpsPoint.h
index e3f722bede..f30f155e8f 100644
--- a/src/pathops/SkPathOpsPoint.h
+++ b/src/pathops/SkPathOpsPoint.h
@@ -58,12 +58,20 @@ struct SkDVector {
}
// similar to cross, this bastardization considers nearly coincident to be zero
+ // uses ulps epsilon == 16
double crossCheck(const SkDVector& a) const {
double xy = fX * a.fY;
double yx = fY * a.fX;
return AlmostEqualUlps(xy, yx) ? 0 : xy - yx;
}
+ // allow tinier numbers
+ double crossNoNormalCheck(const SkDVector& a) const {
+ double xy = fX * a.fY;
+ double yx = fY * a.fX;
+ return AlmostEqualUlpsNoNormalCheck(xy, yx) ? 0 : xy - yx;
+ }
+
double dot(const SkDVector& a) const {
return fX * a.fX + fY * a.fY;
}
@@ -75,6 +83,12 @@ struct SkDVector {
double lengthSquared() const {
return fX * fX + fY * fY;
}
+
+ void normalize() {
+ double inverseLength = 1 / this->length();
+ fX *= inverseLength;
+ fY *= inverseLength;
+ }
};
struct SkDPoint {
@@ -164,7 +178,7 @@ struct SkDPoint {
float tiniest = SkTMin(SkTMin(SkTMin(a.fX, b.fX), a.fY), b.fY);
float largest = SkTMax(SkTMax(SkTMax(a.fX, b.fX), a.fY), b.fY);
largest = SkTMax(largest, -tiniest);
- return AlmostPequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
+ return AlmostDequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
}
// only used by testing
diff --git a/src/pathops/SkPathOpsQuad.cpp b/src/pathops/SkPathOpsQuad.cpp
index 3deab21133..dafa3f5b75 100644
--- a/src/pathops/SkPathOpsQuad.cpp
+++ b/src/pathops/SkPathOpsQuad.cpp
@@ -191,6 +191,12 @@ SkDPoint SkDQuad::ptAtT(double t) const {
}
static double interp_quad_coords(const double* src, double t) {
+ if (0 == t) {
+ return src[0];
+ }
+ if (1 == t) {
+ return src[4];
+ }
double ab = SkDInterp(src[0], src[2], t);
double bc = SkDInterp(src[2], src[4], t);
double abc = SkDInterp(ab, bc, t);
@@ -228,8 +234,11 @@ Group the known values on one side:
B = D*2 - A/2 - C/2
*/
-// OPTIMIZE : special case either or both of t1 = 0, t2 = 1
+// OPTIMIZE? : special case t1 = 1 && t2 = 0
SkDQuad SkDQuad::subDivide(double t1, double t2) const {
+ if (0 == t1 && 1 == t2) {
+ return *this;
+ }
SkDQuad dst;
double ax = dst[0].fX = interp_quad_coords(&fPts[0].fX, t1);
double ay = dst[0].fY = interp_quad_coords(&fPts[0].fY, t1);
@@ -263,7 +272,7 @@ SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, dou
b = i.pt(0);
} else {
SkASSERT(i.used() <= 2);
- b = SkDPoint::Mid(b0[1], b1[1]);
+ return SkDPoint::Mid(b0[1], b1[1]);
}
if (t1 == 0 || t2 == 0) {
align(0, &b);
diff --git a/src/pathops/SkPathOpsSimplify.cpp b/src/pathops/SkPathOpsSimplify.cpp
index fa1003054e..dcd75f1666 100644
--- a/src/pathops/SkPathOpsSimplify.cpp
+++ b/src/pathops/SkPathOpsSimplify.cpp
@@ -10,8 +10,7 @@
#include "SkPathOpsCommon.h"
#include "SkPathWriter.h"
-static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
- SkChunkAlloc* allocator, bool* closable) {
+static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple, bool* closable) {
bool unsortable = false;
do {
SkOpSpan* span = FindSortableTop(contourList);
@@ -40,11 +39,9 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
if (!current->addCurveTo(start, end, simple)) {
return false;
}
- #if DEBUG_ACTIVE_SPANS
if (!simple->isClosed()) {
- DebugShowActiveSpans(contourList);
+ SkPathOpsDebug::ShowActiveSpans(contourList);
}
- #endif
}
break;
}
@@ -86,9 +83,7 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
}
}
current = FindChase(&chase, &start, &end);
- #if DEBUG_ACTIVE_SPANS
- DebugShowActiveSpans(contourList);
- #endif
+ SkPathOpsDebug::ShowActiveSpans(contourList);
if (!current) {
break;
}
@@ -99,8 +94,7 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
}
// returns true if all edges were processed
-static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
- SkChunkAlloc* allocator, bool* closable) {
+static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple, bool* closable) {
SkOpSegment* current;
SkOpSpanBase* start;
SkOpSpanBase* end;
@@ -108,11 +102,9 @@ static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
*closable = true;
while ((current = FindUndone(contourList, &start, &end))) {
do {
- #if DEBUG_ACTIVE_SPANS
if (!unsortable && current->done()) {
- DebugShowActiveSpans(contourList);
+ SkPathOpsDebug::ShowActiveSpans(contourList);
}
- #endif
SkASSERT(unsortable || !current->done());
SkOpSpanBase* nextStart = start;
SkOpSpanBase* nextEnd = end;
@@ -124,11 +116,9 @@ static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
if (!current->addCurveTo(start, end, simple)) {
return false;
}
- #if DEBUG_ACTIVE_SPANS
if (!simple->isClosed()) {
- DebugShowActiveSpans(contourList);
+ SkPathOpsDebug::ShowActiveSpans(contourList);
}
- #endif
}
break;
}
@@ -156,16 +146,14 @@ static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
*closable = false;
}
simple->close();
- #if DEBUG_ACTIVE_SPANS
- DebugShowActiveSpans(contourList);
- #endif
+ SkPathOpsDebug::ShowActiveSpans(contourList);
}
return true;
}
// FIXME : add this as a member of SkPath
-bool Simplify(const SkPath& path, SkPath* result) {
- SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
+bool SimplifyDebug(const SkPath& path, SkPath* result
+ SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
// returns 1 for evenodd, -1 for winding, regardless of inverse-ness
SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
: SkPath::kEvenOdd_FillType;
@@ -177,16 +165,26 @@ bool Simplify(const SkPath& path, SkPath* result) {
return true;
}
// turn path into list of segments
- SkOpCoincidence coincidence;
+ SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
SkOpContour contour;
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
- SkOpGlobalState globalState(&coincidence, contourList SkDEBUGPARAMS(false)
- SkDEBUGPARAMS(nullptr));
+ SkOpGlobalState globalState(contourList, &allocator
+ SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
+ SkOpCoincidence coincidence(&globalState);
+ SkScalar scaleFactor = ScaleFactor(path);
+ SkPath scaledPath;
+ const SkPath* workingPath;
+ if (scaleFactor > SK_Scalar1) {
+ ScalePath(path, 1.f / scaleFactor, &scaledPath);
+ workingPath = &scaledPath;
+ } else {
+ workingPath = &path;
+ }
#if DEBUG_SORT
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
- SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
- if (!builder.finish(&allocator)) {
+ SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
+ if (!builder.finish()) {
return false;
}
#if DEBUG_DUMP_SEGMENTS
@@ -201,13 +199,13 @@ bool Simplify(const SkPath& path, SkPath* result) {
SkOpContour* current = contourList;
do {
SkOpContour* next = current;
- while (AddIntersectTs(current, next, &coincidence, &allocator)
+ while (AddIntersectTs(current, next, &coincidence)
&& (next = next->next()));
} while ((current = current->next()));
#if DEBUG_VALIDATE
globalState.setPhase(SkOpGlobalState::kWalking);
#endif
- if (!HandleCoincidence(contourList, &coincidence, &allocator)) {
+ if (!HandleCoincidence(contourList, &coincidence)) {
return false;
}
#if DEBUG_DUMP_ALIGNMENT
@@ -219,8 +217,8 @@ bool Simplify(const SkPath& path, SkPath* result) {
SkPathWriter wrapper(*result);
bool closable SK_INIT_TO_AVOID_WARNING;
if (builder.xorMask() == kWinding_PathOpsMask
- ? !bridgeWinding(contourList, &wrapper, &allocator, &closable)
- : !bridgeXor(contourList, &wrapper, &allocator, &closable)) {
+ ? !bridgeWinding(contourList, &wrapper, &closable)
+ : !bridgeXor(contourList, &wrapper, &closable)) {
return false;
}
if (!closable)
@@ -232,6 +230,12 @@ bool Simplify(const SkPath& path, SkPath* result) {
*result = *assembled.nativePath();
result->setFillType(fillType);
}
+ if (scaleFactor > 1) {
+ ScalePath(*result, scaleFactor, result);
+ }
return true;
}
+bool Simplify(const SkPath& path, SkPath* result) {
+ return SimplifyDebug(path, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
+}
diff --git a/src/pathops/SkPathOpsTSect.h b/src/pathops/SkPathOpsTSect.h
index 48aa540945..9032af83eb 100644
--- a/src/pathops/SkPathOpsTSect.h
+++ b/src/pathops/SkPathOpsTSect.h
@@ -250,7 +250,7 @@ private:
SkTSpan<TCurve, OppCurve>* addFollowing(SkTSpan<TCurve, OppCurve>* prior);
void addForPerp(SkTSpan<OppCurve, TCurve>* span, double t);
SkTSpan<TCurve, OppCurve>* addOne();
-
+
SkTSpan<TCurve, OppCurve>* addSplitAt(SkTSpan<TCurve, OppCurve>* span, double t) {
SkTSpan<TCurve, OppCurve>* result = this->addOne();
result->splitAt(span, t, &fHeap);
@@ -343,7 +343,7 @@ void SkTCoincident<TCurve, OppCurve>::setPerp(const TCurve& c1, double t,
if (used == 0 || used == 3) {
this->init();
return;
- }
+ }
fPerpT = i[0][0];
fPerpPt = i.pt(0);
SkASSERT(used <= 2);
@@ -858,7 +858,7 @@ SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::addOne() {
result->reset();
result->fHasPerp = false;
result->fDeleted = false;
- ++fActiveCount;
+ ++fActiveCount;
PATH_OPS_DEBUG_T_SECT_CODE(result->fID = fDebugCount++ * 2 + fID);
SkDEBUGCODE(result->fDebugSect = this);
#ifdef SK_DEBUG
@@ -969,6 +969,9 @@ void SkTSect<TCurve, OppCurve>::coincidentCheck(SkTSect<OppCurve, TCurve>* sect2
do {
coinStart = this->extractCoincident(sect2, coinStart, last);
} while (coinStart && !last->fDeleted);
+ if (!fHead || !sect2->fHead) {
+ break;
+ }
} while ((first = next));
}
@@ -1208,7 +1211,7 @@ SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::extractCoincident(
}
this->validate();
sect2->validate();
- return last && !last->fDeleted ? last : nullptr;
+ return last && !last->fDeleted && fHead && sect2->fHead ? last : nullptr;
}
template<typename TCurve, typename OppCurve>
@@ -1511,7 +1514,7 @@ template<typename TCurve, typename OppCurve>
void SkTSect<TCurve, OppCurve>::matchedDirCheck(double t, const SkTSect<OppCurve, TCurve>* sect2,
double t2, bool* calcMatched, bool* oppMatched) const {
if (*calcMatched) {
- SkASSERT(*oppMatched == this->matchedDirection(t, sect2, t2));
+ SkASSERT(*oppMatched == this->matchedDirection(t, sect2, t2));
} else {
*oppMatched = this->matchedDirection(t, sect2, t2);
*calcMatched = true;
@@ -1584,7 +1587,7 @@ SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::prev(
test = test->fNext;
SkASSERT(test);
}
- return result;
+ return result;
}
template<typename TCurve, typename OppCurve>
@@ -1939,7 +1942,7 @@ struct SkClosestRecord {
fC1Index = mate.fC1Index;
fC2Index = mate.fC2Index;
}
-
+
void reset() {
fClosest = FLT_MAX;
SkDEBUGCODE(fC1Span = nullptr);
@@ -2099,7 +2102,7 @@ void SkTSect<TCurve, OppCurve>::BinarySearch(SkTSect<TCurve, OppCurve>* sect1,
gets stuck in a loop. It adds an extension to allow a coincident end
perpendicular to track its intersection in the opposite curve. However,
the bounding box of the extension does not intersect the original curve,
- so the extension is discarded, only to be added again the next time around. */
+ so the extension is discarded, only to be added again the next time around. */
sect1->coincidentForce(sect2, start1s, start1e);
sect1->validate();
sect2->validate();
diff --git a/src/pathops/SkPathOpsTightBounds.cpp b/src/pathops/SkPathOpsTightBounds.cpp
index 19593c228b..e3a3108bd7 100644
--- a/src/pathops/SkPathOpsTightBounds.cpp
+++ b/src/pathops/SkPathOpsTightBounds.cpp
@@ -11,11 +11,20 @@ bool TightBounds(const SkPath& path, SkRect* result) {
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
SkOpContour contour;
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
- SkOpGlobalState globalState(nullptr, contourList SkDEBUGPARAMS(false)
+ SkOpGlobalState globalState(contourList, &allocator SkDEBUGPARAMS(false)
SkDEBUGPARAMS(nullptr));
// turn path into list of segments
- SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
- if (!builder.finish(&allocator)) {
+ SkScalar scaleFactor = ScaleFactor(path);
+ SkPath scaledPath;
+ const SkPath* workingPath;
+ if (scaleFactor > SK_Scalar1) {
+ ScalePath(path, 1.f / scaleFactor, &scaledPath);
+ workingPath = &scaledPath;
+ } else {
+ workingPath = &path;
+ }
+ SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
+ if (!builder.finish()) {
return false;
}
if (!SortContourList(&contourList, false, false)) {
diff --git a/src/pathops/SkPathOpsTypes.cpp b/src/pathops/SkPathOpsTypes.cpp
index bad1bc7a8b..d25df12873 100644
--- a/src/pathops/SkPathOpsTypes.cpp
+++ b/src/pathops/SkPathOpsTypes.cpp
@@ -25,6 +25,13 @@ static bool equal_ulps(float a, float b, int epsilon, int depsilon) {
return aBits < bBits + epsilon && bBits < aBits + epsilon;
}
+static bool equal_ulps_no_normal_check(float a, float b, int epsilon, int depsilon) {
+ int aBits = SkFloatAs2sCompliment(a);
+ int bBits = SkFloatAs2sCompliment(b);
+ // Find the difference in ULPs.
+ return aBits < bBits + epsilon && bBits < aBits + epsilon;
+}
+
static bool equal_ulps_pin(float a, float b, int epsilon, int depsilon) {
if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) {
return false;
@@ -120,6 +127,11 @@ bool AlmostEqualUlps(float a, float b) {
return equal_ulps(a, b, UlpsEpsilon, UlpsEpsilon);
}
+bool AlmostEqualUlpsNoNormalCheck(float a, float b) {
+ const int UlpsEpsilon = 16;
+ return equal_ulps_no_normal_check(a, b, UlpsEpsilon, UlpsEpsilon);
+}
+
bool AlmostEqualUlps_Pin(float a, float b) {
const int UlpsEpsilon = 16;
return equal_ulps_pin(a, b, UlpsEpsilon, UlpsEpsilon);
@@ -212,10 +224,12 @@ double SkDCubeRoot(double x) {
return result;
}
-SkOpGlobalState::SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head
+SkOpGlobalState::SkOpGlobalState(SkOpContourHead* head,
+ SkChunkAlloc* allocator
SkDEBUGPARAMS(bool debugSkipAssert)
SkDEBUGPARAMS(const char* testName))
- : fCoincidence(coincidence)
+ : fAllocator(allocator)
+ , fCoincidence(nullptr)
, fContourHead(head)
, fNested(0)
, fWindingFailed(false)
@@ -227,13 +241,9 @@ SkOpGlobalState::SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead*
SkDEBUGPARAMS(fContourID(0))
SkDEBUGPARAMS(fPtTID(0))
SkDEBUGPARAMS(fSegmentID(0))
- SkDEBUGPARAMS(fSpanID(0))
- SkDEBUGPARAMS(fDebugSkipAssert(debugSkipAssert)) {
- if (coincidence) {
- coincidence->debugSetGlobalState(this);
- }
+SkDEBUGPARAMS(fSpanID(0))
+SkDEBUGPARAMS(fDebugSkipAssert(debugSkipAssert)) {
#if DEBUG_T_SECT_LOOP_COUNT
debugResetLoopCounts();
#endif
}
-
diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h
index 00c3e5fe93..ad2ad463e6 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -22,6 +22,7 @@ enum SkPathOpsMask {
kEvenOdd_PathOpsMask = 1
};
+class SkChunkAlloc;
class SkOpCoincidence;
class SkOpContour;
class SkOpContourHead;
@@ -30,8 +31,8 @@ class SkIntersectionHelper;
class SkOpGlobalState {
public:
- SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head
- SkDEBUGPARAMS(bool debugSkipAssert)
+ SkOpGlobalState(SkOpContourHead* head,
+ SkChunkAlloc* allocator SkDEBUGPARAMS(bool debugSkipAssert)
SkDEBUGPARAMS(const char* testName));
enum Phase {
@@ -44,6 +45,10 @@ public:
kMaxWindingTries = 10
};
+ SkChunkAlloc* allocator() {
+ return fAllocator;
+ }
+
bool angleCoincidence() const {
return fAngleCoincidence;
}
@@ -65,7 +70,8 @@ public:
}
#ifdef SK_DEBUG
- const struct SkOpAngle* debugAngle(int id) const;
+ const class SkOpAngle* debugAngle(int id) const;
+ const SkOpCoincidence* debugCoincidence() const;
SkOpContour* debugContour(int id);
const class SkOpPtT* debugPtT(int id) const;
bool debugRunFail() const;
@@ -83,6 +89,11 @@ public:
void debugResetLoopCounts();
#endif
+#if DEBUG_COINCIDENCE
+ void debugSetCheckHealth(bool check) { fDebugCheckHealth = check; }
+ bool debugCheckHealth() const { return fDebugCheckHealth; }
+#endif
+
int nested() const {
return fNested;
}
@@ -120,6 +131,10 @@ public:
void setAngleCoincidence() {
fAngleCoincidence = true;
}
+
+ void setCoincidence(SkOpCoincidence* coincidence) {
+ fCoincidence = coincidence;
+ }
void setContourHead(SkOpContourHead* contourHead) {
fContourHead = contourHead;
@@ -140,6 +155,7 @@ public:
}
private:
+ SkChunkAlloc* fAllocator;
SkOpCoincidence* fCoincidence;
SkOpContourHead* fContourHead;
int fNested;
@@ -162,6 +178,9 @@ private:
SkPoint fDebugWorstPts[24];
float fDebugWorstWeight[6];
#endif
+#if DEBUG_COINCIDENCE
+ bool fDebugCheckHealth;
+#endif
};
// Use Almost Equal when comparing coordinates. Use epsilon to compare T values.
@@ -170,6 +189,11 @@ inline bool AlmostEqualUlps(double a, double b) {
return AlmostEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b));
}
+bool AlmostEqualUlpsNoNormalCheck(float a, float b);
+inline bool AlmostEqualUlpsNoNormalCheck(double a, double b) {
+ return AlmostEqualUlpsNoNormalCheck(SkDoubleToScalar(a), SkDoubleToScalar(b));
+}
+
bool AlmostEqualUlps_Pin(float a, float b);
inline bool AlmostEqualUlps_Pin(double a, double b) {
return AlmostEqualUlps_Pin(SkDoubleToScalar(a), SkDoubleToScalar(b));
@@ -246,6 +270,8 @@ const double MORE_ROUGH_EPSILON = FLT_EPSILON * 256;
const double WAY_ROUGH_EPSILON = FLT_EPSILON * 2048;
const double BUMP_EPSILON = FLT_EPSILON * 4096;
+const SkScalar INVERSE_NUMBER_RANGE = FLT_EPSILON_ORDERABLE_ERR;
+
inline bool zero_or_one(double x) {
return x == 0 || x == 1;
}
@@ -298,7 +324,6 @@ inline bool approximately_zero_inverse(double x) {
return fabs(x) > FLT_EPSILON_INVERSE;
}
-// OPTIMIZATION: if called multiple times with the same denom, we want to pass 1/y instead
inline bool approximately_zero_when_compared_to(double x, double y) {
return x == 0 || fabs(x) < fabs(y * FLT_EPSILON);
}
@@ -307,6 +332,10 @@ inline bool precisely_zero_when_compared_to(double x, double y) {
return x == 0 || fabs(x) < fabs(y * DBL_EPSILON);
}
+inline bool roughly_zero_when_compared_to(double x, double y) {
+ return x == 0 || fabs(x) < fabs(y * ROUGH_EPSILON);
+}
+
// Use this for comparing Ts in the range of 0 to 1. For general numbers (larger and smaller) use
// AlmostEqualUlps instead.
inline bool approximately_equal(double x, double y) {
diff --git a/src/pathops/SkPathOpsWinding.cpp b/src/pathops/SkPathOpsWinding.cpp
index 584ebd457a..4c6c636e2b 100644
--- a/src/pathops/SkPathOpsWinding.cpp
+++ b/src/pathops/SkPathOpsWinding.cpp
@@ -192,7 +192,7 @@ SkOpSpan* SkOpSegment::windingSpanAtT(double tHit) {
next = span->next();
if (approximately_equal(tHit, next->t())) {
return nullptr;
- }
+ }
if (tHit < next->t()) {
return span;
}
@@ -243,6 +243,10 @@ bool SkOpSpan::sortableTop(SkOpContour* contourHead) {
}
SkOpRayHit* hitHead = &hitBase;
dir = static_cast<SkOpRayDir>(static_cast<int>(dir) + dirOffset);
+ if (hitBase.fSpan && hitBase.fSpan->segment()->verb() > SkPath::kLine_Verb
+ && !pt_yx(hitBase.fSlope.asSkVector(), dir)) {
+ return false;
+ }
SkOpContour* contour = contourHead;
do {
contour->rayCheck(hitBase, dir, &hitHead, &allocator);
@@ -256,11 +260,11 @@ bool SkOpSpan::sortableTop(SkOpContour* contourHead) {
}
int count = sorted.count();
SkTQSort(sorted.begin(), sorted.end() - 1, xy_index(dir)
- ? less_than(dir) ? hit_compare_y : reverse_hit_compare_y
+ ? less_than(dir) ? hit_compare_y : reverse_hit_compare_y
: less_than(dir) ? hit_compare_x : reverse_hit_compare_x);
// verify windings
#if DEBUG_WINDING
- SkDebugf("%s dir=%s seg=%d t=%1.9g pt=(%1.9g,%1.9g)\n", __FUNCTION__,
+ SkDebugf("%s dir=%s seg=%d t=%1.9g pt=(%1.9g,%1.9g)\n", __FUNCTION__,
gDebugRayDirName[static_cast<int>(dir)], hitBase.fSpan->segment()->debugID(),
hitBase.fT, hitBase.fPt.fX, hitBase.fPt.fY);
for (int index = 0; index < count; ++index) {
@@ -378,15 +382,20 @@ SkOpSpan* SkOpSegment::findSortableTop(SkOpContour* contourHead) {
SkOpSpan* SkOpContour::findSortableTop(SkOpContour* contourHead) {
SkOpSegment* testSegment = &fHead;
+ bool allDone = true;
do {
if (testSegment->done()) {
continue;
}
+ allDone = false;
SkOpSpan* result = testSegment->findSortableTop(contourHead);
if (result) {
return result;
}
} while ((testSegment = testSegment->next()));
+ if (allDone) {
+ fDone = true;
+ }
return nullptr;
}
diff --git a/src/pathops/SkReduceOrder.cpp b/src/pathops/SkReduceOrder.cpp
index 52a19d6138..48624baee9 100644
--- a/src/pathops/SkReduceOrder.cpp
+++ b/src/pathops/SkReduceOrder.cpp
@@ -78,10 +78,12 @@ int SkReduceOrder::reduce(const SkDQuad& quad) {
minYSet |= 1 << index;
}
}
+ if ((minXSet & 0x05) == 0x5 && (minYSet & 0x05) == 0x5) { // test for degenerate
+ // this quad starts and ends at the same place, so never contributes
+ // to the fill
+ return coincident_line(quad, fQuad);
+ }
if (minXSet == 0x7) { // test for vertical line
- if (minYSet == 0x7) { // return 1 if all three are coincident
- return coincident_line(quad, fQuad);
- }
return vertical_line(quad, fQuad);
}
if (minYSet == 0x7) { // test for horizontal line