aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar caryclark <caryclark@google.com>2014-11-13 06:58:52 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2014-11-13 06:58:52 -0800
commit65f553182ab7069378ef863d30094d0327f178d0 (patch)
tree4e7a435941ae82ddd6cab0abcfb2ed7946f79969 /src
parentb1cff03325c42bb1cd87204d9b0dd3d6b9678d3e (diff)
These tests stress pathops by describing the union of circle-like paths that have tiny line segments embedded and double back to create near-coincident conditions.
The fixes include - detect when finding the active top loops between two possible answers - preflight chasing winding to ensure answer is consistent - binary search more often when quadratic intersection fails - add more failure paths when an intersect is missed While this fixes the chrome bug, reenabling path ops in svg should be deferred until additional fixes are landed. TBR= BUG=421132 Committed: https://skia.googlesource.com/skia/+/6f726addf3178b01949bb389ef83cf14a1d7b6b2 Review URL: https://codereview.chromium.org/633393002
Diffstat (limited to 'src')
-rw-r--r--src/pathops/SkAddIntersections.cpp10
-rw-r--r--src/pathops/SkDCubicIntersection.cpp10
-rw-r--r--src/pathops/SkDLineIntersection.cpp31
-rw-r--r--src/pathops/SkDQuadIntersection.cpp70
-rw-r--r--src/pathops/SkIntersections.cpp6
-rw-r--r--src/pathops/SkIntersections.h17
-rw-r--r--src/pathops/SkOpAngle.cpp11
-rw-r--r--src/pathops/SkOpAngle.h8
-rw-r--r--src/pathops/SkOpContour.cpp2
-rw-r--r--src/pathops/SkOpContour.h9
-rw-r--r--src/pathops/SkOpSegment.cpp563
-rw-r--r--src/pathops/SkOpSegment.h42
-rw-r--r--src/pathops/SkPathOpsCommon.cpp63
-rw-r--r--src/pathops/SkPathOpsDebug.h4
-rw-r--r--src/pathops/SkPathOpsOp.cpp64
-rw-r--r--src/pathops/SkPathOpsPoint.h1
-rw-r--r--src/pathops/SkPathOpsTypes.h6
-rw-r--r--src/pathops/SkReduceOrder.cpp4
18 files changed, 765 insertions, 156 deletions
diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp
index 27422eda5f..c27434f9f7 100644
--- a/src/pathops/SkAddIntersections.cpp
+++ b/src/pathops/SkAddIntersections.cpp
@@ -307,6 +307,7 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
}
case SkIntersectionHelper::kQuad_Segment: {
pts = ts.quadQuad(wt.pts(), wn.pts());
+ ts.alignQuadPts(wt.pts(), wn.pts());
debugShowQuadIntersection(pts, wt, wn, ts);
break;
}
@@ -366,8 +367,7 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
if (wt.addCoincident(wn, ts, swap)) {
continue;
}
- ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1)
- pts = 1;
+ pts = ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1)
} else if (wn.segmentType() >= SkIntersectionHelper::kQuad_Segment
&& wt.segmentType() >= SkIntersectionHelper::kQuad_Segment
&& ts.isCoincident(0)) {
@@ -375,8 +375,7 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
if (wt.addCoincident(wn, ts, swap)) {
continue;
}
- ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1)
- pts = 1;
+ pts = ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1)
}
}
if (pts >= 2) {
@@ -387,8 +386,7 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
&& wn.isPartial(ts[!swap][pt], ts[!swap][pt + 1], point, next)) {
if (!wt.addPartialCoincident(wn, ts, pt, swap)) {
// remove extra point if two map to same float values
- ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1)
- pts = 1;
+ pts = ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1)
}
}
}
diff --git a/src/pathops/SkDCubicIntersection.cpp b/src/pathops/SkDCubicIntersection.cpp
index 9d83242eda..2fb35e1827 100644
--- a/src/pathops/SkDCubicIntersection.cpp
+++ b/src/pathops/SkDCubicIntersection.cpp
@@ -109,12 +109,14 @@ static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDC
__FUNCTION__, t1Start, t1, t2Start, t2);
SkIntersections xlocals;
xlocals.allowNear(false);
+ xlocals.allowFlatMeasure(true);
intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, xlocals);
SkDebugf(" xlocals.fUsed=%d\n", xlocals.used());
}
#endif
SkIntersections locals;
locals.allowNear(false);
+ locals.allowFlatMeasure(true);
intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, locals);
int tCount = locals.used();
for (int tIdx = 0; tIdx < tCount; ++tIdx) {
@@ -296,6 +298,7 @@ bool SkIntersections::cubicExactEnd(const SkDCubic& cubic1, bool start, const Sk
tmpLine[1].fY -= cubic2[2 - start].fX - cubic2[t1Index].fX;
SkIntersections impTs;
impTs.allowNear(false);
+ impTs.allowFlatMeasure(true);
impTs.intersectRay(cubic1, tmpLine);
for (int index = 0; index < impTs.used(); ++index) {
SkDPoint realPt = impTs.pt(index);
@@ -556,6 +559,7 @@ int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
}
SkIntersections i;
i.fAllowNear = false;
+ i.fFlatMeasure = true;
i.fMax = 9;
::intersect(c1, 0, 1, c2, 0, 1, 1, i);
int compCount = i.used();
@@ -662,7 +666,7 @@ int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
// OPTIMIZATION If this is a common use case, optimize by duplicating
// the intersect 3 loop to avoid the promotion / demotion code
int SkIntersections::intersect(const SkDCubic& cubic, const SkDQuad& quad) {
- fMax = 6;
+ fMax = 7;
SkDCubic up = quad.toCubic();
(void) intersect(cubic, up);
return used();
@@ -684,7 +688,9 @@ int SkIntersections::intersect(const SkDCubic& c) {
// OPTIMIZATION: could quick reject if neither end point tangent ray intersected the line
// segment formed by the opposite end point to the control point
(void) intersect(c, c);
- if (used() > 0) {
+ if (used() > 1) {
+ fUsed = 0;
+ } else if (used() > 0) {
if (approximately_equal_double(fT[0][0], fT[1][0])) {
fUsed = 0;
} else {
diff --git a/src/pathops/SkDLineIntersection.cpp b/src/pathops/SkDLineIntersection.cpp
index b209474066..8fc673f2fb 100644
--- a/src/pathops/SkDLineIntersection.cpp
+++ b/src/pathops/SkDLineIntersection.cpp
@@ -26,19 +26,24 @@ SkDPoint SkIntersections::Line(const SkDLine& a, const SkDLine& b) {
return p;
}
-void SkIntersections::cleanUpCoincidence() {
- SkASSERT(fUsed == 2);
- // both t values are good
- bool startMatch = fT[0][0] == 0 && (fT[1][0] == 0 || fT[1][0] == 1);
- bool endMatch = fT[0][1] == 1 && (fT[1][1] == 0 || fT[1][1] == 1);
- if (startMatch || endMatch) {
- removeOne(startMatch);
- return;
- }
- // either t value is good
- bool pStartMatch = fT[0][0] == 0 || fT[1][0] == 0 || fT[1][0] == 1;
- bool pEndMatch = fT[0][1] == 1 || fT[1][1] == 0 || fT[1][1] == 1;
- removeOne(pStartMatch || !pEndMatch);
+int SkIntersections::cleanUpCoincidence() {
+ do {
+ int last = fUsed - 1;
+ for (int index = 0; index < last; ++index) {
+ if (fT[0][index] == fT[0][index + 1]) {
+ removeOne(index + (int) (fT[1][index] == 0 || fT[1][index] == 1));
+ goto tryAgain;
+ }
+ }
+ for (int index = 0; index < last; ++index) {
+ if (fT[1][index] == fT[1][index + 1]) {
+ removeOne(index + (int) (fT[0][index] == 0 || fT[0][index] == 1));
+ goto tryAgain;
+ }
+ }
+ return fUsed;
+tryAgain: ;
+ } while (true);
}
void SkIntersections::cleanUpParallelLines(bool parallel) {
diff --git a/src/pathops/SkDQuadIntersection.cpp b/src/pathops/SkDQuadIntersection.cpp
index 239711c321..fcb9171f32 100644
--- a/src/pathops/SkDQuadIntersection.cpp
+++ b/src/pathops/SkDQuadIntersection.cpp
@@ -73,6 +73,7 @@ static int addValidRoots(const double roots[4], const int count, double valid[4]
} else if (approximately_greater_than_one(t)) {
t = 1;
}
+ SkASSERT(t >= 0 && t <= 1);
valid[result++] = t;
}
return result;
@@ -242,10 +243,18 @@ static double flat_measure(const SkDQuad& q) {
// FIXME ? should this measure both and then use the quad that is the flattest as the line?
static bool is_linear(const SkDQuad& q1, const SkDQuad& q2, SkIntersections* i) {
- double measure = flat_measure(q1);
- // OPTIMIZE: (get rid of sqrt) use approximately_zero
- if (!approximately_zero_sqrt(measure)) {
- return false;
+ if (i->flatMeasure()) {
+ // for backward compatibility, use the old method when called from cubics
+ // FIXME: figure out how to fix cubics when it calls the new path
+ double measure = flat_measure(q1);
+ // OPTIMIZE: (get rid of sqrt) use approximately_zero
+ if (!approximately_zero_sqrt(measure)) { // approximately_zero_sqrt
+ return false;
+ }
+ } else {
+ if (!q1.isLinear(0, 2)) {
+ return false;
+ }
}
return is_linear_inner(q1, 0, 1, q2, 0, 1, i, NULL);
}
@@ -305,6 +314,16 @@ static bool binary_search(const SkDQuad& quad1, const SkDQuad& quad2, double* t1
SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) == (%1.9g,%1.9g)\n", __FUNCTION__,
t1Seed, t2Seed, t1[1].fX, t1[1].fY, t2[1].fX, t2[1].fY);
#endif
+ if (*t1Seed < 0) {
+ *t1Seed = 0;
+ } else if (*t1Seed > 1) {
+ *t1Seed = 1;
+ }
+ if (*t2Seed < 0) {
+ *t2Seed = 0;
+ } else if (*t2Seed > 1) {
+ *t2Seed = 1;
+ }
return true;
}
if (calcMask & (1 << 0)) t1[0] = quad1.ptAtT(SkTMax(0., *t1Seed - tStep));
@@ -398,11 +417,13 @@ static void lookNearEnd(const SkDQuad& q1, const SkDQuad& q2, int testT,
int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
fMax = 4;
+ bool exactMatch = false;
// if the quads share an end point, check to see if they overlap
for (int i1 = 0; i1 < 3; i1 += 2) {
for (int i2 = 0; i2 < 3; i2 += 2) {
if (q1[i1].asSkPoint() == q2[i2].asSkPoint()) {
insert(i1 >> 1, i2 >> 1, q1[i1]);
+ exactMatch = true;
}
}
}
@@ -469,6 +490,7 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
int rootCount = findRoots(i2, q1, roots1, useCubic, flip1, 0);
// OPTIMIZATION: could short circuit here if all roots are < 0 or > 1
double roots1Copy[4];
+ SkDEBUGCODE(sk_bzero(roots1Copy, sizeof(roots1Copy)));
int r1Count = addValidRoots(roots1, rootCount, roots1Copy);
SkDPoint pts1[4];
for (index = 0; index < r1Count; ++index) {
@@ -482,12 +504,14 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
for (index = 0; index < r2Count; ++index) {
pts2[index] = q2.ptAtT(roots2Copy[index]);
}
+ bool triedBinary = false;
if (r1Count == r2Count && r1Count <= 1) {
if (r1Count == 1 && used() == 0) {
if (pts1[0].approximatelyEqual(pts2[0])) {
insert(roots1Copy[0], roots2Copy[0], pts1[0]);
} else {
// find intersection by chasing t
+ triedBinary = true;
if (binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) {
insert(roots1Copy[0], roots2Copy[0], pts1[0]);
}
@@ -528,7 +552,18 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
}
}
if (r1Count && r2Count && !foundSomething) {
+ if (exactMatch) {
+ SkASSERT(fUsed > 0);
+ return fUsed;
+ }
relaxed_is_linear(&q1, 0, 1, &q2, 0, 1, this);
+ if (fUsed) {
+ return fUsed;
+ }
+ // maybe the curves are nearly coincident
+ if (!triedBinary && binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) {
+ insert(roots1Copy[0], roots2Copy[0], pts1[0]);
+ }
return fUsed;
}
int used = 0;
@@ -553,3 +588,30 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
} while (++used < r1Count);
return fUsed;
}
+
+void SkIntersections::alignQuadPts(const SkPoint q1[3], const SkPoint q2[3]) {
+ for (int index = 0; index < used(); ++index) {
+ const SkPoint result = pt(index).asSkPoint();
+ if (q1[0] == result || q1[2] == result || q2[0] == result || q2[2] == result) {
+ continue;
+ }
+ if (SkDPoint::ApproximatelyEqual(q1[0], result)) {
+ fPt[index].set(q1[0]);
+// SkASSERT(way_roughly_zero(fT[0][index])); // this value can be bigger than way rough
+ fT[0][index] = 0;
+ } else if (SkDPoint::ApproximatelyEqual(q1[2], result)) {
+ fPt[index].set(q1[2]);
+// SkASSERT(way_roughly_equal(fT[0][index], 1));
+ fT[0][index] = 1;
+ }
+ if (SkDPoint::ApproximatelyEqual(q2[0], result)) {
+ fPt[index].set(q2[0]);
+// SkASSERT(way_roughly_zero(fT[1][index]));
+ fT[1][index] = 0;
+ } else if (SkDPoint::ApproximatelyEqual(q2[2], result)) {
+ fPt[index].set(q2[2]);
+// SkASSERT(way_roughly_equal(fT[1][index], 1));
+ fT[1][index] = 1;
+ }
+ }
+}
diff --git a/src/pathops/SkIntersections.cpp b/src/pathops/SkIntersections.cpp
index 62c1e411ad..e9875cf69d 100644
--- a/src/pathops/SkIntersections.cpp
+++ b/src/pathops/SkIntersections.cpp
@@ -79,6 +79,8 @@ int SkIntersections::insert(double one, double two, const SkDPoint& pt) {
|| (precisely_equal(one, 1) && !precisely_equal(oldOne, 1))
|| (precisely_zero(two) && !precisely_zero(oldTwo))
|| (precisely_equal(two, 1) && !precisely_equal(oldTwo, 1))) {
+ SkASSERT(one >= 0 && one <= 1);
+ SkASSERT(two >= 0 && two <= 1);
fT[0][index] = one;
fT[1][index] = two;
fPt[index] = pt;
@@ -111,6 +113,8 @@ int SkIntersections::insert(double one, double two, const SkDPoint& pt) {
fIsCoincident[1] += fIsCoincident[1] & clearMask;
}
fPt[index] = pt;
+ SkASSERT(one >= 0 && one <= 1);
+ SkASSERT(two >= 0 && two <= 1);
fT[0][index] = one;
fT[1][index] = two;
++fUsed;
@@ -171,7 +175,7 @@ void SkIntersections::removeOne(int index) {
memmove(&fPt2[index], &fPt2[index + 1], sizeof(fPt2[0]) * remaining);
memmove(&fT[0][index], &fT[0][index + 1], sizeof(fT[0][0]) * remaining);
memmove(&fT[1][index], &fT[1][index + 1], sizeof(fT[1][0]) * remaining);
- SkASSERT(fIsCoincident[0] == 0);
+// SkASSERT(fIsCoincident[0] == 0);
int coBit = fIsCoincident[0] & (1 << index);
fIsCoincident[0] -= ((fIsCoincident[0] >> 1) & ~((1 << index) - 1)) + coBit;
SkASSERT(!(coBit ^ (fIsCoincident[1] & (1 << index))));
diff --git a/src/pathops/SkIntersections.h b/src/pathops/SkIntersections.h
index 040671093c..a1bde512db 100644
--- a/src/pathops/SkIntersections.h
+++ b/src/pathops/SkIntersections.h
@@ -16,6 +16,7 @@ class SkIntersections {
public:
SkIntersections()
: fSwap(0)
+ , fFlatMeasure(false)
#ifdef SK_DEBUG
, fDepth(0)
#endif
@@ -39,6 +40,10 @@ public:
};
TArray operator[](int n) const { return TArray(fT[n]); }
+ void allowFlatMeasure(bool flatAllowed) {
+ fFlatMeasure = flatAllowed;
+ }
+
void allowNear(bool nearAllowed) {
fAllowNear = nearAllowed;
}
@@ -88,10 +93,14 @@ public:
cubic.set(a);
SkDQuad quad;
quad.set(b);
- fMax = 6;
+ fMax = 7;
return intersect(cubic, quad);
}
+ bool flatMeasure() const {
+ return fFlatMeasure;
+ }
+
bool hasT(double t) const {
SkASSERT(t == 0 || t == 1);
return fUsed > 0 && (t == 0 ? fT[0][0] == 0 : fT[0][fUsed - 1] == 1);
@@ -201,7 +210,7 @@ public:
bool swapped() const {
return fSwap;
}
-
+
int used() const {
return fUsed;
}
@@ -214,8 +223,9 @@ public:
SkASSERT(++fDepth < 16);
}
+ void alignQuadPts(const SkPoint a[3], const SkPoint b[3]);
void append(const SkIntersections& );
- void cleanUpCoincidence();
+ int cleanUpCoincidence();
int coincidentUsed() const;
void cubicInsert(double one, double two, const SkDPoint& pt, const SkDCubic& c1,
const SkDCubic& c2);
@@ -282,6 +292,7 @@ private:
unsigned char fMax;
bool fAllowNear;
bool fSwap;
+ bool fFlatMeasure; // backwards-compatibility when cubics uses quad intersection
#ifdef SK_DEBUG
int fDepth;
#endif
diff --git a/src/pathops/SkOpAngle.cpp b/src/pathops/SkOpAngle.cpp
index 0c87d3ba9e..b3a188c1e8 100644
--- a/src/pathops/SkOpAngle.cpp
+++ b/src/pathops/SkOpAngle.cpp
@@ -409,7 +409,12 @@ bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const {
for (int index = 0; index < 2; ++index) {
const SkOpSegment& segment = index ? *rh.fSegment : *fSegment;
SkIntersections i;
- (*CurveIntersectRay[index ? rPts : lPts])(segment.pts(), rays[index], &i);
+ int cPts = index ? rPts : lPts;
+ (*CurveIntersectRay[cPts])(segment.pts(), rays[index], &i);
+ // if the curve is a line, then the line and the ray intersect only at their crossing
+ if (cPts == 1) { // line
+ continue;
+ }
// SkASSERT(i.used() >= 1);
// if (i.used() <= 1) {
// continue;
@@ -657,7 +662,7 @@ void SkOpAngle::insert(SkOpAngle* angle) {
}
SkOpAngle* next = fNext;
if (next->fNext == this) {
- if (angle->overlap(*this)) {
+ if (angle->overlap(*this)) { // angles are essentially coincident
return;
}
if (singleton || angle->after(this)) {
@@ -777,7 +782,7 @@ bool SkOpAngle::merge(SkOpAngle* angle) {
working = next;
} while (working != angle);
// it's likely that a pair of the angles are unorderable
-#if DEBUG_ANGLE
+#if 0 && DEBUG_ANGLE
SkOpAngle* last = angle;
working = angle->fNext;
do {
diff --git a/src/pathops/SkOpAngle.h b/src/pathops/SkOpAngle.h
index 098c470128..1dc4250613 100644
--- a/src/pathops/SkOpAngle.h
+++ b/src/pathops/SkOpAngle.h
@@ -50,6 +50,14 @@ public:
SkOpAngle* previous() const;
+ int sectorEnd() const {
+ return fSectorEnd;
+ }
+
+ int sectorStart() const {
+ return fSectorStart;
+ }
+
void set(const SkOpSegment* segment, int start, int end);
void setLastMarked(SkOpSpan* marked) {
diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp
index 6d6ad7926e..28c072a3c1 100644
--- a/src/pathops/SkOpContour.cpp
+++ b/src/pathops/SkOpContour.cpp
@@ -13,7 +13,7 @@ bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
const SkIntersections& ts, bool swap) {
SkPoint pt0 = ts.pt(0).asSkPoint();
SkPoint pt1 = ts.pt(1).asSkPoint();
- if (pt0 == pt1) {
+ if (pt0 == pt1 || ts[0][0] == ts[0][1] || ts[1][0] == ts[1][1]) {
// FIXME: one could imagine a case where it would be incorrect to ignore this
// suppose two self-intersecting cubics overlap to be coincident --
// this needs to check that by some measure the t values are far enough apart
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index 899367ab0e..7a1cc09247 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -127,9 +127,9 @@ public:
}
}
- void checkEnds() {
+ bool checkEnds() {
if (!fContainsCurves) {
- return;
+ return true;
}
int segmentCount = fSegments.count();
for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
@@ -140,8 +140,11 @@ public:
if (segment->done()) {
continue; // likely coincident, nothing to do
}
- segment->checkEnds();
+ if (!segment->checkEnds()) {
+ return false;
+ }
}
+ return true;
}
void checkMultiples() {
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 95046e2fd2..1fb5afa028 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -160,6 +160,10 @@ next:
bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
int sumMiWinding = updateWinding(endIndex, index);
int sumSuWinding = updateOppWinding(endIndex, index);
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(sumMiWinding) <= DEBUG_LIMIT_WIND_SUM);
+ SkASSERT(abs(sumSuWinding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
if (fOperand) {
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
@@ -617,6 +621,11 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
if ((span->fDone = newT == 1)) {
++fDoneSpans;
}
+ setSpanFlags(pt, newT, span);
+ return insertedAt;
+}
+
+void SkOpSegment::setSpanFlags(const SkPoint& pt, double newT, SkOpSpan* span) {
int less = -1;
// FIXME: note that this relies on spans being a continguous array
// find range of spans with nearly the same point as this one
@@ -652,10 +661,10 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
--more;
}
if (less == more) {
- return insertedAt;
+ return;
}
if (precisely_negative(span[more].fT - span[less].fT)) {
- return insertedAt;
+ return;
}
// if the total range of t values is big enough, mark all tiny
bool tiny = span[less].fPt == span[more].fPt;
@@ -668,7 +677,80 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
++fDoneSpans;
}
} while (++index < more);
- return insertedAt;
+ return;
+}
+
+void SkOpSegment::resetSpanFlags() {
+ fSmall = fTiny = false;
+ fDoneSpans = 0;
+ int start = 0;
+ int last = this->count() - 1;
+ do {
+ SkOpSpan* startSpan = &this->fTs[start];
+ double startT = startSpan->fT;
+ startSpan->fSmall = startSpan->fTiny = false; // sets range initial
+ bool terminus = startT == 1;
+ if ((startSpan->fDone = !startSpan->fWindValue | terminus)) {
+ ++fDoneSpans;
+ }
+ ++start; // range initial + 1
+ if (terminus) {
+ continue;
+ }
+ const SkPoint& pt = startSpan->fPt;
+ int end = start; // range initial + 1
+ while (end <= last) {
+ const SkOpSpan& endSpan = this->span(end);
+ if (!AlmostEqualUlps(endSpan.fPt, pt)) {
+ break;
+ }
+ if (fVerb == SkPath::kCubic_Verb) {
+ double tMid = (startSpan->fT + endSpan.fT) / 2;
+ SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
+ if (!midEndPt.approximatelyEqual(xyAtT(startSpan))) {
+ break;
+ }
+ }
+ ++end;
+ }
+ if (start == end) { // end == range final + 1
+ continue;
+ }
+ while (--end >= start) { // end == range final
+ const SkOpSpan& endSpan = this->span(end);
+ const SkOpSpan& priorSpan = this->span(end - 1);
+ if (endSpan.fPt != priorSpan.fPt || endSpan.fT != priorSpan.fT) {
+ break; // end == range final + 1
+ }
+ }
+ if (end < start) { // end == range final + 1
+ continue;
+ }
+ int index = start - 1; // index == range initial
+ start = end; // start = range final + 1
+ const SkOpSpan& nextSpan = this->span(end);
+ if (precisely_negative(nextSpan.fT - startSpan->fT)) {
+ while (++index < end) {
+ startSpan = &this->fTs[index];
+ startSpan->fSmall = startSpan->fTiny = false; // sets range initial + 1
+ if ((startSpan->fDone = !startSpan->fWindValue)) {
+ ++fDoneSpans;
+ }
+ }
+ continue;
+ }
+ if (!startSpan->fWindValue) {
+ --fDoneSpans; // added back below
+ }
+ bool tiny = nextSpan.fPt == startSpan->fPt;
+ do {
+ fSmall = startSpan->fSmall = true; // sets range initial
+ fTiny |= startSpan->fTiny = tiny;
+ startSpan->fDone = true;
+ ++fDoneSpans;
+ startSpan = &this->fTs[++index];
+ } while (index < end); // loop through tiny small range end (last)
+ } while (start <= last);
}
// set spans from start to end to decrement by one
@@ -776,6 +858,7 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
break;
}
}
+ oFoundEnd = endPt == oTest->fPt;
do {
SkASSERT(originalWindValue == oTest->fWindValue);
if (decrement) {
@@ -970,6 +1053,151 @@ void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
debugValidate();
}
+void SkOpSegment::alignRange(int lower, int upper,
+ const SkOpSegment* other, int oLower, int oUpper) {
+ for (int oIndex = oLower; oIndex <= oUpper; ++oIndex) {
+ const SkOpSpan& oSpan = other->span(oIndex);
+ const SkOpSegment* oOther = oSpan.fOther;
+ if (oOther == this) {
+ continue;
+ }
+ SkOpSpan* matchSpan;
+ int matchIndex;
+ const SkOpSpan* refSpan;
+ for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+ const SkOpSpan& iSpan = this->span(iIndex);
+ const SkOpSegment* iOther = iSpan.fOther;
+ if (iOther == other) {
+ continue;
+ }
+ if (iOther == oOther) {
+ goto nextI;
+ }
+ }
+ {
+ // oSpan does not have a match in this
+ int iCount = this->count();
+ const SkOpSpan* iMatch = NULL;
+ double iMatchTDiff;
+ matchIndex = -1;
+ for (int iIndex = 0; iIndex < iCount; ++iIndex) {
+ const SkOpSpan& iSpan = this->span(iIndex);
+ const SkOpSegment* iOther = iSpan.fOther;
+ if (iOther != oOther) {
+ continue;
+ }
+ double testTDiff = fabs(iSpan.fOtherT - oSpan.fOtherT);
+ if (!iMatch || testTDiff < iMatchTDiff) {
+ matchIndex = iIndex;
+ iMatch = &iSpan;
+ iMatchTDiff = testTDiff;
+ }
+ }
+ if (matchIndex < 0) {
+ continue; // the entry is missing, & will be picked up later (FIXME: fix it here?)
+ }
+ matchSpan = &this->fTs[matchIndex];
+ refSpan = &this->span(lower);
+ if (!SkDPoint::ApproximatelyEqual(matchSpan->fPt, refSpan->fPt)) {
+ goto nextI;
+ }
+ if (matchIndex != lower - 1 && matchIndex != upper + 1) {
+ // the consecutive spans need to be rearranged to get the missing one close
+ continue; // FIXME: more work to do
+ }
+ }
+ {
+ this->fixOtherTIndex();
+ SkScalar newT;
+ if (matchSpan->fT != 0 && matchSpan->fT != 1) {
+ newT = matchSpan->fT = refSpan->fT;
+ matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = refSpan->fT;
+ } else { // leave span at the start or end there and adjust the neighbors
+ newT = matchSpan->fT;
+ for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+ matchSpan = &this->fTs[iIndex];
+ matchSpan->fT = newT;
+ matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = newT;
+ }
+ }
+ this->resetSpanFlags(); // fix up small / tiny / done
+ // align ts of other ranges with adjacent spans that match the aligned points
+ lower = SkTMin(lower, matchIndex);
+ while (lower > 0) {
+ const SkOpSpan& span = this->span(lower - 1);
+ if (span.fT != newT) {
+ break;
+ }
+ --lower;
+ }
+ upper = SkTMax(upper, matchIndex);
+ int last = this->count() - 1;
+ while (upper < last) {
+ const SkOpSpan& span = this->span(upper + 1);
+ if (span.fT != newT) {
+ break;
+ }
+ ++upper;
+ }
+ for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+ const SkOpSpan& span = this->span(iIndex);
+ SkOpSegment* aOther = span.fOther;
+ int aLower = span.fOtherIndex;
+ SkScalar aT = span.fOtherT;
+ bool aResetFlags = false;
+ while (aLower > 0) {
+ SkOpSpan* aSpan = &aOther->fTs[aLower - 1];
+ for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+ if (aSpan->fPt == this->fTs[iIndex].fPt) {
+ goto matchFound;
+ }
+ }
+ break;
+ matchFound:
+ --aLower;
+ }
+ int aUpper = span.fOtherIndex;
+ int aLast = aOther->count() - 1;
+ while (aUpper < aLast) {
+ SkOpSpan* aSpan = &aOther->fTs[aUpper + 1];
+ for (int iIndex = lower; iIndex <= upper; ++iIndex) {
+ if (aSpan->fPt == this->fTs[iIndex].fPt) {
+ goto matchFound2;
+ }
+ }
+ break;
+ matchFound2:
+ ++aUpper;
+ }
+ if (aOther->fTs[aLower].fT == 0) {
+ aT = 0;
+ } else if (aOther->fTs[aUpper].fT == 1) {
+ aT = 1;
+ }
+ bool aFixed = false;
+ for (int aIndex = aLower; aIndex <= aUpper; ++aIndex) {
+ SkOpSpan* aSpan = &aOther->fTs[aIndex];
+ if (aSpan->fT == aT) {
+ continue;
+ }
+ SkASSERT(way_roughly_equal(aSpan->fT, aT));
+ if (!aFixed) {
+ aOther->fixOtherTIndex();
+ aFixed = true;
+ }
+ aSpan->fT = aT;
+ aSpan->fOther->fTs[aSpan->fOtherIndex].fOtherT = aT;
+ aResetFlags = true;
+ }
+ if (aResetFlags) {
+ aOther->resetSpanFlags();
+ }
+ }
+ }
+nextI: ;
+ }
+}
+
void SkOpSegment::alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other,
double otherT, const SkOpSegment* other2, SkOpSpan* oSpan,
SkTDArray<AlignedSpan>* alignedArray) {
@@ -1245,8 +1473,8 @@ void SkOpSegment::bumpCoincidentOBlind(int index, int endIndex) {
// may not have the same intermediate points. Compute the corresponding
// intermediate T values (using this as the master, other as the follower)
// and walk other conditionally -- hoping that it catches up in the end
-void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
- SkTArray<SkPoint, true>* oOutsidePts) {
+bool SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
+ SkTArray<SkPoint, true>* oOutsidePts, const SkPoint& oEndPt) {
int oIndex = *oIndexPtr;
SkOpSpan* const oTest = &fTs[oIndex];
SkOpSpan* oEnd = oTest;
@@ -1259,11 +1487,14 @@ void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
TrackOutside(oOutsidePts, startPt);
}
#endif
+ bool foundEnd = false;
while (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
+ foundEnd |= oEndPt == oEnd->fPt;
zeroSpan(oEnd);
oEnd = &fTs[++oIndex];
}
*oIndexPtr = oIndex;
+ return foundEnd;
}
// FIXME: need to test this case:
@@ -1313,6 +1544,7 @@ bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d
}
// consolidate the winding count even if done
+ bool foundEnd = false;
if ((test->fWindValue == 0 && test->fOppValue == 0)
|| (oTest->fWindValue == 0 && oTest->fOppValue == 0)) {
SkDEBUGCODE(int firstWind = test->fWindValue);
@@ -1336,12 +1568,12 @@ bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d
if (!bumpCoincidentThis(*oTest, binary, &index, &outsidePts)) {
return false;
}
- other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts);
+ foundEnd = other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt);
} else {
if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
return false;
}
- bumpCoincidentOther(*oTest, &index, &outsidePts);
+ foundEnd = bumpCoincidentOther(*oTest, &index, &outsidePts, endPt);
}
}
test = &fTs[index];
@@ -1352,6 +1584,9 @@ bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d
if (endPt == *testPt || precisely_equal(endT, testT)) {
break;
}
+ if (0 && foundEnd) { // FIXME: this is likely needed but wait until a test case triggers it
+ break;
+ }
// SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
} while (endPt != *oTestPt);
// in rare cases, one may have ended before the other
@@ -1364,6 +1599,7 @@ bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d
test->fWindValue = lastWind;
test->fOppValue = lastOpp;
if (zero) {
+ SkASSERT(!test->fDone);
test->fDone = true;
++fDoneSpans;
}
@@ -1402,7 +1638,9 @@ bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d
if (success) {
do {
if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
- other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts);
+ if (other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt)) {
+ break;
+ }
} else {
if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
return false;
@@ -1476,9 +1714,9 @@ const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double other
SkASSERT(other != this);
int insertedAt = addT(other, pt, t);
int otherInsertedAt = other->addT(this, pt2, otherT);
- addOtherT(insertedAt, otherT, otherInsertedAt);
+ this->addOtherT(insertedAt, otherT, otherInsertedAt);
other->addOtherT(otherInsertedAt, t, insertedAt);
- matchWindingValue(insertedAt, t, borrowWind);
+ this->matchWindingValue(insertedAt, t, borrowWind);
other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
SkOpSpan& span = this->fTs[insertedAt];
if (pt != pt2) {
@@ -1486,6 +1724,27 @@ const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double other
SkOpSpan& oSpan = other->fTs[otherInsertedAt];
oSpan.fNear = true;
}
+ // if the newly inserted spans match a neighbor on one but not the other, make them agree
+ int lower = this->nextExactSpan(insertedAt, -1) + 1;
+ int upper = this->nextExactSpan(insertedAt, 1) - 1;
+ if (upper < 0) {
+ upper = this->count() - 1;
+ }
+ int oLower = other->nextExactSpan(otherInsertedAt, -1) + 1;
+ int oUpper = other->nextExactSpan(otherInsertedAt, 1) - 1;
+ if (oUpper < 0) {
+ oUpper = other->count() - 1;
+ }
+ if (lower == upper && oLower == oUpper) {
+ return &span;
+ }
+#if DEBUG_CONCIDENT
+ SkDebugf("%s id=%d lower=%d upper=%d other=%d oLower=%d oUpper=%d\n", __FUNCTION__,
+ debugID(), lower, upper, other->debugID(), oLower, oUpper);
+#endif
+ // find the nearby spans in one range missing in the other
+ this->alignRange(lower, upper, other, oLower, oUpper);
+ other->alignRange(oLower, oUpper, this, lower, upper);
return &span;
}
@@ -1893,8 +2152,10 @@ bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
span->fOppValue &= 1;
}
if (!span->fWindValue && !span->fOppValue) {
- span->fDone = true;
- ++fDoneSpans;
+ if (!span->fDone) {
+ span->fDone = true;
+ ++fDoneSpans;
+ }
return true;
}
return false;
@@ -2118,7 +2379,7 @@ void SkOpSegment::checkDuplicates() {
}
// look to see if the curve end intersects an intermediary that intersects the other
-void SkOpSegment::checkEnds() {
+bool SkOpSegment::checkEnds() {
debugValidate();
SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
int count = fTs.count();
@@ -2193,11 +2454,14 @@ void SkOpSegment::checkEnds() {
if (lastMissing.fT == t
&& lastMissing.fOther == match
&& lastMissing.fOtherT == matchT) {
- SkASSERT(lastMissing.fPt == peekSpan.fPt);
+ SkASSERT(SkDPoint::ApproximatelyEqual(lastMissing.fPt, peekSpan.fPt));
continue;
}
}
-#if DEBUG_CHECK_ENDS
+ if (this == match) {
+ return false; // extremely large paths can trigger this
+ }
+#if DEBUG_CHECK_ALIGN
SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n",
__FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY);
#endif
@@ -2219,7 +2483,7 @@ nextPeekIndex:
}
if (missingSpans.count() == 0) {
debugValidate();
- return;
+ return true;
}
debugValidate();
int missingCount = missingSpans.count();
@@ -2236,6 +2500,7 @@ nextPeekIndex:
missingSpans[index].fOther->fixOtherTIndex();
}
debugValidate();
+ return true;
}
void SkOpSegment::checkLinks(const SkOpSpan* base,
@@ -2257,7 +2522,7 @@ void SkOpSegment::checkLinks(const SkOpSpan* base,
}
test = base;
while (test < last && (++test)->fPt == base->fPt) {
- SkASSERT(this != test->fOther);
+ SkASSERT(this != test->fOther || test->fLoop);
CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
}
}
@@ -2433,9 +2698,15 @@ nextSmallCheck:
const SkOpSpan& otherSpan = missingOther->span(otherTIndex);
if (otherSpan.fSmall) {
const SkOpSpan* nextSpan = &otherSpan;
+ if (nextSpan->fPt == missing.fPt) {
+ continue;
+ }
do {
++nextSpan;
} while (nextSpan->fSmall);
+ if (nextSpan->fT == 1) {
+ continue;
+ }
SkAssertResult(missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt,
nextSpan->fT, missingOther));
} else if (otherSpan.fT > 0) {
@@ -3111,6 +3382,8 @@ int SkOpSegment::findExactT(double t, const SkOpSegment* match) const {
return -1;
}
+
+
int SkOpSegment::findOtherT(double t, const SkOpSegment* match) const {
int count = this->count();
for (int index = 0; index < count; ++index) {
@@ -3197,14 +3470,19 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort
SkOpSegment* next = angle->segment();
SkPathOpsBounds bounds;
next->subDivideBounds(angle->end(), angle->start(), &bounds);
- if (approximately_greater(top, bounds.fTop)) {
+ bool nearSame = AlmostEqualUlps(top, bounds.top());
+ bool lowerSector = !firstAngle || angle->sectorEnd() < firstAngle->sectorStart();
+ bool lesserSector = top > bounds.fTop;
+ if (lesserSector && (!nearSame || lowerSector)) {
top = bounds.fTop;
firstAngle = angle;
}
}
angle = angle->next();
} while (angle != baseAngle);
- SkASSERT(firstAngle);
+ if (!firstAngle) {
+ return NULL; // if all are unorderable, give up
+ }
#if DEBUG_SORT
SkDebugf("%s\n", __FUNCTION__);
firstAngle->debugLoop();
@@ -3301,6 +3579,72 @@ bool SkOpSegment::inCoincidentSpan(double t, const SkOpSegment* other) const {
return foundEnds == 0x3 || foundEnds == 0x5 || foundEnds == 0x6; // two bits set
}
+bool SkOpSegment::inconsistentAngle(int maxWinding, int sumWinding, int oppMaxWinding,
+ int oppSumWinding, const SkOpAngle* angle) const {
+ SkASSERT(angle->segment() == this);
+ if (UseInnerWinding(maxWinding, sumWinding)) {
+ maxWinding = sumWinding;
+ }
+ if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
+ oppMaxWinding = oppSumWinding;
+ }
+ return inconsistentWinding(angle, maxWinding, oppMaxWinding);
+}
+
+bool SkOpSegment::inconsistentWinding(const SkOpAngle* angle, int winding,
+ int oppWinding) const {
+ int index = angle->start();
+ int endIndex = angle->end();
+ int min = SkMin32(index, endIndex);
+ int step = SkSign32(endIndex - index);
+ if (inconsistentWinding(min, winding, oppWinding)) {
+ return true;
+ }
+ const SkOpSegment* other = this;
+ while ((other = other->nextChase(&index, &step, &min, NULL))) {
+ if (other->fTs[min].fWindSum != SK_MinS32) {
+ break;
+ }
+ if (fOperand == other->fOperand) {
+ if (other->inconsistentWinding(min, winding, oppWinding)) {
+ return true;
+ }
+ } else {
+ if (other->inconsistentWinding(min, oppWinding, winding)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool SkOpSegment::inconsistentWinding(int index, int winding, int oppWinding) const {
+ SkASSERT(winding || oppWinding);
+ double referenceT = this->span(index).fT;
+ int lesser = index;
+ while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+ if (inconsistentWinding(__FUNCTION__, lesser, winding, oppWinding)) {
+ return true;
+ }
+ }
+ do {
+ if (inconsistentWinding(__FUNCTION__, index, winding, oppWinding)) {
+ return true;
+ }
+ } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+ return false;
+}
+
+bool SkOpSegment::inconsistentWinding(const char* funName, int tIndex, int winding,
+ int oppWinding) const {
+ const SkOpSpan& span = this->span(tIndex);
+ if (span.fDone && !span.fSmall) {
+ return false;
+ }
+ return (span.fWindSum != SK_MinS32 && span.fWindSum != winding)
+ || (span.fOppSum != SK_MinS32 && span.fOppSum != oppWinding);
+}
+
void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
fDoneSpans = 0;
fOperand = operand;
@@ -3312,16 +3656,18 @@ void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, boo
void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
int local = spanSign(start, end);
+ SkDEBUGCODE(bool success);
if (angleIncludeType == SkOpAngle::kBinarySingle) {
int oppLocal = oppSign(start, end);
- (void) markAndChaseWinding(start, end, local, oppLocal);
+ SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, oppLocal, NULL);
// OPTIMIZATION: the reverse mark and chase could skip the first marking
- (void) markAndChaseWinding(end, start, local, oppLocal);
+ SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, oppLocal, NULL);
} else {
- (void) markAndChaseWinding(start, end, local);
+ SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, NULL);
// OPTIMIZATION: the reverse mark and chase could skip the first marking
- (void) markAndChaseWinding(end, start, local);
+ SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, NULL);
}
+ SkASSERT(success);
}
/*
@@ -3333,7 +3679,7 @@ If there was a winding, then it may or may not need adjusting. If the span the w
from has the same x direction as this span, the winding should change. If the dx is opposite, then
the same winding is shared by both.
*/
-void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
+bool SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
int oppWind, SkScalar hitOppDx) {
SkASSERT(hitDx || !winding);
SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
@@ -3361,9 +3707,11 @@ void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc
#if DEBUG_WINDING_AT_T
SkDebugf(" winding=%d oppWind=%d\n", winding, oppWind);
#endif
- (void) markAndChaseWinding(start, end, winding, oppWind);
+ // if this fails to mark (because the edges are too small) inform caller to try again
+ bool success = markAndChaseWinding(start, end, winding, oppWind, NULL);
// OPTIMIZATION: the reverse mark and chase could skip the first marking
- (void) markAndChaseWinding(end, start, winding, oppWind);
+ success |= markAndChaseWinding(end, start, winding, oppWind, NULL);
+ return success;
}
bool SkOpSegment::inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const {
@@ -3427,7 +3775,9 @@ bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, const SkPoi
if (otherWind == 0) {
return false;
}
- SkASSERT(next >= 0);
+ if (next < 0) {
+ return false; // can happen if t values were adjusted but coincident ts were not
+ }
int tIndex = 0;
do {
SkOpSpan* test = &fTs[tIndex];
@@ -3442,7 +3792,9 @@ bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, const SkPoi
if (cancel) {
match->addTCancel(startPt, endPt, other);
} else {
- SkAssertResult(match->addTCoincident(startPt, endPt, endT, other));
+ if (!match->addTCoincident(startPt, endPt, endT, other)) {
+ return false;
+ }
}
return true;
}
@@ -3486,29 +3838,16 @@ SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
return last;
}
-SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding) {
+bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, SkOpSpan** lastPtr) {
int index = angle->start();
int endIndex = angle->end();
- int step = SkSign32(endIndex - index);
- int min = SkMin32(index, endIndex);
- markWinding(min, winding);
- SkOpSpan* last = NULL;
- SkOpSegment* other = this;
- while ((other = other->nextChase(&index, &step, &min, &last))) {
- if (other->fTs[min].fWindSum != SK_MinS32) {
-// SkASSERT(other->fTs[min].fWindSum == winding);
- SkASSERT(!last);
- break;
- }
- other->markWinding(min, winding);
- }
- return last;
+ return markAndChaseWinding(index, endIndex, winding, lastPtr);
}
-SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding) {
+bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, SkOpSpan** lastPtr) {
int min = SkMin32(index, endIndex);
int step = SkSign32(endIndex - index);
- markWinding(min, winding);
+ bool success = markWinding(min, winding);
SkOpSpan* last = NULL;
SkOpSegment* other = this;
while ((other = other->nextChase(&index, &step, &min, &last))) {
@@ -3517,15 +3856,19 @@ SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding)
SkASSERT(!last);
break;
}
- other->markWinding(min, winding);
+ (void) other->markWinding(min, winding);
}
- return last;
+ if (lastPtr) {
+ *lastPtr = last;
+ }
+ return success;
}
-SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
+bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding,
+ SkOpSpan** lastPtr) {
int min = SkMin32(index, endIndex);
int step = SkSign32(endIndex - index);
- markWinding(min, winding, oppWinding);
+ bool success = markWinding(min, winding, oppWinding);
SkOpSpan* last = NULL;
SkOpSegment* other = this;
while ((other = other->nextChase(&index, &step, &min, &last))) {
@@ -3549,18 +3892,22 @@ SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding,
break;
}
if (fOperand == other->fOperand) {
- other->markWinding(min, winding, oppWinding);
+ (void) other->markWinding(min, winding, oppWinding);
} else {
- other->markWinding(min, oppWinding, winding);
+ (void) other->markWinding(min, oppWinding, winding);
}
}
- return last;
+ if (lastPtr) {
+ *lastPtr = last;
+ }
+ return success;
}
-SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding) {
+bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding,
+ SkOpSpan** lastPtr) {
int start = angle->start();
int end = angle->end();
- return markAndChaseWinding(start, end, winding, oppWinding);
+ return markAndChaseWinding(start, end, winding, oppWinding, lastPtr);
}
SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
@@ -3568,7 +3915,8 @@ SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle
if (UseInnerWinding(maxWinding, sumWinding)) {
maxWinding = sumWinding;
}
- SkOpSpan* last = markAndChaseWinding(angle, maxWinding);
+ SkOpSpan* last;
+ SkAssertResult(markAndChaseWinding(angle, maxWinding, &last));
#if DEBUG_WINDING
if (last) {
SkDebugf("%s last id=%d windSum=", __FUNCTION__,
@@ -3589,7 +3937,9 @@ SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWindi
if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
oppMaxWinding = oppSumWinding;
}
- SkOpSpan* last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
+ SkOpSpan* last;
+ // caller doesn't require that this marks anything
+ (void) markAndChaseWinding(angle, maxWinding, oppMaxWinding, &last);
#if DEBUG_WINDING
if (last) {
SkDebugf("%s last id=%d windSum=", __FUNCTION__,
@@ -3632,6 +3982,18 @@ void SkOpSegment::markDoneBinary(int index) {
debugValidate();
}
+void SkOpSegment::markDoneFinal(int index) {
+ double referenceT = fTs[index].fT;
+ int lesser = index;
+ while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+ markOneDoneFinal(__FUNCTION__, lesser);
+ }
+ do {
+ markOneDoneFinal(__FUNCTION__, index);
+ } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+ debugValidate();
+}
+
void SkOpSegment::markDoneUnary(int index) {
double referenceT = fTs[index].fT;
int lesser = index;
@@ -3645,12 +4007,22 @@ void SkOpSegment::markDoneUnary(int index) {
}
void SkOpSegment::markOneDone(const char* funName, int tIndex, int winding) {
- SkOpSpan* span = markOneWinding(funName, tIndex, winding);
- if (!span || span->fDone) {
+ SkOpSpan* span;
+ (void) markOneWinding(funName, tIndex, winding, &span); // allowed to do nothing
+ if (span->fDone) {
return;
}
span->fDone = true;
- fDoneSpans++;
+ ++fDoneSpans;
+}
+
+void SkOpSegment::markOneDoneFinal(const char* funName, int tIndex) {
+ SkOpSpan* span = &fTs[tIndex];
+ if (span->fDone) {
+ return;
+ }
+ span->fDone = true;
+ ++fDoneSpans;
}
void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
@@ -3660,7 +4032,7 @@ void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
}
SkASSERT(!span->fDone);
span->fDone = true;
- fDoneSpans++;
+ ++fDoneSpans;
}
void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
@@ -3673,46 +4045,52 @@ void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
}
SkASSERT(!span->fDone);
span->fDone = true;
- fDoneSpans++;
+ ++fDoneSpans;
}
-SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding) {
- SkOpSpan& span = fTs[tIndex];
- if (span.fDone && !span.fSmall) {
- return NULL;
+bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding, SkOpSpan** lastPtr) {
+ SkOpSpan* span = &fTs[tIndex];
+ if (lastPtr) {
+ *lastPtr = span;
+ }
+ if (span->fDone && !span->fSmall) {
+ return false;
}
#if DEBUG_MARK_DONE
- debugShowNewWinding(funName, span, winding);
+ debugShowNewWinding(funName, *span, winding);
#endif
- SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
+ SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
#if DEBUG_LIMIT_WIND_SUM
SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
#endif
- span.fWindSum = winding;
- return &span;
+ span->fWindSum = winding;
+ return true;
}
-SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
- int oppWinding) {
- SkOpSpan& span = fTs[tIndex];
- if (span.fDone && !span.fSmall) {
- return NULL;
+bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
+ int oppWinding, SkOpSpan** lastPtr) {
+ SkOpSpan* span = &fTs[tIndex];
+ if (span->fDone && !span->fSmall) {
+ return false;
}
#if DEBUG_MARK_DONE
- debugShowNewWinding(funName, span, winding, oppWinding);
+ debugShowNewWinding(funName, *span, winding, oppWinding);
#endif
- SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
+ SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
#if DEBUG_LIMIT_WIND_SUM
SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
#endif
- span.fWindSum = winding;
- SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
+ span->fWindSum = winding;
+ SkASSERT(span->fOppSum == SK_MinS32 || span->fOppSum == oppWinding);
#if DEBUG_LIMIT_WIND_SUM
SkASSERT(abs(oppWinding) <= DEBUG_LIMIT_WIND_SUM);
#endif
- span.fOppSum = oppWinding;
+ span->fOppSum = oppWinding;
debugValidate();
- return &span;
+ if (lastPtr) {
+ *lastPtr = span;
+ }
+ return true;
}
// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
@@ -3836,32 +4214,36 @@ SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
return &span;
}
-void SkOpSegment::markWinding(int index, int winding) {
+bool SkOpSegment::markWinding(int index, int winding) {
// SkASSERT(!done());
SkASSERT(winding);
double referenceT = fTs[index].fT;
int lesser = index;
+ bool success = false;
while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
- markOneWinding(__FUNCTION__, lesser, winding);
+ success |= markOneWinding(__FUNCTION__, lesser, winding, NULL);
}
do {
- markOneWinding(__FUNCTION__, index, winding);
+ success |= markOneWinding(__FUNCTION__, index, winding, NULL);
} while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
debugValidate();
+ return success;
}
-void SkOpSegment::markWinding(int index, int winding, int oppWinding) {
+bool SkOpSegment::markWinding(int index, int winding, int oppWinding) {
// SkASSERT(!done());
SkASSERT(winding || oppWinding);
double referenceT = fTs[index].fT;
int lesser = index;
+ bool success = false;
while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
- markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
+ success |= markOneWinding(__FUNCTION__, lesser, winding, oppWinding, NULL);
}
do {
- markOneWinding(__FUNCTION__, index, winding, oppWinding);
+ success |= markOneWinding(__FUNCTION__, index, winding, oppWinding, NULL);
} while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
debugValidate();
+ return success;
}
void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
@@ -3924,19 +4306,20 @@ bool SkOpSegment::nextCandidate(int* start, int* end) const {
return true;
}
-static SkOpSegment* set_last(SkOpSpan** last, SkOpSpan* endSpan) {
+static SkOpSegment* set_last(SkOpSpan** last, const SkOpSpan* endSpan) {
if (last && !endSpan->fSmall) {
- *last = endSpan;
+ *last = const_cast<SkOpSpan*>(endSpan); // FIXME: get rid of cast
}
return NULL;
}
-SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr, SkOpSpan** last) {
+SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr,
+ SkOpSpan** last) const {
int origIndex = *indexPtr;
int step = *stepPtr;
int end = nextExactSpan(origIndex, step);
SkASSERT(end >= 0);
- SkOpSpan& endSpan = fTs[end];
+ const SkOpSpan& endSpan = this->span(end);
SkOpAngle* angle = step > 0 ? endSpan.fFromAngle : endSpan.fToAngle;
int foundIndex;
int otherEnd;
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index 4c35ac7e7e..b4da929d99 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -302,7 +302,7 @@ public:
double calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
double hiEnd, const SkOpSegment* other, int thisEnd);
void checkDuplicates();
- void checkEnds();
+ bool checkEnds();
void checkMultiples();
void checkSmall();
bool checkSmall(int index) const;
@@ -323,8 +323,10 @@ public:
int findT(double t, const SkPoint& , const SkOpSegment* ) const;
SkOpSegment* findTop(int* tIndex, int* endIndex, bool* unsortable, bool firstPass);
void fixOtherTIndex();
+ bool inconsistentAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
+ const SkOpAngle* angle) const;
void initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType);
- void initWinding(int start, int end, double tHit, int winding, SkScalar hitDx, int oppWind,
+ bool initWinding(int start, int end, double tHit, int winding, SkScalar hitDx, int oppWind,
SkScalar hitOppDx);
bool isMissing(double startT, const SkPoint& pt) const;
bool isTiny(const SkOpAngle* angle) const;
@@ -332,11 +334,13 @@ public:
bool cancel);
SkOpSpan* markAndChaseDoneBinary(int index, int endIndex);
SkOpSpan* markAndChaseDoneUnary(int index, int endIndex);
- SkOpSpan* markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding);
+ bool markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding,
+ SkOpSpan** lastPtr);
SkOpSpan* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
const SkOpAngle* angle);
void markDone(int index, int winding);
void markDoneBinary(int index);
+ void markDoneFinal(int index);
void markDoneUnary(int index);
bool nextCandidate(int* start, int* end) const;
int nextSpan(int from, int step) const;
@@ -403,15 +407,16 @@ private:
SkOpAngle* addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** );
SkOpAngle* addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** );
SkOpAngle* addSingletonAngles(int step);
+ void alignRange(int lower, int upper, const SkOpSegment* other, int oLower, int oUpper);
void alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other, double otherT,
const SkOpSegment* other2, SkOpSpan* oSpan, SkTDArray<AlignedSpan>* );
bool betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const;
void bumpCoincidentBlind(bool binary, int index, int last);
bool bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* index,
- SkTArray<SkPoint, true>* outsideTs);
+ SkTArray<SkPoint, true>* outsideTs);
void bumpCoincidentOBlind(int index, int last);
- void bumpCoincidentOther(const SkOpSpan& oTest, int* index,
- SkTArray<SkPoint, true>* outsideTs);
+ bool bumpCoincidentOther(const SkOpSpan& oTest, int* index,
+ SkTArray<SkPoint, true>* outsideTs, const SkPoint& endPt);
bool bumpSpan(SkOpSpan* span, int windDelta, int oppDelta);
bool calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts);
bool checkForSmall(const SkOpSpan* span, const SkPoint& pt, double newT,
@@ -438,6 +443,9 @@ private:
const SkOpSpan& firstSpan(const SkOpSpan& thisSpan) const;
void init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd);
bool inCoincidentSpan(double t, const SkOpSegment* other) const;
+ bool inconsistentWinding(const SkOpAngle* , int maxWinding, int oppMaxWinding) const;
+ bool inconsistentWinding(int min, int maxWinding, int oppMaxWinding) const;
+ bool inconsistentWinding(const char* funName, int tIndex, int winding, int oppWinding) const;
bool inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const;
#if OLD_CHASE
bool isSimple(int end) const;
@@ -449,30 +457,35 @@ private:
void matchWindingValue(int tIndex, double t, bool borrowWind);
SkOpSpan* markAndChaseDone(int index, int endIndex, int winding);
SkOpSpan* markAndChaseDoneBinary(const SkOpAngle* angle, int winding, int oppWinding);
- SkOpSpan* markAndChaseWinding(const SkOpAngle* angle, int winding);
- SkOpSpan* markAndChaseWinding(int index, int endIndex, int winding);
- SkOpSpan* markAndChaseWinding(int index, int endIndex, int winding, int oppWinding);
+ bool markAndChaseWinding(const SkOpAngle* angle, int winding, SkOpSpan** lastPtr);
+ bool markAndChaseWinding(int index, int endIndex, int winding, SkOpSpan** lastPtr);
+ bool markAndChaseWinding(int index, int endIndex, int winding, int oppWinding,
+ SkOpSpan** lastPtr);
SkOpSpan* markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle);
void markDoneBinary(int index, int winding, int oppWinding);
SkOpSpan* markAndChaseDoneUnary(const SkOpAngle* angle, int winding);
void markOneDone(const char* funName, int tIndex, int winding);
void markOneDoneBinary(const char* funName, int tIndex);
void markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding);
+ void markOneDoneFinal(const char* funName, int tIndex);
void markOneDoneUnary(const char* funName, int tIndex);
- SkOpSpan* markOneWinding(const char* funName, int tIndex, int winding);
- SkOpSpan* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding);
- void markWinding(int index, int winding);
- void markWinding(int index, int winding, int oppWinding);
+ bool markOneWinding(const char* funName, int tIndex, int winding, SkOpSpan** lastPtr);
+ bool markOneWinding(const char* funName, int tIndex, int winding, int oppWinding,
+ SkOpSpan** lastPtr);
+ bool markWinding(int index, int winding);
+ bool markWinding(int index, int winding, int oppWinding);
bool monotonicInY(int tStart, int tEnd) const;
bool multipleEnds() const { return fTs[count() - 2].fT == 1; }
bool multipleStarts() const { return fTs[1].fT == 0; }
- SkOpSegment* nextChase(int* index, int* step, int* min, SkOpSpan** last);
+ SkOpSegment* nextChase(int* index, int* step, int* min, SkOpSpan** last) const;
int nextExactSpan(int from, int step) const;
+ void resetSpanFlags();
bool serpentine(int tStart, int tEnd) const;
void setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
void setFromAngle(int endIndex, SkOpAngle* );
+ void setSpanFlags(const SkPoint& pt, double newT, SkOpSpan* span);
void setToAngle(int endIndex, SkOpAngle* );
void setUpWindings(int index, int endIndex, int* sumMiWinding,
int* maxWinding, int* sumWinding);
@@ -527,6 +540,7 @@ private:
void debugConstructQuad(SkPoint shortQuad[3]);
void debugReset();
void dumpDPts() const;
+ void dumpHexPts() const;
void dumpSpan(int index) const;
const SkPoint* fPts;
diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp
index f7b7273a8d..1a5bfc1889 100644
--- a/src/pathops/SkPathOpsCommon.cpp
+++ b/src/pathops/SkPathOpsCommon.cpp
@@ -161,7 +161,7 @@ SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex)
if (!sortable) {
continue;
}
- // find first angle, initialize winding to computed fWindSum
+ // find first angle, initialize winding to computed wind sum
const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
const SkOpAngle* firstAngle;
SkDEBUGCODE(firstAngle = angle);
@@ -208,7 +208,8 @@ SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex)
if (SkOpSegment::UseInnerWinding(maxWinding, winding)) {
maxWinding = winding;
}
- (void) segment->markAndChaseWinding(angle, maxWinding, 0);
+ // allowed to do nothing
+ (void) segment->markAndChaseWinding(angle, maxWinding, 0, NULL);
break;
}
}
@@ -315,6 +316,12 @@ static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
return;
}
+struct SortableTop { // error if local in pre-C++11
+ SkOpSegment* fSegment;
+ int fIndex;
+ int fEndIndex;
+};
+
SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
SkOpAngle::IncludeType angleIncludeType, bool* firstContour, int* indexPtr,
int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical,
@@ -356,6 +363,8 @@ SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
double tHit;
SkScalar hitDx = 0;
SkScalar hitOppDx = 0;
+ // keep track of subsequent returns to detect infinite loops
+ SkTDArray<SortableTop> sortableTops;
do {
// if current is vertical, find another candidate which is not
// if only remaining candidates are vertical, then they can be marked done
@@ -366,6 +375,35 @@ SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
tryAgain = false;
contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
&hitDx, &tryAgain, onlyVertical, false);
+ if (tryAgain) {
+ bool giveUp = false;
+ int count = sortableTops.count();
+ for (int index = 0; index < count; ++index) {
+ const SortableTop& prev = sortableTops[index];
+ if (giveUp) {
+ prev.fSegment->markDoneFinal(prev.fIndex);
+ } else if (prev.fSegment == current
+ && (prev.fIndex == *indexPtr || prev.fEndIndex == *endIndexPtr)) {
+ // remaining edges are non-vertical and cannot have their winding computed
+ // mark them as done and return, and hope that assembly can fill the holes
+ giveUp = true;
+ index = -1;
+ }
+ }
+ if (giveUp) {
+ *done = true;
+ return NULL;
+ }
+ }
+ SortableTop* sortableTop = sortableTops.append();
+ sortableTop->fSegment = current;
+ sortableTop->fIndex = *indexPtr;
+ sortableTop->fEndIndex = *endIndexPtr;
+#if DEBUG_SORT
+ SkDebugf("%s current=%d index=%d endIndex=%d tHit=%1.9g hitDx=%1.9g try=%d vert=%d\n",
+ __FUNCTION__, current->debugID(), *indexPtr, *endIndexPtr, tHit, hitDx, tryAgain,
+ *onlyVertical);
+#endif
if (*onlyVertical) {
return current;
}
@@ -378,10 +416,16 @@ SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
&hitOppDx, &tryAgain, NULL, true);
} while (tryAgain);
- current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx, oppContourWinding,
- hitOppDx);
+ bool success = current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx,
+ oppContourWinding, hitOppDx);
if (current->done()) {
return NULL;
+ } else if (!success) { // check if the span has a valid winding
+ int min = SkTMin(*indexPtr, *endIndexPtr);
+ const SkOpSpan& span = current->span(min);
+ if (span.fWindSum == SK_MinS32) {
+ return NULL;
+ }
}
return current;
}
@@ -405,14 +449,17 @@ static void checkDuplicates(SkTArray<SkOpContour*, true>* contourList) {
}
}
-static void checkEnds(SkTArray<SkOpContour*, true>* contourList) {
+static bool checkEnds(SkTArray<SkOpContour*, true>* contourList) {
// it's hard to determine if the end of a cubic or conic nearly intersects another curve.
// instead, look to see if the connecting curve intersected at that same end.
int contourCount = (*contourList).count();
for (int cTest = 0; cTest < contourCount; ++cTest) {
SkOpContour* contour = (*contourList)[cTest];
- contour->checkEnds();
+ if (!contour->checkEnds()) {
+ return false;
+ }
}
+ return true;
}
static bool checkMultiples(SkTArray<SkOpContour*, true>* contourList) {
@@ -706,7 +753,9 @@ bool HandleCoincidence(SkTArray<SkOpContour*, true>* contourList, int total) {
SkOpContour::debugShowWindingValues(contourList);
#endif
fixOtherTIndex(contourList);
- checkEnds(contourList); // check if connecting curve intersected at the same end
+ if (!checkEnds(contourList)) { // check if connecting curve intersected at the same end
+ return false;
+ }
bool hasM = checkMultiples(contourList); // check if intersections agree on t and point values
SkTDArray<SkOpSegment::AlignedSpan> aligned;
if (hasM) {
diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h
index 18097e7480..5770aefec5 100644
--- a/src/pathops/SkPathOpsDebug.h
+++ b/src/pathops/SkPathOpsDebug.h
@@ -46,7 +46,7 @@
#define DEBUG_ANGLE 0
#define DEBUG_AS_C_CODE 1
#define DEBUG_ASSEMBLE 0
-#define DEBUG_CHECK_ENDS 0
+#define DEBUG_CHECK_ALIGN 0
#define DEBUG_CHECK_TINY 0
#define DEBUG_CONCIDENT 0
#define DEBUG_CROSS 0
@@ -82,7 +82,7 @@
#define DEBUG_ANGLE 1
#define DEBUG_AS_C_CODE 1
#define DEBUG_ASSEMBLE 1
-#define DEBUG_CHECK_ENDS 1
+#define DEBUG_CHECK_ALIGN 1
#define DEBUG_CHECK_TINY 1
#define DEBUG_CONCIDENT 1
#define DEBUG_CROSS 01
diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp
index 72efb89d10..f2b25c04ec 100644
--- a/src/pathops/SkPathOpsOp.cpp
+++ b/src/pathops/SkPathOpsOp.cpp
@@ -45,22 +45,28 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* e
continue;
}
const SkOpAngle* firstAngle = angle;
- SkDEBUGCODE(bool loop = false);
- int winding;
+ bool loop = false;
+ int winding = SK_MinS32;
do {
angle = angle->next();
- SkASSERT(angle != firstAngle || !loop);
- SkDEBUGCODE(loop |= angle == firstAngle);
+ if (angle == firstAngle && loop) {
+ break; // if we get here, there's no winding, loop is unorderable
+ }
+ loop |= angle == firstAngle;
segment = angle->segment();
winding = segment->windSum(angle);
} while (winding == SK_MinS32);
+ if (winding == SK_MinS32) {
+ continue;
+ }
int sumMiWinding = segment->updateWindingReverse(angle);
int sumSuWinding = segment->updateOppWindingReverse(angle);
if (segment->operand()) {
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
SkOpSegment* first = NULL;
- while ((angle = angle->next()) != firstAngle) {
+ bool badData = false;
+ while ((angle = angle->next()) != firstAngle && !badData) {
segment = angle->segment();
int start = angle->start();
int end = angle->end();
@@ -73,11 +79,19 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* e
*tIndex = start;
*endIndex = end;
}
+ if (segment->inconsistentAngle(maxWinding, sumWinding, oppMaxWinding,
+ oppSumWinding, angle)) {
+ badData = true;
+ break;
+ }
// OPTIMIZATION: should this also add to the chase?
(void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
oppSumWinding, angle);
}
}
+ if (badData) {
+ continue;
+ }
if (first) {
#if TRY_ROTATE
*chase.insert(0) = span;
@@ -245,7 +259,47 @@ static const bool gOutInverse[kReverseDifference_PathOp + 1][2][2] = {
{{ false, true }, { false, false }}, // rev diff
};
+#define DEBUGGING_PATHOPS_FROM_HOST 0 // enable to debug svg in chrome -- note path hardcoded below
+#if DEBUGGING_PATHOPS_FROM_HOST
+#include "SkData.h"
+#include "SkStream.h"
+
+static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
+ SkDynamicMemoryWStream wStream;
+ path.dump(&wStream, force, dumpAsHex);
+ SkAutoDataUnref data(wStream.copyToData());
+ fprintf(file, "%.*s\n", (int) data->size(), data->data());
+}
+
+static int dumpID = 0;
+
+static void dump_op(const SkPath& one, const SkPath& two, SkPathOp op) {
+#if SK_BUILD_FOR_MAC
+ FILE* file = fopen("/Users/caryclark/Documents/svgop.txt", "w");
+#else
+ FILE* file = fopen("/usr/local/google/home/caryclark/Documents/svgop.txt", "w");
+#endif
+ fprintf(file,
+ "\nstatic void fuzz763_%d(skiatest::Reporter* reporter, const char* filename) {\n",
+ ++dumpID);
+ fprintf(file, " SkPath path;\n");
+ fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
+ dump_path(file, one, false, true);
+ fprintf(file, " SkPath path1(path);\n");
+ fprintf(file, " path.reset();\n");
+ fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
+ 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");
+ fclose(file);
+}
+#endif
+
bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
+#if DEBUGGING_PATHOPS_FROM_HOST
+ dump_op(one, two, op);
+#endif
#if DEBUG_SHOW_TEST_NAME
char* debugName = DEBUG_FILENAME_STRING;
if (debugName && debugName[0]) {
diff --git a/src/pathops/SkPathOpsPoint.h b/src/pathops/SkPathOpsPoint.h
index 5c2e3a50dd..7ddfbfb5d1 100644
--- a/src/pathops/SkPathOpsPoint.h
+++ b/src/pathops/SkPathOpsPoint.h
@@ -227,6 +227,7 @@ struct SkDPoint {
// utilities callable by the user from the debugger when the implementation code is linked in
void dump() const;
static void Dump(const SkPoint& pt);
+ static void DumpHex(const SkPoint& pt);
};
#endif
diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h
index 96627842b3..01fec0d0b6 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -141,6 +141,12 @@ inline bool roughly_zero(double x) {
return fabs(x) < ROUGH_EPSILON;
}
+#if 0 // unused for now
+inline bool way_roughly_zero(double x) {
+ return fabs(x) < WAY_ROUGH_EPSILON;
+}
+#endif
+
inline bool approximately_zero_inverse(double x) {
return fabs(x) > FLT_EPSILON_INVERSE;
}
diff --git a/src/pathops/SkReduceOrder.cpp b/src/pathops/SkReduceOrder.cpp
index bb2038b45f..6f06447a47 100644
--- a/src/pathops/SkReduceOrder.cpp
+++ b/src/pathops/SkReduceOrder.cpp
@@ -88,12 +88,12 @@ int SkReduceOrder::reduce(const SkDQuad& quad) {
}
}
if (minXSet == 0x7) { // test for vertical line
- if (minYSet == 0x7) { // return 1 if all four are coincident
+ if (minYSet == 0x7) { // return 1 if all three are coincident
return coincident_line(quad, fQuad);
}
return vertical_line(quad, fQuad);
}
- if (minYSet == 0xF) { // test for horizontal line
+ if (minYSet == 0x7) { // test for horizontal line
return horizontal_line(quad, fQuad);
}
int result = check_linear(quad, minX, maxX, minY, maxY, fQuad);