aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/pathops
diff options
context:
space:
mode:
authorGravatar reed <reed@google.com>2015-03-24 13:55:33 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-03-24 13:55:33 -0700
commit0dc4dd6dda9a7912f696b46d9c02155ec1d1ba5f (patch)
tree994c85a8e418986415175ddccc71adf924df3846 /src/pathops
parent82dec0e16ae10026194ce45b67af931700510450 (diff)
Revert of pathops version two (patchset #16 id:150001 of https://codereview.chromium.org/1002693002/)
Reason for revert: ASAN investigation Original issue's description: > pathops version two > > R=reed@google.com > > marked 'no commit' to attempt to get trybots to run > > TBR=reed@google.com > > Committed: https://skia.googlesource.com/skia/+/ccec0f958ffc71a9986d236bc2eb335cb2111119 TBR=caryclark@google.com NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true Review URL: https://codereview.chromium.org/1029993002
Diffstat (limited to 'src/pathops')
-rw-r--r--src/pathops/SkAddIntersections.cpp185
-rw-r--r--src/pathops/SkAddIntersections.h8
-rw-r--r--src/pathops/SkDCubicIntersection.cpp704
-rw-r--r--src/pathops/SkDCubicLineIntersection.cpp88
-rw-r--r--src/pathops/SkDCubicToQuads.cpp150
-rw-r--r--src/pathops/SkDLineIntersection.cpp84
-rw-r--r--src/pathops/SkDQuadImplicit.cpp117
-rw-r--r--src/pathops/SkDQuadImplicit.h39
-rw-r--r--src/pathops/SkDQuadIntersection.cpp617
-rw-r--r--src/pathops/SkDQuadLineIntersection.cpp74
-rw-r--r--src/pathops/SkIntersectionHelper.h91
-rw-r--r--src/pathops/SkIntersections.cpp110
-rw-r--r--src/pathops/SkIntersections.h89
-rw-r--r--src/pathops/SkOpAngle.cpp745
-rw-r--r--src/pathops/SkOpAngle.h179
-rwxr-xr-xsrc/pathops/SkOpCoincidence.cpp388
-rw-r--r--src/pathops/SkOpCoincidence.h20
-rw-r--r--src/pathops/SkOpContour.cpp738
-rw-r--r--src/pathops/SkOpContour.h474
-rw-r--r--src/pathops/SkOpEdgeBuilder.cpp98
-rw-r--r--src/pathops/SkOpEdgeBuilder.h39
-rw-r--r--src/pathops/SkOpSegment.cpp4798
-rw-r--r--src/pathops/SkOpSegment.h704
-rwxr-xr-xsrc/pathops/SkOpSpan.cpp355
-rw-r--r--src/pathops/SkOpSpan.h466
-rw-r--r--src/pathops/SkOpTAllocator.h6
-rw-r--r--src/pathops/SkPathOpsCommon.cpp586
-rw-r--r--src/pathops/SkPathOpsCommon.h26
-rw-r--r--src/pathops/SkPathOpsCubic.cpp191
-rw-r--r--src/pathops/SkPathOpsCubic.h67
-rw-r--r--src/pathops/SkPathOpsCubicSect.h175
-rw-r--r--src/pathops/SkPathOpsDebug.cpp683
-rw-r--r--src/pathops/SkPathOpsDebug.h224
-rw-r--r--src/pathops/SkPathOpsLine.cpp22
-rw-r--r--src/pathops/SkPathOpsLine.h13
-rw-r--r--src/pathops/SkPathOpsOp.cpp184
-rw-r--r--src/pathops/SkPathOpsPoint.h53
-rw-r--r--[-rwxr-xr-x]src/pathops/SkPathOpsPostSect.cpp27
-rw-r--r--src/pathops/SkPathOpsQuad.cpp75
-rw-r--r--src/pathops/SkPathOpsQuad.h35
-rw-r--r--src/pathops/SkPathOpsQuadSect.h175
-rw-r--r--src/pathops/SkPathOpsRect.cpp19
-rw-r--r--src/pathops/SkPathOpsRect.h30
-rw-r--r--src/pathops/SkPathOpsSimplify.cpp161
-rw-r--r--src/pathops/SkPathOpsTCubicSect.cpp6
-rw-r--r--src/pathops/SkPathOpsTQuadSect.cpp6
-rw-r--r--src/pathops/SkPathOpsTSect.h1636
-rw-r--r--src/pathops/SkPathOpsTightBounds.cpp12
-rw-r--r--src/pathops/SkPathOpsTriangle.cpp51
-rw-r--r--src/pathops/SkPathOpsTriangle.h20
-rw-r--r--src/pathops/SkPathOpsTypes.h129
-rw-r--r--src/pathops/SkQuarticRoot.cpp168
-rw-r--r--src/pathops/SkQuarticRoot.h16
-rw-r--r--src/pathops/SkReduceOrder.cpp5
-rw-r--r--src/pathops/SkReduceOrder.h2
55 files changed, 10086 insertions, 6077 deletions
diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp
index b507eb7560..c27434f9f7 100644
--- a/src/pathops/SkAddIntersections.cpp
+++ b/src/pathops/SkAddIntersections.cpp
@@ -5,7 +5,6 @@
* found in the LICENSE file.
*/
#include "SkAddIntersections.h"
-#include "SkOpCoincidence.h"
#include "SkPathOpsBounds.h"
#if DEBUG_ADD_INTERSECTING_TS
@@ -131,6 +130,20 @@ static void debugShowCubicIntersection(int pts, const SkIntersectionHelper& wt,
SkDebugf("\n");
}
+static void debugShowCubicIntersection(int pts, const SkIntersectionHelper& wt,
+ const SkIntersections& i) {
+ SkASSERT(i.used() == pts);
+ if (!pts) {
+ SkDebugf("%s no self intersect " CUBIC_DEBUG_STR "\n", __FUNCTION__,
+ CUBIC_DEBUG_DATA(wt.pts()));
+ return;
+ }
+ SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CUBIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__,
+ i[0][0], CUBIC_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0));
+ SkDebugf(" " T_DEBUG_STR(wtTs, 1), i[1][0]);
+ SkDebugf("\n");
+}
+
#else
static void debugShowLineIntersection(int , const SkIntersectionHelper& ,
const SkIntersectionHelper& , const SkIntersections& ) {
@@ -155,10 +168,13 @@ static void debugShowCubicQuadIntersection(int , const SkIntersectionHelper& ,
static void debugShowCubicIntersection(int , const SkIntersectionHelper& ,
const SkIntersectionHelper& , const SkIntersections& ) {
}
+
+static void debugShowCubicIntersection(int , const SkIntersectionHelper& ,
+ const SkIntersections& ) {
+}
#endif
-bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
- SkChunkAlloc* allocator) {
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
if (test != next) {
if (AlmostLessUlps(test->bounds().fBottom, next->bounds().fTop)) {
return false;
@@ -170,11 +186,10 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
}
SkIntersectionHelper wt;
wt.init(test);
+ bool foundCommonContour = test == next;
do {
SkIntersectionHelper wn;
wn.init(next);
- test->debugValidate();
- next->debugValidate();
if (test == next && !wn.startAfter(wt)) {
continue;
}
@@ -291,22 +306,14 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
break;
}
case SkIntersectionHelper::kQuad_Segment: {
- SkDQuad quad1;
- quad1.set(wt.pts());
- SkDQuad quad2;
- quad2.set(wn.pts());
- pts = ts.intersect(quad1, quad2);
+ pts = ts.quadQuad(wt.pts(), wn.pts());
+ ts.alignQuadPts(wt.pts(), wn.pts());
debugShowQuadIntersection(pts, wt, wn, ts);
break;
}
case SkIntersectionHelper::kCubic_Segment: {
swap = true;
- SkDQuad quad1;
- quad1.set(wt.pts());
- SkDCubic cubic1 = quad1.toCubic();
- SkDCubic cubic2;
- cubic2.set(wn.pts());
- pts = ts.intersect(cubic2, cubic1);
+ pts = ts.cubicQuad(wn.pts(), wt.pts());
debugShowCubicQuadIntersection(pts, wn, wt, ts);
break;
}
@@ -332,21 +339,12 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
break;
}
case SkIntersectionHelper::kQuad_Segment: {
- SkDCubic cubic1;
- cubic1.set(wt.pts());
- SkDQuad quad2;
- quad2.set(wn.pts());
- SkDCubic cubic2 = quad2.toCubic();
- pts = ts.intersect(cubic1, cubic2);
+ pts = ts.cubicQuad(wt.pts(), wn.pts());
debugShowCubicQuadIntersection(pts, wt, wn, ts);
break;
}
case SkIntersectionHelper::kCubic_Segment: {
- SkDCubic cubic1;
- cubic1.set(wt.pts());
- SkDCubic cubic2;
- cubic2.set(wn.pts());
- pts = ts.intersect(cubic1, cubic2);
+ pts = ts.cubicCubic(wt.pts(), wn.pts());
debugShowCubicIntersection(pts, wt, wn, ts);
break;
}
@@ -357,53 +355,102 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
default:
SkASSERT(0);
}
- int coinIndex = -1;
- SkOpPtT* coinPtT[2];
+ if (!foundCommonContour && pts > 0) {
+ test->addCross(next);
+ next->addCross(test);
+ foundCommonContour = true;
+ }
+ // in addition to recording T values, record matching segment
+ if (pts == 2) {
+ if (wn.segmentType() <= SkIntersectionHelper::kLine_Segment
+ && wt.segmentType() <= SkIntersectionHelper::kLine_Segment) {
+ if (wt.addCoincident(wn, ts, swap)) {
+ continue;
+ }
+ pts = ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1)
+ } else if (wn.segmentType() >= SkIntersectionHelper::kQuad_Segment
+ && wt.segmentType() >= SkIntersectionHelper::kQuad_Segment
+ && ts.isCoincident(0)) {
+ SkASSERT(ts.coincidentUsed() == 2);
+ if (wt.addCoincident(wn, ts, swap)) {
+ continue;
+ }
+ pts = ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1)
+ }
+ }
+ if (pts >= 2) {
+ for (int pt = 0; pt < pts - 1; ++pt) {
+ const SkDPoint& point = ts.pt(pt);
+ const SkDPoint& next = ts.pt(pt + 1);
+ if (wt.isPartial(ts[swap][pt], ts[swap][pt + 1], point, 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
+ pts = ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1)
+ }
+ }
+ }
+ }
for (int pt = 0; pt < pts; ++pt) {
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);
- wn.segment()->debugValidate();
- SkOpPtT* nextTAt = wn.segment()->addT(ts[!swap][pt], SkOpSegment::kAllowAlias,
- allocator);
- testTAt->addOpp(nextTAt);
- if (testTAt->fPt != nextTAt->fPt) {
- testTAt->span()->unaligned();
- nextTAt->span()->unaligned();
- }
- wt.segment()->debugValidate();
- wn.segment()->debugValidate();
- if (!ts.isCoincident(pt)) {
- continue;
- }
- if (coinIndex < 0) {
- coinPtT[0] = testTAt;
- coinPtT[1] = nextTAt;
- coinIndex = pt;
- continue;
- }
- if (coinPtT[0]->span() == testTAt->span()) {
- coinIndex = -1;
- continue;
- }
- if (coinPtT[1]->span() == nextTAt->span()) {
- coinIndex = -1; // coincidence span collapsed
- continue;
- }
- if (swap) {
- SkTSwap(coinPtT[0], coinPtT[1]);
- SkTSwap(testTAt, nextTAt);
- }
- SkASSERT(coinPtT[0]->span()->t() < testTAt->span()->t());
- coincidence->add(coinPtT[0], testTAt, coinPtT[1], nextTAt, allocator);
- wt.segment()->debugValidate();
- wn.segment()->debugValidate();
- coinIndex = -1;
+ SkPoint point = ts.pt(pt).asSkPoint();
+ wt.alignTPt(wn, swap, pt, &ts, &point);
+ int testTAt = wt.addT(wn, point, ts[swap][pt]);
+ int nextTAt = wn.addT(wt, point, ts[!swap][pt]);
+ wt.addOtherT(testTAt, ts[!swap][pt], nextTAt);
+ wn.addOtherT(nextTAt, ts[swap][pt], testTAt);
}
- SkASSERT(coinIndex < 0); // expect coincidence to be paired
} while (wn.advance());
} while (wt.advance());
return true;
}
+
+void AddSelfIntersectTs(SkOpContour* test) {
+ SkIntersectionHelper wt;
+ wt.init(test);
+ do {
+ if (wt.segmentType() != SkIntersectionHelper::kCubic_Segment) {
+ continue;
+ }
+ SkIntersections ts;
+ int pts = ts.cubic(wt.pts());
+ debugShowCubicIntersection(pts, wt, ts);
+ if (!pts) {
+ continue;
+ }
+ SkASSERT(pts == 1);
+ SkASSERT(ts[0][0] >= 0 && ts[0][0] <= 1);
+ SkASSERT(ts[1][0] >= 0 && ts[1][0] <= 1);
+ SkPoint point = ts.pt(0).asSkPoint();
+ int testTAt = wt.addSelfT(point, ts[0][0]);
+ int nextTAt = wt.addSelfT(point, ts[1][0]);
+ wt.addOtherT(testTAt, ts[1][0], nextTAt);
+ wt.addOtherT(nextTAt, ts[0][0], testTAt);
+ } while (wt.advance());
+}
+
+// resolve any coincident pairs found while intersecting, and
+// see if coincidence is formed by clipping non-concident segments
+bool CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total) {
+ int contourCount = (*contourList).count();
+ for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+ SkOpContour* contour = (*contourList)[cIndex];
+ contour->resolveNearCoincidence();
+ }
+ for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+ SkOpContour* contour = (*contourList)[cIndex];
+ contour->addCoincidentPoints();
+ }
+ for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+ SkOpContour* contour = (*contourList)[cIndex];
+ if (!contour->calcCoincidentWinding()) {
+ return false;
+ }
+ }
+ for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+ SkOpContour* contour = (*contourList)[cIndex];
+ contour->calcPartialCoincidentWinding();
+ }
+ return true;
+}
diff --git a/src/pathops/SkAddIntersections.h b/src/pathops/SkAddIntersections.h
index 23654e5e91..4c1947b635 100644
--- a/src/pathops/SkAddIntersections.h
+++ b/src/pathops/SkAddIntersections.h
@@ -9,10 +9,10 @@
#include "SkIntersectionHelper.h"
#include "SkIntersections.h"
+#include "SkTArray.h"
-class SkOpCoincidence;
-
-bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
- SkChunkAlloc* allocator);
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next);
+void AddSelfIntersectTs(SkOpContour* test);
+bool CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total);
#endif
diff --git a/src/pathops/SkDCubicIntersection.cpp b/src/pathops/SkDCubicIntersection.cpp
new file mode 100644
index 0000000000..2fb35e1827
--- /dev/null
+++ b/src/pathops/SkDCubicIntersection.cpp
@@ -0,0 +1,704 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkIntersections.h"
+#include "SkPathOpsCubic.h"
+#include "SkPathOpsLine.h"
+#include "SkPathOpsPoint.h"
+#include "SkPathOpsQuad.h"
+#include "SkPathOpsRect.h"
+#include "SkReduceOrder.h"
+#include "SkTSort.h"
+
+#if ONE_OFF_DEBUG
+static const double tLimits1[2][2] = {{0.3, 0.4}, {0.8, 0.9}};
+static const double tLimits2[2][2] = {{-0.8, -0.9}, {-0.8, -0.9}};
+#endif
+
+#define DEBUG_QUAD_PART ONE_OFF_DEBUG && 1
+#define DEBUG_QUAD_PART_SHOW_SIMPLE DEBUG_QUAD_PART && 0
+#define SWAP_TOP_DEBUG 0
+
+static const int kCubicToQuadSubdivisionDepth = 8; // slots reserved for cubic to quads subdivision
+
+static int quadPart(const SkDCubic& cubic, double tStart, double tEnd, SkReduceOrder* reducer) {
+ SkDCubic part = cubic.subDivide(tStart, tEnd);
+ SkDQuad quad = part.toQuad();
+ // FIXME: should reduceOrder be looser in this use case if quartic is going to blow up on an
+ // extremely shallow quadratic?
+ int order = reducer->reduce(quad);
+#if DEBUG_QUAD_PART
+ SkDebugf("%s cubic=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
+ " t=(%1.9g,%1.9g)\n", __FUNCTION__, cubic[0].fX, cubic[0].fY,
+ cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
+ cubic[3].fX, cubic[3].fY, tStart, tEnd);
+ SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n"
+ " {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
+ part[0].fX, part[0].fY, part[1].fX, part[1].fY, part[2].fX, part[2].fY,
+ part[3].fX, part[3].fY, quad[0].fX, quad[0].fY,
+ quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY);
+#if DEBUG_QUAD_PART_SHOW_SIMPLE
+ SkDebugf("%s simple=(%1.9g,%1.9g", __FUNCTION__, reducer->fQuad[0].fX, reducer->fQuad[0].fY);
+ if (order > 1) {
+ SkDebugf(" %1.9g,%1.9g", reducer->fQuad[1].fX, reducer->fQuad[1].fY);
+ }
+ if (order > 2) {
+ SkDebugf(" %1.9g,%1.9g", reducer->fQuad[2].fX, reducer->fQuad[2].fY);
+ }
+ SkDebugf(")\n");
+ SkASSERT(order < 4 && order > 0);
+#endif
+#endif
+ return order;
+}
+
+static void intersectWithOrder(const SkDQuad& simple1, int order1, const SkDQuad& simple2,
+ int order2, SkIntersections& i) {
+ if (order1 == 3 && order2 == 3) {
+ i.intersect(simple1, simple2);
+ } else if (order1 <= 2 && order2 <= 2) {
+ i.intersect((const SkDLine&) simple1, (const SkDLine&) simple2);
+ } else if (order1 == 3 && order2 <= 2) {
+ i.intersect(simple1, (const SkDLine&) simple2);
+ } else {
+ SkASSERT(order1 <= 2 && order2 == 3);
+ i.intersect(simple2, (const SkDLine&) simple1);
+ i.swapPts();
+ }
+}
+
+// this flavor centers potential intersections recursively. In contrast, '2' may inadvertently
+// chase intersections near quadratic ends, requiring odd hacks to find them.
+static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDCubic& cubic2,
+ double t2s, double t2e, double precisionScale, SkIntersections& i) {
+ i.upDepth();
+ SkDCubic c1 = cubic1.subDivide(t1s, t1e);
+ SkDCubic c2 = cubic2.subDivide(t2s, t2e);
+ SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts1;
+ // OPTIMIZE: if c1 == c2, call once (happens when detecting self-intersection)
+ c1.toQuadraticTs(c1.calcPrecision() * precisionScale, &ts1);
+ SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts2;
+ c2.toQuadraticTs(c2.calcPrecision() * precisionScale, &ts2);
+ double t1Start = t1s;
+ int ts1Count = ts1.count();
+ for (int i1 = 0; i1 <= ts1Count; ++i1) {
+ const double tEnd1 = i1 < ts1Count ? ts1[i1] : 1;
+ const double t1 = t1s + (t1e - t1s) * tEnd1;
+ SkReduceOrder s1;
+ int o1 = quadPart(cubic1, t1Start, t1, &s1);
+ double t2Start = t2s;
+ int ts2Count = ts2.count();
+ for (int i2 = 0; i2 <= ts2Count; ++i2) {
+ const double tEnd2 = i2 < ts2Count ? ts2[i2] : 1;
+ const double t2 = t2s + (t2e - t2s) * tEnd2;
+ if (&cubic1 == &cubic2 && t1Start >= t2Start) {
+ t2Start = t2;
+ continue;
+ }
+ SkReduceOrder s2;
+ int o2 = quadPart(cubic2, t2Start, t2, &s2);
+ #if ONE_OFF_DEBUG
+ char tab[] = " ";
+ if (tLimits1[0][0] >= t1Start && tLimits1[0][1] <= t1
+ && tLimits1[1][0] >= t2Start && tLimits1[1][1] <= t2) {
+ SkDebugf("%.*s %s t1=(%1.9g,%1.9g) t2=(%1.9g,%1.9g)", i.depth()*2, tab,
+ __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) {
+ double to1 = t1Start + (t1 - t1Start) * locals[0][tIdx];
+ double to2 = t2Start + (t2 - t2Start) * locals[1][tIdx];
+ // if the computed t is not sufficiently precise, iterate
+ SkDPoint p1 = cubic1.ptAtT(to1);
+ SkDPoint p2 = cubic2.ptAtT(to2);
+ if (p1.approximatelyEqual(p2)) {
+ // FIXME: local edge may be coincident -- experiment with not propagating coincidence to caller
+// SkASSERT(!locals.isCoincident(tIdx));
+ if (&cubic1 != &cubic2 || !approximately_equal(to1, to2)) {
+ if (i.swapped()) { // FIXME: insert should respect swap
+ i.insert(to2, to1, p1);
+ } else {
+ i.insert(to1, to2, p1);
+ }
+ }
+ } else {
+/*for random cubics, 16 below catches 99.997% of the intersections. To test for the remaining 0.003%
+ look for nearly coincident curves. and check each 1/16th section.
+*/
+ double offset = precisionScale / 16; // FIXME: const is arbitrary: test, refine
+ double c1Bottom = tIdx == 0 ? 0 :
+ (t1Start + (t1 - t1Start) * locals[0][tIdx - 1] + to1) / 2;
+ double c1Min = SkTMax(c1Bottom, to1 - offset);
+ double c1Top = tIdx == tCount - 1 ? 1 :
+ (t1Start + (t1 - t1Start) * locals[0][tIdx + 1] + to1) / 2;
+ double c1Max = SkTMin(c1Top, to1 + offset);
+ double c2Min = SkTMax(0., to2 - offset);
+ double c2Max = SkTMin(1., to2 + offset);
+ #if ONE_OFF_DEBUG
+ SkDebugf("%.*s %s 1 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
+ __FUNCTION__,
+ c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
+ && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
+ to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
+ && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
+ c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
+ && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
+ to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
+ && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
+ SkDebugf("%.*s %s 1 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
+ " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
+ i.depth()*2, tab, __FUNCTION__, c1Bottom, c1Top, 0., 1.,
+ to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
+ SkDebugf("%.*s %s 1 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
+ " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
+ c1Max, c2Min, c2Max);
+ #endif
+ intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
+ #if ONE_OFF_DEBUG
+ SkDebugf("%.*s %s 1 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
+ i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
+ #endif
+ if (tCount > 1) {
+ c1Min = SkTMax(0., to1 - offset);
+ c1Max = SkTMin(1., to1 + offset);
+ double c2Bottom = tIdx == 0 ? to2 :
+ (t2Start + (t2 - t2Start) * locals[1][tIdx - 1] + to2) / 2;
+ double c2Top = tIdx == tCount - 1 ? to2 :
+ (t2Start + (t2 - t2Start) * locals[1][tIdx + 1] + to2) / 2;
+ if (c2Bottom > c2Top) {
+ SkTSwap(c2Bottom, c2Top);
+ }
+ if (c2Bottom == to2) {
+ c2Bottom = 0;
+ }
+ if (c2Top == to2) {
+ c2Top = 1;
+ }
+ c2Min = SkTMax(c2Bottom, to2 - offset);
+ c2Max = SkTMin(c2Top, to2 + offset);
+ #if ONE_OFF_DEBUG
+ SkDebugf("%.*s %s 2 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
+ __FUNCTION__,
+ c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
+ && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
+ to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
+ && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
+ c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
+ && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
+ to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
+ && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
+ SkDebugf("%.*s %s 2 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
+ " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
+ i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top,
+ to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
+ SkDebugf("%.*s %s 2 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
+ " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
+ c1Max, c2Min, c2Max);
+ #endif
+ intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
+ #if ONE_OFF_DEBUG
+ SkDebugf("%.*s %s 2 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
+ i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
+ #endif
+ c1Min = SkTMax(c1Bottom, to1 - offset);
+ c1Max = SkTMin(c1Top, to1 + offset);
+ #if ONE_OFF_DEBUG
+ SkDebugf("%.*s %s 3 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
+ __FUNCTION__,
+ c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
+ && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
+ to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
+ && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
+ c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
+ && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
+ to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
+ && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
+ SkDebugf("%.*s %s 3 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
+ " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
+ i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top,
+ to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
+ SkDebugf("%.*s %s 3 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
+ " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
+ c1Max, c2Min, c2Max);
+ #endif
+ intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
+ #if ONE_OFF_DEBUG
+ SkDebugf("%.*s %s 3 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
+ i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
+ #endif
+ }
+ // intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
+ // FIXME: if no intersection is found, either quadratics intersected where
+ // cubics did not, or the intersection was missed. In the former case, expect
+ // the quadratics to be nearly parallel at the point of intersection, and check
+ // for that.
+ }
+ }
+ t2Start = t2;
+ }
+ t1Start = t1;
+ }
+ i.downDepth();
+}
+
+ // if two ends intersect, check middle for coincidence
+bool SkIntersections::cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2) {
+ if (fUsed < 2) {
+ return false;
+ }
+ int last = fUsed - 1;
+ double tRange1 = fT[0][last] - fT[0][0];
+ double tRange2 = fT[1][last] - fT[1][0];
+ for (int index = 1; index < 5; ++index) {
+ double testT1 = fT[0][0] + tRange1 * index / 5;
+ double testT2 = fT[1][0] + tRange2 * index / 5;
+ SkDPoint testPt1 = c1.ptAtT(testT1);
+ SkDPoint testPt2 = c2.ptAtT(testT2);
+ if (!testPt1.approximatelyEqual(testPt2)) {
+ return false;
+ }
+ }
+ if (fUsed > 2) {
+ fPt[1] = fPt[last];
+ fT[0][1] = fT[0][last];
+ fT[1][1] = fT[1][last];
+ fUsed = 2;
+ }
+ fIsCoincident[0] = fIsCoincident[1] = 0x03;
+ return true;
+}
+
+#define LINE_FRACTION 0.1
+
+// intersect the end of the cubic with the other. Try lines from the end to control and opposite
+// end to determine range of t on opposite cubic.
+bool SkIntersections::cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2) {
+ int t1Index = start ? 0 : 3;
+ double testT = (double) !start;
+ bool swap = swapped();
+ // quad/quad at this point checks to see if exact matches have already been found
+ // cubic/cubic can't reject so easily since cubics can intersect same point more than once
+ SkDLine tmpLine;
+ tmpLine[0] = tmpLine[1] = cubic2[t1Index];
+ tmpLine[1].fX += cubic2[2 - start].fY - cubic2[t1Index].fY;
+ 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);
+ if (!tmpLine[0].approximatelyEqual(realPt)) {
+ continue;
+ }
+ if (swap) {
+ cubicInsert(testT, impTs[0][index], tmpLine[0], cubic2, cubic1);
+ } else {
+ cubicInsert(impTs[0][index], testT, tmpLine[0], cubic1, cubic2);
+ }
+ return true;
+ }
+ return false;
+}
+
+
+void SkIntersections::cubicInsert(double one, double two, const SkDPoint& pt,
+ const SkDCubic& cubic1, const SkDCubic& cubic2) {
+ for (int index = 0; index < fUsed; ++index) {
+ if (fT[0][index] == one) {
+ double oldTwo = fT[1][index];
+ if (oldTwo == two) {
+ return;
+ }
+ SkDPoint mid = cubic2.ptAtT((oldTwo + two) / 2);
+ if (mid.approximatelyEqual(fPt[index])) {
+ return;
+ }
+ }
+ if (fT[1][index] == two) {
+ SkDPoint mid = cubic1.ptAtT((fT[0][index] + two) / 2);
+ if (mid.approximatelyEqual(fPt[index])) {
+ return;
+ }
+ }
+ }
+ insert(one, two, pt);
+}
+
+void SkIntersections::cubicNearEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2,
+ const SkDRect& bounds2) {
+ SkDLine line;
+ int t1Index = start ? 0 : 3;
+ double testT = (double) !start;
+ // don't bother if the two cubics are connnected
+ static const int kPointsInCubic = 4; // FIXME: move to DCubic, replace '4' with this
+ static const int kMaxLineCubicIntersections = 3;
+ SkSTArray<(kMaxLineCubicIntersections - 1) * kMaxLineCubicIntersections, double, true> tVals;
+ line[0] = cubic1[t1Index];
+ // this variant looks for intersections with the end point and lines parallel to other points
+ for (int index = 0; index < kPointsInCubic; ++index) {
+ if (index == t1Index) {
+ continue;
+ }
+ SkDVector dxy1 = cubic1[index] - line[0];
+ dxy1 /= SkDCubic::gPrecisionUnit;
+ line[1] = line[0] + dxy1;
+ SkDRect lineBounds;
+ lineBounds.setBounds(line);
+ if (!bounds2.intersects(&lineBounds)) {
+ continue;
+ }
+ SkIntersections local;
+ if (!local.intersect(cubic2, line)) {
+ continue;
+ }
+ for (int idx2 = 0; idx2 < local.used(); ++idx2) {
+ double foundT = local[0][idx2];
+ if (approximately_less_than_zero(foundT)
+ || approximately_greater_than_one(foundT)) {
+ continue;
+ }
+ if (local.pt(idx2).approximatelyEqual(line[0])) {
+ if (swapped()) { // FIXME: insert should respect swap
+ insert(foundT, testT, line[0]);
+ } else {
+ insert(testT, foundT, line[0]);
+ }
+ } else {
+ tVals.push_back(foundT);
+ }
+ }
+ }
+ if (tVals.count() == 0) {
+ return;
+ }
+ SkTQSort<double>(tVals.begin(), tVals.end() - 1);
+ double tMin1 = start ? 0 : 1 - LINE_FRACTION;
+ double tMax1 = start ? LINE_FRACTION : 1;
+ int tIdx = 0;
+ do {
+ int tLast = tIdx;
+ while (tLast + 1 < tVals.count() && roughly_equal(tVals[tLast + 1], tVals[tIdx])) {
+ ++tLast;
+ }
+ double tMin2 = SkTMax(tVals[tIdx] - LINE_FRACTION, 0.0);
+ double tMax2 = SkTMin(tVals[tLast] + LINE_FRACTION, 1.0);
+ int lastUsed = used();
+ if (start ? tMax1 < tMin2 : tMax2 < tMin1) {
+ ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
+ }
+ if (lastUsed == used()) {
+ tMin2 = SkTMax(tVals[tIdx] - (1.0 / SkDCubic::gPrecisionUnit), 0.0);
+ tMax2 = SkTMin(tVals[tLast] + (1.0 / SkDCubic::gPrecisionUnit), 1.0);
+ if (start ? tMax1 < tMin2 : tMax2 < tMin1) {
+ ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
+ }
+ }
+ tIdx = tLast + 1;
+ } while (tIdx < tVals.count());
+ return;
+}
+
+const double CLOSE_ENOUGH = 0.001;
+
+static bool closeStart(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) {
+ if (i[cubicIndex][0] != 0 || i[cubicIndex][1] > CLOSE_ENOUGH) {
+ return false;
+ }
+ pt = cubic.ptAtT((i[cubicIndex][0] + i[cubicIndex][1]) / 2);
+ return true;
+}
+
+static bool closeEnd(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) {
+ int last = i.used() - 1;
+ if (i[cubicIndex][last] != 1 || i[cubicIndex][last - 1] < 1 - CLOSE_ENOUGH) {
+ return false;
+ }
+ pt = cubic.ptAtT((i[cubicIndex][last] + i[cubicIndex][last - 1]) / 2);
+ return true;
+}
+
+static bool only_end_pts_in_common(const SkDCubic& c1, const SkDCubic& c2) {
+// the idea here is to see at minimum do a quick reject by rotating all points
+// to either side of the line formed by connecting the endpoints
+// if the opposite curves points are on the line or on the other side, the
+// curves at most intersect at the endpoints
+ for (int oddMan = 0; oddMan < 4; ++oddMan) {
+ const SkDPoint* endPt[3];
+ for (int opp = 1; opp < 4; ++opp) {
+ int end = oddMan ^ opp; // choose a value not equal to oddMan
+ endPt[opp - 1] = &c1[end];
+ }
+ for (int triTest = 0; triTest < 3; ++triTest) {
+ double origX = endPt[triTest]->fX;
+ double origY = endPt[triTest]->fY;
+ int oppTest = triTest + 1;
+ if (3 == oppTest) {
+ oppTest = 0;
+ }
+ double adj = endPt[oppTest]->fX - origX;
+ double opp = endPt[oppTest]->fY - origY;
+ if (adj == 0 && opp == 0) { // if the other point equals the test point, ignore it
+ continue;
+ }
+ double sign = (c1[oddMan].fY - origY) * adj - (c1[oddMan].fX - origX) * opp;
+ if (approximately_zero(sign)) {
+ goto tryNextHalfPlane;
+ }
+ for (int n = 0; n < 4; ++n) {
+ double test = (c2[n].fY - origY) * adj - (c2[n].fX - origX) * opp;
+ if (test * sign > 0 && !precisely_zero(test)) {
+ goto tryNextHalfPlane;
+ }
+ }
+ }
+ return true;
+tryNextHalfPlane:
+ ;
+ }
+ return false;
+}
+
+int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
+ if (fMax == 0) {
+ fMax = 9;
+ }
+ bool selfIntersect = &c1 == &c2;
+ if (selfIntersect) {
+ if (c1[0].approximatelyEqual(c1[3])) {
+ insert(0, 1, c1[0]);
+ return fUsed;
+ }
+ } else {
+ // OPTIMIZATION: set exact end bits here to avoid cubic exact end later
+ for (int i1 = 0; i1 < 4; i1 += 3) {
+ for (int i2 = 0; i2 < 4; i2 += 3) {
+ if (c1[i1].approximatelyEqual(c2[i2])) {
+ insert(i1 >> 1, i2 >> 1, c1[i1]);
+ }
+ }
+ }
+ }
+ SkASSERT(fUsed < 4);
+ if (!selfIntersect) {
+ if (only_end_pts_in_common(c1, c2)) {
+ return fUsed;
+ }
+ if (only_end_pts_in_common(c2, c1)) {
+ return fUsed;
+ }
+ }
+ // quad/quad does linear test here -- cubic does not
+ // cubics which are really lines should have been detected in reduce step earlier
+ int exactEndBits = 0;
+ if (selfIntersect) {
+ if (fUsed) {
+ return fUsed;
+ }
+ } else {
+ exactEndBits |= cubicExactEnd(c1, false, c2) << 0;
+ exactEndBits |= cubicExactEnd(c1, true, c2) << 1;
+ swap();
+ exactEndBits |= cubicExactEnd(c2, false, c1) << 2;
+ exactEndBits |= cubicExactEnd(c2, true, c1) << 3;
+ swap();
+ }
+ if (cubicCheckCoincidence(c1, c2)) {
+ SkASSERT(!selfIntersect);
+ return fUsed;
+ }
+ // FIXME: pass in cached bounds from caller
+ SkDRect c2Bounds;
+ c2Bounds.setBounds(c2);
+ if (!(exactEndBits & 4)) {
+ cubicNearEnd(c1, false, c2, c2Bounds);
+ }
+ if (!(exactEndBits & 8)) {
+ if (selfIntersect && fUsed) {
+ return fUsed;
+ }
+ cubicNearEnd(c1, true, c2, c2Bounds);
+ if (selfIntersect && fUsed && ((approximately_less_than_zero(fT[0][0])
+ && approximately_less_than_zero(fT[1][0]))
+ || (approximately_greater_than_one(fT[0][0])
+ && approximately_greater_than_one(fT[1][0])))) {
+ SkASSERT(fUsed == 1);
+ fUsed = 0;
+ return fUsed;
+ }
+ }
+ if (!selfIntersect) {
+ SkDRect c1Bounds;
+ c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ?
+ swap();
+ if (!(exactEndBits & 1)) {
+ cubicNearEnd(c2, false, c1, c1Bounds);
+ }
+ if (!(exactEndBits & 2)) {
+ cubicNearEnd(c2, true, c1, c1Bounds);
+ }
+ swap();
+ }
+ if (cubicCheckCoincidence(c1, c2)) {
+ SkASSERT(!selfIntersect);
+ return fUsed;
+ }
+ SkIntersections i;
+ i.fAllowNear = false;
+ i.fFlatMeasure = true;
+ i.fMax = 9;
+ ::intersect(c1, 0, 1, c2, 0, 1, 1, i);
+ int compCount = i.used();
+ if (compCount) {
+ int exactCount = used();
+ if (exactCount == 0) {
+ *this = i;
+ } else {
+ // at least one is exact or near, and at least one was computed. Eliminate duplicates
+ for (int exIdx = 0; exIdx < exactCount; ++exIdx) {
+ for (int cpIdx = 0; cpIdx < compCount; ) {
+ if (fT[0][0] == i[0][0] && fT[1][0] == i[1][0]) {
+ i.removeOne(cpIdx);
+ --compCount;
+ continue;
+ }
+ double tAvg = (fT[0][exIdx] + i[0][cpIdx]) / 2;
+ SkDPoint pt = c1.ptAtT(tAvg);
+ if (!pt.approximatelyEqual(fPt[exIdx])) {
+ ++cpIdx;
+ continue;
+ }
+ tAvg = (fT[1][exIdx] + i[1][cpIdx]) / 2;
+ pt = c2.ptAtT(tAvg);
+ if (!pt.approximatelyEqual(fPt[exIdx])) {
+ ++cpIdx;
+ continue;
+ }
+ i.removeOne(cpIdx);
+ --compCount;
+ }
+ }
+ // if mid t evaluates to nearly the same point, skip the t
+ for (int cpIdx = 0; cpIdx < compCount - 1; ) {
+ double tAvg = (fT[0][cpIdx] + i[0][cpIdx + 1]) / 2;
+ SkDPoint pt = c1.ptAtT(tAvg);
+ if (!pt.approximatelyEqual(fPt[cpIdx])) {
+ ++cpIdx;
+ continue;
+ }
+ tAvg = (fT[1][cpIdx] + i[1][cpIdx + 1]) / 2;
+ pt = c2.ptAtT(tAvg);
+ if (!pt.approximatelyEqual(fPt[cpIdx])) {
+ ++cpIdx;
+ continue;
+ }
+ i.removeOne(cpIdx);
+ --compCount;
+ }
+ // in addition to adding below missing function, think about how to say
+ append(i);
+ }
+ }
+ // If an end point and a second point very close to the end is returned, the second
+ // point may have been detected because the approximate quads
+ // intersected at the end and close to it. Verify that the second point is valid.
+ if (fUsed <= 1) {
+ return fUsed;
+ }
+ SkDPoint pt[2];
+ if (closeStart(c1, 0, *this, pt[0]) && closeStart(c2, 1, *this, pt[1])
+ && pt[0].approximatelyEqual(pt[1])) {
+ removeOne(1);
+ }
+ if (closeEnd(c1, 0, *this, pt[0]) && closeEnd(c2, 1, *this, pt[1])
+ && pt[0].approximatelyEqual(pt[1])) {
+ removeOne(used() - 2);
+ }
+ // vet the pairs of t values to see if the mid value is also on the curve. If so, mark
+ // the span as coincident
+ if (fUsed >= 2 && !coincidentUsed()) {
+ int last = fUsed - 1;
+ int match = 0;
+ for (int index = 0; index < last; ++index) {
+ double mid1 = (fT[0][index] + fT[0][index + 1]) / 2;
+ double mid2 = (fT[1][index] + fT[1][index + 1]) / 2;
+ pt[0] = c1.ptAtT(mid1);
+ pt[1] = c2.ptAtT(mid2);
+ if (pt[0].approximatelyEqual(pt[1])) {
+ match |= 1 << index;
+ }
+ }
+ if (match) {
+#if DEBUG_CONCIDENT
+ if (((match + 1) & match) != 0) {
+ SkDebugf("%s coincident hole\n", __FUNCTION__);
+ }
+#endif
+ // for now, assume that everything from start to finish is coincident
+ if (fUsed > 2) {
+ fPt[1] = fPt[last];
+ fT[0][1] = fT[0][last];
+ fT[1][1] = fT[1][last];
+ fIsCoincident[0] = 0x03;
+ fIsCoincident[1] = 0x03;
+ fUsed = 2;
+ }
+ }
+ }
+ return fUsed;
+}
+
+// Up promote the quad to a cubic.
+// 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 = 7;
+ SkDCubic up = quad.toCubic();
+ (void) intersect(cubic, up);
+ return used();
+}
+
+/* http://www.ag.jku.at/compass/compasssample.pdf
+( Self-Intersection Problems and Approximate Implicitization by Jan B. Thomassen
+Centre of Mathematics for Applications, University of Oslo http://www.cma.uio.no janbth@math.uio.no
+SINTEF Applied Mathematics http://www.sintef.no )
+describes a method to find the self intersection of a cubic by taking the gradient of the implicit
+form dotted with the normal, and solving for the roots. My math foo is too poor to implement this.*/
+
+int SkIntersections::intersect(const SkDCubic& c) {
+ fMax = 1;
+ // check to see if x or y end points are the extrema. Are other quick rejects possible?
+ if (c.endsAreExtremaInXOrY()) {
+ return false;
+ }
+ // 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() > 1) {
+ fUsed = 0;
+ } else if (used() > 0) {
+ if (approximately_equal_double(fT[0][0], fT[1][0])) {
+ fUsed = 0;
+ } else {
+ SkASSERT(used() == 1);
+ if (fT[0][0] > fT[1][0]) {
+ swapPts();
+ }
+ }
+ }
+ return used();
+}
diff --git a/src/pathops/SkDCubicLineIntersection.cpp b/src/pathops/SkDCubicLineIntersection.cpp
index f5fe01503b..696c42e835 100644
--- a/src/pathops/SkDCubicLineIntersection.cpp
+++ b/src/pathops/SkDCubicLineIntersection.cpp
@@ -93,29 +93,6 @@ public:
fAllowNear = allow;
}
- void checkCoincident() {
- int last = fIntersections->used() - 1;
- for (int index = 0; index < last; ) {
- double cubicMidT = ((*fIntersections)[0][index] + (*fIntersections)[0][index + 1]) / 2;
- SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
- double t = fLine.nearPoint(cubicMidPt, NULL);
- if (t < 0) {
- ++index;
- continue;
- }
- if (fIntersections->isCoincident(index)) {
- fIntersections->removeOne(index);
- --last;
- } else if (fIntersections->isCoincident(index + 1)) {
- fIntersections->removeOne(index + 1);
- --last;
- } else {
- fIntersections->setCoincident(index++);
- }
- fIntersections->setCoincident(index);
- }
- }
-
// see parallel routine in line quadratic intersections
int intersectRay(double roots[3]) {
double adj = fLine[1].fX - fLine[0].fX;
@@ -154,11 +131,32 @@ public:
double cubicT = rootVals[index];
double lineT = findLineT(cubicT);
SkDPoint pt;
- if (pinTs(&cubicT, &lineT, &pt, kPointUninitialized) && uniqueAnswer(cubicT, pt)) {
+ if (pinTs(&cubicT, &lineT, &pt, kPointUninitialized)) {
+ #if ONE_OFF_DEBUG
+ SkDPoint cPt = fCubic.ptAtT(cubicT);
+ SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
+ cPt.fX, cPt.fY);
+ #endif
+ for (int inner = 0; inner < fIntersections->used(); ++inner) {
+ if (fIntersections->pt(inner) != pt) {
+ continue;
+ }
+ double existingCubicT = (*fIntersections)[0][inner];
+ if (cubicT == existingCubicT) {
+ goto skipInsert;
+ }
+ // check if midway on cubic is also same point. If so, discard this
+ double cubicMidT = (existingCubicT + cubicT) / 2;
+ SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
+ if (cubicMidPt.approximatelyEqual(pt)) {
+ goto skipInsert;
+ }
+ }
fIntersections->insert(cubicT, lineT, pt);
+ skipInsert:
+ ;
}
}
- checkCoincident();
return fIntersections->used();
}
@@ -188,43 +186,20 @@ public:
int count = HorizontalIntersect(fCubic, axisIntercept, roots);
for (int index = 0; index < count; ++index) {
double cubicT = roots[index];
- SkDPoint pt = { fCubic.ptAtT(cubicT).fX, axisIntercept };
+ SkDPoint pt;
+ pt.fX = fCubic.ptAtT(cubicT).fX;
+ pt.fY = axisIntercept;
double lineT = (pt.fX - left) / (right - left);
- if (pinTs(&cubicT, &lineT, &pt, kPointInitialized) && uniqueAnswer(cubicT, pt)) {
+ if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) {
fIntersections->insert(cubicT, lineT, pt);
}
}
if (flipped) {
fIntersections->flip();
}
- checkCoincident();
return fIntersections->used();
}
- bool uniqueAnswer(double cubicT, const SkDPoint& pt) {
- for (int inner = 0; inner < fIntersections->used(); ++inner) {
- if (fIntersections->pt(inner) != pt) {
- continue;
- }
- double existingCubicT = (*fIntersections)[0][inner];
- if (cubicT == existingCubicT) {
- return false;
- }
- // check if midway on cubic is also same point. If so, discard this
- double cubicMidT = (existingCubicT + cubicT) / 2;
- SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
- if (cubicMidPt.approximatelyEqual(pt)) {
- return false;
- }
- }
-#if ONE_OFF_DEBUG
- SkDPoint cPt = fCubic.ptAtT(cubicT);
- SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
- cPt.fX, cPt.fY);
-#endif
- return true;
- }
-
static int VerticalIntersect(const SkDCubic& c, double axisIntercept, double roots[3]) {
double A, B, C, D;
SkDCubic::Coefficients(&c[0].fX, &A, &B, &C, &D);
@@ -251,16 +226,17 @@ public:
int count = VerticalIntersect(fCubic, axisIntercept, roots);
for (int index = 0; index < count; ++index) {
double cubicT = roots[index];
- SkDPoint pt = { axisIntercept, fCubic.ptAtT(cubicT).fY };
+ SkDPoint pt;
+ pt.fX = axisIntercept;
+ pt.fY = fCubic.ptAtT(cubicT).fY;
double lineT = (pt.fY - top) / (bottom - top);
- if (pinTs(&cubicT, &lineT, &pt, kPointInitialized) && uniqueAnswer(cubicT, pt)) {
+ if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) {
fIntersections->insert(cubicT, lineT, pt);
}
}
if (flipped) {
fIntersections->flip();
}
- checkCoincident();
return fIntersections->used();
}
@@ -366,7 +342,7 @@ public:
double lT = *lineT = SkPinT(*lineT);
SkDPoint lPt = fLine.ptAtT(lT);
SkDPoint cPt = fCubic.ptAtT(cT);
- if (!lPt.roughlyEqual(cPt)) {
+ if (!lPt.moreRoughlyEqual(cPt)) {
return false;
}
// FIXME: if points are roughly equal but not approximately equal, need to do
diff --git a/src/pathops/SkDCubicToQuads.cpp b/src/pathops/SkDCubicToQuads.cpp
index 2d034b69e8..a28564d4c2 100644
--- a/src/pathops/SkDCubicToQuads.cpp
+++ b/src/pathops/SkDCubicToQuads.cpp
@@ -19,10 +19,62 @@ If this is a degree-elevated cubic, then both equations will give the same answe
it's likely not, your best bet is to average them. So,
P1 = -1/4 Q0 + 3/4 Q1 + 3/4 Q2 - 1/4 Q3
+
+SkDCubic defined by: P1/2 - anchor points, C1/C2 control points
+|x| is the euclidean norm of x
+mid-point approx of cubic: a quad that shares the same anchors with the cubic and has the
+ control point at C = (3·C2 - P2 + 3·C1 - P1)/4
+
+Algorithm
+
+pick an absolute precision (prec)
+Compute the Tdiv as the root of (cubic) equation
+sqrt(3)/18 · |P2 - 3·C2 + 3·C1 - P1|/2 · Tdiv ^ 3 = prec
+if Tdiv < 0.5 divide the cubic at Tdiv. First segment [0..Tdiv] can be approximated with by a
+ quadratic, with a defect less than prec, by the mid-point approximation.
+ Repeat from step 2 with the second resulted segment (corresponding to 1-Tdiv)
+0.5<=Tdiv<1 - simply divide the cubic in two. The two halves can be approximated by the mid-point
+ approximation
+Tdiv>=1 - the entire cubic can be approximated by the mid-point approximation
+
+confirmed by (maybe stolen from)
+http://www.caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
+// maybe in turn derived from http://www.cccg.ca/proceedings/2004/36.pdf
+// also stored at http://www.cis.usouthal.edu/~hain/general/Publications/Bezier/bezier%20cccg04%20paper.pdf
+
*/
#include "SkPathOpsCubic.h"
+#include "SkPathOpsLine.h"
#include "SkPathOpsQuad.h"
+#include "SkReduceOrder.h"
+#include "SkTArray.h"
+#include "SkTSort.h"
+
+#define USE_CUBIC_END_POINTS 1
+
+static double calc_t_div(const SkDCubic& cubic, double precision, double start) {
+ const double adjust = sqrt(3.) / 36;
+ SkDCubic sub;
+ const SkDCubic* cPtr;
+ if (start == 0) {
+ cPtr = &cubic;
+ } else {
+ // OPTIMIZE: special-case half-split ?
+ sub = cubic.subDivide(start, 1);
+ cPtr = &sub;
+ }
+ const SkDCubic& c = *cPtr;
+ double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX;
+ double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY;
+ double dist = sqrt(dx * dx + dy * dy);
+ double tDiv3 = precision / (adjust * dist);
+ double t = SkDCubeRoot(tDiv3);
+ if (start > 0) {
+ t = start + (1 - start) * t;
+ }
+ return t;
+}
SkDQuad SkDCubic::toQuad() const {
SkDQuad quad;
@@ -34,3 +86,101 @@ SkDQuad SkDCubic::toQuad() const {
quad[2] = fPts[3];
return quad;
}
+
+static bool add_simple_ts(const SkDCubic& cubic, double precision, SkTArray<double, true>* ts) {
+ double tDiv = calc_t_div(cubic, precision, 0);
+ if (tDiv >= 1) {
+ return true;
+ }
+ if (tDiv >= 0.5) {
+ ts->push_back(0.5);
+ return true;
+ }
+ return false;
+}
+
+static void addTs(const SkDCubic& cubic, double precision, double start, double end,
+ SkTArray<double, true>* ts) {
+ double tDiv = calc_t_div(cubic, precision, 0);
+ double parts = ceil(1.0 / tDiv);
+ for (double index = 0; index < parts; ++index) {
+ double newT = start + (index / parts) * (end - start);
+ if (newT > 0 && newT < 1) {
+ ts->push_back(newT);
+ }
+ }
+}
+
+// flavor that returns T values only, deferring computing the quads until they are needed
+// FIXME: when called from recursive intersect 2, this could take the original cubic
+// and do a more precise job when calling chop at and sub divide by computing the fractional ts.
+// it would still take the prechopped cubic for reduce order and find cubic inflections
+void SkDCubic::toQuadraticTs(double precision, SkTArray<double, true>* ts) const {
+ SkReduceOrder reducer;
+ int order = reducer.reduce(*this, SkReduceOrder::kAllow_Quadratics);
+ if (order < 3) {
+ return;
+ }
+ double inflectT[5];
+ int inflections = findInflections(inflectT);
+ SkASSERT(inflections <= 2);
+ if (!endsAreExtremaInXOrY()) {
+ inflections += findMaxCurvature(&inflectT[inflections]);
+ SkASSERT(inflections <= 5);
+ }
+ SkTQSort<double>(inflectT, &inflectT[inflections - 1]);
+ // OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its
+ // own subroutine?
+ while (inflections && approximately_less_than_zero(inflectT[0])) {
+ memmove(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections);
+ }
+ int start = 0;
+ int next = 1;
+ while (next < inflections) {
+ if (!approximately_equal(inflectT[start], inflectT[next])) {
+ ++start;
+ ++next;
+ continue;
+ }
+ memmove(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start));
+ }
+
+ while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) {
+ --inflections;
+ }
+ SkDCubicPair pair;
+ if (inflections == 1) {
+ pair = chopAt(inflectT[0]);
+ int orderP1 = reducer.reduce(pair.first(), SkReduceOrder::kNo_Quadratics);
+ if (orderP1 < 2) {
+ --inflections;
+ } else {
+ int orderP2 = reducer.reduce(pair.second(), SkReduceOrder::kNo_Quadratics);
+ if (orderP2 < 2) {
+ --inflections;
+ }
+ }
+ }
+ if (inflections == 0 && add_simple_ts(*this, precision, ts)) {
+ return;
+ }
+ if (inflections == 1) {
+ pair = chopAt(inflectT[0]);
+ addTs(pair.first(), precision, 0, inflectT[0], ts);
+ addTs(pair.second(), precision, inflectT[0], 1, ts);
+ return;
+ }
+ if (inflections > 1) {
+ SkDCubic part = subDivide(0, inflectT[0]);
+ addTs(part, precision, 0, inflectT[0], ts);
+ int last = inflections - 1;
+ for (int idx = 0; idx < last; ++idx) {
+ part = subDivide(inflectT[idx], inflectT[idx + 1]);
+ addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
+ }
+ part = subDivide(inflectT[last], 1);
+ addTs(part, precision, inflectT[last], 1, ts);
+ return;
+ }
+ addTs(*this, precision, 0, 1, ts);
+}
diff --git a/src/pathops/SkDLineIntersection.cpp b/src/pathops/SkDLineIntersection.cpp
index ed96b9c5d7..8fc673f2fb 100644
--- a/src/pathops/SkDLineIntersection.cpp
+++ b/src/pathops/SkDLineIntersection.cpp
@@ -7,6 +7,45 @@
#include "SkIntersections.h"
#include "SkPathOpsLine.h"
+/* Determine the intersection point of two lines. This assumes the lines are not parallel,
+ and that that the lines are infinite.
+ From http://en.wikipedia.org/wiki/Line-line_intersection
+ */
+SkDPoint SkIntersections::Line(const SkDLine& a, const SkDLine& b) {
+ double axLen = a[1].fX - a[0].fX;
+ double ayLen = a[1].fY - a[0].fY;
+ double bxLen = b[1].fX - b[0].fX;
+ double byLen = b[1].fY - b[0].fY;
+ double denom = byLen * axLen - ayLen * bxLen;
+ SkASSERT(denom);
+ double term1 = a[1].fX * a[0].fY - a[1].fY * a[0].fX;
+ double term2 = b[1].fX * b[0].fY - b[1].fY * b[0].fX;
+ SkDPoint p;
+ p.fX = (term1 * bxLen - axLen * term2) / denom;
+ p.fY = (term1 * byLen - ayLen * term2) / denom;
+ return p;
+}
+
+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) {
while (fUsed > 2) {
removeOne(1);
@@ -19,9 +58,6 @@ void SkIntersections::cleanUpParallelLines(bool parallel) {
removeOne(endMatch);
}
}
- if (fUsed == 2) {
- fIsCoincident[0] = fIsCoincident[1] = 0x03;
- }
}
void SkIntersections::computePoints(const SkDLine& line, int used) {
@@ -45,6 +81,12 @@ int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) {
SkDVector ab0 = a[0] - b[0];
double numerA = ab0.fY * bLen.fX - bLen.fY * ab0.fX;
double numerB = ab0.fY * aLen.fX - aLen.fY * ab0.fX;
+#if 0
+ if (!between(0, numerA, denom) || !between(0, numerB, denom)) {
+ fUsed = 0;
+ return 0;
+ }
+#endif
numerA /= denom;
numerB /= denom;
int used;
@@ -148,6 +190,7 @@ int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
}
SkASSERT(a[iA] != b[nearer]);
SkASSERT(iA == (bNearA[nearer] > 0.5));
+ fNearlySame[iA] = true;
insertNear(iA, nearer, a[iA], b[nearer]);
aNearB[iA] = -1;
bNearA[nearer] = -1;
@@ -192,6 +235,18 @@ static double horizontal_intercept(const SkDLine& line, double y) {
return SkPinT((y - line[0].fY) / (line[1].fY - line[0].fY));
}
+int SkIntersections::horizontal(const SkDLine& line, double y) {
+ fMax = 2;
+ int horizontalType = horizontal_coincident(line, y);
+ if (horizontalType == 1) {
+ fT[0][0] = horizontal_intercept(line, y);
+ } else if (horizontalType == 2) {
+ fT[0][0] = 0;
+ fT[0][1] = 1;
+ }
+ return fUsed = horizontalType;
+}
+
int SkIntersections::horizontal(const SkDLine& line, double left, double right,
double y, bool flipped) {
fMax = 3; // clean up parallel at the end will limit the result to 2 at the most
@@ -268,6 +323,18 @@ static double vertical_intercept(const SkDLine& line, double x) {
return SkPinT((x - line[0].fX) / (line[1].fX - line[0].fX));
}
+int SkIntersections::vertical(const SkDLine& line, double x) {
+ fMax = 2;
+ int verticalType = vertical_coincident(line, x);
+ if (verticalType == 1) {
+ fT[0][0] = vertical_intercept(line, x);
+ } else if (verticalType == 2) {
+ fT[0][0] = 0;
+ fT[0][1] = 1;
+ }
+ return fUsed = verticalType;
+}
+
int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
double x, bool flipped) {
fMax = 3; // cleanup parallel lines will bring this back line
@@ -326,3 +393,14 @@ int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
return fUsed;
}
+// from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
+// 4 subs, 2 muls, 1 cmp
+static bool ccw(const SkDPoint& A, const SkDPoint& B, const SkDPoint& C) {
+ return (C.fY - A.fY) * (B.fX - A.fX) > (B.fY - A.fY) * (C.fX - A.fX);
+}
+
+// 16 subs, 8 muls, 6 cmps
+bool SkIntersections::Test(const SkDLine& a, const SkDLine& b) {
+ return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
+ && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
+}
diff --git a/src/pathops/SkDQuadImplicit.cpp b/src/pathops/SkDQuadImplicit.cpp
new file mode 100644
index 0000000000..f0f66d1a10
--- /dev/null
+++ b/src/pathops/SkDQuadImplicit.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkDQuadImplicit.h"
+
+/* from http://tom.cs.byu.edu/~tom/papers/cvgip84.pdf 4.1
+ *
+ * This paper proves that Syvester's method can compute the implicit form of
+ * the quadratic from the parameterized form.
+ *
+ * Given x = a*t*t + b*t + c (the parameterized form)
+ * y = d*t*t + e*t + f
+ *
+ * we want to find an equation of the implicit form:
+ *
+ * A*x*x + B*x*y + C*y*y + D*x + E*y + F = 0
+ *
+ * The implicit form can be expressed as a 4x4 determinant, as shown.
+ *
+ * The resultant obtained by Syvester's method is
+ *
+ * | a b (c - x) 0 |
+ * | 0 a b (c - x) |
+ * | d e (f - y) 0 |
+ * | 0 d e (f - y) |
+ *
+ * which expands to
+ *
+ * d*d*x*x + -2*a*d*x*y + a*a*y*y
+ * + (-2*c*d*d + b*e*d - a*e*e + 2*a*f*d)*x
+ * + (-2*f*a*a + e*b*a - d*b*b + 2*d*c*a)*y
+ * +
+ * | a b c 0 |
+ * | 0 a b c | == 0.
+ * | d e f 0 |
+ * | 0 d e f |
+ *
+ * Expanding the constant determinant results in
+ *
+ * | a b c | | b c 0 |
+ * a*| e f 0 | + d*| a b c | ==
+ * | d e f | | d e f |
+ *
+ * a*(a*f*f + c*e*e - c*f*d - b*e*f) + d*(b*b*f + c*c*d - c*a*f - c*e*b)
+ *
+ */
+
+// use the tricky arithmetic path, but leave the original to compare just in case
+static bool straight_forward = false;
+
+SkDQuadImplicit::SkDQuadImplicit(const SkDQuad& q) {
+ double a, b, c;
+ SkDQuad::SetABC(&q[0].fX, &a, &b, &c);
+ double d, e, f;
+ SkDQuad::SetABC(&q[0].fY, &d, &e, &f);
+ // compute the implicit coefficients
+ if (straight_forward) { // 42 muls, 13 adds
+ fP[kXx_Coeff] = d * d;
+ fP[kXy_Coeff] = -2 * a * d;
+ fP[kYy_Coeff] = a * a;
+ fP[kX_Coeff] = -2*c*d*d + b*e*d - a*e*e + 2*a*f*d;
+ fP[kY_Coeff] = -2*f*a*a + e*b*a - d*b*b + 2*d*c*a;
+ fP[kC_Coeff] = a*(a*f*f + c*e*e - c*f*d - b*e*f)
+ + d*(b*b*f + c*c*d - c*a*f - c*e*b);
+ } else { // 26 muls, 11 adds
+ double aa = a * a;
+ double ad = a * d;
+ double dd = d * d;
+ fP[kXx_Coeff] = dd;
+ fP[kXy_Coeff] = -2 * ad;
+ fP[kYy_Coeff] = aa;
+ double be = b * e;
+ double bde = be * d;
+ double cdd = c * dd;
+ double ee = e * e;
+ fP[kX_Coeff] = -2*cdd + bde - a*ee + 2*ad*f;
+ double aaf = aa * f;
+ double abe = a * be;
+ double ac = a * c;
+ double bb_2ac = b*b - 2*ac;
+ fP[kY_Coeff] = -2*aaf + abe - d*bb_2ac;
+ fP[kC_Coeff] = aaf*f + ac*ee + d*f*bb_2ac - abe*f + c*cdd - c*bde;
+ }
+}
+
+ /* Given a pair of quadratics, determine their parametric coefficients.
+ * If the scaled coefficients are nearly equal, then the part of the quadratics
+ * may be coincident.
+ * OPTIMIZATION -- since comparison short-circuits on no match,
+ * lazily compute the coefficients, comparing the easiest to compute first.
+ * xx and yy first; then xy; and so on.
+ */
+bool SkDQuadImplicit::match(const SkDQuadImplicit& p2) const {
+ int first = 0;
+ for (int index = 0; index <= kC_Coeff; ++index) {
+ if (approximately_zero(fP[index]) && approximately_zero(p2.fP[index])) {
+ first += first == index;
+ continue;
+ }
+ if (first == index) {
+ continue;
+ }
+ if (!AlmostDequalUlps(fP[index] * p2.fP[first], fP[first] * p2.fP[index])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool SkDQuadImplicit::Match(const SkDQuad& quad1, const SkDQuad& quad2) {
+ SkDQuadImplicit i1(quad1); // a'xx , b'xy , c'yy , d'x , e'y , f
+ SkDQuadImplicit i2(quad2);
+ return i1.match(i2);
+}
diff --git a/src/pathops/SkDQuadImplicit.h b/src/pathops/SkDQuadImplicit.h
new file mode 100644
index 0000000000..24f1aac2ef
--- /dev/null
+++ b/src/pathops/SkDQuadImplicit.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkDQuadImplicit_DEFINED
+#define SkDQuadImplicit_DEFINED
+
+#include "SkPathOpsQuad.h"
+
+class SkDQuadImplicit {
+public:
+ explicit SkDQuadImplicit(const SkDQuad& q);
+
+ bool match(const SkDQuadImplicit& two) const;
+ static bool Match(const SkDQuad& quad1, const SkDQuad& quad2);
+
+ double x2() const { return fP[kXx_Coeff]; }
+ double xy() const { return fP[kXy_Coeff]; }
+ double y2() const { return fP[kYy_Coeff]; }
+ double x() const { return fP[kX_Coeff]; }
+ double y() const { return fP[kY_Coeff]; }
+ double c() const { return fP[kC_Coeff]; }
+
+private:
+ enum Coeffs {
+ kXx_Coeff,
+ kXy_Coeff,
+ kYy_Coeff,
+ kX_Coeff,
+ kY_Coeff,
+ kC_Coeff,
+ };
+
+ double fP[kC_Coeff + 1];
+};
+
+#endif
diff --git a/src/pathops/SkDQuadIntersection.cpp b/src/pathops/SkDQuadIntersection.cpp
new file mode 100644
index 0000000000..fcb9171f32
--- /dev/null
+++ b/src/pathops/SkDQuadIntersection.cpp
@@ -0,0 +1,617 @@
+// Another approach is to start with the implicit form of one curve and solve
+// (seek implicit coefficients in QuadraticParameter.cpp
+// by substituting in the parametric form of the other.
+// The downside of this approach is that early rejects are difficult to come by.
+// http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html#step
+
+#include "SkDQuadImplicit.h"
+#include "SkIntersections.h"
+#include "SkPathOpsLine.h"
+#include "SkQuarticRoot.h"
+#include "SkTArray.h"
+#include "SkTSort.h"
+
+/* given the implicit form 0 = Ax^2 + Bxy + Cy^2 + Dx + Ey + F
+ * and given x = at^2 + bt + c (the parameterized form)
+ * y = dt^2 + et + f
+ * then
+ * 0 = A(at^2+bt+c)(at^2+bt+c)+B(at^2+bt+c)(dt^2+et+f)+C(dt^2+et+f)(dt^2+et+f)+D(at^2+bt+c)+E(dt^2+et+f)+F
+ */
+
+static int findRoots(const SkDQuadImplicit& i, const SkDQuad& quad, double roots[4],
+ bool oneHint, bool flip, int firstCubicRoot) {
+ SkDQuad flipped;
+ const SkDQuad& q = flip ? (flipped = quad.flip()) : quad;
+ double a, b, c;
+ SkDQuad::SetABC(&q[0].fX, &a, &b, &c);
+ double d, e, f;
+ SkDQuad::SetABC(&q[0].fY, &d, &e, &f);
+ const double t4 = i.x2() * a * a
+ + i.xy() * a * d
+ + i.y2() * d * d;
+ const double t3 = 2 * i.x2() * a * b
+ + i.xy() * (a * e + b * d)
+ + 2 * i.y2() * d * e;
+ const double t2 = i.x2() * (b * b + 2 * a * c)
+ + i.xy() * (c * d + b * e + a * f)
+ + i.y2() * (e * e + 2 * d * f)
+ + i.x() * a
+ + i.y() * d;
+ const double t1 = 2 * i.x2() * b * c
+ + i.xy() * (c * e + b * f)
+ + 2 * i.y2() * e * f
+ + i.x() * b
+ + i.y() * e;
+ const double t0 = i.x2() * c * c
+ + i.xy() * c * f
+ + i.y2() * f * f
+ + i.x() * c
+ + i.y() * f
+ + i.c();
+ int rootCount = SkReducedQuarticRoots(t4, t3, t2, t1, t0, oneHint, roots);
+ if (rootCount < 0) {
+ rootCount = SkQuarticRootsReal(firstCubicRoot, t4, t3, t2, t1, t0, roots);
+ }
+ if (flip) {
+ for (int index = 0; index < rootCount; ++index) {
+ roots[index] = 1 - roots[index];
+ }
+ }
+ return rootCount;
+}
+
+static int addValidRoots(const double roots[4], const int count, double valid[4]) {
+ int result = 0;
+ int index;
+ for (index = 0; index < count; ++index) {
+ if (!approximately_zero_or_more(roots[index]) || !approximately_one_or_less(roots[index])) {
+ continue;
+ }
+ double t = 1 - roots[index];
+ if (approximately_less_than_zero(t)) {
+ t = 0;
+ } else if (approximately_greater_than_one(t)) {
+ t = 1;
+ }
+ SkASSERT(t >= 0 && t <= 1);
+ valid[result++] = t;
+ }
+ return result;
+}
+
+static bool only_end_pts_in_common(const SkDQuad& q1, const SkDQuad& q2) {
+// the idea here is to see at minimum do a quick reject by rotating all points
+// to either side of the line formed by connecting the endpoints
+// if the opposite curves points are on the line or on the other side, the
+// curves at most intersect at the endpoints
+ for (int oddMan = 0; oddMan < 3; ++oddMan) {
+ const SkDPoint* endPt[2];
+ for (int opp = 1; opp < 3; ++opp) {
+ int end = oddMan ^ opp; // choose a value not equal to oddMan
+ if (3 == end) { // and correct so that largest value is 1 or 2
+ end = opp;
+ }
+ endPt[opp - 1] = &q1[end];
+ }
+ double origX = endPt[0]->fX;
+ double origY = endPt[0]->fY;
+ double adj = endPt[1]->fX - origX;
+ double opp = endPt[1]->fY - origY;
+ double sign = (q1[oddMan].fY - origY) * adj - (q1[oddMan].fX - origX) * opp;
+ if (approximately_zero(sign)) {
+ goto tryNextHalfPlane;
+ }
+ for (int n = 0; n < 3; ++n) {
+ double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
+ if (test * sign > 0 && !precisely_zero(test)) {
+ goto tryNextHalfPlane;
+ }
+ }
+ return true;
+tryNextHalfPlane:
+ ;
+ }
+ return false;
+}
+
+// returns false if there's more than one intercept or the intercept doesn't match the point
+// returns true if the intercept was successfully added or if the
+// original quads need to be subdivided
+static bool add_intercept(const SkDQuad& q1, const SkDQuad& q2, double tMin, double tMax,
+ SkIntersections* i, bool* subDivide) {
+ double tMid = (tMin + tMax) / 2;
+ SkDPoint mid = q2.ptAtT(tMid);
+ SkDLine line;
+ line[0] = line[1] = mid;
+ SkDVector dxdy = q2.dxdyAtT(tMid);
+ line[0] -= dxdy;
+ line[1] += dxdy;
+ SkIntersections rootTs;
+ rootTs.allowNear(false);
+ int roots = rootTs.intersect(q1, line);
+ if (roots == 0) {
+ if (subDivide) {
+ *subDivide = true;
+ }
+ return true;
+ }
+ if (roots == 2) {
+ return false;
+ }
+ SkDPoint pt2 = q1.ptAtT(rootTs[0][0]);
+ if (!pt2.approximatelyEqual(mid)) {
+ return false;
+ }
+ i->insertSwap(rootTs[0][0], tMid, pt2);
+ return true;
+}
+
+static bool is_linear_inner(const SkDQuad& q1, double t1s, double t1e, const SkDQuad& q2,
+ double t2s, double t2e, SkIntersections* i, bool* subDivide) {
+ SkDQuad hull = q1.subDivide(t1s, t1e);
+ SkDLine line = {{hull[2], hull[0]}};
+ const SkDLine* testLines[] = { &line, (const SkDLine*) &hull[0], (const SkDLine*) &hull[1] };
+ const size_t kTestCount = SK_ARRAY_COUNT(testLines);
+ SkSTArray<kTestCount * 2, double, true> tsFound;
+ for (size_t index = 0; index < kTestCount; ++index) {
+ SkIntersections rootTs;
+ rootTs.allowNear(false);
+ int roots = rootTs.intersect(q2, *testLines[index]);
+ for (int idx2 = 0; idx2 < roots; ++idx2) {
+ double t = rootTs[0][idx2];
+#if 0 // def SK_DEBUG // FIXME : accurate for error = 16, error of 17.5 seen
+// {{{136.08723965397621, 1648.2814535211637}, {593.49031197259478, 1190.8784277439891}, {593.49031197259478, 544.0128173828125}}}
+// {{{-968.181396484375, 544.0128173828125}, {592.2825927734375, 870.552490234375}, {593.435302734375, 557.8828125}}}
+
+ SkDPoint qPt = q2.ptAtT(t);
+ SkDPoint lPt = testLines[index]->ptAtT(rootTs[1][idx2]);
+ SkASSERT(qPt.approximatelyDEqual(lPt));
+#endif
+ if (approximately_negative(t - t2s) || approximately_positive(t - t2e)) {
+ continue;
+ }
+ tsFound.push_back(rootTs[0][idx2]);
+ }
+ }
+ int tCount = tsFound.count();
+ if (tCount <= 0) {
+ return true;
+ }
+ double tMin, tMax;
+ if (tCount == 1) {
+ tMin = tMax = tsFound[0];
+ } else {
+ SkASSERT(tCount > 1);
+ SkTQSort<double>(tsFound.begin(), tsFound.end() - 1);
+ tMin = tsFound[0];
+ tMax = tsFound[tsFound.count() - 1];
+ }
+ SkDPoint end = q2.ptAtT(t2s);
+ bool startInTriangle = hull.pointInHull(end);
+ if (startInTriangle) {
+ tMin = t2s;
+ }
+ end = q2.ptAtT(t2e);
+ bool endInTriangle = hull.pointInHull(end);
+ if (endInTriangle) {
+ tMax = t2e;
+ }
+ int split = 0;
+ SkDVector dxy1, dxy2;
+ if (tMin != tMax || tCount > 2) {
+ dxy2 = q2.dxdyAtT(tMin);
+ for (int index = 1; index < tCount; ++index) {
+ dxy1 = dxy2;
+ dxy2 = q2.dxdyAtT(tsFound[index]);
+ double dot = dxy1.dot(dxy2);
+ if (dot < 0) {
+ split = index - 1;
+ break;
+ }
+ }
+ }
+ if (split == 0) { // there's one point
+ if (add_intercept(q1, q2, tMin, tMax, i, subDivide)) {
+ return true;
+ }
+ i->swap();
+ return is_linear_inner(q2, tMin, tMax, q1, t1s, t1e, i, subDivide);
+ }
+ // At this point, we have two ranges of t values -- treat each separately at the split
+ bool result;
+ if (add_intercept(q1, q2, tMin, tsFound[split - 1], i, subDivide)) {
+ result = true;
+ } else {
+ i->swap();
+ result = is_linear_inner(q2, tMin, tsFound[split - 1], q1, t1s, t1e, i, subDivide);
+ }
+ if (add_intercept(q1, q2, tsFound[split], tMax, i, subDivide)) {
+ result = true;
+ } else {
+ i->swap();
+ result |= is_linear_inner(q2, tsFound[split], tMax, q1, t1s, t1e, i, subDivide);
+ }
+ return result;
+}
+
+static double flat_measure(const SkDQuad& q) {
+ SkDVector mid = q[1] - q[0];
+ SkDVector dxy = q[2] - q[0];
+ double length = dxy.length(); // OPTIMIZE: get rid of sqrt
+ return fabs(mid.cross(dxy) / length);
+}
+
+// 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) {
+ 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);
+}
+
+// FIXME: if flat measure is sufficiently large, then probably the quartic solution failed
+// avoid imprecision incurred with chopAt
+static void relaxed_is_linear(const SkDQuad* q1, double s1, double e1, const SkDQuad* q2,
+ double s2, double e2, SkIntersections* i) {
+ double m1 = flat_measure(*q1);
+ double m2 = flat_measure(*q2);
+ i->reset();
+ const SkDQuad* rounder, *flatter;
+ double sf, midf, ef, sr, er;
+ if (m2 < m1) {
+ rounder = q1;
+ sr = s1;
+ er = e1;
+ flatter = q2;
+ sf = s2;
+ midf = (s2 + e2) / 2;
+ ef = e2;
+ } else {
+ rounder = q2;
+ sr = s2;
+ er = e2;
+ flatter = q1;
+ sf = s1;
+ midf = (s1 + e1) / 2;
+ ef = e1;
+ }
+ bool subDivide = false;
+ is_linear_inner(*flatter, sf, ef, *rounder, sr, er, i, &subDivide);
+ if (subDivide) {
+ relaxed_is_linear(flatter, sf, midf, rounder, sr, er, i);
+ relaxed_is_linear(flatter, midf, ef, rounder, sr, er, i);
+ }
+ if (m2 < m1) {
+ i->swapPts();
+ }
+}
+
+// each time through the loop, this computes values it had from the last loop
+// if i == j == 1, the center values are still good
+// otherwise, for i != 1 or j != 1, four of the values are still good
+// and if i == 1 ^ j == 1, an additional value is good
+static bool binary_search(const SkDQuad& quad1, const SkDQuad& quad2, double* t1Seed,
+ double* t2Seed, SkDPoint* pt) {
+ double tStep = ROUGH_EPSILON;
+ SkDPoint t1[3], t2[3];
+ int calcMask = ~0;
+ do {
+ if (calcMask & (1 << 1)) t1[1] = quad1.ptAtT(*t1Seed);
+ if (calcMask & (1 << 4)) t2[1] = quad2.ptAtT(*t2Seed);
+ if (t1[1].approximatelyEqual(t2[1])) {
+ *pt = t1[1];
+ #if ONE_OFF_DEBUG
+ 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));
+ if (calcMask & (1 << 2)) t1[2] = quad1.ptAtT(SkTMin(1., *t1Seed + tStep));
+ if (calcMask & (1 << 3)) t2[0] = quad2.ptAtT(SkTMax(0., *t2Seed - tStep));
+ if (calcMask & (1 << 5)) t2[2] = quad2.ptAtT(SkTMin(1., *t2Seed + tStep));
+ double dist[3][3];
+ // OPTIMIZE: using calcMask value permits skipping some distance calcuations
+ // if prior loop's results are moved to correct slot for reuse
+ dist[1][1] = t1[1].distanceSquared(t2[1]);
+ int best_i = 1, best_j = 1;
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ if (i == 1 && j == 1) {
+ continue;
+ }
+ dist[i][j] = t1[i].distanceSquared(t2[j]);
+ if (dist[best_i][best_j] > dist[i][j]) {
+ best_i = i;
+ best_j = j;
+ }
+ }
+ }
+ if (best_i == 1 && best_j == 1) {
+ tStep /= 2;
+ if (tStep < FLT_EPSILON_HALF) {
+ break;
+ }
+ calcMask = (1 << 0) | (1 << 2) | (1 << 3) | (1 << 5);
+ continue;
+ }
+ if (best_i == 0) {
+ *t1Seed -= tStep;
+ t1[2] = t1[1];
+ t1[1] = t1[0];
+ calcMask = 1 << 0;
+ } else if (best_i == 2) {
+ *t1Seed += tStep;
+ t1[0] = t1[1];
+ t1[1] = t1[2];
+ calcMask = 1 << 2;
+ } else {
+ calcMask = 0;
+ }
+ if (best_j == 0) {
+ *t2Seed -= tStep;
+ t2[2] = t2[1];
+ t2[1] = t2[0];
+ calcMask |= 1 << 3;
+ } else if (best_j == 2) {
+ *t2Seed += tStep;
+ t2[0] = t2[1];
+ t2[1] = t2[2];
+ calcMask |= 1 << 5;
+ }
+ } while (true);
+#if ONE_OFF_DEBUG
+ SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) != (%1.9g,%1.9g) %s\n", __FUNCTION__,
+ t1Seed, t2Seed, t1[1].fX, t1[1].fY, t1[2].fX, t1[2].fY);
+#endif
+ return false;
+}
+
+static void lookNearEnd(const SkDQuad& q1, const SkDQuad& q2, int testT,
+ const SkIntersections& orig, bool swap, SkIntersections* i) {
+ if (orig.used() == 1 && orig[!swap][0] == testT) {
+ return;
+ }
+ if (orig.used() == 2 && orig[!swap][1] == testT) {
+ return;
+ }
+ SkDLine tmpLine;
+ int testTIndex = testT << 1;
+ tmpLine[0] = tmpLine[1] = q2[testTIndex];
+ tmpLine[1].fX += q2[1].fY - q2[testTIndex].fY;
+ tmpLine[1].fY -= q2[1].fX - q2[testTIndex].fX;
+ SkIntersections impTs;
+ impTs.intersectRay(q1, tmpLine);
+ for (int index = 0; index < impTs.used(); ++index) {
+ SkDPoint realPt = impTs.pt(index);
+ if (!tmpLine[0].approximatelyPEqual(realPt)) {
+ continue;
+ }
+ if (swap) {
+ i->insert(testT, impTs[0][index], tmpLine[0]);
+ } else {
+ i->insert(impTs[0][index], testT, tmpLine[0]);
+ }
+ }
+}
+
+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;
+ }
+ }
+ }
+ SkASSERT(fUsed < 3);
+ if (only_end_pts_in_common(q1, q2)) {
+ return fUsed;
+ }
+ if (only_end_pts_in_common(q2, q1)) {
+ return fUsed;
+ }
+ // see if either quad is really a line
+ // FIXME: figure out why reduce step didn't find this earlier
+ if (is_linear(q1, q2, this)) {
+ return fUsed;
+ }
+ SkIntersections swapped;
+ swapped.setMax(fMax);
+ if (is_linear(q2, q1, &swapped)) {
+ swapped.swapPts();
+ *this = swapped;
+ return fUsed;
+ }
+ SkIntersections copyI(*this);
+ lookNearEnd(q1, q2, 0, *this, false, &copyI);
+ lookNearEnd(q1, q2, 1, *this, false, &copyI);
+ lookNearEnd(q2, q1, 0, *this, true, &copyI);
+ lookNearEnd(q2, q1, 1, *this, true, &copyI);
+ int innerEqual = 0;
+ if (copyI.fUsed >= 2) {
+ SkASSERT(copyI.fUsed <= 4);
+ double width = copyI[0][1] - copyI[0][0];
+ int midEnd = 1;
+ for (int index = 2; index < copyI.fUsed; ++index) {
+ double testWidth = copyI[0][index] - copyI[0][index - 1];
+ if (testWidth <= width) {
+ continue;
+ }
+ midEnd = index;
+ }
+ for (int index = 0; index < 2; ++index) {
+ double testT = (copyI[0][midEnd] * (index + 1)
+ + copyI[0][midEnd - 1] * (2 - index)) / 3;
+ SkDPoint testPt1 = q1.ptAtT(testT);
+ testT = (copyI[1][midEnd] * (index + 1) + copyI[1][midEnd - 1] * (2 - index)) / 3;
+ SkDPoint testPt2 = q2.ptAtT(testT);
+ innerEqual += testPt1.approximatelyEqual(testPt2);
+ }
+ }
+ bool expectCoincident = copyI.fUsed >= 2 && innerEqual == 2;
+ if (expectCoincident) {
+ reset();
+ insertCoincident(copyI[0][0], copyI[1][0], copyI.fPt[0]);
+ int last = copyI.fUsed - 1;
+ insertCoincident(copyI[0][last], copyI[1][last], copyI.fPt[last]);
+ return fUsed;
+ }
+ SkDQuadImplicit i1(q1);
+ SkDQuadImplicit i2(q2);
+ int index;
+ bool flip1 = q1[2] == q2[0];
+ bool flip2 = q1[0] == q2[2];
+ bool useCubic = q1[0] == q2[0];
+ double roots1[4];
+ 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) {
+ pts1[index] = q1.ptAtT(roots1Copy[index]);
+ }
+ double roots2[4];
+ int rootCount2 = findRoots(i1, q2, roots2, useCubic, flip2, 0);
+ double roots2Copy[4];
+ int r2Count = addValidRoots(roots2, rootCount2, roots2Copy);
+ SkDPoint pts2[4];
+ 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]);
+ }
+ }
+ }
+ return fUsed;
+ }
+ int closest[4];
+ double dist[4];
+ bool foundSomething = false;
+ for (index = 0; index < r1Count; ++index) {
+ dist[index] = DBL_MAX;
+ closest[index] = -1;
+ for (int ndex2 = 0; ndex2 < r2Count; ++ndex2) {
+ if (!pts2[ndex2].approximatelyEqual(pts1[index])) {
+ continue;
+ }
+ double dx = pts2[ndex2].fX - pts1[index].fX;
+ double dy = pts2[ndex2].fY - pts1[index].fY;
+ double distance = dx * dx + dy * dy;
+ if (dist[index] <= distance) {
+ continue;
+ }
+ for (int outer = 0; outer < index; ++outer) {
+ if (closest[outer] != ndex2) {
+ continue;
+ }
+ if (dist[outer] < distance) {
+ goto next;
+ }
+ closest[outer] = -1;
+ }
+ dist[index] = distance;
+ closest[index] = ndex2;
+ foundSomething = true;
+ next:
+ ;
+ }
+ }
+ 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;
+ do {
+ double lowest = DBL_MAX;
+ int lowestIndex = -1;
+ for (index = 0; index < r1Count; ++index) {
+ if (closest[index] < 0) {
+ continue;
+ }
+ if (roots1Copy[index] < lowest) {
+ lowestIndex = index;
+ lowest = roots1Copy[index];
+ }
+ }
+ if (lowestIndex < 0) {
+ break;
+ }
+ insert(roots1Copy[lowestIndex], roots2Copy[closest[lowestIndex]],
+ pts1[lowestIndex]);
+ closest[lowestIndex] = -1;
+ } 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/SkDQuadLineIntersection.cpp b/src/pathops/SkDQuadLineIntersection.cpp
index b8a9a641dd..ef8edb02cd 100644
--- a/src/pathops/SkDQuadLineIntersection.cpp
+++ b/src/pathops/SkDQuadLineIntersection.cpp
@@ -105,29 +105,6 @@ public:
fAllowNear = allow;
}
- void checkCoincident() {
- int last = fIntersections->used() - 1;
- for (int index = 0; index < last; ) {
- double quadMidT = ((*fIntersections)[0][index] + (*fIntersections)[0][index + 1]) / 2;
- SkDPoint quadMidPt = fQuad.ptAtT(quadMidT);
- double t = fLine.nearPoint(quadMidPt, NULL);
- if (t < 0) {
- ++index;
- continue;
- }
- if (fIntersections->isCoincident(index)) {
- fIntersections->removeOne(index);
- --last;
- } else if (fIntersections->isCoincident(index + 1)) {
- fIntersections->removeOne(index + 1);
- --last;
- } else {
- fIntersections->setCoincident(index++);
- }
- fIntersections->setCoincident(index);
- }
- }
-
int intersectRay(double roots[2]) {
/*
solve by rotating line+quad so line is horizontal, then finding the roots
@@ -163,17 +140,20 @@ public:
if (fAllowNear) {
addNearEndPoints();
}
- double rootVals[2];
- int roots = intersectRay(rootVals);
- for (int index = 0; index < roots; ++index) {
- double quadT = rootVals[index];
- double lineT = findLineT(quadT);
- SkDPoint pt;
- if (pinTs(&quadT, &lineT, &pt, kPointUninitialized) && uniqueAnswer(quadT, pt)) {
- fIntersections->insert(quadT, lineT, pt);
+ if (fIntersections->used() == 2) {
+ // FIXME : need sharable code that turns spans into coincident if middle point is on
+ } else {
+ double rootVals[2];
+ int roots = intersectRay(rootVals);
+ for (int index = 0; index < roots; ++index) {
+ double quadT = rootVals[index];
+ double lineT = findLineT(quadT);
+ SkDPoint pt;
+ if (pinTs(&quadT, &lineT, &pt, kPointUninitialized)) {
+ fIntersections->insert(quadT, lineT, pt);
+ }
}
}
- checkCoincident();
return fIntersections->used();
}
@@ -198,41 +178,16 @@ public:
double quadT = rootVals[index];
SkDPoint pt = fQuad.ptAtT(quadT);
double lineT = (pt.fX - left) / (right - left);
- if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
+ if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) {
fIntersections->insert(quadT, lineT, pt);
}
}
if (flipped) {
fIntersections->flip();
}
- checkCoincident();
return fIntersections->used();
}
- bool uniqueAnswer(double quadT, const SkDPoint& pt) {
- for (int inner = 0; inner < fIntersections->used(); ++inner) {
- if (fIntersections->pt(inner) != pt) {
- continue;
- }
- double existingQuadT = (*fIntersections)[0][inner];
- if (quadT == existingQuadT) {
- return false;
- }
- // check if midway on quad is also same point. If so, discard this
- double quadMidT = (existingQuadT + quadT) / 2;
- SkDPoint quadMidPt = fQuad.ptAtT(quadMidT);
- if (quadMidPt.approximatelyEqual(pt)) {
- return false;
- }
- }
-#if ONE_OFF_DEBUG
- SkDPoint qPt = fQuad.ptAtT(quadT);
- SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
- qPt.fX, qPt.fY);
-#endif
- return true;
- }
-
int verticalIntersect(double axisIntercept, double roots[2]) {
double D = fQuad[2].fX; // f
double E = fQuad[1].fX; // e
@@ -254,14 +209,13 @@ public:
double quadT = rootVals[index];
SkDPoint pt = fQuad.ptAtT(quadT);
double lineT = (pt.fY - top) / (bottom - top);
- if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
+ if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) {
fIntersections->insert(quadT, lineT, pt);
}
}
if (flipped) {
fIntersections->flip();
}
- checkCoincident();
return fIntersections->used();
}
diff --git a/src/pathops/SkIntersectionHelper.h b/src/pathops/SkIntersectionHelper.h
index c633fd02df..3569c934de 100644
--- a/src/pathops/SkIntersectionHelper.h
+++ b/src/pathops/SkIntersectionHelper.h
@@ -5,7 +5,6 @@
* found in the LICENSE file.
*/
#include "SkOpContour.h"
-#include "SkOpSegment.h"
#include "SkPath.h"
#ifdef SK_DEBUG
@@ -22,9 +21,42 @@ public:
kCubic_Segment = SkPath::kCubic_Verb,
};
+ bool addCoincident(SkIntersectionHelper& other, const SkIntersections& ts, bool swap) {
+ return fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
+ }
+
+ // FIXME: does it make sense to write otherIndex now if we're going to
+ // fix it up later?
+ void addOtherT(int index, double otherT, int otherIndex) {
+ fContour->addOtherT(fIndex, index, otherT, otherIndex);
+ }
+
+ bool addPartialCoincident(SkIntersectionHelper& other, const SkIntersections& ts, int index,
+ bool swap) {
+ return fContour->addPartialCoincident(fIndex, other.fContour, other.fIndex, ts, index,
+ swap);
+ }
+
+ // Avoid collapsing t values that are close to the same since
+ // we walk ts to describe consecutive intersections. Since a pair of ts can
+ // be nearly equal, any problems caused by this should be taken care
+ // of later.
+ // On the edge or out of range values are negative; add 2 to get end
+ int addT(const SkIntersectionHelper& other, const SkPoint& pt, double newT) {
+ return fContour->addT(fIndex, other.fContour, other.fIndex, pt, newT);
+ }
+
+ int addSelfT(const SkPoint& pt, double newT) {
+ return fContour->addSelfT(fIndex, pt, newT);
+ }
+
bool advance() {
- fSegment = fSegment->next();
- return fSegment != NULL;
+ return ++fIndex < fLast;
+ }
+
+ void alignTPt(SkIntersectionHelper& other, bool swap, int index,
+ SkIntersections* ts, SkPoint* point) {
+ fContour->alignTPt(fIndex, other.fContour, other.fIndex, swap, index, ts, point);
}
SkScalar bottom() const {
@@ -32,15 +64,30 @@ public:
}
const SkPathOpsBounds& bounds() const {
- return fSegment->bounds();
+ return fContour->segments()[fIndex].bounds();
}
- SkOpContour* contour() const {
- return fSegment->contour();
+ void init(SkOpContour* contour) {
+ fContour = contour;
+ fIndex = 0;
+ fLast = contour->segments().count();
}
- void init(SkOpContour* contour) {
- fSegment = contour->first();
+ bool isAdjacent(const SkIntersectionHelper& next) {
+ return fContour == next.fContour && fIndex + 1 == next.fIndex;
+ }
+
+ bool isFirstLast(const SkIntersectionHelper& next) {
+ return fContour == next.fContour && fIndex == 0
+ && next.fIndex == fLast - 1;
+ }
+
+ bool isPartial(double t1, double t2, const SkDPoint& pt1, const SkDPoint& pt2) const {
+ const SkOpSegment& segment = fContour->segments()[fIndex];
+ double mid = (t1 + t2) / 2;
+ SkDPoint midPtByT = segment.dPtAtT(mid);
+ SkDPoint midPtByAvg = SkDPoint::Mid(pt1, pt2);
+ return midPtByT.approximatelyPEqual(midPtByAvg);
}
SkScalar left() const {
@@ -48,40 +95,41 @@ public:
}
const SkPoint* pts() const {
- return fSegment->pts();
+ return fContour->segments()[fIndex].pts();
}
SkScalar right() const {
return bounds().fRight;
}
- SkOpSegment* segment() const {
- return fSegment;
- }
-
SegmentType segmentType() const {
- SegmentType type = (SegmentType) fSegment->verb();
+ const SkOpSegment& segment = fContour->segments()[fIndex];
+ SegmentType type = (SegmentType) segment.verb();
if (type != kLine_Segment) {
return type;
}
- if (fSegment->isHorizontal()) {
+ if (segment.isHorizontal()) {
return kHorizontalLine_Segment;
}
- if (fSegment->isVertical()) {
+ if (segment.isVertical()) {
return kVerticalLine_Segment;
}
return kLine_Segment;
}
bool startAfter(const SkIntersectionHelper& after) {
- fSegment = after.fSegment->next();
- return fSegment != NULL;
+ fIndex = after.fIndex;
+ return advance();
}
SkScalar top() const {
return bounds().fTop;
}
+ SkPath::Verb verb() const {
+ return fContour->segments()[fIndex].verb();
+ }
+
SkScalar x() const {
return bounds().fLeft;
}
@@ -99,5 +147,10 @@ public:
}
private:
- SkOpSegment* fSegment;
+ // utility callable by the user from the debugger when the implementation code is linked in
+ void dump() const;
+
+ SkOpContour* fContour;
+ int fIndex;
+ int fLast;
};
diff --git a/src/pathops/SkIntersections.cpp b/src/pathops/SkIntersections.cpp
index 007efa7ff1..e9875cf69d 100644
--- a/src/pathops/SkIntersections.cpp
+++ b/src/pathops/SkIntersections.cpp
@@ -7,25 +7,26 @@
#include "SkIntersections.h"
-int SkIntersections::closestTo(double rangeStart, double rangeEnd, const SkDPoint& testPt,
- double* closestDist) const {
- int closest = -1;
- *closestDist = SK_ScalarMax;
- for (int index = 0; index < fUsed; ++index) {
- if (!between(rangeStart, fT[0][index], rangeEnd)) {
- continue;
- }
- const SkDPoint& iPt = fPt[index];
- double dist = testPt.distanceSquared(iPt);
- if (*closestDist > dist) {
- *closestDist = dist;
- closest = index;
- }
+void SkIntersections::append(const SkIntersections& i) {
+ for (int index = 0; index < i.fUsed; ++index) {
+ insert(i[0][index], i[1][index], i.pt(index));
}
- return closest;
}
-// called only by test code
+int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar, SkScalar, SkScalar, bool) = {
+ NULL,
+ &SkIntersections::verticalLine,
+ &SkIntersections::verticalQuad,
+ &SkIntersections::verticalCubic
+};
+
+int ( SkIntersections::* const CurveRay[])(const SkPoint[], const SkDLine&) = {
+ NULL,
+ &SkIntersections::lineRay,
+ &SkIntersections::quadRay,
+ &SkIntersections::cubicRay
+};
+
int SkIntersections::coincidentUsed() const {
if (!fIsCoincident[0]) {
SkASSERT(!fIsCoincident[1]);
@@ -47,12 +48,12 @@ int SkIntersections::coincidentUsed() const {
return count;
}
-int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar, SkScalar, SkScalar, bool) = {
- NULL,
- &SkIntersections::verticalLine,
- &SkIntersections::verticalQuad,
- &SkIntersections::verticalCubic
-};
+int SkIntersections::cubicRay(const SkPoint pts[4], const SkDLine& line) {
+ SkDCubic cubic;
+ cubic.set(pts);
+ fMax = 3;
+ return intersectRay(cubic, line);
+}
void SkIntersections::flip() {
for (int index = 0; index < fUsed; ++index) {
@@ -104,6 +105,7 @@ int SkIntersections::insert(double one, double two, const SkDPoint& pt) {
int remaining = fUsed - index;
if (remaining > 0) {
memmove(&fPt[index + 1], &fPt[index], sizeof(fPt[0]) * remaining);
+ memmove(&fPt2[index + 1], &fPt2[index], sizeof(fPt2[0]) * remaining);
memmove(&fT[0][index + 1], &fT[0][index], sizeof(fT[0][0]) * remaining);
memmove(&fT[1][index + 1], &fT[1][index], sizeof(fT[1][0]) * remaining);
int clearMask = ~((1 << index) - 1);
@@ -123,53 +125,39 @@ void SkIntersections::insertNear(double one, double two, const SkDPoint& pt1, co
SkASSERT(one == 0 || one == 1);
SkASSERT(two == 0 || two == 1);
SkASSERT(pt1 != pt2);
- fNearlySame[one ? 1 : 0] = true;
+ SkASSERT(fNearlySame[(int) one]);
(void) insert(one, two, pt1);
- fPt2[one ? 1 : 0] = pt2;
+ fPt2[one ? fUsed - 1 : 0] = pt2;
}
-int SkIntersections::insertCoincident(double one, double two, const SkDPoint& pt) {
+void SkIntersections::insertCoincident(double one, double two, const SkDPoint& pt) {
int index = insertSwap(one, two, pt);
- if (index >= 0) {
- setCoincident(index);
- }
- return index;
-}
-
-void SkIntersections::setCoincident(int index) {
- SkASSERT(index >= 0);
int bit = 1 << index;
fIsCoincident[0] |= bit;
fIsCoincident[1] |= bit;
}
-void SkIntersections::merge(const SkIntersections& a, int aIndex, const SkIntersections& b,
- int bIndex) {
- this->reset();
- fT[0][0] = a.fT[0][aIndex];
- fT[1][0] = b.fT[0][bIndex];
- fPt[0] = a.fPt[aIndex];
- fPt2[0] = b.fPt[bIndex];
- fUsed = 1;
+int SkIntersections::lineRay(const SkPoint pts[2], const SkDLine& line) {
+ SkDLine l;
+ l.set(pts);
+ fMax = 2;
+ return intersectRay(l, line);
}
-int SkIntersections::mostOutside(double rangeStart, double rangeEnd, const SkDPoint& origin) const {
- int result = -1;
- for (int index = 0; index < fUsed; ++index) {
- if (!between(rangeStart, fT[0][index], rangeEnd)) {
- continue;
- }
- if (result < 0) {
- result = index;
- continue;
- }
- SkDVector best = fPt[result] - origin;
- SkDVector test = fPt[index] - origin;
- if (test.crossCheck(best) < 0) {
- result = index;
- }
+void SkIntersections::offset(int base, double start, double end) {
+ for (int index = base; index < fUsed; ++index) {
+ double val = fT[fSwap][index];
+ val *= end - start;
+ val += start;
+ fT[fSwap][index] = val;
}
- return result;
+}
+
+int SkIntersections::quadRay(const SkPoint pts[3], const SkDLine& line) {
+ SkDQuad quad;
+ quad.set(pts);
+ fMax = 2;
+ return intersectRay(quad, line);
}
void SkIntersections::quickRemoveOne(int index, int replace) {
@@ -184,6 +172,7 @@ void SkIntersections::removeOne(int index) {
return;
}
memmove(&fPt[index], &fPt[index + 1], sizeof(fPt[0]) * remaining);
+ 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);
@@ -193,6 +182,13 @@ void SkIntersections::removeOne(int index) {
fIsCoincident[1] -= ((fIsCoincident[1] >> 1) & ~((1 << index) - 1)) + coBit;
}
+void SkIntersections::swapPts() {
+ int index;
+ for (index = 0; index < fUsed; ++index) {
+ SkTSwap(fT[0][index], fT[1][index]);
+ }
+}
+
int SkIntersections::verticalLine(const SkPoint a[2], SkScalar top, SkScalar bottom,
SkScalar x, bool flipped) {
SkDLine line;
diff --git a/src/pathops/SkIntersections.h b/src/pathops/SkIntersections.h
index 15bac19def..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
@@ -23,6 +24,7 @@ public:
sk_bzero(fPt, sizeof(fPt));
sk_bzero(fPt2, sizeof(fPt2));
sk_bzero(fT, sizeof(fT));
+ sk_bzero(fIsCoincident, sizeof(fIsCoincident));
sk_bzero(fNearlySame, sizeof(fNearlySame));
reset();
fMax = 0; // require that the caller set the max
@@ -30,7 +32,7 @@ public:
class TArray {
public:
- explicit TArray(const double ts[10]) : fTArray(ts) {}
+ explicit TArray(const double ts[9]) : fTArray(ts) {}
double operator[](int n) const {
return fTArray[n];
}
@@ -38,15 +40,28 @@ public:
};
TArray operator[](int n) const { return TArray(fT[n]); }
+ void allowFlatMeasure(bool flatAllowed) {
+ fFlatMeasure = flatAllowed;
+ }
+
void allowNear(bool nearAllowed) {
fAllowNear = nearAllowed;
}
- void clearCoincidence(int index) {
- SkASSERT(index >= 0);
- int bit = 1 << index;
- fIsCoincident[0] &= ~bit;
- fIsCoincident[1] &= ~bit;
+ int cubic(const SkPoint a[4]) {
+ SkDCubic cubic;
+ cubic.set(a);
+ fMax = 1; // self intersect
+ return intersect(cubic);
+ }
+
+ int cubicCubic(const SkPoint a[4], const SkPoint b[4]) {
+ SkDCubic aCubic;
+ aCubic.set(a);
+ SkDCubic bCubic;
+ bCubic.set(b);
+ fMax = 9;
+ return intersect(aCubic, bCubic);
}
int cubicHorizontal(const SkPoint a[4], SkScalar left, SkScalar right, SkScalar y,
@@ -73,6 +88,19 @@ public:
return intersect(cubic, line);
}
+ int cubicQuad(const SkPoint a[4], const SkPoint b[3]) {
+ SkDCubic cubic;
+ cubic.set(a);
+ SkDQuad quad;
+ quad.set(b);
+ 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);
@@ -150,11 +178,19 @@ public:
return intersect(quad, line);
}
+ int quadQuad(const SkPoint a[3], const SkPoint b[3]) {
+ SkDQuad aQuad;
+ aQuad.set(a);
+ SkDQuad bQuad;
+ bQuad.set(b);
+ fMax = 4;
+ return intersect(aQuad, bQuad);
+ }
+
// leaves swap, max alone
void reset() {
fAllowNear = true;
fUsed = 0;
- sk_bzero(fIsCoincident, sizeof(fIsCoincident));
}
void set(bool swap, int tIndex, double t) {
@@ -169,6 +205,8 @@ public:
fSwap ^= true;
}
+ void swapPts();
+
bool swapped() const {
return fSwap;
}
@@ -181,27 +219,19 @@ public:
SkASSERT(--fDepth >= 0);
}
- bool unBumpT(int index) {
- SkASSERT(fUsed == 1);
- fT[0][index] = fT[0][index] * (1 + BUMP_EPSILON * 2) - BUMP_EPSILON;
- if (!between(0, fT[0][index], 1)) {
- fUsed = 0;
- return false;
- }
- return true;
- }
-
void upDepth() {
SkASSERT(++fDepth < 16);
}
void alignQuadPts(const SkPoint a[3], const SkPoint b[3]);
+ void append(const SkIntersections& );
int cleanUpCoincidence();
- int closestTo(double rangeStart, double rangeEnd, const SkDPoint& testPt, double* dist) const;
int coincidentUsed() const;
void cubicInsert(double one, double two, const SkDPoint& pt, const SkDCubic& c1,
const SkDCubic& c2);
+ int cubicRay(const SkPoint pts[4], const SkDLine& line);
void flip();
+ int horizontal(const SkDLine&, double y);
int horizontal(const SkDLine&, double left, double right, double y, bool flipped);
int horizontal(const SkDQuad&, double left, double right, double y, bool flipped);
int horizontal(const SkDQuad&, double left, double right, double y, double tRange[2]);
@@ -212,20 +242,25 @@ public:
int insert(double one, double two, const SkDPoint& pt);
void insertNear(double one, double two, const SkDPoint& pt1, const SkDPoint& pt2);
// start if index == 0 : end if index == 1
- int insertCoincident(double one, double two, const SkDPoint& pt);
+ void insertCoincident(double one, double two, const SkDPoint& pt);
int intersect(const SkDLine&, const SkDLine&);
int intersect(const SkDQuad&, const SkDLine&);
int intersect(const SkDQuad&, const SkDQuad&);
+ int intersect(const SkDCubic&); // return true if cubic self-intersects
int intersect(const SkDCubic&, const SkDLine&);
+ int intersect(const SkDCubic&, const SkDQuad&);
int intersect(const SkDCubic&, const SkDCubic&);
int intersectRay(const SkDLine&, const SkDLine&);
int intersectRay(const SkDQuad&, const SkDLine&);
int intersectRay(const SkDCubic&, const SkDLine&);
- void merge(const SkIntersections& , int , const SkIntersections& , int );
- int mostOutside(double rangeStart, double rangeEnd, const SkDPoint& origin) const;
+ static SkDPoint Line(const SkDLine&, const SkDLine&);
+ int lineRay(const SkPoint pts[2], const SkDLine& line);
+ void offset(int base, double start, double end);
void quickRemoveOne(int index, int replace);
+ int quadRay(const SkPoint pts[3], const SkDLine& line);
void removeOne(int index);
- void setCoincident(int index);
+ static bool Test(const SkDLine& , const SkDLine&);
+ int vertical(const SkDLine&, double x);
int vertical(const SkDLine&, double top, double bottom, double x, bool flipped);
int vertical(const SkDQuad&, double top, double bottom, double x, bool flipped);
int vertical(const SkDCubic&, double top, double bottom, double x, bool flipped);
@@ -241,8 +276,6 @@ public:
#endif
}
- void dump() const; // implemented for testing only
-
private:
bool cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2);
bool cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2);
@@ -250,20 +283,22 @@ private:
void cleanUpParallelLines(bool parallel);
void computePoints(const SkDLine& line, int used);
- SkDPoint fPt[10]; // FIXME: since scans store points as SkPoint, this should also
- SkDPoint fPt2[2]; // used by nearly same to store alternate intersection point
- double fT[2][10];
+ SkDPoint fPt[9]; // FIXME: since scans store points as SkPoint, this should also
+ SkDPoint fPt2[9]; // used by nearly same to store alternate intersection point
+ double fT[2][9];
uint16_t fIsCoincident[2]; // bit set for each curve's coincident T
bool fNearlySame[2]; // true if end points nearly match
unsigned char fUsed;
unsigned char fMax;
bool fAllowNear;
bool fSwap;
+ bool fFlatMeasure; // backwards-compatibility when cubics uses quad intersection
#ifdef SK_DEBUG
int fDepth;
#endif
};
+extern int (SkIntersections::* const CurveRay[])(const SkPoint[], const SkDLine& );
extern int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar top, SkScalar bottom,
SkScalar x, bool flipped);
diff --git a/src/pathops/SkOpAngle.cpp b/src/pathops/SkOpAngle.cpp
index c13a51a8cc..b3a188c1e8 100644
--- a/src/pathops/SkOpAngle.cpp
+++ b/src/pathops/SkOpAngle.cpp
@@ -4,26 +4,26 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#include "SkIntersections.h"
#include "SkOpAngle.h"
#include "SkOpSegment.h"
#include "SkPathOpsCurve.h"
#include "SkTSort.h"
+#if DEBUG_ANGLE
+#include "SkString.h"
+#endif
+
/* Angles are sorted counterclockwise. The smallest angle has a positive x and the smallest
positive y. The largest angle has a positive x and a zero y. */
#if DEBUG_ANGLE
- static bool CompareResult(const char* func, SkString* bugOut, SkString* bugPart, int append,
- bool compare) {
+ static bool CompareResult(SkString* bugOut, int append, bool compare) {
SkDebugf("%s %c %d\n", bugOut->c_str(), compare ? 'T' : 'F', append);
- SkDebugf("%sPart %s\n", func, bugPart[0].c_str());
- SkDebugf("%sPart %s\n", func, bugPart[1].c_str());
- SkDebugf("%sPart %s\n", func, bugPart[2].c_str());
return compare;
}
- #define COMPARE_RESULT(append, compare) CompareResult(__FUNCTION__, &bugOut, bugPart, append, \
- compare)
+ #define COMPARE_RESULT(append, compare) CompareResult(&bugOut, append, compare)
#else
#define COMPARE_RESULT(append, compare) compare
#endif
@@ -58,50 +58,51 @@
*/
// return true if lh < this < rh
-bool SkOpAngle::after(SkOpAngle* test) {
- SkOpAngle* lh = test;
- SkOpAngle* rh = lh->fNext;
- SkASSERT(lh != rh);
+bool SkOpAngle::after(const SkOpAngle* test) const {
+ const SkOpAngle& lh = *test;
+ const SkOpAngle& rh = *lh.fNext;
+ SkASSERT(&lh != &rh);
#if DEBUG_ANGLE
SkString bugOut;
bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
" < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
" < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__,
- lh->segment()->debugID(), lh->debugID(), lh->fSectorStart, lh->fSectorEnd,
- lh->fStart->t(), lh->fEnd->t(),
- segment()->debugID(), debugID(), fSectorStart, fSectorEnd, fStart->t(), fEnd->t(),
- rh->segment()->debugID(), rh->debugID(), rh->fSectorStart, rh->fSectorEnd,
- rh->fStart->t(), rh->fEnd->t());
- SkString bugPart[3] = { lh->debugPart(), this->debugPart(), rh->debugPart() };
+ lh.fSegment->debugID(), lh.debugID(), lh.fSectorStart, lh.fSectorEnd,
+ lh.fSegment->t(lh.fStart), lh.fSegment->t(lh.fEnd),
+ fSegment->debugID(), debugID(), fSectorStart, fSectorEnd, fSegment->t(fStart),
+ fSegment->t(fEnd),
+ rh.fSegment->debugID(), rh.debugID(), rh.fSectorStart, rh.fSectorEnd,
+ rh.fSegment->t(rh.fStart), rh.fSegment->t(rh.fEnd));
#endif
- if (lh->fComputeSector && !lh->computeSector()) {
+ if (lh.fComputeSector && !const_cast<SkOpAngle&>(lh).computeSector()) {
return COMPARE_RESULT(1, true);
}
- if (fComputeSector && !this->computeSector()) {
+ if (fComputeSector && !const_cast<SkOpAngle*>(this)->computeSector()) {
return COMPARE_RESULT(2, true);
}
- if (rh->fComputeSector && !rh->computeSector()) {
+ if (rh.fComputeSector && !const_cast<SkOpAngle&>(rh).computeSector()) {
return COMPARE_RESULT(3, true);
}
#if DEBUG_ANGLE // reset bugOut with computed sectors
bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
" < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
" < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__,
- lh->segment()->debugID(), lh->debugID(), lh->fSectorStart, lh->fSectorEnd,
- lh->fStart->t(), lh->fEnd->t(),
- segment()->debugID(), debugID(), fSectorStart, fSectorEnd, fStart->t(), fEnd->t(),
- rh->segment()->debugID(), rh->debugID(), rh->fSectorStart, rh->fSectorEnd,
- rh->fStart->t(), rh->fEnd->t());
+ lh.fSegment->debugID(), lh.debugID(), lh.fSectorStart, lh.fSectorEnd,
+ lh.fSegment->t(lh.fStart), lh.fSegment->t(lh.fEnd),
+ fSegment->debugID(), debugID(), fSectorStart, fSectorEnd, fSegment->t(fStart),
+ fSegment->t(fEnd),
+ rh.fSegment->debugID(), rh.debugID(), rh.fSectorStart, rh.fSectorEnd,
+ rh.fSegment->t(rh.fStart), rh.fSegment->t(rh.fEnd));
#endif
- bool ltrOverlap = (lh->fSectorMask | rh->fSectorMask) & fSectorMask;
- bool lrOverlap = lh->fSectorMask & rh->fSectorMask;
+ bool ltrOverlap = (lh.fSectorMask | rh.fSectorMask) & fSectorMask;
+ bool lrOverlap = lh.fSectorMask & rh.fSectorMask;
int lrOrder; // set to -1 if either order works
if (!lrOverlap) { // no lh/rh sector overlap
if (!ltrOverlap) { // no lh/this/rh sector overlap
- return COMPARE_RESULT(4, (lh->fSectorEnd > rh->fSectorStart)
- ^ (fSectorStart > lh->fSectorEnd) ^ (fSectorStart > rh->fSectorStart));
+ return COMPARE_RESULT(4, (lh.fSectorEnd > rh.fSectorStart)
+ ^ (fSectorStart > lh.fSectorEnd) ^ (fSectorStart > rh.fSectorStart));
}
- int lrGap = (rh->fSectorStart - lh->fSectorStart + 32) & 0x1f;
+ int lrGap = (rh.fSectorStart - lh.fSectorStart + 32) & 0x1f;
/* A tiny change can move the start +/- 4. The order can only be determined if
lr gap is not 12 to 20 or -12 to -20.
-31 ..-21 1
@@ -114,24 +115,24 @@ bool SkOpAngle::after(SkOpAngle* test) {
*/
lrOrder = lrGap > 20 ? 0 : lrGap > 11 ? -1 : 1;
} else {
- lrOrder = (int) lh->orderable(rh);
+ lrOrder = (int) lh.orderable(rh);
if (!ltrOverlap) {
return COMPARE_RESULT(5, !lrOrder);
}
}
int ltOrder;
- SkASSERT((lh->fSectorMask & fSectorMask) || (rh->fSectorMask & fSectorMask));
- if (lh->fSectorMask & fSectorMask) {
- ltOrder = (int) lh->orderable(this);
+ SkASSERT((lh.fSectorMask & fSectorMask) || (rh.fSectorMask & fSectorMask));
+ if (lh.fSectorMask & fSectorMask) {
+ ltOrder = (int) lh.orderable(*this);
} else {
- int ltGap = (fSectorStart - lh->fSectorStart + 32) & 0x1f;
+ int ltGap = (fSectorStart - lh.fSectorStart + 32) & 0x1f;
ltOrder = ltGap > 20 ? 0 : ltGap > 11 ? -1 : 1;
}
int trOrder;
- if (rh->fSectorMask & fSectorMask) {
+ if (rh.fSectorMask & fSectorMask) {
trOrder = (int) orderable(rh);
} else {
- int trGap = (rh->fSectorStart - fSectorStart + 32) & 0x1f;
+ int trGap = (rh.fSectorStart - fSectorStart + 32) & 0x1f;
trOrder = trGap > 20 ? 0 : trGap > 11 ? -1 : 1;
}
if (lrOrder >= 0 && ltOrder >= 0 && trOrder >= 0) {
@@ -144,20 +145,20 @@ bool SkOpAngle::after(SkOpAngle* test) {
if (ltOrder == 0 && lrOrder == 0) {
SkASSERT(trOrder < 0);
// FIXME : once this is verified to work, remove one opposite angle call
- SkDEBUGCODE(bool lrOpposite = lh->oppositePlanes(rh));
- bool ltOpposite = lh->oppositePlanes(this);
+ SkDEBUGCODE(bool lrOpposite = lh.oppositePlanes(rh));
+ bool ltOpposite = lh.oppositePlanes(*this);
SkASSERT(lrOpposite != ltOpposite);
return COMPARE_RESULT(8, ltOpposite);
} else if (ltOrder == 1 && trOrder == 0) {
SkASSERT(lrOrder < 0);
- SkDEBUGCODE(bool ltOpposite = lh->oppositePlanes(this));
+ 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);
SkDEBUGCODE(bool trOpposite = oppositePlanes(rh));
- bool lrOpposite = lh->oppositePlanes(rh);
+ bool lrOpposite = lh.oppositePlanes(rh);
SkASSERT(lrOpposite != trOpposite);
return COMPARE_RESULT(10, lrOpposite);
}
@@ -172,50 +173,77 @@ bool SkOpAngle::after(SkOpAngle* test) {
// given a line, see if the opposite curve's convex hull is all on one side
// returns -1=not on one side 0=this CW of test 1=this CCW of test
-int SkOpAngle::allOnOneSide(const SkOpAngle* test) {
+int SkOpAngle::allOnOneSide(const SkOpAngle& test) const {
SkASSERT(!fIsCurve);
- SkASSERT(test->fIsCurve);
- const SkDPoint& origin = test->fCurvePart[0];
+ 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;
+ if (fSegment->verb() == SkPath::kLine_Verb) {
+ const SkPoint* linePts = fSegment->pts();
+ int lineStart = fStart < fEnd ? 0 : 1;
line = linePts[lineStart ^ 1] - linePts[lineStart];
} else {
SkPoint shortPts[2] = { fCurvePart[0].asSkPoint(), fCurvePart[1].asSkPoint() };
line = shortPts[1] - shortPts[0];
}
float crosses[3];
- SkPath::Verb testVerb = test->segment()->verb();
+ SkPath::Verb testVerb = test.fSegment->verb();
int iMax = SkPathOpsVerbToPoints(testVerb);
// SkASSERT(origin == test.fCurveHalf[0]);
- const SkDCubic& testCurve = test->fCurvePart;
- for (int index = 1; index <= iMax; ++index) {
- float xy1 = (float) (line.fX * (testCurve[index].fY - origin.fY));
- float xy2 = (float) (line.fY * (testCurve[index].fX - origin.fX));
- crosses[index - 1] = AlmostEqualUlps(xy1, xy2) ? 0 : xy1 - xy2;
- }
- if (crosses[0] * crosses[1] < 0) {
- return -1;
- }
- if (SkPath::kCubic_Verb == testVerb) {
- if (crosses[0] * crosses[2] < 0 || crosses[1] * crosses[2] < 0) {
+ const SkDCubic& testCurve = test.fCurvePart;
+// do {
+ for (int index = 1; index <= iMax; ++index) {
+ float xy1 = (float) (line.fX * (testCurve[index].fY - origin.fY));
+ float xy2 = (float) (line.fY * (testCurve[index].fX - origin.fX));
+ crosses[index - 1] = AlmostEqualUlps(xy1, xy2) ? 0 : xy1 - xy2;
+ }
+ if (crosses[0] * crosses[1] < 0) {
return -1;
}
- }
- if (crosses[0]) {
- return crosses[0] < 0;
- }
- if (crosses[1]) {
- return crosses[1] < 0;
- }
- if (SkPath::kCubic_Verb == testVerb && crosses[2]) {
- return crosses[2] < 0;
- }
+ if (SkPath::kCubic_Verb == testVerb) {
+ if (crosses[0] * crosses[2] < 0 || crosses[1] * crosses[2] < 0) {
+ return -1;
+ }
+ }
+ if (crosses[0]) {
+ return crosses[0] < 0;
+ }
+ if (crosses[1]) {
+ return crosses[1] < 0;
+ }
+ if (SkPath::kCubic_Verb == testVerb && crosses[2]) {
+ return crosses[2] < 0;
+ }
fUnorderable = true;
return -1;
}
+bool SkOpAngle::calcSlop(double x, double y, double rx, double ry, bool* result) const {
+ double absX = fabs(x);
+ double absY = fabs(y);
+ double length = absX < absY ? absX / 2 + absY : absX + absY / 2;
+ int exponent;
+ (void) frexp(length, &exponent);
+ double epsilon = ldexp(FLT_EPSILON, exponent);
+ SkPath::Verb verb = fSegment->verb();
+ SkASSERT(verb == SkPath::kQuad_Verb || verb == SkPath::kCubic_Verb);
+ // FIXME: the quad and cubic factors are made up ; determine actual values
+ double slop = verb == SkPath::kQuad_Verb ? 4 * epsilon : 512 * epsilon;
+ double xSlop = slop;
+ double ySlop = x * y < 0 ? -xSlop : xSlop; // OPTIMIZATION: use copysign / _copysign ?
+ double x1 = x - xSlop;
+ double y1 = y + ySlop;
+ double x_ry1 = x1 * ry;
+ double rx_y1 = rx * y1;
+ *result = x_ry1 < rx_y1;
+ double x2 = x + xSlop;
+ double y2 = y - ySlop;
+ double x_ry2 = x2 * ry;
+ double rx_y2 = rx * y2;
+ bool less2 = x_ry2 < rx_y2;
+ return *result == less2;
+}
+
bool SkOpAngle::checkCrossesZero() const {
int start = SkTMin(fSectorStart, fSectorEnd);
int end = SkTMax(fSectorStart, fSectorEnd);
@@ -223,94 +251,31 @@ bool SkOpAngle::checkCrossesZero() const {
return crossesZero;
}
-// 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
-*/
-void SkOpAngle::checkNearCoincidence() {
- SkOpAngle* test = this;
- do {
- SkOpSegment* testSegment = test->segment();
- double testStartT = test->start()->t();
- SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
- double testEndT = test->end()->t();
- SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
- double testLenSq = testStartPt.distanceSquared(testEndPt);
- if (0) {
- SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
- }
- double testMidT = (testStartT + testEndT) / 2;
- SkOpAngle* next = test;
- while ((next = next->fNext) != this) {
- SkOpSegment* nextSegment = next->segment();
- double testMidDistSq = testSegment->distSq(testMidT, next);
- double testEndDistSq = testSegment->distSq(testEndT, next);
- double nextStartT = next->start()->t();
- SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
- double distSq = testStartPt.distanceSquared(nextStartPt);
- double nextEndT = next->end()->t();
- double nextMidT = (nextStartT + nextEndT) / 2;
- double nextMidDistSq = nextSegment->distSq(nextMidT, test);
- double nextEndDistSq = nextSegment->distSq(nextEndT, test);
- if (0) {
- SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
- testSegment->debugID(), nextSegment->debugID());
- SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
- SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
- SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
- SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
- SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
- double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
- SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
- SkDebugf("\n");
- }
- }
- test = test->fNext;
- } while (test->fNext != this);
-}
-
-bool SkOpAngle::checkParallel(SkOpAngle* rh) {
+bool SkOpAngle::checkParallel(const SkOpAngle& rh) const {
SkDVector scratch[2];
const SkDVector* sweep, * tweep;
- if (!this->fUnorderedSweep) {
- sweep = this->fSweep;
+ if (!fUnorderedSweep) {
+ sweep = fSweep;
} else {
- scratch[0] = this->fCurvePart[1] - this->fCurvePart[0];
+ scratch[0] = fCurvePart[1] - fCurvePart[0];
sweep = &scratch[0];
}
- if (!rh->fUnorderedSweep) {
- tweep = rh->fSweep;
+ if (!rh.fUnorderedSweep) {
+ tweep = rh.fSweep;
} else {
- scratch[1] = rh->fCurvePart[1] - rh->fCurvePart[0];
+ scratch[1] = rh.fCurvePart[1] - rh.fCurvePart[0];
tweep = &scratch[1];
}
double s0xt0 = sweep->crossCheck(*tweep);
if (tangentsDiverge(rh, s0xt0)) {
return s0xt0 < 0;
}
- // 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 (this->endToSide(rh, &inside)) {
- return inside;
- }
- if (rh->endToSide(this, &inside)) {
- return !inside;
- }
- if (this->midToSide(rh, &inside)) {
- return inside;
- }
- if (rh->midToSide(this, &inside)) {
- return !inside;
- }
- // compute the cross check from the mid T values (last resort)
- SkDVector m0 = segment()->dPtAtT(this->midT()) - this->fCurvePart[0];
- SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fCurvePart[0];
+ SkDVector m0 = fSegment->dPtAtT(midT()) - fCurvePart[0];
+ SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[0];
double m0xm1 = m0.crossCheck(m1);
if (m0xm1 == 0) {
- this->fUnorderable = true;
- rh->fUnorderable = true;
+ fUnorderable = true;
+ rh.fUnorderable = true;
return true;
}
return m0xm1 < 0;
@@ -323,51 +288,48 @@ bool SkOpAngle::computeSector() {
if (fComputedSector) {
return !fUnorderable;
}
+// SkASSERT(fSegment->verb() != SkPath::kLine_Verb && small());
fComputedSector = true;
- bool stepUp = fStart->t() < fEnd->t();
- const SkOpSpanBase* checkEnd = fEnd;
- if (checkEnd->final() && stepUp) {
- fUnorderable = true;
- return false;
- }
+ int step = fStart < fEnd ? 1 : -1;
+ int limit = step > 0 ? fSegment->count() : -1;
+ int checkEnd = fEnd;
do {
// advance end
- const SkOpSegment* other = checkEnd->segment();
- const SkOpSpanBase* oSpan = other->head();
- do {
- if (oSpan->segment() != segment()) {
+ const SkOpSpan& span = fSegment->span(checkEnd);
+ const SkOpSegment* other = span.fOther;
+ int oCount = other->count();
+ for (int oIndex = 0; oIndex < oCount; ++oIndex) {
+ const SkOpSpan& oSpan = other->span(oIndex);
+ if (oSpan.fOther != fSegment) {
continue;
}
- if (oSpan == checkEnd) {
+ if (oSpan.fOtherIndex == checkEnd) {
continue;
}
- if (!approximately_equal(oSpan->t(), checkEnd->t())) {
+ if (!approximately_equal(oSpan.fOtherT, span.fT)) {
continue;
}
goto recomputeSector;
- } while (!oSpan->final() && (oSpan = oSpan->upCast()->next()));
- checkEnd = stepUp ? !checkEnd->final()
- ? checkEnd->upCast()->next() : NULL
- : checkEnd->prev();
- } while (checkEnd);
+ }
+ checkEnd += step;
+ } while (checkEnd != limit);
recomputeSector:
- SkOpSpanBase* computedEnd = stepUp ? checkEnd ? checkEnd->prev() : fEnd->segment()->head()
- : checkEnd ? checkEnd->upCast()->next() : fEnd->segment()->tail();
- if (checkEnd == fEnd || computedEnd == fEnd || computedEnd == fStart) {
+ if (checkEnd == fEnd || checkEnd - step == fEnd) {
fUnorderable = true;
return false;
}
- SkOpSpanBase* saveEnd = fEnd;
- fComputedEnd = fEnd = computedEnd;
+ int saveEnd = fEnd;
+ fComputedEnd = fEnd = checkEnd - step;
setSpans();
setSector();
fEnd = saveEnd;
return !fUnorderable;
}
-int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) const {
- const SkDVector* sweep = this->fSweep;
- const SkDVector* tweep = rh->fSweep;
+// returns -1 if overlaps 0 if no overlap cw 1 if no overlap ccw
+int SkOpAngle::convexHullOverlaps(const SkOpAngle& rh) const {
+ const SkDVector* sweep = fSweep;
+ const SkDVector* tweep = rh.fSweep;
double s0xs1 = sweep[0].crossCheck(sweep[1]);
double s0xt0 = sweep[0].crossCheck(tweep[0]);
double s1xt0 = sweep[1].crossCheck(tweep[0]);
@@ -397,8 +359,8 @@ int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) const {
// if the outside sweeps are greater than 180 degress:
// first assume the inital tangents are the ordering
// if the midpoint direction matches the inital order, that is enough
- SkDVector m0 = this->segment()->dPtAtT(this->midT()) - this->fCurvePart[0];
- SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fCurvePart[0];
+ SkDVector m0 = fSegment->dPtAtT(midT()) - fCurvePart[0];
+ SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[0];
double m0xm1 = m0.crossCheck(m1);
if (s0xt0 > 0 && m0xm1 > 0) {
return 0;
@@ -432,30 +394,34 @@ double SkOpAngle::distEndRatio(double dist) const {
return sqrt(longest) / dist;
}
-bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
- SkPath::Verb lVerb = this->segment()->verb();
- SkPath::Verb rVerb = rh->segment()->verb();
+bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const {
+ SkPath::Verb lVerb = fSegment->verb();
+ SkPath::Verb rVerb = rh.fSegment->verb();
int lPts = SkPathOpsVerbToPoints(lVerb);
int rPts = SkPathOpsVerbToPoints(rVerb);
- SkDLine rays[] = {{{this->fCurvePart[0], rh->fCurvePart[rPts]}},
- {{this->fCurvePart[0], this->fCurvePart[lPts]}}};
+ SkDLine rays[] = {{{fCurvePart[0], rh.fCurvePart[rPts]}},
+ {{fCurvePart[0], fCurvePart[lPts]}}};
if (rays[0][1] == rays[1][1]) {
return checkParallel(rh);
}
double smallTs[2] = {-1, -1};
bool limited[2] = {false, false};
for (int index = 0; index < 2; ++index) {
+ const SkOpSegment& segment = index ? *rh.fSegment : *fSegment;
+ SkIntersections 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;
}
- const SkOpSegment& segment = index ? *rh->segment() : *this->segment();
- SkIntersections i;
- (*CurveIntersectRay[cPts])(segment.pts(), rays[index], &i);
- double tStart = index ? rh->fStart->t() : this->fStart->t();
- double tEnd = index ? rh->fComputedEnd->t() : this->fComputedEnd->t();
- bool testAscends = tStart < (index ? rh->fComputedEnd->t() : this->fComputedEnd->t());
+// SkASSERT(i.used() >= 1);
+// if (i.used() <= 1) {
+// continue;
+// }
+ double tStart = segment.t(index ? rh.fStart : fStart);
+ double tEnd = segment.t(index ? rh.fComputedEnd : fComputedEnd);
+ bool testAscends = index ? rh.fStart < rh.fComputedEnd : fStart < fComputedEnd;
double t = testAscends ? 0 : 1;
for (int idx2 = 0; idx2 < i.used(); ++idx2) {
double testT = i[0][idx2];
@@ -469,6 +435,29 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
limited[index] = approximately_equal_orderable(t, tEnd);
}
}
+#if 0
+ if (smallTs[0] < 0 && smallTs[1] < 0) { // if neither ray intersects, do endpoint sort
+ double m0xm1 = 0;
+ if (lVerb == SkPath::kLine_Verb) {
+ SkASSERT(rVerb != SkPath::kLine_Verb);
+ SkDVector m0 = rays[1][1] - fCurvePart[0];
+ SkDPoint endPt;
+ endPt.set(rh.fSegment->pts()[rh.fStart < rh.fEnd ? rPts : 0]);
+ SkDVector m1 = endPt - fCurvePart[0];
+ m0xm1 = m0.crossCheck(m1);
+ }
+ if (rVerb == SkPath::kLine_Verb) {
+ SkDPoint endPt;
+ endPt.set(fSegment->pts()[fStart < fEnd ? lPts : 0]);
+ SkDVector m0 = endPt - fCurvePart[0];
+ SkDVector m1 = rays[0][1] - fCurvePart[0];
+ m0xm1 = m0.crossCheck(m1);
+ }
+ if (m0xm1 != 0) {
+ return m0xm1 < 0;
+ }
+ }
+#endif
bool sRayLonger = false;
SkDVector sCept = {0, 0};
double sCeptT = -1;
@@ -478,7 +467,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
if (smallTs[index] < 0) {
continue;
}
- const SkOpSegment& segment = index ? *rh->segment() : *this->segment();
+ const SkOpSegment& segment = index ? *rh.fSegment : *fSegment;
const SkDPoint& dPt = segment.dPtAtT(smallTs[index]);
SkDVector cept = dPt - rays[index][0];
// If this point is on the curve, it should have been detected earlier by ordinary
@@ -509,7 +498,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
double minX, minY, maxX, maxY;
minX = minY = SK_ScalarInfinity;
maxX = maxY = -SK_ScalarInfinity;
- const SkDCubic& curve = index ? rh->fCurvePart : this->fCurvePart;
+ const SkDCubic& curve = index ? rh.fCurvePart : fCurvePart;
int ptCount = index ? rPts : lPts;
for (int idx2 = 0; idx2 <= ptCount; ++idx2) {
minX = SkTMin(minX, curve[idx2].fX);
@@ -519,7 +508,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
}
double maxWidth = SkTMax(maxX - minX, maxY - minY);
delta /= maxWidth;
- if (delta > 1e-3 && (useIntersect ^= true)) { // FIXME: move this magic number
+ if (delta > 1e-4 && (useIntersect ^= true)) { // FIXME: move this magic number
sRayLonger = rayLonger;
sCept = cept;
sCeptT = smallTs[index];
@@ -527,9 +516,9 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
}
}
if (useIntersect) {
- const SkDCubic& curve = sIndex ? rh->fCurvePart : this->fCurvePart;
- const SkOpSegment& segment = sIndex ? *rh->segment() : *this->segment();
- double tStart = sIndex ? rh->fStart->t() : fStart->t();
+ const SkDCubic& curve = sIndex ? rh.fCurvePart : fCurvePart;
+ const SkOpSegment& segment = sIndex ? *rh.fSegment : *fSegment;
+ double tStart = segment.t(sIndex ? rh.fStart : fStart);
SkDVector mid = segment.dPtAtT(tStart + (sCeptT - tStart) / 2) - curve[0];
double septDir = mid.crossCheck(sCept);
if (!septDir) {
@@ -541,65 +530,12 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
}
}
-bool SkOpAngle::endToSide(const SkOpAngle* rh, bool* inside) const {
- const SkOpSegment* segment = this->segment();
- SkPath::Verb verb = segment->verb();
- int pts = SkPathOpsVerbToPoints(verb);
- SkDLine rayEnd;
- rayEnd[0].set(this->fEnd->pt());
- rayEnd[1] = rayEnd[0];
- SkDVector slopeAtEnd = (*CurveDSlopeAtT[pts])(segment->pts(), this->fEnd->t());
- rayEnd[1].fX += slopeAtEnd.fY;
- rayEnd[1].fY -= slopeAtEnd.fX;
- SkIntersections iEnd;
- const SkOpSegment* oppSegment = rh->segment();
- SkPath::Verb oppVerb = oppSegment->verb();
- int oppPts = SkPathOpsVerbToPoints(oppVerb);
- (*CurveIntersectRay[oppPts])(oppSegment->pts(), rayEnd, &iEnd);
- double endDist;
- int closestEnd = iEnd.closestTo(rh->fStart->t(), rh->fEnd->t(), rayEnd[0], &endDist);
- if (closestEnd < 0) {
- return false;
- }
- if (!endDist) {
- return false;
- }
- SkDPoint start;
- start.set(this->fStart->pt());
- // OPTIMIZATION: multiple times in the code we find the max scalar
- double minX, minY, maxX, maxY;
- minX = minY = SK_ScalarInfinity;
- maxX = maxY = -SK_ScalarInfinity;
- const SkDCubic& curve = rh->fCurvePart;
- for (int idx2 = 0; idx2 <= oppPts; ++idx2) {
- minX = SkTMin(minX, curve[idx2].fX);
- minY = SkTMin(minY, curve[idx2].fY);
- maxX = SkTMax(maxX, curve[idx2].fX);
- maxY = SkTMax(maxY, curve[idx2].fY);
- }
- double maxWidth = SkTMax(maxX - minX, maxY - minY);
- endDist /= maxWidth;
- if (endDist < 5e-11) { // 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);
- if (!dir) {
- return false;
- }
- *inside = dir < 0;
- return true;
-}
-
// Most of the time, the first one can be found trivially by detecting the smallest sector value.
// If all angles have the same sector value, actual sorting is required.
-SkOpAngle* SkOpAngle::findFirst() {
- SkOpAngle* best = this;
+const SkOpAngle* SkOpAngle::findFirst() const {
+ const SkOpAngle* best = this;
int bestStart = SkTMin(fSectorStart, fSectorEnd);
- SkOpAngle* angle = this;
+ const SkOpAngle* angle = this;
while ((angle = angle->fNext) != this) {
int angleEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd);
if (angleEnd < bestStart) {
@@ -612,7 +548,7 @@ SkOpAngle* SkOpAngle::findFirst() {
}
}
// back up to the first possible angle
- SkOpAngle* firstBest = best;
+ const SkOpAngle* firstBest = best;
angle = best;
int bestEnd = SkTMax(best->fSectorStart, best->fSectorEnd);
while ((angle = angle->previous()) != firstBest) {
@@ -636,7 +572,7 @@ SkOpAngle* SkOpAngle::findFirst() {
if (angle->fStop) {
return firstBest;
}
- bool orderable = best->orderable(angle); // note: may return an unorderable angle
+ bool orderable = best->orderable(*angle); // note: may return an unorderable angle
if (orderable == 0) {
return angle;
}
@@ -703,11 +639,6 @@ int SkOpAngle::findSector(SkPath::Verb verb, double x, double y) const {
return sector;
}
-SkOpGlobalState* SkOpAngle::globalState() const {
- return this->segment()->globalState();
-}
-
-
// OPTIMIZE: if this loops to only one other angle, after first compare fails, insert on other side
// OPTIMIZE: return where insertion succeeded. Then, start next insertion on opposite side
void SkOpAngle::insert(SkOpAngle* angle) {
@@ -731,6 +662,9 @@ void SkOpAngle::insert(SkOpAngle* angle) {
}
SkOpAngle* next = fNext;
if (next->fNext == this) {
+ if (angle->overlap(*this)) { // angles are essentially coincident
+ return;
+ }
if (singleton || angle->after(this)) {
this->fNext = angle;
angle->fNext = next;
@@ -744,6 +678,9 @@ void SkOpAngle::insert(SkOpAngle* angle) {
SkOpAngle* last = this;
do {
SkASSERT(last->fNext == next);
+ if (angle->overlap(*last) || angle->overlap(*next)) {
+ return;
+ }
if (angle->after(last)) {
last->fNext = angle;
angle->fNext = next;
@@ -752,49 +689,48 @@ void SkOpAngle::insert(SkOpAngle* angle) {
}
last = next;
next = next->fNext;
- if (last == this) {
- if (next->fUnorderable) {
- fUnorderable = true;
- } else {
- globalState()->setAngleCoincidence();
- this->fNext = angle;
- angle->fNext = next;
- angle->fCheckCoincidence = true;
- }
+ if (last == this && next->fUnorderable) {
+ fUnorderable = true;
return;
}
+ SkASSERT(last != this);
} while (true);
}
-SkOpSpanBase* SkOpAngle::lastMarked() const {
+bool SkOpAngle::isHorizontal() const {
+ return !fIsCurve && fSweep[0].fY == 0;
+}
+
+SkOpSpan* SkOpAngle::lastMarked() const {
if (fLastMarked) {
- if (fLastMarked->chased()) {
+ if (fLastMarked->fChased) {
return NULL;
}
- fLastMarked->setChased(true);
+ fLastMarked->fChased = true;
}
return fLastMarked;
}
-bool SkOpAngle::loopContains(const SkOpAngle* angle) const {
+bool SkOpAngle::loopContains(const SkOpAngle& test) const {
if (!fNext) {
return false;
}
const SkOpAngle* first = this;
const SkOpAngle* loop = this;
- const SkOpSegment* tSegment = angle->fStart->segment();
- double tStart = angle->fStart->t();
- double tEnd = angle->fEnd->t();
+ const SkOpSegment* tSegment = test.fSegment;
+ double tStart = tSegment->span(test.fStart).fT;
+ double tEnd = tSegment->span(test.fEnd).fT;
do {
- const SkOpSegment* lSegment = loop->fStart->segment();
+ const SkOpSegment* lSegment = loop->fSegment;
+ // FIXME : use precisely_equal ? or compare points exactly ?
if (lSegment != tSegment) {
continue;
}
- double lStart = loop->fStart->t();
+ double lStart = lSegment->span(loop->fStart).fT;
if (lStart != tEnd) {
continue;
}
- double lEnd = loop->fEnd->t();
+ double lEnd = lSegment->span(loop->fEnd).fT;
if (lEnd == tStart) {
return true;
}
@@ -846,65 +782,39 @@ bool SkOpAngle::merge(SkOpAngle* angle) {
working = next;
} while (working != angle);
// it's likely that a pair of the angles are unorderable
+#if 0 && DEBUG_ANGLE
+ SkOpAngle* last = angle;
+ working = angle->fNext;
+ do {
+ SkASSERT(last->fNext == working);
+ last->fNext = working->fNext;
+ SkASSERT(working->after(last));
+ last->fNext = working;
+ last = working;
+ working = working->fNext;
+ } while (last != angle);
+#endif
debugValidateNext();
return true;
}
double SkOpAngle::midT() const {
- return (fStart->t() + fEnd->t()) / 2;
-}
-
-bool SkOpAngle::midToSide(const SkOpAngle* rh, bool* inside) const {
- const SkOpSegment* segment = this->segment();
- SkPath::Verb verb = segment->verb();
- int pts = SkPathOpsVerbToPoints(verb);
- const SkPoint& startPt = this->fStart->pt();
- const SkPoint& endPt = this->fEnd->pt();
- SkDPoint dStartPt;
- dStartPt.set(startPt);
- SkDLine rayMid;
- rayMid[0].fX = (startPt.fX + endPt.fX) / 2;
- rayMid[0].fY = (startPt.fY + endPt.fY) / 2;
- rayMid[1].fX = rayMid[0].fX + (endPt.fY - startPt.fY);
- rayMid[1].fY = rayMid[0].fY - (endPt.fX - startPt.fX);
- SkIntersections iMid;
- (*CurveIntersectRay[pts])(segment->pts(), rayMid, &iMid);
- int iOutside = iMid.mostOutside(this->fStart->t(), this->fEnd->t(), dStartPt);
- if (iOutside < 0) {
- return false;
- }
- const SkOpSegment* oppSegment = rh->segment();
- SkPath::Verb oppVerb = oppSegment->verb();
- int oppPts = SkPathOpsVerbToPoints(oppVerb);
- SkIntersections oppMid;
- (*CurveIntersectRay[oppPts])(oppSegment->pts(), rayMid, &oppMid);
- int oppOutside = oppMid.mostOutside(rh->fStart->t(), rh->fEnd->t(), dStartPt);
- if (oppOutside < 0) {
- return false;
- }
- SkDVector iSide = iMid.pt(iOutside) - dStartPt;
- SkDVector oppSide = oppMid.pt(oppOutside) - dStartPt;
- double dir = iSide.crossCheck(oppSide);
- if (!dir) {
- return false;
- }
- *inside = dir < 0;
- return true;
+ return (fSegment->t(fStart) + fSegment->t(fEnd)) / 2;
}
-bool SkOpAngle::oppositePlanes(const SkOpAngle* rh) const {
- int startSpan = abs(rh->fSectorStart - fSectorStart);
+bool SkOpAngle::oppositePlanes(const SkOpAngle& rh) const {
+ int startSpan = abs(rh.fSectorStart - fSectorStart);
return startSpan >= 8;
}
-bool SkOpAngle::orderable(SkOpAngle* rh) {
+bool SkOpAngle::orderable(const SkOpAngle& rh) const {
int result;
if (!fIsCurve) {
- if (!rh->fIsCurve) {
+ if (!rh.fIsCurve) {
double leftX = fTangentHalf.dx();
double leftY = fTangentHalf.dy();
- double rightX = rh->fTangentHalf.dx();
- double rightY = rh->fTangentHalf.dy();
+ double rightX = rh.fTangentHalf.dx();
+ double rightY = rh.fTangentHalf.dy();
double x_ry = leftX * rightY;
double rx_y = rightX * leftY;
if (x_ry == rx_y) {
@@ -919,14 +829,14 @@ bool SkOpAngle::orderable(SkOpAngle* rh) {
if ((result = allOnOneSide(rh)) >= 0) {
return result;
}
- if (fUnorderable || approximately_zero(rh->fSide)) {
+ if (fUnorderable || approximately_zero(rh.fSide)) {
goto unorderable;
}
- } else if (!rh->fIsCurve) {
- if ((result = rh->allOnOneSide(this)) >= 0) {
+ } else if (!rh.fIsCurve) {
+ if ((result = rh.allOnOneSide(*this)) >= 0) {
return !result;
}
- if (rh->fUnorderable || approximately_zero(fSide)) {
+ if (rh.fUnorderable || approximately_zero(fSide)) {
goto unorderable;
}
}
@@ -936,10 +846,27 @@ bool SkOpAngle::orderable(SkOpAngle* rh) {
return endsIntersect(rh);
unorderable:
fUnorderable = true;
- rh->fUnorderable = true;
+ rh.fUnorderable = true;
return true;
}
+bool SkOpAngle::overlap(const SkOpAngle& other) const {
+ int min = SkTMin(fStart, fEnd);
+ const SkOpSpan& span = fSegment->span(min);
+ const SkOpSegment* oSeg = other.fSegment;
+ int oMin = SkTMin(other.fStart, other.fEnd);
+ const SkOpSpan& oSpan = oSeg->span(oMin);
+ if (!span.fSmall && !oSpan.fSmall) {
+ return false;
+ }
+ if (fSegment->span(fStart).fPt != oSeg->span(other.fStart).fPt) {
+ return false;
+ }
+ // see if small span is contained by opposite span
+ return span.fSmall ? oSeg->containsPt(fSegment->span(fEnd).fPt, other.fEnd, other.fStart)
+ : fSegment->containsPt(oSeg->span(other.fEnd).fPt, fEnd, fStart);
+}
+
// OPTIMIZE: if this shows up in a profile, add a previous pointer
// as is, this should be rarely called
SkOpAngle* SkOpAngle::previous() const {
@@ -953,32 +880,26 @@ SkOpAngle* SkOpAngle::previous() const {
} while (true);
}
-SkOpSegment* SkOpAngle::segment() const {
- return fStart->segment();
-}
-
-void SkOpAngle::set(SkOpSpanBase* start, SkOpSpanBase* end) {
+void SkOpAngle::set(const SkOpSegment* segment, int start, int end) {
+ fSegment = segment;
fStart = start;
fComputedEnd = fEnd = end;
- SkASSERT(start != end);
fNext = NULL;
- fComputeSector = fComputedSector = fCheckCoincidence = false;
+ fComputeSector = fComputedSector = false;
fStop = false;
setSpans();
setSector();
- PATH_OPS_DEBUG_CODE(fID = start->globalState()->nextAngleID());
}
void SkOpAngle::setCurveHullSweep() {
fUnorderedSweep = false;
fSweep[0] = fCurvePart[1] - fCurvePart[0];
- const SkOpSegment* segment = fStart->segment();
- if (SkPath::kLine_Verb == segment->verb()) {
+ if (SkPath::kLine_Verb == fSegment->verb()) {
fSweep[1] = fSweep[0];
return;
}
fSweep[1] = fCurvePart[2] - fCurvePart[0];
- if (SkPath::kCubic_Verb != segment->verb()) {
+ if (SkPath::kCubic_Verb != fSegment->verb()) {
if (!fSweep[0].fX && !fSweep[0].fY) {
fSweep[0] = fSweep[1];
}
@@ -1012,16 +933,64 @@ void SkOpAngle::setCurveHullSweep() {
fSweep[1] = thirdSweep;
}
+void SkOpAngle::setSector() {
+ SkPath::Verb verb = fSegment->verb();
+ if (SkPath::kLine_Verb != verb && small()) {
+ goto deferTilLater;
+ }
+ fSectorStart = findSector(verb, fSweep[0].fX, fSweep[0].fY);
+ if (fSectorStart < 0) {
+ goto deferTilLater;
+ }
+ if (!fIsCurve) { // if it's a line or line-like, note that both sectors are the same
+ SkASSERT(fSectorStart >= 0);
+ fSectorEnd = fSectorStart;
+ fSectorMask = 1 << fSectorStart;
+ return;
+ }
+ SkASSERT(SkPath::kLine_Verb != verb);
+ fSectorEnd = findSector(verb, fSweep[1].fX, fSweep[1].fY);
+ if (fSectorEnd < 0) {
+deferTilLater:
+ fSectorStart = fSectorEnd = -1;
+ fSectorMask = 0;
+ fComputeSector = true; // can't determine sector until segment length can be found
+ return;
+ }
+ if (fSectorEnd == fSectorStart) {
+ SkASSERT((fSectorStart & 3) != 3); // if the sector has no span, it can't be an exact angle
+ fSectorMask = 1 << fSectorStart;
+ return;
+ }
+ bool crossesZero = checkCrossesZero();
+ int start = SkTMin(fSectorStart, fSectorEnd);
+ bool curveBendsCCW = (fSectorStart == start) ^ crossesZero;
+ // bump the start and end of the sector span if they are on exact compass points
+ if ((fSectorStart & 3) == 3) {
+ fSectorStart = (fSectorStart + (curveBendsCCW ? 1 : 31)) & 0x1f;
+ }
+ if ((fSectorEnd & 3) == 3) {
+ fSectorEnd = (fSectorEnd + (curveBendsCCW ? 31 : 1)) & 0x1f;
+ }
+ crossesZero = checkCrossesZero();
+ start = SkTMin(fSectorStart, fSectorEnd);
+ int end = SkTMax(fSectorStart, fSectorEnd);
+ if (!crossesZero) {
+ fSectorMask = (unsigned) -1 >> (31 - end + start) << start;
+ } else {
+ fSectorMask = (unsigned) -1 >> (31 - start) | (-1 << end);
+ }
+}
+
void SkOpAngle::setSpans() {
- fUnorderable = false;
+ fUnorderable = fSegment->isTiny(this);
fLastMarked = NULL;
- const SkOpSegment* segment = fStart->segment();
- const SkPoint* pts = segment->pts();
+ const SkPoint* pts = fSegment->pts();
SkDEBUGCODE(fCurvePart[2].fX = fCurvePart[2].fY = fCurvePart[3].fX = fCurvePart[3].fY
= SK_ScalarNaN);
- segment->subDivide(fStart, fEnd, &fCurvePart);
+ fSegment->subDivide(fStart, fEnd, &fCurvePart);
setCurveHullSweep();
- const SkPath::Verb verb = segment->verb();
+ const SkPath::Verb verb = fSegment->verb();
if (verb != SkPath::kLine_Verb
&& !(fIsCurve = fSweep[0].crossCheck(fSweep[1]) != 0)) {
SkDLine lineHalf;
@@ -1033,9 +1002,9 @@ void SkOpAngle::setSpans() {
switch (verb) {
case SkPath::kLine_Verb: {
SkASSERT(fStart != fEnd);
- const SkPoint& cP1 = pts[fStart->t() < fEnd->t()];
+ const SkPoint& cP1 = pts[fStart < fEnd];
SkDLine lineHalf;
- lineHalf[0].set(fStart->pt());
+ lineHalf[0].set(fSegment->span(fStart).fPt);
lineHalf[1].set(cP1);
fTangentHalf.lineEndPoints(lineHalf);
fSide = 0;
@@ -1054,8 +1023,8 @@ void SkOpAngle::setSpans() {
double testTs[4];
// OPTIMIZATION: keep inflections precomputed with cubic segment?
int testCount = SkDCubic::FindInflections(pts, testTs);
- double startT = fStart->t();
- double endT = fEnd->t();
+ double startT = fSegment->t(fStart);
+ double endT = fSegment->t(fEnd);
double limitT = endT;
int index;
for (index = 0; index < testCount; ++index) {
@@ -1095,63 +1064,19 @@ void SkOpAngle::setSpans() {
}
}
-void SkOpAngle::setSector() {
- const SkOpSegment* segment = fStart->segment();
- SkPath::Verb verb = segment->verb();
- fSectorStart = this->findSector(verb, fSweep[0].fX, fSweep[0].fY);
- if (fSectorStart < 0) {
- goto deferTilLater;
- }
- if (!fIsCurve) { // if it's a line or line-like, note that both sectors are the same
- SkASSERT(fSectorStart >= 0);
- fSectorEnd = fSectorStart;
- fSectorMask = 1 << fSectorStart;
- return;
- }
- SkASSERT(SkPath::kLine_Verb != verb);
- fSectorEnd = this->findSector(verb, fSweep[1].fX, fSweep[1].fY);
- if (fSectorEnd < 0) {
-deferTilLater:
- fSectorStart = fSectorEnd = -1;
- fSectorMask = 0;
- fComputeSector = true; // can't determine sector until segment length can be found
- return;
- }
- if (fSectorEnd == fSectorStart
- && (fSectorStart & 3) != 3) { // if the sector has no span, it can't be an exact angle
- fSectorMask = 1 << fSectorStart;
- return;
- }
- bool crossesZero = this->checkCrossesZero();
- int start = SkTMin(fSectorStart, fSectorEnd);
- bool curveBendsCCW = (fSectorStart == start) ^ crossesZero;
- // bump the start and end of the sector span if they are on exact compass points
- if ((fSectorStart & 3) == 3) {
- fSectorStart = (fSectorStart + (curveBendsCCW ? 1 : 31)) & 0x1f;
- }
- if ((fSectorEnd & 3) == 3) {
- fSectorEnd = (fSectorEnd + (curveBendsCCW ? 31 : 1)) & 0x1f;
- }
- crossesZero = this->checkCrossesZero();
- start = SkTMin(fSectorStart, fSectorEnd);
- int end = SkTMax(fSectorStart, fSectorEnd);
- if (!crossesZero) {
- fSectorMask = (unsigned) -1 >> (31 - end + start) << start;
- } else {
- fSectorMask = (unsigned) -1 >> (31 - start) | (-1 << end);
+bool SkOpAngle::small() const {
+ int min = SkMin32(fStart, fEnd);
+ int max = SkMax32(fStart, fEnd);
+ for (int index = min; index < max; ++index) {
+ const SkOpSpan& mSpan = fSegment->span(index);
+ if (!mSpan.fSmall) {
+ return false;
+ }
}
+ return true;
}
-int SkOpAngle::sign() const {
- SkASSERT(fStart->t() != fEnd->t());
- return fStart->t() < fEnd->t() ? -1 : 1;
-}
-
-SkOpSpan* SkOpAngle::starter() {
- return fStart->starter(fEnd);
-}
-
-bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
+bool SkOpAngle::tangentsDiverge(const SkOpAngle& rh, double s0xt0) const {
if (s0xt0 == 0) {
return false;
}
@@ -1165,7 +1090,7 @@ bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
// m = (v2.y * v1.x - v2.x * v1.y) / (v2.x * v1.x + v2.y * v1.y)
// m = v1.cross(v2) / v1.dot(v2)
const SkDVector* sweep = fSweep;
- const SkDVector* tweep = rh->fSweep;
+ const SkDVector* tweep = rh.fSweep;
double s0dt0 = sweep[0].dot(tweep[0]);
if (!s0dt0) {
return true;
@@ -1175,6 +1100,36 @@ bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
double sDist = sweep[0].length() * m;
double tDist = tweep[0].length() * m;
bool useS = fabs(sDist) < fabs(tDist);
- double mFactor = fabs(useS ? this->distEndRatio(sDist) : rh->distEndRatio(tDist));
+ double mFactor = fabs(useS ? distEndRatio(sDist) : rh.distEndRatio(tDist));
return mFactor < 5000; // empirically found limit
}
+
+SkOpAngleSet::SkOpAngleSet()
+ : fAngles(NULL)
+#if DEBUG_ANGLE
+ , fCount(0)
+#endif
+{
+}
+
+SkOpAngleSet::~SkOpAngleSet() {
+ SkDELETE(fAngles);
+}
+
+SkOpAngle& SkOpAngleSet::push_back() {
+ if (!fAngles) {
+ fAngles = SkNEW_ARGS(SkChunkAlloc, (2));
+ }
+ void* ptr = fAngles->allocThrow(sizeof(SkOpAngle));
+ SkOpAngle* angle = (SkOpAngle*) ptr;
+#if DEBUG_ANGLE
+ angle->setID(++fCount);
+#endif
+ return *angle;
+}
+
+void SkOpAngleSet::reset() {
+ if (fAngles) {
+ fAngles->reset();
+ }
+}
diff --git a/src/pathops/SkOpAngle.h b/src/pathops/SkOpAngle.h
index 84b37010c9..1dc4250613 100644
--- a/src/pathops/SkOpAngle.h
+++ b/src/pathops/SkOpAngle.h
@@ -7,18 +7,17 @@
#ifndef SkOpAngle_DEFINED
#define SkOpAngle_DEFINED
+#include "SkChunkAlloc.h"
#include "SkLineParameters.h"
-#if DEBUG_ANGLE
-#include "SkString.h"
-#endif
-class SkOpContour;
-class SkOpPtT;
class SkOpSegment;
-class SkOpSpanBase;
-class SkOpSpan;
+struct SkOpSpan;
-struct SkOpAngle {
+// sorting angles
+// given angles of {dx dy ddx ddy dddx dddy} sort them
+class SkOpAngle {
+public:
+ enum { kStackBasedCount = 8 }; // FIXME: determine what this should be
enum IncludeType {
kUnaryWinding,
kUnaryXor,
@@ -26,66 +25,29 @@ struct SkOpAngle {
kBinaryOpp,
};
- bool after(SkOpAngle* test);
- int allOnOneSide(const SkOpAngle* test);
- bool checkCrossesZero() const;
- void checkNearCoincidence();
- bool checkParallel(SkOpAngle* );
- bool computeSector();
- int convexHullOverlaps(const SkOpAngle* ) const;
-
- const SkOpAngle* debugAngle(int id) const;
- SkOpContour* debugContour(int id);
- int debugID() const {
- return PATH_OPS_DEBUG_RELEASE(fID, -1);
+ int end() const {
+ return fEnd;
}
-#if DEBUG_SORT
- void debugLoop() const;
-#endif
+ const SkOpAngle* findFirst() const;
-#if DEBUG_ANGLE
- SkString debugPart() const;
-#endif
- const SkOpPtT* debugPtT(int id) const;
- const SkOpSegment* debugSegment(int id) const;
- const SkOpSpanBase* debugSpan(int id) 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
- void dump() const;
- void dumpCurves() const;
- void dumpLoop() const;
- void dumpOne(bool functionHeader) const;
- void dumpTo(const SkOpSegment* fromSeg, const SkOpAngle* ) const;
- void dumpTest() const;
-
- SkOpSpanBase* end() const {
- return fEnd;
+ bool inLoop() const {
+ return !!fNext;
}
- bool endsIntersect(SkOpAngle* );
- bool endToSide(const SkOpAngle* rh, bool* inside) const;
- SkOpAngle* findFirst();
- int findSector(SkPath::Verb verb, double x, double y) const;
- SkOpGlobalState* globalState() const;
void insert(SkOpAngle* );
- SkOpSpanBase* lastMarked() const;
- bool loopContains(const SkOpAngle* ) const;
+ bool isHorizontal() const;
+ SkOpSpan* lastMarked() const;
+ bool loopContains(const SkOpAngle& ) const;
int loopCount() const;
void markStops();
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 {
@@ -96,57 +58,120 @@ struct SkOpAngle {
return fSectorStart;
}
- SkOpSegment* segment() const;
+ void set(const SkOpSegment* segment, int start, int end);
- void set(SkOpSpanBase* start, SkOpSpanBase* end);
- void setCurveHullSweep();
+ void setLastMarked(SkOpSpan* marked) {
+ fLastMarked = marked;
+ }
- void setID(int id) {
- PATH_OPS_DEBUG_CODE(fID = id);
+ SkOpSegment* segment() const {
+ return const_cast<SkOpSegment*>(fSegment);
}
- void setLastMarked(SkOpSpanBase* marked) {
- fLastMarked = marked;
+ int sign() const {
+ return SkSign32(fStart - fEnd);
}
- void setSector();
- void setSpans();
- int sign() const;
+ bool small() const;
- SkOpSpanBase* start() const {
+ int start() const {
return fStart;
}
- SkOpSpan* starter();
- bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
-
bool unorderable() const {
return fUnorderable;
}
- SkDCubic fCurvePart; // the curve from start to end
+ // available to testing only
+#if DEBUG_SORT
+ void debugLoop() const; // called by code during run
+#endif
+#if DEBUG_ANGLE
+ void debugSameAs(const SkOpAngle* compare) const;
+#endif
+ void dump() const;
+ void dumpLoop() const;
+ void dumpTo(const SkOpSegment* fromSeg, const SkOpAngle* ) const;
+
+#if DEBUG_ANGLE
+ int debugID() const { return fID; }
+
+ void setID(int id) {
+ fID = id;
+ }
+#else
+ int debugID() const { return 0; }
+#endif
+
+#if DEBUG_VALIDATE
+ void debugValidateLoop() const;
+#endif
+
+private:
+ bool after(const SkOpAngle* test) const;
+ int allOnOneSide(const SkOpAngle& test) const;
+ bool calcSlop(double x, double y, double rx, double ry, bool* result) const;
+ bool checkCrossesZero() const;
+ bool checkParallel(const SkOpAngle& ) const;
+ bool computeSector();
+ int convexHullOverlaps(const SkOpAngle& ) const;
+ double distEndRatio(double dist) const;
+ int findSector(SkPath::Verb verb, double x, double y) const;
+ bool endsIntersect(const SkOpAngle& ) const;
+ double midT() const;
+ bool oppositePlanes(const SkOpAngle& rh) const;
+ bool orderable(const SkOpAngle& rh) const; // false == this < rh ; true == this > rh
+ bool overlap(const SkOpAngle& test) const;
+ void setCurveHullSweep();
+ void setSector();
+ void setSpans();
+ bool tangentsDiverge(const SkOpAngle& rh, double s0xt0) const;
+
+ SkDCubic fCurvePart; // the curve from start to end
double fSide;
SkLineParameters fTangentHalf; // used only to sort a pair of lines or line-like sections
+ const SkOpSegment* fSegment;
SkOpAngle* fNext;
- SkOpSpanBase* fLastMarked;
+ SkOpSpan* fLastMarked;
SkDVector fSweep[2];
- SkOpSpanBase* fStart;
- SkOpSpanBase* fEnd;
- SkOpSpanBase* fComputedEnd;
+ int fStart;
+ int fEnd;
+ int fComputedEnd;
int fSectorMask;
int8_t fSectorStart; // in 32nds of a circle
int8_t fSectorEnd;
bool fIsCurve;
- bool fStop; // set if ordered angle is greater than the previous
- bool fUnorderable;
+ bool fStop; // set if ordered angle is greater than the previous
+ mutable bool fUnorderable; // this is editable by orderable()
bool fUnorderedSweep; // set when a cubic's first control point between the sweep vectors
bool fComputeSector;
bool fComputedSector;
- bool fCheckCoincidence;
- PATH_OPS_DEBUG_CODE(int fID);
+#if DEBUG_ANGLE
+ int fID;
+#endif
+#if DEBUG_VALIDATE
+ void debugValidateNext() const; // in debug builds, verify that angle loop is uncorrupted
+#else
+ void debugValidateNext() const {}
+#endif
+ void dumpOne(bool showFunc) const; // available to testing only
+ void dumpPartials() const; // utility to be called by user from debugger
+ friend class PathOpsAngleTester;
};
-
+class SkOpAngleSet {
+public:
+ SkOpAngleSet();
+ ~SkOpAngleSet();
+ SkOpAngle& push_back();
+ void reset();
+private:
+ void dump() const; // utility to be called by user from debugger
+ SkChunkAlloc* fAngles;
+#if DEBUG_ANGLE
+ int fCount;
+#endif
+};
#endif
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
deleted file mode 100755
index 45eee0a38e..0000000000
--- a/src/pathops/SkOpCoincidence.cpp
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SkOpCoincidence.h"
-#include "SkOpSegment.h"
-#include "SkPathOpsTSect.h"
-
-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;
- this->fHead = coinRec;
-}
-
-static void 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;
-}
-
-bool SkOpCoincidence::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* allocator) {
- double coinTs, coinTe, oppTs, oppTe;
- tRange(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
- tRange(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
- SkOpSegment* coinSeg = coinPtTStart->segment();
- SkOpSegment* oppSeg = oppPtTStart->segment();
- SkASSERT(coinSeg != oppSeg);
- SkCoincidentSpans* check = this->fHead;
- do {
- const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment();
- if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) {
- continue;
- }
- const SkOpSegment* checkOppSeg = check->fOppPtTStart->segment();
- if (checkOppSeg != coinSeg && checkOppSeg != oppSeg) {
- continue;
- }
- 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;
- }
- } 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);
- }
- SkOpPtT* cs = coinSeg->addMissing(coinTs, oppSeg, allocator);
- SkOpPtT* ce = coinSeg->addMissing(coinTe, oppSeg, allocator);
- if (cs == ce) {
- return false;
- }
- 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;
-}
-
-bool SkOpCoincidence::addMissing(SkChunkAlloc* allocator) {
- SkCoincidentSpans* outer = this->fHead;
- if (!outer) {
- return true;
- }
- do {
- SkCoincidentSpans* inner = outer;
- while ((inner = inner->fNext)) {
- double overS, overE;
- if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
- if (!addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
- outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) {
- return false;
- }
- } else if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
- if (!addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
- outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) {
- return false;
- }
- } else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
- if (!addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
- outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) {
- return false;
- }
- } else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
- if (!addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
- inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
- outer->fCoinPtTStart, outer->fCoinPtTEnd,
- inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) {
- return false;
- }
- }
- }
-
- } while ((outer = outer->fNext));
- return true;
-}
-
-
-bool SkOpCoincidence::contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
- SkOpPtT* oppPtTEnd, bool flipped) {
- SkCoincidentSpans* coin = fHead;
- if (!coin) {
- return false;
- }
- do {
- if (coin->fCoinPtTStart == coinPtTStart && coin->fCoinPtTEnd == coinPtTEnd
- && coin->fOppPtTStart == oppPtTStart && coin->fOppPtTEnd == oppPtTEnd
- && coin->fFlipped == flipped) {
- return true;
- }
- } while ((coin = coin->fNext));
- return false;
-}
-
-// walk span sets in parallel, moving winding from one to the other
-bool SkOpCoincidence::apply() {
- SkCoincidentSpans* coin = fHead;
- if (!coin) {
- return true;
- }
- do {
- SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
- SkASSERT(start == start->starter(end));
- bool flipped = coin->fFlipped;
- SkOpSpanBase* oEnd = (flipped ? coin->fOppPtTStart : coin->fOppPtTEnd)->span();
- SkOpSpan* oStart = (flipped ? coin->fOppPtTEnd : coin->fOppPtTStart)->span()->upCast();
- SkASSERT(oStart == oStart->starter(oEnd));
- SkOpSegment* segment = start->segment();
- SkOpSegment* oSegment = oStart->segment();
- bool operandSwap = segment->operand() != oSegment->operand();
- if (flipped) {
- do {
- SkOpSpanBase* oNext = oStart->next();
- if (oNext == oEnd) {
- break;
- }
- oStart = oNext->upCast();
- } while (true);
- }
- bool isXor = segment->isXor();
- bool oppXor = oSegment->isXor();
- do {
- int windValue = start->windValue();
- int oWindValue = oStart->windValue();
- int oppValue = start->oppValue();
- int oOppValue = oStart->oppValue();
- // winding values are added or subtracted depending on direction and wind type
- // same or opposite values are summed depending on the operand value
- if (windValue >= oWindValue) {
- if (operandSwap) {
- SkTSwap(oWindValue, oOppValue);
- }
- if (flipped) {
- windValue -= oWindValue;
- oppValue -= oOppValue;
- } else {
- windValue += oWindValue;
- oppValue += oOppValue;
- }
- if (isXor) {
- windValue &= 1;
- }
- if (oppXor) {
- oppValue &= 1;
- }
- oWindValue = oOppValue = 0;
- } else {
- if (operandSwap) {
- SkTSwap(windValue, oppValue);
- }
- if (flipped) {
- oWindValue -= windValue;
- oOppValue -= oppValue;
- } else {
- oWindValue += windValue;
- oOppValue += oppValue;
- }
- if (isXor) {
- oOppValue &= 1;
- }
- if (oppXor) {
- oWindValue &= 1;
- }
- windValue = oppValue = 0;
- }
- start->setWindValue(windValue);
- start->setOppValue(oppValue);
- oStart->setWindValue(oWindValue);
- oStart->setOppValue(oOppValue);
- if (!windValue && !oppValue) {
- segment->markDone(start);
- }
- if (!oWindValue && !oOppValue) {
- oSegment->markDone(oStart);
- }
- SkOpSpanBase* next = start->next();
- SkOpSpanBase* oNext = flipped ? oStart->prev() : oStart->next();
- if (next == end) {
- break;
- }
- start = next->upCast();
- if (!oNext) {
- return false;
- }
- if (!oNext->upCastable()) {
- return false;
- }
- oStart = oNext->upCast();
- } while (true);
- } while ((coin = coin->fNext));
- return true;
-}
-
-void SkOpCoincidence::detach(SkCoincidentSpans* remove) {
- SkCoincidentSpans* coin = fHead;
- SkCoincidentSpans* prev = NULL;
- SkCoincidentSpans* next;
- do {
- next = coin->fNext;
- if (coin == remove) {
- if (prev) {
- prev->fNext = next;
- } else {
- fHead = next;
- }
- break;
- }
- prev = coin;
- } while ((coin = next));
- SkASSERT(coin);
-}
-
-void SkOpCoincidence::expand() {
- SkCoincidentSpans* coin = fHead;
- if (!coin) {
- return;
- }
- 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;
- }
- }
- SkOpSpanBase* next = end->final() ? NULL : 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;
- }
- }
- } while ((coin = coin->fNext));
-}
-
-void SkOpCoincidence::fixUp(SkOpPtT* deleted, SkOpPtT* kept) {
- SkCoincidentSpans* coin = fHead;
- if (!coin) {
- return;
- }
- do {
- if (coin->fCoinPtTStart == deleted) {
- if (coin->fCoinPtTEnd->span() == kept->span()) {
- return this->detach(coin);
- }
- coin->fCoinPtTStart = kept;
- }
- if (coin->fCoinPtTEnd == deleted) {
- if (coin->fCoinPtTStart->span() == kept->span()) {
- return this->detach(coin);
- }
- coin->fCoinPtTEnd = kept;
- }
- if (coin->fOppPtTStart == deleted) {
- if (coin->fOppPtTEnd->span() == kept->span()) {
- return this->detach(coin);
- }
- coin->fOppPtTStart = kept;
- }
- if (coin->fOppPtTEnd == deleted) {
- if (coin->fOppPtTStart->span() == kept->span()) {
- return this->detach(coin);
- }
- coin->fOppPtTEnd = kept;
- }
- } while ((coin = coin->fNext));
-}
-
-void SkOpCoincidence::mark() {
- SkCoincidentSpans* coin = fHead;
- if (!coin) {
- return;
- }
- do {
- SkOpSpanBase* end = coin->fCoinPtTEnd->span();
- SkOpSpanBase* oldEnd = end;
- SkOpSpan* start = coin->fCoinPtTStart->span()->starter(&end);
- SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
- SkOpSpanBase* oOldEnd = oEnd;
- SkOpSpanBase* oStart = coin->fOppPtTStart->span()->starter(&oEnd);
- bool flipped = (end == oldEnd) != (oEnd == oOldEnd);
- if (flipped) {
- SkTSwap(oStart, oEnd);
- }
- SkOpSpanBase* next = start;
- SkOpSpanBase* oNext = oStart;
- // check to see if coincident span could be bigger
-
- do {
- next = next->upCast()->next();
- oNext = flipped ? oNext->prev() : oNext->upCast()->next();
- if (next == end || oNext == oEnd) {
- break;
- }
- if (!next->containsCoinEnd(oNext)) {
- next->insertCoinEnd(oNext);
- }
- SkOpSpan* nextSpan = next->upCast();
- SkOpSpan* oNextSpan = oNext->upCast();
- if (!nextSpan->containsCoincidence(oNextSpan)) {
- nextSpan->insertCoincidence(oNextSpan);
- }
- } while (true);
- } while ((coin = coin->fNext));
-}
-
-bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e,
- const SkOpPtT* coin2s, const SkOpPtT* coin2e, double* overS, double* overE) const {
- if (coin1s->segment() != coin2s->segment()) {
- return false;
- }
- *overS = SkTMax(SkTMin(coin1s->fT, coin1e->fT), SkTMin(coin2s->fT, coin2e->fT));
- *overE = SkTMin(SkTMax(coin1s->fT, coin1e->fT), SkTMax(coin2s->fT, coin2e->fT));
- return *overS < *overE;
-}
diff --git a/src/pathops/SkOpCoincidence.h b/src/pathops/SkOpCoincidence.h
index b79b88be88..287bfd12d4 100644
--- a/src/pathops/SkOpCoincidence.h
+++ b/src/pathops/SkOpCoincidence.h
@@ -19,8 +19,6 @@ struct SkCoincidentSpans {
SkOpPtT* fOppPtTStart;
SkOpPtT* fOppPtTEnd;
bool fFlipped;
-
- void dump() const;
};
class SkOpCoincidence {
@@ -30,27 +28,13 @@ public:
}
void add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
- SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator);
- bool addMissing(SkChunkAlloc* allocator);
- bool apply();
+ SkOpPtT* oppPtTEnd, bool flipped, SkChunkAlloc* allocator);
+ void apply();
bool contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
SkOpPtT* oppPtTEnd, bool flipped);
- void detach(SkCoincidentSpans* );
void dump() const;
- void expand();
- void fixUp(SkOpPtT* deleted, SkOpPtT* kept);
void mark();
-private:
- 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* allocator);
- bool overlap(const SkOpPtT* coinStart1, const SkOpPtT* coinEnd1,
- const SkOpPtT* coinStart2, const SkOpPtT* coinEnd2,
- double* overS, double* overE) const;
-
SkCoincidentSpans* fHead;
};
diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp
index d17b18905b..28c072a3c1 100644
--- a/src/pathops/SkOpContour.cpp
+++ b/src/pathops/SkOpContour.cpp
@@ -4,35 +4,42 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#include "SkIntersections.h"
#include "SkOpContour.h"
-#include "SkOpTAllocator.h"
#include "SkPathWriter.h"
-#include "SkReduceOrder.h"
#include "SkTSort.h"
-void SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator) {
- switch (verb) {
- case SkPath::kLine_Verb: {
- SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 2);
- memcpy(ptStorage, pts, sizeof(SkPoint) * 2);
- appendSegment(allocator).addLine(ptStorage, this);
- } break;
- case SkPath::kQuad_Verb: {
- SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 3);
- memcpy(ptStorage, pts, sizeof(SkPoint) * 3);
- appendSegment(allocator).addQuad(ptStorage, this);
- } break;
- case SkPath::kCubic_Verb: {
- SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 4);
- memcpy(ptStorage, pts, sizeof(SkPoint) * 4);
- appendSegment(allocator).addCubic(ptStorage, this);
- } break;
- default:
- SkASSERT(0);
- }
-}
-
-SkOpSegment* SkOpContour::nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end) {
+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 || 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
+ // or needs to check to see if the self-intersection bit was set on the cubic segment
+ return false;
+ }
+ SkCoincidence& coincidence = fCoincidences.push_back();
+ coincidence.fOther = other;
+ coincidence.fSegments[0] = index;
+ coincidence.fSegments[1] = otherIndex;
+ coincidence.fTs[swap][0] = ts[0][0];
+ coincidence.fTs[swap][1] = ts[0][1];
+ coincidence.fTs[!swap][0] = ts[1][0];
+ coincidence.fTs[!swap][1] = ts[1][1];
+ coincidence.fPts[swap][0] = pt0;
+ coincidence.fPts[swap][1] = pt1;
+ bool nearStart = ts.nearlySame(0);
+ bool nearEnd = ts.nearlySame(1);
+ coincidence.fPts[!swap][0] = nearStart ? ts.pt2(0).asSkPoint() : pt0;
+ coincidence.fPts[!swap][1] = nearEnd ? ts.pt2(1).asSkPoint() : pt1;
+ coincidence.fNearly[0] = nearStart;
+ coincidence.fNearly[1] = nearEnd;
+ return true;
+}
+
+SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
int segmentCount = fSortedSegments.count();
SkASSERT(segmentCount > 0);
for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
@@ -40,27 +47,627 @@ SkOpSegment* SkOpContour::nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase*
if (testSegment->done()) {
continue;
}
- SkOpSpanBase* span = testSegment->head();
- SkOpSpanBase* testS, * testE;
- while (SkOpSegment::NextCandidate(span, &testS, &testE)) {
- if (!testSegment->isVertical(testS, testE)) {
- *start = testS;
- *end = testE;
+ *start = *end = 0;
+ while (testSegment->nextCandidate(start, end)) {
+ if (!testSegment->isVertical(*start, *end)) {
return testSegment;
}
- span = span->upCast()->next();
}
}
return NULL;
}
+// if one is very large the smaller may have collapsed to nothing
+static void bump_out_close_span(double* startTPtr, double* endTPtr) {
+ double startT = *startTPtr;
+ double endT = *endTPtr;
+ if (approximately_negative(endT - startT)) {
+ if (endT <= 1 - FLT_EPSILON) {
+ *endTPtr += FLT_EPSILON;
+ SkASSERT(*endTPtr <= 1);
+ } else {
+ *startTPtr -= FLT_EPSILON;
+ SkASSERT(*startTPtr >= 0);
+ }
+ }
+}
+
+// first pass, add missing T values
+// second pass, determine winding values of overlaps
+void SkOpContour::addCoincidentPoints() {
+ int count = fCoincidences.count();
+ for (int index = 0; index < count; ++index) {
+ SkCoincidence& coincidence = fCoincidences[index];
+ int thisIndex = coincidence.fSegments[0];
+ SkOpSegment& thisOne = fSegments[thisIndex];
+ SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ SkOpSegment& other = otherContour->fSegments[otherIndex];
+ if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
+ // OPTIMIZATION: remove from array
+ continue;
+ }
+ #if DEBUG_CONCIDENT
+ thisOne.debugShowTs("-");
+ other.debugShowTs("o");
+ #endif
+ double startT = coincidence.fTs[0][0];
+ double endT = coincidence.fTs[0][1];
+ bool startSwapped, oStartSwapped, cancelers;
+ if ((cancelers = startSwapped = startT > endT)) {
+ SkTSwap(startT, endT);
+ }
+ bump_out_close_span(&startT, &endT);
+ SkASSERT(!approximately_negative(endT - startT));
+ double oStartT = coincidence.fTs[1][0];
+ double oEndT = coincidence.fTs[1][1];
+ if ((oStartSwapped = oStartT > oEndT)) {
+ SkTSwap(oStartT, oEndT);
+ cancelers ^= true;
+ }
+ bump_out_close_span(&oStartT, &oEndT);
+ SkASSERT(!approximately_negative(oEndT - oStartT));
+ const SkPoint& startPt = coincidence.fPts[0][startSwapped];
+ if (cancelers) {
+ // make sure startT and endT have t entries
+ if (startT > 0 || oEndT < 1
+ || thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) {
+ thisOne.addTPair(startT, &other, oEndT, true, startPt,
+ coincidence.fPts[1][startSwapped]);
+ }
+ const SkPoint& oStartPt = coincidence.fPts[1][oStartSwapped];
+ if (oStartT > 0 || endT < 1
+ || thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) {
+ other.addTPair(oStartT, &thisOne, endT, true, oStartPt,
+ coincidence.fPts[0][oStartSwapped]);
+ }
+ } else {
+ if (startT > 0 || oStartT > 0
+ || thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) {
+ thisOne.addTPair(startT, &other, oStartT, true, startPt,
+ coincidence.fPts[1][startSwapped]);
+ }
+ const SkPoint& oEndPt = coincidence.fPts[1][!oStartSwapped];
+ if (endT < 1 || oEndT < 1
+ || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) {
+ other.addTPair(oEndT, &thisOne, endT, true, oEndPt,
+ coincidence.fPts[0][!oStartSwapped]);
+ }
+ }
+ #if DEBUG_CONCIDENT
+ thisOne.debugShowTs("+");
+ other.debugShowTs("o");
+ #endif
+ }
+ // if there are multiple pairs of coincidence that share an edge, see if the opposite
+ // are also coincident
+ for (int index = 0; index < count - 1; ++index) {
+ const SkCoincidence& coincidence = fCoincidences[index];
+ int thisIndex = coincidence.fSegments[0];
+ SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ for (int idx2 = 1; idx2 < count; ++idx2) {
+ const SkCoincidence& innerCoin = fCoincidences[idx2];
+ int innerThisIndex = innerCoin.fSegments[0];
+ if (thisIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 1, innerCoin, 1, false);
+ }
+ if (this == otherContour && otherIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 0, innerCoin, 1, false);
+ }
+ SkOpContour* innerOtherContour = innerCoin.fOther;
+ innerThisIndex = innerCoin.fSegments[1];
+ if (this == innerOtherContour && thisIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 1, innerCoin, 0, false);
+ }
+ if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 0, innerCoin, 0, false);
+ }
+ }
+ }
+}
+
+bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex,
+ const SkIntersections& ts, int ptIndex, bool swap) {
+ SkPoint pt0 = ts.pt(ptIndex).asSkPoint();
+ SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint();
+ if (SkDPoint::ApproximatelyEqual(pt0, pt1)) {
+ // FIXME: one could imagine a case where it would be incorrect to ignore this
+ // suppose two self-intersecting cubics overlap to form a partial coincidence --
+ // although it isn't clear why the regular coincidence could wouldn't pick this up
+ // this is exceptional enough to ignore for now
+ return false;
+ }
+ SkCoincidence& coincidence = fPartialCoincidences.push_back();
+ coincidence.fOther = other;
+ coincidence.fSegments[0] = index;
+ coincidence.fSegments[1] = otherIndex;
+ coincidence.fTs[swap][0] = ts[0][ptIndex];
+ coincidence.fTs[swap][1] = ts[0][ptIndex + 1];
+ coincidence.fTs[!swap][0] = ts[1][ptIndex];
+ coincidence.fTs[!swap][1] = ts[1][ptIndex + 1];
+ coincidence.fPts[0][0] = coincidence.fPts[1][0] = pt0;
+ coincidence.fPts[0][1] = coincidence.fPts[1][1] = pt1;
+ coincidence.fNearly[0] = 0;
+ coincidence.fNearly[1] = 0;
+ return true;
+}
+
+void SkOpContour::align(const SkOpSegment::AlignedSpan& aligned, bool swap,
+ SkCoincidence* coincidence) {
+ for (int idx2 = 0; idx2 < 2; ++idx2) {
+ if (coincidence->fPts[0][idx2] == aligned.fOldPt
+ && coincidence->fTs[swap][idx2] == aligned.fOldT) {
+ SkASSERT(SkDPoint::RoughlyEqual(coincidence->fPts[0][idx2], aligned.fPt));
+ coincidence->fPts[0][idx2] = aligned.fPt;
+ SkASSERT(way_roughly_equal(coincidence->fTs[swap][idx2], aligned.fT));
+ coincidence->fTs[swap][idx2] = aligned.fT;
+ }
+ }
+}
+
+void SkOpContour::alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
+ SkTArray<SkCoincidence, true>* coincidences) {
+ int count = coincidences->count();
+ for (int index = 0; index < count; ++index) {
+ SkCoincidence& coincidence = (*coincidences)[index];
+ int thisIndex = coincidence.fSegments[0];
+ const SkOpSegment* thisOne = &fSegments[thisIndex];
+ const SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ const SkOpSegment* other = &otherContour->fSegments[otherIndex];
+ if (thisOne == aligned.fOther1 && other == aligned.fOther2) {
+ align(aligned, false, &coincidence);
+ } else if (thisOne == aligned.fOther2 && other == aligned.fOther1) {
+ align(aligned, true, &coincidence);
+ }
+ }
+}
+
+void SkOpContour::alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
+ bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const {
+ int zeroPt;
+ if ((zeroPt = alignT(swap, tIndex, ts)) >= 0) {
+ alignPt(segmentIndex, point, zeroPt);
+ }
+ if ((zeroPt = other->alignT(!swap, tIndex, ts)) >= 0) {
+ other->alignPt(otherIndex, point, zeroPt);
+ }
+}
+
+void SkOpContour::alignPt(int index, SkPoint* point, int zeroPt) const {
+ const SkOpSegment& segment = fSegments[index];
+ if (0 == zeroPt) {
+ *point = segment.pts()[0];
+ } else {
+ *point = segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
+ }
+}
+
+int SkOpContour::alignT(bool swap, int tIndex, SkIntersections* ts) const {
+ double tVal = (*ts)[swap][tIndex];
+ if (tVal != 0 && precisely_zero(tVal)) {
+ ts->set(swap, tIndex, 0);
+ return 0;
+ }
+ if (tVal != 1 && precisely_equal(tVal, 1)) {
+ ts->set(swap, tIndex, 1);
+ return 1;
+ }
+ return -1;
+}
+
+bool SkOpContour::calcAngles() {
+ int segmentCount = fSegments.count();
+ for (int test = 0; test < segmentCount; ++test) {
+ if (!fSegments[test].calcAngles()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool SkOpContour::calcCoincidentWinding() {
+ int count = fCoincidences.count();
+#if DEBUG_CONCIDENT
+ if (count > 0) {
+ SkDebugf("%s count=%d\n", __FUNCTION__, count);
+ }
+#endif
+ for (int index = 0; index < count; ++index) {
+ SkCoincidence& coincidence = fCoincidences[index];
+ if (!calcCommonCoincidentWinding(coincidence)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void SkOpContour::calcPartialCoincidentWinding() {
+ int count = fPartialCoincidences.count();
+#if DEBUG_CONCIDENT
+ if (count > 0) {
+ SkDebugf("%s count=%d\n", __FUNCTION__, count);
+ }
+#endif
+ for (int index = 0; index < count; ++index) {
+ SkCoincidence& coincidence = fPartialCoincidences[index];
+ calcCommonCoincidentWinding(coincidence);
+ }
+ // if there are multiple pairs of partial coincidence that share an edge, see if the opposite
+ // are also coincident
+ for (int index = 0; index < count - 1; ++index) {
+ const SkCoincidence& coincidence = fPartialCoincidences[index];
+ int thisIndex = coincidence.fSegments[0];
+ SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ for (int idx2 = 1; idx2 < count; ++idx2) {
+ const SkCoincidence& innerCoin = fPartialCoincidences[idx2];
+ int innerThisIndex = innerCoin.fSegments[0];
+ if (thisIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 1, innerCoin, 1, true);
+ }
+ if (this == otherContour && otherIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 0, innerCoin, 1, true);
+ }
+ SkOpContour* innerOtherContour = innerCoin.fOther;
+ innerThisIndex = innerCoin.fSegments[1];
+ if (this == innerOtherContour && thisIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 1, innerCoin, 0, true);
+ }
+ if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 0, innerCoin, 0, true);
+ }
+ }
+ }
+}
+
+void SkOpContour::checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
+ const SkCoincidence& twoCoin, int twoIdx, bool partial) {
+ SkASSERT((oneIdx ? this : oneCoin.fOther) == (twoIdx ? this : twoCoin.fOther));
+ SkASSERT(oneCoin.fSegments[!oneIdx] == twoCoin.fSegments[!twoIdx]);
+ // look for common overlap
+ double min = SK_ScalarMax;
+ double max = SK_ScalarMin;
+ double min1 = oneCoin.fTs[!oneIdx][0];
+ double max1 = oneCoin.fTs[!oneIdx][1];
+ double min2 = twoCoin.fTs[!twoIdx][0];
+ double max2 = twoCoin.fTs[!twoIdx][1];
+ bool cancelers = (min1 < max1) != (min2 < max2);
+ if (min1 > max1) {
+ SkTSwap(min1, max1);
+ }
+ if (min2 > max2) {
+ SkTSwap(min2, max2);
+ }
+ if (between(min1, min2, max1)) {
+ min = min2;
+ }
+ if (between(min1, max2, max1)) {
+ max = max2;
+ }
+ if (between(min2, min1, max2)) {
+ min = SkTMin(min, min1);
+ }
+ if (between(min2, max1, max2)) {
+ max = SkTMax(max, max1);
+ }
+ if (min >= max) {
+ return; // no overlap
+ }
+ // look to see if opposite are different segments
+ int seg1Index = oneCoin.fSegments[oneIdx];
+ int seg2Index = twoCoin.fSegments[twoIdx];
+ if (seg1Index == seg2Index) {
+ return;
+ }
+ SkOpContour* contour1 = oneIdx ? oneCoin.fOther : this;
+ SkOpContour* contour2 = twoIdx ? twoCoin.fOther : this;
+ SkOpSegment* segment1 = &contour1->fSegments[seg1Index];
+ SkOpSegment* segment2 = &contour2->fSegments[seg2Index];
+ // find opposite t value ranges corresponding to reference min/max range
+ const SkOpContour* refContour = oneIdx ? this : oneCoin.fOther;
+ const int refSegIndex = oneCoin.fSegments[!oneIdx];
+ const SkOpSegment* refSegment = &refContour->fSegments[refSegIndex];
+ int seg1Start = segment1->findOtherT(min, refSegment);
+ int seg1End = segment1->findOtherT(max, refSegment);
+ int seg2Start = segment2->findOtherT(min, refSegment);
+ int seg2End = segment2->findOtherT(max, refSegment);
+ // if the opposite pairs already contain min/max, we're done
+ if (seg1Start >= 0 && seg1End >= 0 && seg2Start >= 0 && seg2End >= 0) {
+ return;
+ }
+ double loEnd = SkTMin(min1, min2);
+ double hiEnd = SkTMax(max1, max2);
+ // insert the missing coincident point(s)
+ double missingT1 = -1;
+ double otherT1 = -1;
+ if (seg1Start < 0) {
+ if (seg2Start < 0) {
+ return;
+ }
+ missingT1 = segment1->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
+ segment2, seg1End);
+ if (missingT1 < 0) {
+ return;
+ }
+ const SkOpSpan* missingSpan = &segment2->span(seg2Start);
+ otherT1 = missingSpan->fT;
+ } else if (seg2Start < 0) {
+ SkASSERT(seg1Start >= 0);
+ missingT1 = segment2->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
+ segment1, seg2End);
+ if (missingT1 < 0) {
+ return;
+ }
+ const SkOpSpan* missingSpan = &segment1->span(seg1Start);
+ otherT1 = missingSpan->fT;
+ }
+ SkPoint missingPt1;
+ SkOpSegment* addTo1 = NULL;
+ SkOpSegment* addOther1 = seg1Start < 0 ? segment2 : segment1;
+ int minTIndex = refSegment->findExactT(min, addOther1);
+ SkASSERT(minTIndex >= 0);
+ if (missingT1 >= 0) {
+ missingPt1 = refSegment->span(minTIndex).fPt;
+ addTo1 = seg1Start < 0 ? segment1 : segment2;
+ }
+ double missingT2 = -1;
+ double otherT2 = -1;
+ if (seg1End < 0) {
+ if (seg2End < 0) {
+ return;
+ }
+ missingT2 = segment1->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
+ segment2, seg1Start);
+ if (missingT2 < 0) {
+ return;
+ }
+ const SkOpSpan* missingSpan = &segment2->span(seg2End);
+ otherT2 = missingSpan->fT;
+ } else if (seg2End < 0) {
+ SkASSERT(seg1End >= 0);
+ missingT2 = segment2->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
+ segment1, seg2Start);
+ if (missingT2 < 0) {
+ return;
+ }
+ const SkOpSpan* missingSpan = &segment1->span(seg1End);
+ otherT2 = missingSpan->fT;
+ }
+ SkPoint missingPt2;
+ SkOpSegment* addTo2 = NULL;
+ SkOpSegment* addOther2 = seg1End < 0 ? segment2 : segment1;
+ int maxTIndex = refSegment->findExactT(max, addOther2);
+ SkASSERT(maxTIndex >= 0);
+ if (missingT2 >= 0) {
+ missingPt2 = refSegment->span(maxTIndex).fPt;
+ addTo2 = seg1End < 0 ? segment1 : segment2;
+ }
+ if (missingT1 >= 0) {
+ addTo1->pinT(missingPt1, &missingT1);
+ addTo1->addTPair(missingT1, addOther1, otherT1, false, missingPt1);
+ } else {
+ SkASSERT(minTIndex >= 0);
+ missingPt1 = refSegment->span(minTIndex).fPt;
+ }
+ if (missingT2 >= 0) {
+ addTo2->pinT(missingPt2, &missingT2);
+ addTo2->addTPair(missingT2, addOther2, otherT2, false, missingPt2);
+ } else {
+ SkASSERT(minTIndex >= 0);
+ missingPt2 = refSegment->span(maxTIndex).fPt;
+ }
+ if (!partial) {
+ return;
+ }
+ if (cancelers) {
+ if (missingT1 >= 0) {
+ if (addTo1->reversePoints(missingPt1, missingPt2)) {
+ SkTSwap(missingPt1, missingPt2);
+ }
+ addTo1->addTCancel(missingPt1, missingPt2, addOther1);
+ } else {
+ if (addTo2->reversePoints(missingPt1, missingPt2)) {
+ SkTSwap(missingPt1, missingPt2);
+ }
+ addTo2->addTCancel(missingPt1, missingPt2, addOther2);
+ }
+ } else if (missingT1 >= 0) {
+ SkAssertResult(addTo1->addTCoincident(missingPt1, missingPt2,
+ addTo1 == addTo2 ? missingT2 : otherT2, addOther1));
+ } else {
+ SkAssertResult(addTo2->addTCoincident(missingPt2, missingPt1,
+ addTo2 == addTo1 ? missingT1 : otherT1, addOther2));
+ }
+}
+
+void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) {
+ int count = coincidences.count();
+#if DEBUG_CONCIDENT
+ if (count > 0) {
+ SkDebugf("%s count=%d\n", __FUNCTION__, count);
+ }
+#endif
+ // look for a lineup where the partial implies another adjoining coincidence
+ for (int index = 0; index < count; ++index) {
+ const SkCoincidence& coincidence = coincidences[index];
+ int thisIndex = coincidence.fSegments[0];
+ SkOpSegment& thisOne = fSegments[thisIndex];
+ if (thisOne.done()) {
+ continue;
+ }
+ SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ SkOpSegment& other = otherContour->fSegments[otherIndex];
+ if (other.done()) {
+ continue;
+ }
+ double startT = coincidence.fTs[0][0];
+ double endT = coincidence.fTs[0][1];
+ if (startT == endT) { // this can happen in very large compares
+ continue;
+ }
+ double oStartT = coincidence.fTs[1][0];
+ double oEndT = coincidence.fTs[1][1];
+ if (oStartT == oEndT) {
+ continue;
+ }
+ bool swapStart = startT > endT;
+ bool swapOther = oStartT > oEndT;
+ const SkPoint* startPt = &coincidence.fPts[0][0];
+ const SkPoint* endPt = &coincidence.fPts[0][1];
+ if (swapStart) {
+ SkTSwap(startT, endT);
+ SkTSwap(oStartT, oEndT);
+ SkTSwap(startPt, endPt);
+ }
+ bool cancel = swapOther != swapStart;
+ int step = swapStart ? -1 : 1;
+ int oStep = swapOther ? -1 : 1;
+ double oMatchStart = cancel ? oEndT : oStartT;
+ if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) {
+ bool added = false;
+ if (oMatchStart != 0) {
+ const SkPoint& oMatchStartPt = cancel ? *endPt : *startPt;
+ added = thisOne.joinCoincidence(&other, oMatchStart, oMatchStartPt, oStep, cancel);
+ }
+ if (!cancel && startT != 0 && !added) {
+ (void) other.joinCoincidence(&thisOne, startT, *startPt, step, cancel);
+ }
+ }
+ double oMatchEnd = cancel ? oStartT : oEndT;
+ if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) {
+ bool added = false;
+ if (cancel && endT != 1 && !added) {
+ (void) other.joinCoincidence(&thisOne, endT, *endPt, -step, cancel);
+ }
+ }
+ }
+}
+
+bool SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
+ if (coincidence.fNearly[0] && coincidence.fNearly[1]) {
+ return true;
+ }
+ int thisIndex = coincidence.fSegments[0];
+ SkOpSegment& thisOne = fSegments[thisIndex];
+ if (thisOne.done()) {
+ return true;
+ }
+ SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ SkOpSegment& other = otherContour->fSegments[otherIndex];
+ if (other.done()) {
+ return true;
+ }
+ double startT = coincidence.fTs[0][0];
+ double endT = coincidence.fTs[0][1];
+ const SkPoint* startPt = &coincidence.fPts[0][0];
+ const SkPoint* endPt = &coincidence.fPts[0][1];
+ bool cancelers;
+ if ((cancelers = startT > endT)) {
+ SkTSwap<double>(startT, endT);
+ SkTSwap<const SkPoint*>(startPt, endPt);
+ }
+ bump_out_close_span(&startT, &endT);
+ SkASSERT(!approximately_negative(endT - startT));
+ double oStartT = coincidence.fTs[1][0];
+ double oEndT = coincidence.fTs[1][1];
+ if (oStartT > oEndT) {
+ SkTSwap<double>(oStartT, oEndT);
+ cancelers ^= true;
+ }
+ bump_out_close_span(&oStartT, &oEndT);
+ SkASSERT(!approximately_negative(oEndT - oStartT));
+ bool success = true;
+ if (cancelers) {
+ thisOne.addTCancel(*startPt, *endPt, &other);
+ } else {
+ success = thisOne.addTCoincident(*startPt, *endPt, endT, &other);
+ }
+#if DEBUG_CONCIDENT
+ thisOne.debugShowTs("p");
+ other.debugShowTs("o");
+#endif
+ return success;
+}
+
+void SkOpContour::resolveNearCoincidence() {
+ int count = fCoincidences.count();
+ for (int index = 0; index < count; ++index) {
+ SkCoincidence& coincidence = fCoincidences[index];
+ if (!coincidence.fNearly[0] || !coincidence.fNearly[1]) {
+ continue;
+ }
+ int thisIndex = coincidence.fSegments[0];
+ SkOpSegment& thisOne = fSegments[thisIndex];
+ SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ SkOpSegment& other = otherContour->fSegments[otherIndex];
+ if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
+ // OPTIMIZATION: remove from coincidence array
+ continue;
+ }
+ #if DEBUG_CONCIDENT
+ thisOne.debugShowTs("-");
+ other.debugShowTs("o");
+ #endif
+ double startT = coincidence.fTs[0][0];
+ double endT = coincidence.fTs[0][1];
+ bool cancelers;
+ if ((cancelers = startT > endT)) {
+ SkTSwap<double>(startT, endT);
+ }
+ if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
+ if (endT <= 1 - FLT_EPSILON) {
+ endT += FLT_EPSILON;
+ SkASSERT(endT <= 1);
+ } else {
+ startT -= FLT_EPSILON;
+ SkASSERT(startT >= 0);
+ }
+ }
+ SkASSERT(!approximately_negative(endT - startT));
+ double oStartT = coincidence.fTs[1][0];
+ double oEndT = coincidence.fTs[1][1];
+ if (oStartT > oEndT) {
+ SkTSwap<double>(oStartT, oEndT);
+ cancelers ^= true;
+ }
+ SkASSERT(!approximately_negative(oEndT - oStartT));
+ if (cancelers) {
+ thisOne.blindCancel(coincidence, &other);
+ } else {
+ thisOne.blindCoincident(coincidence, &other);
+ }
+ }
+}
+
+void SkOpContour::sortAngles() {
+ int segmentCount = fSegments.count();
+ for (int test = 0; test < segmentCount; ++test) {
+ fSegments[test].sortAngles();
+ }
+}
+
+void SkOpContour::sortSegments() {
+ int segmentCount = fSegments.count();
+ fSortedSegments.push_back_n(segmentCount);
+ for (int test = 0; test < segmentCount; ++test) {
+ fSortedSegments[test] = &fSegments[test];
+ }
+ SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
+ fFirstSorted = 0;
+}
+
void SkOpContour::toPath(SkPathWriter* path) const {
- const SkPoint& pt = fHead.pts()[0];
+ int segmentCount = fSegments.count();
+ const SkPoint& pt = fSegments.front().pts()[0];
path->deferredMove(pt);
- const SkOpSegment* segment = &fHead;
- do {
- segment->addCurveTo(segment->head(), segment->tail(), path, true);
- } while ((segment = segment->next()));
+ for (int test = 0; test < segmentCount; ++test) {
+ fSegments[test].addCurveTo(0, 1, path, true);
+ }
path->close();
}
@@ -99,14 +706,57 @@ void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
}
}
-SkOpSegment* SkOpContour::undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr) {
- SkOpSegment* segment = &fHead;
- do {
- if (segment->done()) {
+SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
+ int segmentCount = fSegments.count();
+ for (int test = 0; test < segmentCount; ++test) {
+ SkOpSegment* testSegment = &fSegments[test];
+ if (testSegment->done()) {
continue;
}
- segment->undoneSpan(startPtr, endPtr);
- return segment;
- } while ((segment = segment->next()));
+ testSegment->undoneSpan(start, end);
+ return testSegment;
+ }
return NULL;
}
+
+#if DEBUG_SHOW_WINDING
+int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
+ int count = fSegments.count();
+ int sum = 0;
+ for (int index = 0; index < count; ++index) {
+ sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
+ }
+// SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
+ return sum;
+}
+
+void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
+// int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
+// int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
+ int ofInterest = 1 << 5 | 1 << 8;
+ int total = 0;
+ int index;
+ for (index = 0; index < contourList.count(); ++index) {
+ total += contourList[index]->segments().count();
+ }
+ int sum = 0;
+ for (index = 0; index < contourList.count(); ++index) {
+ sum += contourList[index]->debugShowWindingValues(total, ofInterest);
+ }
+// SkDebugf("%s total=%d\n", __FUNCTION__, sum);
+}
+#endif
+
+void SkOpContour::setBounds() {
+ int count = fSegments.count();
+ if (count == 0) {
+ SkDebugf("%s empty contour\n", __FUNCTION__);
+ SkASSERT(0);
+ // FIXME: delete empty contour?
+ return;
+ }
+ fBounds = fSegments.front().bounds();
+ for (int index = 1; index < count; ++index) {
+ fBounds.add(fSegments[index].bounds());
+ }
+}
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index be1f59f4bb..7a1cc09247 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -8,16 +8,31 @@
#define SkOpContour_DEFINED
#include "SkOpSegment.h"
-#include "SkTDArray.h"
-#include "SkTSort.h"
+#include "SkTArray.h"
-class SkChunkAlloc;
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+#include "SkThread.h"
+#endif
+
+class SkIntersections;
+class SkOpContour;
class SkPathWriter;
+struct SkCoincidence {
+ SkOpContour* fOther;
+ int fSegments[2];
+ double fTs[2][2];
+ SkPoint fPts[2][2];
+ int fNearly[2];
+};
+
class SkOpContour {
public:
SkOpContour() {
reset();
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+ fID = sk_atomic_inc(&SkPathOpsDebug::gContourID);
+#endif
}
bool operator<(const SkOpContour& rh) const {
@@ -26,255 +41,211 @@ public:
: fBounds.fTop < rh.fBounds.fTop;
}
- void addCubic(SkPoint pts[4], SkChunkAlloc* allocator) {
- appendSegment(allocator).addCubic(pts, this);
- }
-
- void addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator);
-
- void addLine(SkPoint pts[2], SkChunkAlloc* allocator) {
- appendSegment(allocator).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()));
- }
-
- SkOpSegment& appendSegment(SkChunkAlloc* allocator) {
- SkOpSegment* result = fCount++
- ? SkOpTAllocator<SkOpSegment>::Allocate(allocator) : &fHead;
- result->setPrev(fTail);
- if (fTail) {
- fTail->setNext(result);
- }
- fTail = result;
- return *result;
- }
+ bool addCoincident(int index, SkOpContour* other, int otherIndex,
+ const SkIntersections& ts, bool swap);
+ void addCoincidentPoints();
- SkOpContour* appendContour(SkChunkAlloc* allocator) {
- SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(allocator);
-
- SkOpContour* prev = this;
- SkOpContour* next;
- while ((next = prev->next())) {
- prev = next;
+ void addCross(const SkOpContour* crosser) {
+#ifdef DEBUG_CROSS
+ for (int index = 0; index < fCrosses.count(); ++index) {
+ SkASSERT(fCrosses[index] != crosser);
}
- prev->setNext(contour);
- return contour;
- }
-
- const SkPathOpsBounds& bounds() const {
- return fBounds;
+#endif
+ fCrosses.push_back(crosser);
}
- void calcAngles(SkChunkAlloc* allocator) {
- SkASSERT(fCount > 0);
- SkOpSegment* segment = &fHead;
- do {
- segment->calcAngles(allocator);
- } while ((segment = segment->next()));
+ void addCubic(const SkPoint pts[4]) {
+ fSegments.push_back().addCubic(pts, fOperand, fXor);
+ fContainsCurves = fContainsCubics = true;
}
- void complete() {
- setBounds();
+ int addLine(const SkPoint pts[2]) {
+ fSegments.push_back().addLine(pts, fOperand, fXor);
+ return fSegments.count();
}
- int count() const {
- return fCount;
+ void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
+ fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
}
- int debugID() const {
- return PATH_OPS_DEBUG_RELEASE(fID, -1);
- }
+ bool addPartialCoincident(int index, SkOpContour* other, int otherIndex,
+ const SkIntersections& ts, int ptIndex, bool swap);
- int debugIndent() const {
- return PATH_OPS_DEBUG_RELEASE(fIndent, 0);
+ int addQuad(const SkPoint pts[3]) {
+ fSegments.push_back().addQuad(pts, fOperand, fXor);
+ fContainsCurves = true;
+ return fSegments.count();
}
-#if DEBUG_ACTIVE_SPANS
- void debugShowActiveSpans() {
- SkOpSegment* segment = &fHead;
- do {
- segment->debugShowActiveSpans();
- } while ((segment = segment->next()));
+ int addT(int segIndex, SkOpContour* other, int otherIndex, const SkPoint& pt, double newT) {
+ setContainsIntercepts();
+ return fSegments[segIndex].addT(&other->fSegments[otherIndex], pt, newT);
}
-#endif
- const SkOpAngle* debugAngle(int id) const {
- return PATH_OPS_DEBUG_RELEASE(globalState()->debugAngle(id), NULL);
+ int addSelfT(int segIndex, const SkPoint& pt, double newT) {
+ setContainsIntercepts();
+ return fSegments[segIndex].addSelfT(pt, newT);
}
- SkOpContour* debugContour(int id) {
- return PATH_OPS_DEBUG_RELEASE(globalState()->debugContour(id), NULL);
- }
+ void align(const SkOpSegment::AlignedSpan& aligned, bool swap, SkCoincidence* coincidence);
+ void alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
+ SkTArray<SkCoincidence, true>* coincidences);
- const SkOpPtT* debugPtT(int id) const {
- return PATH_OPS_DEBUG_RELEASE(globalState()->debugPtT(id), NULL);
+ void alignCoincidence(const SkOpSegment::AlignedSpan& aligned) {
+ alignCoincidence(aligned, &fCoincidences);
+ alignCoincidence(aligned, &fPartialCoincidences);
}
- const SkOpSegment* debugSegment(int id) const {
- return PATH_OPS_DEBUG_RELEASE(globalState()->debugSegment(id), NULL);
+ void alignMultiples(SkTDArray<SkOpSegment::AlignedSpan>* aligned) {
+ int segmentCount = fSegments.count();
+ for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+ SkOpSegment& segment = fSegments[sIndex];
+ if (segment.hasMultiples()) {
+ segment.alignMultiples(aligned);
+ }
+ }
}
- const SkOpSpanBase* debugSpan(int id) const {
- return PATH_OPS_DEBUG_RELEASE(globalState()->debugSpan(id), NULL);
- }
+ void alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
+ bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const;
- SkOpGlobalState* globalState() const {
- return fState;
+ const SkPathOpsBounds& bounds() const {
+ return fBounds;
}
- void debugValidate() const {
-#if DEBUG_VALIDATE
- const SkOpSegment* segment = &fHead;
- const SkOpSegment* prior = NULL;
- do {
- segment->debugValidate();
- SkASSERT(segment->prev() == prior);
- prior = segment;
- } while ((segment = segment->next()));
- SkASSERT(prior == fTail);
-#endif
- }
+ bool calcAngles();
+ bool calcCoincidentWinding();
+ void calcPartialCoincidentWinding();
- bool done() const {
- return fDone;
+ void checkDuplicates() {
+ int segmentCount = fSegments.count();
+ for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+ SkOpSegment& segment = fSegments[sIndex];
+ if (segment.count() > 2) {
+ segment.checkDuplicates();
+ }
+ }
}
- void dump();
- void dumpAll();
- void dumpAngles() const;
- void dumpPt(int ) const;
- void dumpPts() const;
- void dumpPtsX() const;
- void dumpSegment(int ) const;
- void dumpSegments(SkPathOp op) const;
- void dumpSpan(int ) const;
- void dumpSpans() const;
-
- const SkPoint& end() const {
- return fTail->pts()[SkPathOpsVerbToPoints(fTail->verb())];
+ bool checkEnds() {
+ if (!fContainsCurves) {
+ return true;
+ }
+ int segmentCount = fSegments.count();
+ for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+ SkOpSegment* segment = &fSegments[sIndex];
+ if (segment->verb() == SkPath::kLine_Verb) {
+ continue;
+ }
+ if (segment->done()) {
+ continue; // likely coincident, nothing to do
+ }
+ if (!segment->checkEnds()) {
+ return false;
+ }
+ }
+ return true;
}
- SkOpSegment* first() {
- SkASSERT(fCount > 0);
- return &fHead;
+ void checkMultiples() {
+ int segmentCount = fSegments.count();
+ for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+ SkOpSegment& segment = fSegments[sIndex];
+ if (segment.count() > 2) {
+ segment.checkMultiples();
+ fMultiples |= segment.hasMultiples();
+ }
+ }
}
- const SkOpSegment* first() const {
- SkASSERT(fCount > 0);
- return &fHead;
+ void checkSmall() {
+ int segmentCount = fSegments.count();
+ for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+ SkOpSegment& segment = fSegments[sIndex];
+ // OPTIMIZATION : skip segments that are done?
+ if (segment.hasSmall()) {
+ segment.checkSmall();
+ }
+ }
}
- void indentDump() {
- PATH_OPS_DEBUG_CODE(fIndent += 2);
+ // if same point has different T values, choose a common T
+ void checkTiny() {
+ int segmentCount = fSegments.count();
+ if (segmentCount <= 2) {
+ return;
+ }
+ for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+ SkOpSegment& segment = fSegments[sIndex];
+ if (segment.hasTiny()) {
+ segment.checkTiny();
+ }
+ }
}
- void init(SkOpGlobalState* globalState, bool operand, bool isXor) {
- fState = globalState;
- fOperand = operand;
- fXor = isXor;
+ void complete() {
+ setBounds();
+ fContainsIntercepts = false;
}
- bool isXor() const {
- return fXor;
+ bool containsCubics() const {
+ return fContainsCubics;
}
- void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
- SkASSERT(fCount > 0);
- SkOpSegment* segment = &fHead;
- do {
- if (fState->angleCoincidence()) {
- segment->checkAngleCoin(coincidences, allocator);
- } else {
- segment->missingCoincidence(coincidences, allocator);
+ bool crosses(const SkOpContour* crosser) const {
+ for (int index = 0; index < fCrosses.count(); ++index) {
+ if (fCrosses[index] == crosser) {
+ return true;
}
- } while ((segment = segment->next()));
+ }
+ return false;
}
- bool moveNearby() {
- SkASSERT(fCount > 0);
- SkOpSegment* segment = &fHead;
- do {
- if (!segment->moveNearby()) {
- return false;
- }
- } while ((segment = segment->next()));
- return true;
+ bool done() const {
+ return fDone;
}
- SkOpContour* next() {
- return fNext;
+ const SkPoint& end() const {
+ const SkOpSegment& segment = fSegments.back();
+ return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
}
- const SkOpContour* next() const {
- return fNext;
+ void fixOtherTIndex() {
+ int segmentCount = fSegments.count();
+ for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+ fSegments[sIndex].fixOtherTIndex();
+ }
}
- SkOpSegment* nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end);
-
- bool operand() const {
- return fOperand;
+ bool hasMultiples() const {
+ return fMultiples;
}
- bool oppXor() const {
- return fOppXor;
+ void joinCoincidence() {
+ joinCoincidence(fCoincidences, false);
+ joinCoincidence(fPartialCoincidences, true);
}
- void outdentDump() {
- PATH_OPS_DEBUG_CODE(fIndent -= 2);
- }
+ SkOpSegment* nonVerticalSegment(int* start, int* end);
- void remove(SkOpContour* contour) {
- if (contour == this) {
- SkASSERT(fCount == 0);
- return;
- }
- SkASSERT(contour->fNext == NULL);
- SkOpContour* prev = this;
- SkOpContour* next;
- while ((next = prev->next()) != contour) {
- SkASSERT(next);
- prev = next;
- }
- SkASSERT(prev);
- prev->setNext(NULL);
+ bool operand() const {
+ return fOperand;
}
void reset() {
- fTail = NULL;
- fNext = NULL;
- fCount = 0;
- fDone = false;
- SkDEBUGCODE(fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin));
- SkDEBUGCODE(fFirstSorted = -1);
- PATH_OPS_DEBUG_CODE(fIndent = 0);
- }
-
- void setBounds() {
- SkASSERT(fCount > 0);
- const SkOpSegment* segment = &fHead;
- fBounds = segment->bounds();
- while ((segment = segment->next())) {
- fBounds.add(segment->bounds());
- }
+ fSegments.reset();
+ fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
+ fContainsCurves = fContainsCubics = fContainsIntercepts = fDone = fMultiples = false;
}
- void setGlobalState(SkOpGlobalState* state) {
- fState = state;
+ void resolveNearCoincidence();
+
+ SkTArray<SkOpSegment>& segments() {
+ return fSegments;
}
- void setNext(SkOpContour* contour) {
- SkASSERT(!fNext == !!contour);
- fNext = contour;
+ void setContainsIntercepts() {
+ fContainsIntercepts = true;
}
void setOperand(bool isOp) {
@@ -283,68 +254,107 @@ public:
void setOppXor(bool isOppXor) {
fOppXor = isOppXor;
+ int segmentCount = fSegments.count();
+ for (int test = 0; test < segmentCount; ++test) {
+ fSegments[test].setOppXor(isOppXor);
+ }
}
void setXor(bool isXor) {
fXor = isXor;
}
- SkPath::Verb simplifyCubic(SkPoint pts[4]);
-
- void sortAngles() {
- SkASSERT(fCount > 0);
- SkOpSegment* segment = &fHead;
- do {
- segment->sortAngles();
- } while ((segment = segment->next()));
- }
-
- void sortSegments() {
- SkOpSegment* segment = &fHead;
- do {
- *fSortedSegments.append() = segment;
- } while ((segment = segment->next()));
- SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
- fFirstSorted = 0;
- }
+ void sortAngles();
+ void sortSegments();
const SkPoint& start() const {
- return fHead.pts()[0];
+ return fSegments.front().pts()[0];
}
+ void toPath(SkPathWriter* path) const;
+
void toPartialBackward(SkPathWriter* path) const {
- const SkOpSegment* segment = fTail;
- do {
- segment->addCurveTo(segment->tail(), segment->head(), path, true);
- } while ((segment = segment->prev()));
+ int segmentCount = fSegments.count();
+ for (int test = segmentCount - 1; test >= 0; --test) {
+ fSegments[test].addCurveTo(1, 0, path, true);
+ }
}
void toPartialForward(SkPathWriter* path) const {
- const SkOpSegment* segment = &fHead;
- do {
- segment->addCurveTo(segment->head(), segment->tail(), path, true);
- } while ((segment = segment->next()));
+ int segmentCount = fSegments.count();
+ for (int test = 0; test < segmentCount; ++test) {
+ fSegments[test].addCurveTo(0, 1, path, true);
+ }
}
- void toPath(SkPathWriter* path) const;
void topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, SkOpSegment** topStart);
- SkOpSegment* undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr);
+ SkOpSegment* undoneSegment(int* start, int* end);
+
+ int updateSegment(int index, const SkPoint* pts) {
+ SkOpSegment& segment = fSegments[index];
+ segment.updatePts(pts);
+ return SkPathOpsVerbToPoints(segment.verb()) + 1;
+ }
+
+#if DEBUG_TEST
+ SkTArray<SkOpSegment>& debugSegments() {
+ return fSegments;
+ }
+#endif
+
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
+ void debugShowActiveSpans() {
+ for (int index = 0; index < fSegments.count(); ++index) {
+ fSegments[index].debugShowActiveSpans();
+ }
+ }
+#endif
+
+#if DEBUG_SHOW_WINDING
+ int debugShowWindingValues(int totalSegments, int ofInterest);
+ static void debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList);
+#endif
+
+ // available to test routines only
+ void dump() const;
+ void dumpAngles() const;
+ void dumpCoincidence(const SkCoincidence& ) const;
+ void dumpCoincidences() const;
+ void dumpPt(int ) const;
+ void dumpPts() const;
+ void dumpSpan(int ) const;
+ void dumpSpans() const;
private:
- SkOpGlobalState* fState;
- SkOpSegment fHead;
- SkOpSegment* fTail;
- SkOpContour* fNext;
- SkTDArray<SkOpSegment*> fSortedSegments; // set by find top segment
- SkPathOpsBounds fBounds;
- int fCount;
+ void alignPt(int index, SkPoint* point, int zeroPt) const;
+ int alignT(bool swap, int tIndex, SkIntersections* ts) const;
+ bool calcCommonCoincidentWinding(const SkCoincidence& );
+ void checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
+ const SkCoincidence& twoCoin, int twoIdx, bool partial);
+ void joinCoincidence(const SkTArray<SkCoincidence, true>& , bool partial);
+ void setBounds();
+
+ SkTArray<SkOpSegment> fSegments;
+ SkTArray<SkOpSegment*, true> fSortedSegments;
int fFirstSorted;
- bool fDone; // set by find top segment
+ SkTArray<SkCoincidence, true> fCoincidences;
+ SkTArray<SkCoincidence, true> fPartialCoincidences;
+ SkTArray<const SkOpContour*, true> fCrosses;
+ SkPathOpsBounds fBounds;
+ bool fContainsIntercepts; // FIXME: is this used by anybody?
+ bool fContainsCubics;
+ bool fContainsCurves;
+ bool fDone;
+ bool fMultiples; // set if some segment has multiple identical intersections with other curves
bool fOperand; // true for the second argument to a binary operator
- bool fXor; // set if original path had even-odd fill
- bool fOppXor; // set if opposite path had even-odd fill
- PATH_OPS_DEBUG_CODE(int fID);
- PATH_OPS_DEBUG_CODE(int fIndent);
+ bool fXor;
+ bool fOppXor;
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+ int debugID() const { return fID; }
+ int fID;
+#else
+ int debugID() const { return -1; }
+#endif
};
#endif
diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp
index bd21d729b6..803a5f4739 100644
--- a/src/pathops/SkOpEdgeBuilder.cpp
+++ b/src/pathops/SkOpEdgeBuilder.cpp
@@ -9,7 +9,7 @@
#include "SkReduceOrder.h"
void SkOpEdgeBuilder::init() {
- fCurrentContour = fContoursHead;
+ fCurrentContour = NULL;
fOperand = false;
fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
: kWinding_PathOpsMask;
@@ -19,43 +19,32 @@ void SkOpEdgeBuilder::init() {
void SkOpEdgeBuilder::addOperand(const SkPath& path) {
SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
- fPathVerbs.pop();
+ fPathVerbs.pop_back();
fPath = &path;
fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
: kWinding_PathOpsMask;
preFetch();
}
-int SkOpEdgeBuilder::count() const {
- SkOpContour* contour = fContoursHead;
- int count = 0;
- while (contour) {
- count += contour->count() > 0;
- contour = contour->next();
- }
- return count;
-}
-
-bool SkOpEdgeBuilder::finish(SkChunkAlloc* allocator) {
- fOperand = false;
- if (fUnparseable || !walk(allocator)) {
+bool SkOpEdgeBuilder::finish() {
+ if (fUnparseable || !walk()) {
return false;
}
complete();
- if (fCurrentContour && !fCurrentContour->count()) {
- fContoursHead->remove(fCurrentContour);
+ if (fCurrentContour && !fCurrentContour->segments().count()) {
+ fContours.pop_back();
}
return true;
}
void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) {
if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) {
- *fPathVerbs.append() = SkPath::kLine_Verb;
- *fPathPts.append() = curveStart;
+ fPathVerbs.push_back(SkPath::kLine_Verb);
+ fPathPts.push_back_n(1, &curveStart);
} else {
fPathPts[fPathPts.count() - 1] = curveStart;
}
- *fPathVerbs.append() = SkPath::kClose_Verb;
+ fPathVerbs.push_back(SkPath::kClose_Verb);
}
// very tiny points cause numerical instability : don't allow them
@@ -68,6 +57,7 @@ static void force_small_to_zero(SkPoint* pt) {
}
}
+
int SkOpEdgeBuilder::preFetch() {
if (!fPath->isFinite()) {
fUnparseable = true;
@@ -88,18 +78,18 @@ int SkOpEdgeBuilder::preFetch() {
if (!fAllowOpenContours && lastCurve) {
closeContour(curve[0], curveStart);
}
- *fPathVerbs.append() = verb;
+ fPathVerbs.push_back(verb);
force_small_to_zero(&pts[0]);
- *fPathPts.append() = pts[0];
+ fPathPts.push_back(pts[0]);
curveStart = curve[0] = pts[0];
lastCurve = false;
continue;
case SkPath::kLine_Verb:
force_small_to_zero(&pts[1]);
if (SkDPoint::ApproximatelyEqual(curve[0], pts[1])) {
- uint8_t lastVerb = fPathVerbs.top();
+ uint8_t lastVerb = fPathVerbs.back();
if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) {
- fPathPts.top() = pts[1];
+ fPathPts.back() = pts[1];
}
continue; // skip degenerate points
}
@@ -119,9 +109,9 @@ int SkOpEdgeBuilder::preFetch() {
quadderTol);
const int nQuads = quadder.countQuads();
for (int i = 0; i < nQuads; ++i) {
- *fPathVerbs.append() = SkPath::kQuad_Verb;
+ fPathVerbs.push_back(SkPath::kQuad_Verb);
}
- fPathPts.append(nQuads * 2, &quadPts[1]);
+ fPathPts.push_back_n(nQuads * 2, &quadPts[1]);
curve[0] = pts[2];
lastCurve = true;
}
@@ -145,16 +135,16 @@ int SkOpEdgeBuilder::preFetch() {
case SkPath::kDone_Verb:
continue;
}
- *fPathVerbs.append() = verb;
+ fPathVerbs.push_back(verb);
int ptCount = SkPathOpsVerbToPoints(verb);
- fPathPts.append(ptCount, &pts[1]);
+ fPathPts.push_back_n(ptCount, &pts[1]);
curve[0] = pts[ptCount];
lastCurve = true;
} while (verb != SkPath::kDone_Verb);
if (!fAllowOpenContours && lastCurve) {
closeContour(curve[0], curveStart);
}
- *fPathVerbs.append() = SkPath::kDone_Verb;
+ fPathVerbs.push_back(SkPath::kDone_Verb);
return fPathVerbs.count() - 1;
}
@@ -163,10 +153,10 @@ 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;
+ const SkPoint* pointsPtr = fPathPts.begin() - 1;
SkPath::Verb verb;
while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
if (verbPtr == endOfFirstHalf) {
@@ -175,7 +165,7 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
verbPtr++;
switch (verb) {
case SkPath::kMove_Verb:
- if (fCurrentContour && fCurrentContour->count()) {
+ if (fCurrentContour) {
if (fAllowOpenContours) {
complete();
} else if (!close()) {
@@ -183,44 +173,21 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
}
}
if (!fCurrentContour) {
- fCurrentContour = fContoursHead->appendContour(allocator);
+ fCurrentContour = fContours.push_back_n(1);
+ fCurrentContour->setOperand(fOperand);
+ fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_PathOpsMask);
}
- 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::kCubic_Verb:
+ fCurrentContour->addCubic(pointsPtr);
break;
- case SkPath::kCubic_Verb: {
- // split self-intersecting cubics in two before proceeding
- // if the cubic is convex, it doesn't self intersect.
- SkScalar loopT;
- if (SkDCubic::ComplexBreak(pointsPtr, &loopT)) {
- SkPoint cubicPair[7];
- SkChopCubicAt(pointsPtr, cubicPair, loopT);
- SkPoint cStorage[2][4];
- SkPath::Verb v1 = SkReduceOrder::Cubic(&cubicPair[0], cStorage[0]);
- SkPath::Verb v2 = SkReduceOrder::Cubic(&cubicPair[3], cStorage[1]);
- if (v1 != SkPath::kMove_Verb && v2 != SkPath::kMove_Verb) {
- SkPoint* curve1 = v1 == SkPath::kCubic_Verb ? &cubicPair[0] : cStorage[0];
- SkPoint* curve2 = v2 == SkPath::kCubic_Verb ? &cubicPair[3] : cStorage[1];
- for (size_t index = 0; index < SK_ARRAY_COUNT(curve1); ++index) {
- force_small_to_zero(&curve1[index]);
- force_small_to_zero(&curve2[index]);
- }
- fCurrentContour->addCurve(v1, curve1, fAllocator);
- fCurrentContour->addCurve(v2, curve2, fAllocator);
- } else {
- fCurrentContour->addCubic(pointsPtr, fAllocator);
- }
- } else {
- fCurrentContour->addCubic(pointsPtr, fAllocator);
- }
- } break;
case SkPath::kClose_Verb:
SkASSERT(fCurrentContour);
if (!close()) {
@@ -231,11 +198,10 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
SkDEBUGFAIL("bad verb");
return false;
}
- SkASSERT(fCurrentContour);
- fCurrentContour->debugValidate();
pointsPtr += SkPathOpsVerbToPoints(verb);
+ SkASSERT(fCurrentContour);
}
- if (fCurrentContour && fCurrentContour->count() &&!fAllowOpenContours && !close()) {
+ if (fCurrentContour && !fAllowOpenContours && !close()) {
return false;
}
return true;
diff --git a/src/pathops/SkOpEdgeBuilder.h b/src/pathops/SkOpEdgeBuilder.h
index 3ecc915833..fd0744572d 100644
--- a/src/pathops/SkOpEdgeBuilder.h
+++ b/src/pathops/SkOpEdgeBuilder.h
@@ -9,25 +9,20 @@
#include "SkOpContour.h"
#include "SkPathWriter.h"
+#include "SkTArray.h"
class SkOpEdgeBuilder {
public:
- SkOpEdgeBuilder(const SkPathWriter& path, SkOpContour* contours2, SkChunkAlloc* allocator,
- SkOpGlobalState* globalState)
- : fAllocator(allocator) // FIXME: replace with const, tune this
- , fGlobalState(globalState)
- , fPath(path.nativePath())
- , fContoursHead(contours2)
+ SkOpEdgeBuilder(const SkPathWriter& path, SkTArray<SkOpContour>& contours)
+ : fPath(path.nativePath())
+ , fContours(contours)
, fAllowOpenContours(true) {
init();
}
- SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkChunkAlloc* allocator,
- SkOpGlobalState* globalState)
- : fAllocator(allocator)
- , fGlobalState(globalState)
- , fPath(&path)
- , fContoursHead(contours2)
+ SkOpEdgeBuilder(const SkPath& path, SkTArray<SkOpContour>& contours)
+ : fPath(&path)
+ , fContours(contours)
, fAllowOpenContours(false) {
init();
}
@@ -35,19 +30,13 @@ public:
void addOperand(const SkPath& path);
void complete() {
- if (fCurrentContour && fCurrentContour->count()) {
+ if (fCurrentContour && fCurrentContour->segments().count()) {
fCurrentContour->complete();
fCurrentContour = NULL;
}
}
- int count() const;
- bool finish(SkChunkAlloc* );
-
- const SkOpContour* head() const {
- return fContoursHead;
- }
-
+ bool finish();
void init();
bool unparseable() const { return fUnparseable; }
SkPathOpsMask xorMask() const { return fXorMask[fOperand]; }
@@ -56,15 +45,13 @@ 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;
- SkTDArray<uint8_t> fPathVerbs;
+ SkTArray<SkPoint, true> fPathPts;
+ SkTArray<uint8_t, true> fPathVerbs;
SkOpContour* fCurrentContour;
- SkOpContour* fContoursHead;
+ SkTArray<SkOpContour>& fContours;
SkPathOpsMask fXorMask[2];
int fSecondHalf;
bool fOperand;
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 902f273744..1fb5afa028 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -4,20 +4,11 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-#include "SkOpCoincidence.h"
+#include "SkIntersections.h"
#include "SkOpContour.h"
#include "SkOpSegment.h"
#include "SkPathWriter.h"
-
-/*
-After computing raw intersections, post process all segments to:
-- find small collections of points that can be collapsed to a single point
-- find missing intersections to resolve differences caused by different algorithms
-
-Consider segments containing tiny or small intervals. Consider coincident segments
-because coincidence finds intersections through distance measurement that non-coincident
-intersection tests cannot.
- */
+#include "SkTSort.h"
#define F (false) // discard the edge
#define T (true) // keep the edge
@@ -42,125 +33,147 @@ static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
#undef F
#undef T
-SkOpAngle* SkOpSegment::activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr, bool* done, bool* sortable) {
- if (SkOpAngle* result = activeAngleInner(start, startPtr, endPtr, done, sortable)) {
+enum {
+ kOutsideTrackedTCount = 16, // FIXME: determine what this should be
+ kMissingSpanCount = 4, // FIXME: determine what this should be
+};
+
+const SkOpAngle* SkOpSegment::activeAngle(int index, int* start, int* end, bool* done,
+ bool* sortable) const {
+ if (const SkOpAngle* result = activeAngleInner(index, start, end, done, sortable)) {
return result;
}
- if (SkOpAngle* result = activeAngleOther(start, startPtr, endPtr, done, sortable)) {
- return result;
+ double referenceT = fTs[index].fT;
+ int lesser = index;
+ while (--lesser >= 0
+ && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) {
+ if (const SkOpAngle* result = activeAngleOther(lesser, start, end, done, sortable)) {
+ return result;
+ }
}
+ do {
+ if (const SkOpAngle* result = activeAngleOther(index, start, end, done, sortable)) {
+ return result;
+ }
+ if (++index == fTs.count()) {
+ break;
+ }
+ if (fTs[index - 1].fTiny) {
+ referenceT = fTs[index].fT;
+ continue;
+ }
+ } while (precisely_negative(fTs[index].fT - referenceT));
return NULL;
}
-SkOpAngle* SkOpSegment::activeAngleInner(SkOpSpanBase* start, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr, bool* done, bool* sortable) {
- SkOpSpan* upSpan = start->upCastable();
- if (upSpan) {
- if (upSpan->windValue() || upSpan->oppValue()) {
- SkOpSpanBase* next = upSpan->next();
- if (!*endPtr) {
- *startPtr = start;
- *endPtr = next;
+const SkOpAngle* SkOpSegment::activeAngleInner(int index, int* start, int* end, bool* done,
+ bool* sortable) const {
+ int next = nextExactSpan(index, 1);
+ if (next > 0) {
+ const SkOpSpan& upSpan = fTs[index];
+ if (upSpan.fWindValue || upSpan.fOppValue) {
+ if (*end < 0) {
+ *start = index;
+ *end = next;
}
- if (!upSpan->done()) {
- if (upSpan->windSum() != SK_MinS32) {
- return spanToAngle(start, next);
+ if (!upSpan.fDone) {
+ if (upSpan.fWindSum != SK_MinS32) {
+ return spanToAngle(index, next);
}
*done = false;
}
} else {
- SkASSERT(upSpan->done());
+ SkASSERT(upSpan.fDone);
}
}
- SkOpSpan* downSpan = start->prev();
+ int prev = nextExactSpan(index, -1);
// edge leading into junction
- if (downSpan) {
- if (downSpan->windValue() || downSpan->oppValue()) {
- if (!*endPtr) {
- *startPtr = start;
- *endPtr = downSpan;
- }
- if (!downSpan->done()) {
- if (downSpan->windSum() != SK_MinS32) {
- return spanToAngle(start, downSpan);
+ if (prev >= 0) {
+ const SkOpSpan& downSpan = fTs[prev];
+ if (downSpan.fWindValue || downSpan.fOppValue) {
+ if (*end < 0) {
+ *start = index;
+ *end = prev;
+ }
+ if (!downSpan.fDone) {
+ if (downSpan.fWindSum != SK_MinS32) {
+ return spanToAngle(index, prev);
}
*done = false;
}
} else {
- SkASSERT(downSpan->done());
+ SkASSERT(downSpan.fDone);
}
}
return NULL;
}
-SkOpAngle* SkOpSegment::activeAngleOther(SkOpSpanBase* start, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr, bool* done, bool* sortable) {
- SkOpPtT* oPtT = start->ptT()->next();
- SkOpSegment* other = oPtT->segment();
- SkOpSpanBase* oSpan = oPtT->span();
- return other->activeAngleInner(oSpan, startPtr, endPtr, done, sortable);
+const SkOpAngle* SkOpSegment::activeAngleOther(int index, int* start, int* end, bool* done,
+ bool* sortable) const {
+ const SkOpSpan* span = &fTs[index];
+ SkOpSegment* other = span->fOther;
+ int oIndex = span->fOtherIndex;
+ return other->activeAngleInner(oIndex, start, end, done, sortable);
}
-SkPoint SkOpSegment::activeLeftTop(SkOpSpanBase** firstSpan) {
+SkPoint SkOpSegment::activeLeftTop(int* firstT) const {
SkASSERT(!done());
SkPoint topPt = {SK_ScalarMax, SK_ScalarMax};
+ int count = fTs.count();
// see if either end is not done since we want smaller Y of the pair
bool lastDone = true;
double lastT = -1;
- SkOpSpanBase* span = &fHead;
- do {
- if (lastDone && (span->final() || span->upCast()->done())) {
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (span.fDone && lastDone) {
+ goto next;
+ }
+ if (approximately_negative(span.fT - lastT)) {
goto next;
}
{
- const SkPoint& xy = span->pt();
+ const SkPoint& xy = xyAtT(&span);
if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
topPt = xy;
- if (firstSpan) {
- *firstSpan = span;
+ if (firstT) {
+ *firstT = index;
}
}
if (fVerb != SkPath::kLine_Verb && !lastDone) {
- SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT,
- span->t());
+ SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
&& topPt.fX > curveTop.fX)) {
topPt = curveTop;
- if (firstSpan) {
- *firstSpan = span;
+ if (firstT) {
+ *firstT = index;
}
}
}
- lastT = span->t();
+ lastT = span.fT;
}
next:
- if (span->final()) {
- break;
- }
- lastDone = span->upCast()->done();
- } while ((span = span->upCast()->next()));
+ lastDone = span.fDone;
+ }
return topPt;
}
-bool SkOpSegment::activeOp(SkOpSpanBase* start, SkOpSpanBase* end, int xorMiMask, int xorSuMask,
- SkPathOp op) {
- int sumMiWinding = this->updateWinding(end, start);
- int sumSuWinding = this->updateOppWinding(end, start);
+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 (this->operand()) {
+ if (fOperand) {
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
- return this->activeOp(xorMiMask, xorSuMask, start, end, op, &sumMiWinding, &sumSuWinding);
+ return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding);
}
-bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end,
- SkPathOp op, int* sumMiWinding, int* sumSuWinding) {
+bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
+ int* sumMiWinding, int* sumSuWinding) {
int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
- this->setUpWindings(start, end, sumMiWinding, sumSuWinding,
+ setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
&maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
bool miFrom;
bool miTo;
@@ -180,31 +193,178 @@ bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, Sk
bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
#if DEBUG_ACTIVE_OP
SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n",
- __FUNCTION__, debugID(), start->t(), end->t(),
+ __FUNCTION__, debugID(), span(index).fT, span(endIndex).fT,
SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
#endif
return result;
}
-bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end) {
- int sumWinding = updateWinding(end, start);
- return activeWinding(start, end, &sumWinding);
+bool SkOpSegment::activeWinding(int index, int endIndex) {
+ int sumWinding = updateWinding(endIndex, index);
+ return activeWinding(index, endIndex, &sumWinding);
}
-bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding) {
+bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) {
int maxWinding;
- setUpWinding(start, end, &maxWinding, sumWinding);
+ setUpWinding(index, endIndex, &maxWinding, sumWinding);
bool from = maxWinding != 0;
bool to = *sumWinding != 0;
bool result = gUnaryActiveEdge[from][to];
return result;
}
-void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
- SkPathWriter* path, bool active) const {
+void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt,
+ SkOpSegment* other) {
+ int tIndex = -1;
+ int tCount = fTs.count();
+ int oIndex = -1;
+ int oCount = other->fTs.count();
+ do {
+ ++tIndex;
+ } while (startPt != fTs[tIndex].fPt && tIndex < tCount);
+ int tIndexStart = tIndex;
+ do {
+ ++oIndex;
+ } while (endPt != other->fTs[oIndex].fPt && oIndex < oCount);
+ int oIndexStart = oIndex;
+ const SkPoint* nextPt;
+ do {
+ nextPt = &fTs[++tIndex].fPt;
+ SkASSERT(fTs[tIndex].fT < 1 || startPt != *nextPt);
+ } while (startPt == *nextPt);
+ double nextT = fTs[tIndex].fT;
+ const SkPoint* oNextPt;
+ do {
+ oNextPt = &other->fTs[++oIndex].fPt;
+ SkASSERT(other->fTs[oIndex].fT < 1 || endPt != *oNextPt);
+ } while (endPt == *oNextPt);
+ double oNextT = other->fTs[oIndex].fT;
+ // at this point, spans before and after are at:
+ // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
+ // if tIndexStart == 0, no prior span
+ // if nextT == 1, no following span
+
+ // advance the span with zero winding
+ // if the following span exists (not past the end, non-zero winding)
+ // connect the two edges
+ if (!fTs[tIndexStart].fWindValue) {
+ if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
+#if DEBUG_CONCIDENT
+ SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+ __FUNCTION__, fID, other->fID, tIndexStart - 1,
+ fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
+ xyAtT(tIndexStart).fY);
+#endif
+ SkPoint copy = fTs[tIndexStart].fPt; // add t pair may move the point array
+ addTPair(fTs[tIndexStart].fT, other, other->fTs[oIndex].fT, false, copy);
+ }
+ if (nextT < 1 && fTs[tIndex].fWindValue) {
+#if DEBUG_CONCIDENT
+ SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+ __FUNCTION__, fID, other->fID, tIndex,
+ fTs[tIndex].fT, xyAtT(tIndex).fX,
+ xyAtT(tIndex).fY);
+#endif
+ SkPoint copy = fTs[tIndex].fPt; // add t pair may move the point array
+ addTPair(fTs[tIndex].fT, other, other->fTs[oIndexStart].fT, false, copy);
+ }
+ } else {
+ SkASSERT(!other->fTs[oIndexStart].fWindValue);
+ if (oIndexStart > 0 && other->fTs[oIndexStart - 1].fWindValue) {
+#if DEBUG_CONCIDENT
+ SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+ __FUNCTION__, fID, other->fID, oIndexStart - 1,
+ other->fTs[oIndexStart].fT, other->xyAtT(oIndexStart).fX,
+ other->xyAtT(oIndexStart).fY);
+ other->debugAddTPair(other->fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
+#endif
+ }
+ if (oNextT < 1 && other->fTs[oIndex].fWindValue) {
+#if DEBUG_CONCIDENT
+ SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+ __FUNCTION__, fID, other->fID, oIndex,
+ other->fTs[oIndex].fT, other->xyAtT(oIndex).fX,
+ other->xyAtT(oIndex).fY);
+ other->debugAddTPair(other->fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
+#endif
+ }
+ }
+}
+
+void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
+ SkOpSegment* other) {
+ // walk this to startPt
+ // walk other to startPt
+ // if either is > 0, add a pointer to the other, copying adjacent winding
+ int tIndex = -1;
+ int oIndex = -1;
+ do {
+ ++tIndex;
+ } while (startPt != fTs[tIndex].fPt);
+ int ttIndex = tIndex;
+ bool checkOtherTMatch = false;
+ do {
+ const SkOpSpan& span = fTs[ttIndex];
+ if (startPt != span.fPt) {
+ break;
+ }
+ if (span.fOther == other && span.fPt == startPt) {
+ checkOtherTMatch = true;
+ break;
+ }
+ } while (++ttIndex < count());
+ do {
+ ++oIndex;
+ } while (startPt != other->fTs[oIndex].fPt);
+ bool skipAdd = false;
+ if (checkOtherTMatch) {
+ int ooIndex = oIndex;
+ do {
+ const SkOpSpan& oSpan = other->fTs[ooIndex];
+ if (startPt != oSpan.fPt) {
+ break;
+ }
+ if (oSpan.fT == fTs[ttIndex].fOtherT) {
+ skipAdd = true;
+ break;
+ }
+ } while (++ooIndex < other->count());
+ }
+ if ((tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) && !skipAdd) {
+ addTPair(fTs[tIndex].fT, other, other->fTs[oIndex].fT, false, startPt);
+ }
+ SkPoint nextPt = startPt;
+ do {
+ const SkPoint* workPt;
+ do {
+ workPt = &fTs[++tIndex].fPt;
+ } while (nextPt == *workPt);
+ const SkPoint* oWorkPt;
+ do {
+ oWorkPt = &other->fTs[++oIndex].fPt;
+ } while (nextPt == *oWorkPt);
+ nextPt = *workPt;
+ double tStart = fTs[tIndex].fT;
+ double oStart = other->fTs[oIndex].fT;
+ if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) {
+ break;
+ }
+ if (*workPt == *oWorkPt) {
+ addTPair(tStart, other, oStart, false, nextPt);
+ }
+ } while (endPt != nextPt);
+}
+
+void SkOpSegment::addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
+ init(pts, SkPath::kCubic_Verb, operand, evenOdd);
+ fBounds.setCubicBounds(pts);
+}
+
+void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active) const {
SkPoint edge[4];
const SkPoint* ePtr;
- if ((start == &fHead && end == &fTail) || (start == &fTail && end == &fHead)) {
+ int lastT = fTs.count() - 1;
+ if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
ePtr = fPts;
} else {
// OPTIMIZE? if not active, skip remainder and return xyAtT(end)
@@ -212,7 +372,7 @@ void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
ePtr = edge;
}
if (active) {
- bool reverse = ePtr == fPts && start != &fHead;
+ bool reverse = ePtr == fPts && start != 0;
if (reverse) {
path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
switch (fVerb) {
@@ -228,6 +388,7 @@ void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
default:
SkASSERT(0);
}
+ // return ePtr[0];
} else {
path->deferredMoveLine(ePtr[0]);
switch (fVerb) {
@@ -245,350 +406,1473 @@ void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
}
}
}
+ // return ePtr[SkPathOpsVerbToPoints(fVerb)];
+}
+
+void SkOpSegment::addEndSpan(int endIndex) {
+ SkASSERT(span(endIndex).fT == 1 || (span(endIndex).fTiny
+// && approximately_greater_than_one(span(endIndex).fT)
+ ));
+ int spanCount = fTs.count();
+ int startIndex = endIndex - 1;
+ while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) {
+ --startIndex;
+ SkASSERT(startIndex > 0);
+ --endIndex;
+ }
+ SkOpAngle& angle = fAngles.push_back();
+ angle.set(this, spanCount - 1, startIndex);
+#if DEBUG_ANGLE
+ debugCheckPointsEqualish(endIndex, spanCount);
+#endif
+ setFromAngle(endIndex, &angle);
}
-SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) {
- SkOpSpanBase* existing = NULL;
- SkOpSpanBase* test = &fHead;
- double testT;
+void SkOpSegment::setFromAngle(int endIndex, SkOpAngle* angle) {
+ int spanCount = fTs.count();
do {
- if ((testT = test->ptT()->fT) >= t) {
- if (testT == t) {
- existing = test;
- }
- break;
- }
- } while ((test = test->upCast()->next()));
- SkOpPtT* result;
- if (existing && existing->contains(opp)) {
- result = existing->ptT();
- } else {
- result = this->addT(t, SkOpSegment::kNoAlias, allocator);
+ fTs[endIndex].fFromAngle = angle;
+ } while (++endIndex < spanCount);
+}
+
+void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
+ init(pts, SkPath::kLine_Verb, operand, evenOdd);
+ fBounds.set(pts, 2);
+}
+
+// add 2 to edge or out of range values to get T extremes
+void SkOpSegment::addOtherT(int index, double otherT, int otherIndex) {
+ SkOpSpan& span = fTs[index];
+ if (precisely_zero(otherT)) {
+ otherT = 0;
+ } else if (precisely_equal(otherT, 1)) {
+ otherT = 1;
}
- SkASSERT(result);
- return result;
+ span.fOtherT = otherT;
+ span.fOtherIndex = otherIndex;
}
-SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr,
- SkChunkAlloc* allocator) {
- SkOpSpan* startSpan = fTail.prev();
- SkASSERT(startSpan);
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- *anglePtr = angle;
- angle->set(&fTail, startSpan);
- fTail.setFromAngle(angle);
- SkOpSegment* other = NULL; // these initializations silence a release build warning
- SkOpSpan* oStartSpan = NULL;
- SkOpSpanBase* oEndSpan = NULL;
- SkOpPtT* ptT = fTail.ptT(), * startPtT = ptT;
- while ((ptT = ptT->next()) != startPtT) {
- other = ptT->segment();
- oStartSpan = ptT->span()->upCastable();
- if (oStartSpan && oStartSpan->windValue()) {
- oEndSpan = oStartSpan->next();
+void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
+ init(pts, SkPath::kQuad_Verb, operand, evenOdd);
+ fBounds.setQuadBounds(pts);
+}
+
+SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
+ int spanIndex = count() - 1;
+ int startIndex = nextExactSpan(spanIndex, -1);
+ SkASSERT(startIndex >= 0);
+ SkOpAngle& angle = fAngles.push_back();
+ *anglePtr = &angle;
+ angle.set(this, spanIndex, startIndex);
+ setFromAngle(spanIndex, &angle);
+ SkOpSegment* other;
+ int oStartIndex, oEndIndex;
+ do {
+ const SkOpSpan& span = fTs[spanIndex];
+ SkASSERT(span.fT > 0);
+ other = span.fOther;
+ oStartIndex = span.fOtherIndex;
+ oEndIndex = other->nextExactSpan(oStartIndex, 1);
+ if (oEndIndex > 0 && other->span(oStartIndex).fWindValue) {
break;
}
- oEndSpan = ptT->span();
- oStartSpan = oEndSpan->prev();
- if (oStartSpan && oStartSpan->windValue()) {
+ oEndIndex = oStartIndex;
+ oStartIndex = other->nextExactSpan(oEndIndex, -1);
+ --spanIndex;
+ } while (oStartIndex < 0 || !other->span(oStartIndex).fWindSum);
+ SkOpAngle& oAngle = other->fAngles.push_back();
+ oAngle.set(other, oStartIndex, oEndIndex);
+ other->setToAngle(oEndIndex, &oAngle);
+ *otherPtr = other;
+ return &oAngle;
+}
+
+SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
+ int endIndex = nextExactSpan(0, 1);
+ SkASSERT(endIndex > 0);
+ SkOpAngle& angle = fAngles.push_back();
+ *anglePtr = &angle;
+ angle.set(this, 0, endIndex);
+ setToAngle(endIndex, &angle);
+ int spanIndex = 0;
+ SkOpSegment* other;
+ int oStartIndex, oEndIndex;
+ do {
+ const SkOpSpan& span = fTs[spanIndex];
+ SkASSERT(span.fT < 1);
+ other = span.fOther;
+ oEndIndex = span.fOtherIndex;
+ oStartIndex = other->nextExactSpan(oEndIndex, -1);
+ if (oStartIndex >= 0 && other->span(oStartIndex).fWindValue) {
break;
}
- }
- SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- oAngle->set(oStartSpan, oEndSpan);
- oStartSpan->setToAngle(oAngle);
+ oStartIndex = oEndIndex;
+ oEndIndex = other->nextExactSpan(oStartIndex, 1);
+ ++spanIndex;
+ } while (oEndIndex < 0 || !other->span(oStartIndex).fWindValue);
+ SkOpAngle& oAngle = other->fAngles.push_back();
+ oAngle.set(other, oEndIndex, oStartIndex);
+ other->setFromAngle(oEndIndex, &oAngle);
*otherPtr = other;
- return oAngle;
+ return &oAngle;
}
-SkOpAngle* SkOpSegment::addSingletonAngles(int step, SkChunkAlloc* allocator) {
+SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
SkOpSegment* other;
SkOpAngle* angle, * otherAngle;
if (step > 0) {
- otherAngle = addSingletonAngleUp(&other, &angle, allocator);
+ otherAngle = addSingletonAngleUp(&other, &angle);
} else {
- otherAngle = addSingletonAngleDown(&other, &angle, allocator);
+ otherAngle = addSingletonAngleDown(&other, &angle);
}
angle->insert(otherAngle);
return angle;
}
-SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr,
- SkChunkAlloc* allocator) {
- SkOpSpanBase* endSpan = fHead.next();
- SkASSERT(endSpan);
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- *anglePtr = angle;
- angle->set(&fHead, endSpan);
- fHead.setToAngle(angle);
- SkOpSegment* other = NULL; // these initializations silence a release build warning
- SkOpSpan* oStartSpan = NULL;
- SkOpSpanBase* oEndSpan = NULL;
- SkOpPtT* ptT = fHead.ptT(), * startPtT = ptT;
- while ((ptT = ptT->next()) != startPtT) {
- other = ptT->segment();
- oEndSpan = ptT->span();
- oStartSpan = oEndSpan->prev();
- if (oStartSpan && oStartSpan->windValue()) {
+void SkOpSegment::addStartSpan(int endIndex) {
+ int index = 0;
+ SkOpAngle& angle = fAngles.push_back();
+ angle.set(this, index, endIndex);
+#if DEBUG_ANGLE
+ debugCheckPointsEqualish(index, endIndex);
+#endif
+ setToAngle(endIndex, &angle);
+}
+
+void SkOpSegment::setToAngle(int endIndex, SkOpAngle* angle) {
+ int index = 0;
+ do {
+ fTs[index].fToAngle = angle;
+ } while (++index < endIndex);
+}
+
+ // Defer all coincident edge processing until
+ // after normal intersections have been computed
+
+// no need to be tricky; insert in normal T order
+// resolve overlapping ts when considering coincidence later
+
+ // add non-coincident intersection. Resulting edges are sorted in T.
+int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
+ SkASSERT(this != other || fVerb == SkPath::kCubic_Verb);
+ #if 0 // this needs an even rougher association to be useful
+ SkASSERT(SkDPoint::RoughlyEqual(ptAtT(newT), pt));
+ #endif
+ const SkPoint& firstPt = fPts[0];
+ const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
+ SkASSERT(newT == 0 || !precisely_zero(newT));
+ SkASSERT(newT == 1 || !precisely_equal(newT, 1));
+ // FIXME: in the pathological case where there is a ton of intercepts,
+ // binary search?
+ int insertedAt = -1;
+ int tCount = fTs.count();
+ for (int index = 0; index < tCount; ++index) {
+ // OPTIMIZATION: if there are three or more identical Ts, then
+ // the fourth and following could be further insertion-sorted so
+ // that all the edges are clockwise or counterclockwise.
+ // This could later limit segment tests to the two adjacent
+ // neighbors, although it doesn't help with determining which
+ // circular direction to go in.
+ const SkOpSpan& span = fTs[index];
+ if (newT < span.fT) {
+ insertedAt = index;
break;
}
- oStartSpan = oEndSpan->upCastable();
- if (oStartSpan && oStartSpan->windValue()) {
- oEndSpan = oStartSpan->next();
- break;
+ if (newT == span.fT) {
+ if (pt == span.fPt) {
+ insertedAt = index;
+ break;
+ }
+ if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
+ insertedAt = index;
+ break;
+ }
}
}
- SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- oAngle->set(oEndSpan, oStartSpan);
- oEndSpan->setFromAngle(oAngle);
- *otherPtr = other;
- return oAngle;
+ SkOpSpan* span;
+ if (insertedAt >= 0) {
+ span = fTs.insert(insertedAt);
+ } else {
+ insertedAt = tCount;
+ span = fTs.append();
+ }
+ span->fT = newT;
+ span->fOtherT = -1;
+ span->fOther = other;
+ span->fPt = pt;
+#if 0
+ // cubics, for instance, may not be exact enough to satisfy this check (e.g., cubicOp69d)
+ SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX)
+ && approximately_equal(xyAtT(newT).fY, pt.fY));
+#endif
+ span->fFromAngle = NULL;
+ span->fToAngle = NULL;
+ span->fWindSum = SK_MinS32;
+ span->fOppSum = SK_MinS32;
+ span->fWindValue = 1;
+ span->fOppValue = 0;
+ span->fChased = false;
+ span->fCoincident = false;
+ span->fLoop = false;
+ span->fNear = false;
+ span->fMultiple = false;
+ span->fSmall = false;
+ span->fTiny = false;
+ if ((span->fDone = newT == 1)) {
+ ++fDoneSpans;
+ }
+ setSpanFlags(pt, newT, span);
+ return insertedAt;
}
-SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
- debugValidate();
- SkPoint pt = this->ptAtT(t);
- SkOpSpanBase* span = &fHead;
+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
+ // FIXME: SkDPoint::ApproximatelyEqual is better but breaks tests at the moment
+ while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, pt)) {
+ if (fVerb == SkPath::kCubic_Verb) {
+ double tInterval = newT - span[less].fT;
+ double tMid = newT - tInterval / 2;
+ SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
+ if (!midPt.approximatelyEqual(xyAtT(span))) {
+ break;
+ }
+ }
+ --less;
+ }
+ int more = 1;
+ // FIXME: SkDPoint::ApproximatelyEqual is better but breaks tests at the moment
+ while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].fPt, pt)) {
+ if (fVerb == SkPath::kCubic_Verb) {
+ double tEndInterval = span[more].fT - newT;
+ double tMid = newT - tEndInterval / 2;
+ SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
+ if (!midEndPt.approximatelyEqual(xyAtT(span))) {
+ break;
+ }
+ }
+ ++more;
+ }
+ ++less;
+ --more;
+ while (more - 1 > less && span[more].fPt == span[more - 1].fPt
+ && span[more].fT == span[more - 1].fT) {
+ --more;
+ }
+ if (less == more) {
+ return;
+ }
+ if (precisely_negative(span[more].fT - span[less].fT)) {
+ return;
+ }
+// if the total range of t values is big enough, mark all tiny
+ bool tiny = span[less].fPt == span[more].fPt;
+ int index = less;
do {
- SkOpPtT* result = span->ptT();
- if (t == result->fT) {
- return result;
+ fSmall = span[index].fSmall = true;
+ fTiny |= span[index].fTiny = tiny;
+ if (!span[index].fDone) {
+ span[index].fDone = true;
+ ++fDoneSpans;
+ }
+ } while (++index < more);
+ 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;
}
- if (this->match(result, this, t, pt)) {
- // see if any existing alias matches segment, pt, and t
- SkOpPtT* loop = result->next();
- bool duplicatePt = false;
- while (loop != result) {
- bool ptMatch = loop->fPt == pt;
- if (loop->segment() == this && loop->fT == t && ptMatch) {
- return result;
+ ++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;
}
- duplicatePt |= ptMatch;
- loop = loop->next();
- }
- if (kNoAlias == allowAlias) {
- return result;
- }
- 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
- return alias;
- }
- if (t < result->fT) {
- SkOpSpan* prev = result->span()->prev();
- 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
- return span->ptT();
+ }
+ ++end;
}
- SkASSERT(span != &fTail);
- } while ((span = span->upCast()->next()));
- SkASSERT(0);
- return NULL;
+ 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);
}
-// 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]);
+// set spans from start to end to decrement by one
+// note this walks other backwards
+// FIXME: there's probably an edge case that can be constructed where
+// two span in one segment are separated by float epsilon on one span but
+// not the other, if one segment is very small. For this
+// case the counts asserted below may or may not be enough to separate the
+// spans. Even if the counts work out, what if the spans aren't correctly
+// sorted? It feels better in such a case to match the span's other span
+// pointer since both coincident segments must contain the same spans.
+// FIXME? It seems that decrementing by one will fail for complex paths that
+// have three or more coincident edges. Shouldn't this subtract the difference
+// between the winding values?
+/* |--> |-->
+this 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4
+other 2<<<<1<<<<0 2<<<<1<<<<0 2<<<<1<<<<0
+ ^ ^ <--| <--|
+ startPt endPt test/oTest first pos test/oTest final pos
+*/
+void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) {
+ bool binary = fOperand != other->fOperand;
+ int index = 0;
+ while (startPt != fTs[index].fPt) {
+ SkASSERT(index < fTs.count());
+ ++index;
+ }
+ while (index > 0 && precisely_equal(fTs[index].fT, fTs[index - 1].fT)) {
+ --index;
+ }
+ bool oFoundEnd = false;
+ int oIndex = other->fTs.count();
+ while (startPt != other->fTs[--oIndex].fPt) { // look for startPt match
+ SkASSERT(oIndex > 0);
+ }
+ double oStartT = other->fTs[oIndex].fT;
+ // look for first point beyond match
+ while (startPt == other->fTs[--oIndex].fPt || precisely_equal(oStartT, other->fTs[oIndex].fT)) {
+ if (!oIndex) {
+ return; // tiny spans may move in the wrong direction
+ }
+ }
+ SkOpSpan* test = &fTs[index];
+ SkOpSpan* oTest = &other->fTs[oIndex];
+ SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
+ SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
+ bool decrement, track, bigger;
+ int originalWindValue;
+ const SkPoint* testPt;
+ const SkPoint* oTestPt;
+ do {
+ SkASSERT(test->fT < 1);
+ SkASSERT(oTest->fT < 1);
+ decrement = test->fWindValue && oTest->fWindValue;
+ track = test->fWindValue || oTest->fWindValue;
+ bigger = test->fWindValue >= oTest->fWindValue;
+ testPt = &test->fPt;
+ double testT = test->fT;
+ oTestPt = &oTest->fPt;
+ double oTestT = oTest->fT;
+ do {
+ if (decrement) {
+ if (binary && bigger) {
+ test->fOppValue--;
+ } else {
+ decrementSpan(test);
+ }
+ } else if (track) {
+ TrackOutsidePair(&outsidePts, *testPt, *oTestPt);
+ }
+ SkASSERT(index < fTs.count() - 1);
+ test = &fTs[++index];
+ } while (*testPt == test->fPt || precisely_equal(testT, test->fT));
+ originalWindValue = oTest->fWindValue;
+ do {
+ SkASSERT(oTest->fT < 1);
+ SkASSERT(originalWindValue == oTest->fWindValue);
+ if (decrement) {
+ if (binary && !bigger) {
+ oTest->fOppValue--;
+ } else {
+ other->decrementSpan(oTest);
+ }
+ } else if (track) {
+ TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
+ }
+ if (!oIndex) {
+ break;
+ }
+ oFoundEnd |= endPt == oTest->fPt;
+ oTest = &other->fTs[--oIndex];
+ } while (*oTestPt == oTest->fPt || precisely_equal(oTestT, oTest->fT));
+ } while (endPt != test->fPt && test->fT < 1);
+ // FIXME: determine if canceled edges need outside ts added
+ if (!oFoundEnd) {
+ for (int oIdx2 = oIndex; oIdx2 >= 0; --oIdx2) {
+ SkOpSpan* oTst2 = &other->fTs[oIdx2];
+ if (originalWindValue != oTst2->fWindValue) {
+ goto skipAdvanceOtherCancel;
+ }
+ if (!oTst2->fWindValue) {
+ goto skipAdvanceOtherCancel;
+ }
+ if (endPt == other->fTs[oIdx2].fPt) {
+ break;
+ }
+ }
+ oFoundEnd = endPt == oTest->fPt;
+ do {
+ SkASSERT(originalWindValue == oTest->fWindValue);
+ if (decrement) {
+ if (binary && !bigger) {
+ oTest->fOppValue--;
+ } else {
+ other->decrementSpan(oTest);
+ }
+ } else if (track) {
+ TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
+ }
+ if (!oIndex) {
+ break;
+ }
+ oTest = &other->fTs[--oIndex];
+ oFoundEnd |= endPt == oTest->fPt;
+ } while (!oFoundEnd || endPt == oTest->fPt);
+ }
+skipAdvanceOtherCancel:
+ int outCount = outsidePts.count();
+ if (!done() && outCount) {
+ addCancelOutsides(outsidePts[0], outsidePts[1], other);
+ if (outCount > 2) {
+ addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other);
+ }
+ }
+ if (!other->done() && oOutsidePts.count()) {
+ other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
+ }
+ setCoincidentRange(startPt, endPt, other);
+ other->setCoincidentRange(startPt, endPt, this);
+}
+
+int SkOpSegment::addSelfT(const SkPoint& pt, double newT) {
+ // if the tail nearly intersects itself but not quite, the caller records this separately
+ int result = addT(this, pt, newT);
+ SkOpSpan* span = &fTs[result];
+ fLoop = span->fLoop = true;
+ return result;
+}
+
+// find the starting or ending span with an existing loop of angles
+// FIXME? replicate for all identical starting/ending spans?
+// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
+// FIXME? assert that only one other span has a valid windValue or oppValue
+void SkOpSegment::addSimpleAngle(int index) {
+ SkOpSpan* span = &fTs[index];
+ int idx;
+ int start, end;
+ if (span->fT == 0) {
+ idx = 0;
+ span = &fTs[0];
+ do {
+ if (span->fToAngle) {
+ SkASSERT(span->fToAngle->loopCount() == 2);
+ SkASSERT(!span->fFromAngle);
+ span->fFromAngle = span->fToAngle->next();
+ return;
+ }
+ span = &fTs[++idx];
+ } while (span->fT == 0);
+ SkASSERT(!fTs[0].fTiny && fTs[idx].fT > 0);
+ addStartSpan(idx);
+ start = 0;
+ end = idx;
+ } else {
+ idx = count() - 1;
+ span = &fTs[idx];
+ do {
+ if (span->fFromAngle) {
+ SkASSERT(span->fFromAngle->loopCount() == 2);
+ SkASSERT(!span->fToAngle);
+ span->fToAngle = span->fFromAngle->next();
+ return;
+ }
+ span = &fTs[--idx];
+ } while (span->fT == 1);
+ SkASSERT(!fTs[idx].fTiny && fTs[idx].fT < 1);
+ addEndSpan(++idx);
+ start = idx;
+ end = count();
}
- while ((span = span->upCast()->next())) {
- if (span == &fTail) {
+ SkOpSegment* other;
+ SkOpSpan* oSpan;
+ index = start;
+ do {
+ span = &fTs[index];
+ other = span->fOther;
+ int oFrom = span->fOtherIndex;
+ oSpan = &other->fTs[oFrom];
+ if (oSpan->fT < 1 && oSpan->fWindValue) {
break;
}
- span->align();
+ if (oSpan->fT == 0) {
+ continue;
+ }
+ oFrom = other->nextExactSpan(oFrom, -1);
+ SkOpSpan* oFromSpan = &other->fTs[oFrom];
+ SkASSERT(oFromSpan->fT < 1);
+ if (oFromSpan->fWindValue) {
+ break;
+ }
+ } while (++index < end);
+ SkOpAngle* angle, * oAngle;
+ if (span->fT == 0) {
+ SkASSERT(span->fOtherIndex - 1 >= 0);
+ SkASSERT(span->fOtherT == 1);
+ SkDEBUGCODE(int oPriorIndex = other->nextExactSpan(span->fOtherIndex, -1));
+ SkDEBUGCODE(const SkOpSpan& oPrior = other->span(oPriorIndex));
+ SkASSERT(!oPrior.fTiny && oPrior.fT < 1);
+ other->addEndSpan(span->fOtherIndex);
+ angle = span->fToAngle;
+ oAngle = oSpan->fFromAngle;
+ } else {
+ SkASSERT(span->fOtherIndex + 1 < other->count());
+ SkASSERT(span->fOtherT == 0);
+ SkASSERT(!oSpan->fTiny && (other->fTs[span->fOtherIndex + 1].fT > 0
+ || (other->fTs[span->fOtherIndex + 1].fFromAngle == NULL
+ && other->fTs[span->fOtherIndex + 1].fToAngle == NULL)));
+ int oIndex = 1;
+ do {
+ const SkOpSpan& osSpan = other->span(oIndex);
+ if (osSpan.fFromAngle || osSpan.fT > 0) {
+ break;
+ }
+ ++oIndex;
+ SkASSERT(oIndex < other->count());
+ } while (true);
+ other->addStartSpan(oIndex);
+ angle = span->fFromAngle;
+ oAngle = oSpan->fToAngle;
}
- if (!span->aligned()) {
- span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
+ angle->insert(oAngle);
+}
+
+void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
+ debugValidate();
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ SkOpSpan& span = fTs[index];
+ if (!span.fMultiple) {
+ continue;
+ }
+ int end = nextExactSpan(index, 1);
+ SkASSERT(end > index + 1);
+ const SkPoint& thisPt = span.fPt;
+ while (index < end - 1) {
+ SkOpSegment* other1 = span.fOther;
+ int oCnt = other1->count();
+ for (int idx2 = index + 1; idx2 < end; ++idx2) {
+ SkOpSpan& span2 = fTs[idx2];
+ SkOpSegment* other2 = span2.fOther;
+ for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
+ SkOpSpan& oSpan = other1->fTs[oIdx];
+ if (oSpan.fOther != other2) {
+ continue;
+ }
+ if (oSpan.fPt == thisPt) {
+ goto skipExactMatches;
+ }
+ }
+ for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
+ SkOpSpan& oSpan = other1->fTs[oIdx];
+ if (oSpan.fOther != other2) {
+ continue;
+ }
+ if (SkDPoint::RoughlyEqual(oSpan.fPt, thisPt)) {
+ SkOpSpan& oSpan2 = other2->fTs[oSpan.fOtherIndex];
+ if (zero_or_one(span.fOtherT) || zero_or_one(oSpan.fT)
+ || zero_or_one(span2.fOtherT) || zero_or_one(oSpan2.fT)) {
+ return;
+ }
+ if (!way_roughly_equal(span.fOtherT, oSpan.fT)
+ || !way_roughly_equal(span2.fOtherT, oSpan2.fT)
+ || !way_roughly_equal(span2.fOtherT, oSpan.fOtherT)
+ || !way_roughly_equal(span.fOtherT, oSpan2.fOtherT)) {
+ return;
+ }
+ alignSpan(thisPt, span.fOtherT, other1, span2.fOtherT,
+ other2, &oSpan, alignedArray);
+ alignSpan(thisPt, span2.fOtherT, other2, span.fOtherT,
+ other1, &oSpan2, alignedArray);
+ break;
+ }
+ }
+ skipExactMatches:
+ ;
+ }
+ ++index;
+ }
}
debugValidate();
}
-bool SkOpSegment::BetweenTs(const SkOpSpanBase* lesser, double testT,
- const SkOpSpanBase* greater) {
- if (lesser->t() > greater->t()) {
- SkTSwap<const SkOpSpanBase*>(lesser, greater);
+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: ;
}
- return approximately_between(lesser->t(), testT, greater->t());
}
-void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
- bool activePrior = !fHead.isCanceled();
- if (activePrior && !fHead.simple()) {
- addStartSpan(allocator);
+void SkOpSegment::alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other,
+ double otherT, const SkOpSegment* other2, SkOpSpan* oSpan,
+ SkTDArray<AlignedSpan>* alignedArray) {
+ AlignedSpan* aligned = alignedArray->append();
+ aligned->fOldPt = oSpan->fPt;
+ aligned->fPt = newPt;
+ aligned->fOldT = oSpan->fT;
+ aligned->fT = newT;
+ aligned->fSegment = this; // OPTIMIZE: may be unused, can remove
+ aligned->fOther1 = other;
+ aligned->fOther2 = other2;
+ SkASSERT(SkDPoint::RoughlyEqual(oSpan->fPt, newPt));
+ oSpan->fPt = newPt;
+// SkASSERT(way_roughly_equal(oSpan->fT, newT));
+ oSpan->fT = newT;
+// SkASSERT(way_roughly_equal(oSpan->fOtherT, otherT));
+ oSpan->fOtherT = otherT;
+}
+
+bool SkOpSegment::alignSpan(int index, double thisT, const SkPoint& thisPt) {
+ bool aligned = false;
+ SkOpSpan* span = &fTs[index];
+ SkOpSegment* other = span->fOther;
+ int oIndex = span->fOtherIndex;
+ SkOpSpan* oSpan = &other->fTs[oIndex];
+ if (span->fT != thisT) {
+ span->fT = thisT;
+ oSpan->fOtherT = thisT;
+ aligned = true;
+ }
+ if (span->fPt != thisPt) {
+ span->fPt = thisPt;
+ oSpan->fPt = thisPt;
+ aligned = true;
+ }
+ double oT = oSpan->fT;
+ if (oT == 0) {
+ return aligned;
}
- SkOpSpan* prior = &fHead;
- SkOpSpanBase* spanBase = fHead.next();
- while (spanBase != &fTail) {
- if (activePrior) {
- SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- priorAngle->set(spanBase, prior);
- spanBase->setFromAngle(priorAngle);
+ int oStart = other->nextSpan(oIndex, -1) + 1;
+ oSpan = &other->fTs[oStart];
+ int otherIndex = oStart;
+ if (oT == 1) {
+ if (aligned) {
+ while (oSpan->fPt == thisPt && oSpan->fT != 1) {
+ oSpan->fTiny = true;
+ ++oSpan;
+ }
}
- SkOpSpan* span = spanBase->upCast();
- bool active = !span->isCanceled();
- SkOpSpanBase* next = span->next();
- if (active) {
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- angle->set(span, next);
- span->setToAngle(angle);
+ return aligned;
+ }
+ oT = oSpan->fT;
+ int oEnd = other->nextSpan(oIndex, 1);
+ bool oAligned = false;
+ if (oSpan->fPt != thisPt) {
+ oAligned |= other->alignSpan(oStart, oT, thisPt);
+ }
+ while (++otherIndex < oEnd) {
+ SkOpSpan* oNextSpan = &other->fTs[otherIndex];
+ if (oNextSpan->fT != oT || oNextSpan->fPt != thisPt) {
+ oAligned |= other->alignSpan(otherIndex, oT, thisPt);
}
- activePrior = active;
- prior = span;
- spanBase = next;
}
- if (activePrior && !fTail.simple()) {
- addEndSpan(allocator);
+ if (oAligned) {
+ other->alignSpanState(oStart, oEnd);
}
+ return aligned;
}
-void SkOpSegment::checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
- SkOpSpanBase* base = &fHead;
- SkOpSpan* span;
+void SkOpSegment::alignSpanState(int start, int end) {
+ SkOpSpan* lastSpan = &fTs[--end];
+ bool allSmall = lastSpan->fSmall;
+ bool allTiny = lastSpan->fTiny;
+ bool allDone = lastSpan->fDone;
+ SkDEBUGCODE(int winding = lastSpan->fWindValue);
+ SkDEBUGCODE(int oppWinding = lastSpan->fOppValue);
+ int index = start;
+ while (index < end) {
+ SkOpSpan* span = &fTs[index];
+ span->fSmall = allSmall;
+ span->fTiny = allTiny;
+ if (span->fDone != allDone) {
+ span->fDone = allDone;
+ fDoneSpans += allDone ? 1 : -1;
+ }
+ SkASSERT(span->fWindValue == winding);
+ SkASSERT(span->fOppValue == oppWinding);
+ ++index;
+ }
+}
+
+void SkOpSegment::blindCancel(const SkCoincidence& coincidence, SkOpSegment* other) {
+ bool binary = fOperand != other->fOperand;
+ int index = 0;
+ int last = this->count();
do {
- SkOpAngle* angle = base->fromAngle();
- if (angle && angle->fCheckCoincidence) {
- angle->checkNearCoincidence();
+ SkOpSpan& span = this->fTs[--last];
+ if (span.fT != 1 && !span.fSmall) {
+ break;
+ }
+ span.fCoincident = true;
+ } while (true);
+ int oIndex = other->count();
+ do {
+ SkOpSpan& oSpan = other->fTs[--oIndex];
+ if (oSpan.fT != 1 && !oSpan.fSmall) {
+ break;
}
- if (base->final()) {
- break;
+ oSpan.fCoincident = true;
+ } while (true);
+ do {
+ SkOpSpan* test = &this->fTs[index];
+ int baseWind = test->fWindValue;
+ int baseOpp = test->fOppValue;
+ int endIndex = index;
+ while (++endIndex <= last) {
+ SkOpSpan* endSpan = &this->fTs[endIndex];
+ SkASSERT(endSpan->fT < 1);
+ if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
+ break;
+ }
+ endSpan->fCoincident = true;
}
- span = base->upCast();
- angle = span->toAngle();
- if (angle && angle->fCheckCoincidence) {
- angle->checkNearCoincidence();
+ SkOpSpan* oTest = &other->fTs[oIndex];
+ int oBaseWind = oTest->fWindValue;
+ int oBaseOpp = oTest->fOppValue;
+ int oStartIndex = oIndex;
+ while (--oStartIndex >= 0) {
+ SkOpSpan* oStartSpan = &other->fTs[oStartIndex];
+ if (oStartSpan->fWindValue != oBaseWind || oStartSpan->fOppValue != oBaseOpp) {
+ break;
+ }
+ oStartSpan->fCoincident = true;
}
- } while ((base = span->next()));
+ bool decrement = baseWind && oBaseWind;
+ bool bigger = baseWind >= oBaseWind;
+ do {
+ SkASSERT(test->fT < 1);
+ if (decrement) {
+ if (binary && bigger) {
+ test->fOppValue--;
+ } else {
+ decrementSpan(test);
+ }
+ }
+ test->fCoincident = true;
+ test = &fTs[++index];
+ } while (index < endIndex);
+ do {
+ SkASSERT(oTest->fT < 1);
+ if (decrement) {
+ if (binary && !bigger) {
+ oTest->fOppValue--;
+ } else {
+ other->decrementSpan(oTest);
+ }
+ }
+ oTest->fCoincident = true;
+ oTest = &other->fTs[--oIndex];
+ } while (oIndex > oStartIndex);
+ } while (index <= last && oIndex >= 0);
+ SkASSERT(index > last);
+ SkASSERT(oIndex < 0);
}
-// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
-bool SkOpSegment::clockwise(const SkOpSpanBase* start, const SkOpSpanBase* end, bool* swap) const {
- SkASSERT(fVerb != SkPath::kLine_Verb);
- SkPoint edge[4];
- if (fVerb == SkPath::kCubic_Verb) {
- double startT = start->t();
- double endT = end->t();
- bool flip = startT > endT;
- SkDCubic cubic;
- cubic.set(fPts);
- double inflectionTs[2];
- int inflections = cubic.findInflections(inflectionTs);
- for (int index = 0; index < inflections; ++index) {
- double inflectionT = inflectionTs[index];
- if (between(startT, inflectionT, endT)) {
- if (flip) {
- if (inflectionT != endT) {
- startT = inflectionT;
- }
- } else {
- if (inflectionT != startT) {
- endT = inflectionT;
- }
+void SkOpSegment::blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other) {
+ bool binary = fOperand != other->fOperand;
+ int index = 0;
+ int last = this->count();
+ do {
+ SkOpSpan& span = this->fTs[--last];
+ if (span.fT != 1 && !span.fSmall) {
+ break;
+ }
+ span.fCoincident = true;
+ } while (true);
+ int oIndex = 0;
+ int oLast = other->count();
+ do {
+ SkOpSpan& oSpan = other->fTs[--oLast];
+ if (oSpan.fT != 1 && !oSpan.fSmall) {
+ break;
+ }
+ oSpan.fCoincident = true;
+ } while (true);
+ do {
+ SkOpSpan* test = &this->fTs[index];
+ int baseWind = test->fWindValue;
+ int baseOpp = test->fOppValue;
+ int endIndex = index;
+ SkOpSpan* endSpan;
+ while (++endIndex <= last) {
+ endSpan = &this->fTs[endIndex];
+ SkASSERT(endSpan->fT < 1);
+ if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
+ break;
+ }
+ endSpan->fCoincident = true;
+ }
+ SkOpSpan* oTest = &other->fTs[oIndex];
+ int oBaseWind = oTest->fWindValue;
+ int oBaseOpp = oTest->fOppValue;
+ int oEndIndex = oIndex;
+ SkOpSpan* oEndSpan;
+ while (++oEndIndex <= oLast) {
+ oEndSpan = &this->fTs[oEndIndex];
+ SkASSERT(oEndSpan->fT < 1);
+ if (oEndSpan->fWindValue != oBaseWind || oEndSpan->fOppValue != oBaseOpp) {
+ break;
+ }
+ oEndSpan->fCoincident = true;
+ }
+ // consolidate the winding count even if done
+ if ((test->fWindValue || test->fOppValue) && (oTest->fWindValue || oTest->fOppValue)) {
+ if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
+ bumpCoincidentBlind(binary, index, endIndex);
+ other->bumpCoincidentOBlind(oIndex, oEndIndex);
+ } else {
+ other->bumpCoincidentBlind(binary, oIndex, oEndIndex);
+ bumpCoincidentOBlind(index, endIndex);
+ }
+ }
+ index = endIndex;
+ oIndex = oEndIndex;
+ } while (index <= last && oIndex <= oLast);
+ SkASSERT(index > last);
+ SkASSERT(oIndex > oLast);
+}
+
+void SkOpSegment::bumpCoincidentBlind(bool binary, int index, int endIndex) {
+ const SkOpSpan& oTest = fTs[index];
+ int oWindValue = oTest.fWindValue;
+ int oOppValue = oTest.fOppValue;
+ if (binary) {
+ SkTSwap<int>(oWindValue, oOppValue);
+ }
+ do {
+ (void) bumpSpan(&fTs[index], oWindValue, oOppValue);
+ } while (++index < endIndex);
+}
+
+bool SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
+ SkTArray<SkPoint, true>* outsideTs) {
+ int index = *indexPtr;
+ int oWindValue = oTest.fWindValue;
+ int oOppValue = oTest.fOppValue;
+ if (binary) {
+ SkTSwap<int>(oWindValue, oOppValue);
+ }
+ SkOpSpan* const test = &fTs[index];
+ SkOpSpan* end = test;
+ const SkPoint& oStartPt = oTest.fPt;
+ do {
+ if (end->fDone && !end->fTiny && !end->fSmall) { // extremely large paths trigger this
+ return false;
+ }
+ if (bumpSpan(end, oWindValue, oOppValue)) {
+ TrackOutside(outsideTs, oStartPt);
+ }
+ end = &fTs[++index];
+ } while ((end->fPt == test->fPt || precisely_equal(end->fT, test->fT)) && end->fT < 1);
+ *indexPtr = index;
+ return true;
+}
+
+void SkOpSegment::bumpCoincidentOBlind(int index, int endIndex) {
+ do {
+ zeroSpan(&fTs[index]);
+ } while (++index < endIndex);
+}
+
+// because of the order in which coincidences are resolved, this and other
+// 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
+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;
+ const SkPoint& oStartPt = oTest->fPt;
+ double oStartT = oTest->fT;
+#if 0 // FIXME : figure out what disabling this breaks
+ const SkPoint& startPt = test.fPt;
+ // this is always true since oEnd == oTest && oStartPt == oTest->fPt -- find proper condition
+ if (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
+ 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:
+// contourA has two segments that are coincident
+// contourB has two segments that are coincident in the same place
+// each ends up with +2/0 pairs for winding count
+// since logic below doesn't transfer count (only increments/decrements) can this be
+// resolved to +4/0 ?
+
+// set spans from start to end to increment the greater by one and decrement
+// the lesser
+bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
+ SkOpSegment* other) {
+ bool binary = fOperand != other->fOperand;
+ int index = 0;
+ while (startPt != fTs[index].fPt) {
+ SkASSERT(index < fTs.count());
+ ++index;
+ }
+ double startT = fTs[index].fT;
+ while (index > 0 && precisely_equal(fTs[index - 1].fT, startT)) {
+ --index;
+ }
+ int oIndex = 0;
+ while (startPt != other->fTs[oIndex].fPt) {
+ SkASSERT(oIndex < other->fTs.count());
+ ++oIndex;
+ }
+ double oStartT = other->fTs[oIndex].fT;
+ while (oIndex > 0 && precisely_equal(other->fTs[oIndex - 1].fT, oStartT)) {
+ --oIndex;
+ }
+ SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
+ SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
+ SkOpSpan* test = &fTs[index];
+ const SkPoint* testPt = &test->fPt;
+ double testT = test->fT;
+ SkOpSpan* oTest = &other->fTs[oIndex];
+ const SkPoint* oTestPt = &oTest->fPt;
+ // paths with extreme data will fail this test and eject out of pathops altogether later on
+ // SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
+ do {
+ SkASSERT(test->fT < 1);
+ if (oTest->fT == 1) {
+ // paths with extreme data may be so mismatched that we fail here
+ return false;
+ }
+
+ // 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);
+ SkDEBUGCODE(int firstOpp = test->fOppValue);
+ do {
+ SkASSERT(firstWind == fTs[index].fWindValue);
+ SkASSERT(firstOpp == fTs[index].fOppValue);
+ ++index;
+ SkASSERT(index < fTs.count());
+ } while (*testPt == fTs[index].fPt);
+ SkDEBUGCODE(firstWind = oTest->fWindValue);
+ SkDEBUGCODE(firstOpp = oTest->fOppValue);
+ do {
+ SkASSERT(firstWind == other->fTs[oIndex].fWindValue);
+ SkASSERT(firstOpp == other->fTs[oIndex].fOppValue);
+ ++oIndex;
+ SkASSERT(oIndex < other->fTs.count());
+ } while (*oTestPt == other->fTs[oIndex].fPt);
+ } else {
+ if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
+ if (!bumpCoincidentThis(*oTest, binary, &index, &outsidePts)) {
+ return false;
+ }
+ foundEnd = other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt);
+ } else {
+ if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
+ return false;
}
+ foundEnd = bumpCoincidentOther(*oTest, &index, &outsidePts, endPt);
}
}
- SkDCubic part = cubic.subDivide(startT, endT);
- for (int index = 0; index < 4; ++index) {
- edge[index] = part[index].asSkPoint();
+ test = &fTs[index];
+ testPt = &test->fPt;
+ testT = test->fT;
+ oTest = &other->fTs[oIndex];
+ oTestPt = &oTest->fPt;
+ if (endPt == *testPt || precisely_equal(endT, testT)) {
+ break;
}
- } else {
- subDivide(start, end, edge);
+ 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
+ if (endPt != *testPt && !precisely_equal(endT, testT)) {
+ int lastWind = test[-1].fWindValue;
+ int lastOpp = test[-1].fOppValue;
+ bool zero = lastWind == 0 && lastOpp == 0;
+ do {
+ if (test->fWindValue || test->fOppValue) {
+ test->fWindValue = lastWind;
+ test->fOppValue = lastOpp;
+ if (zero) {
+ SkASSERT(!test->fDone);
+ test->fDone = true;
+ ++fDoneSpans;
+ }
+ }
+ test = &fTs[++index];
+ testPt = &test->fPt;
+ } while (endPt != *testPt);
}
- bool sumSet = false;
- int points = SkPathOpsVerbToPoints(fVerb);
- double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
- if (!sumSet) {
- for (int idx = 0; idx < points; ++idx){
- sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
+ if (endPt != *oTestPt) {
+ // look ahead to see if zeroing more spans will allows us to catch up
+ int oPeekIndex = oIndex;
+ bool success = true;
+ SkOpSpan* oPeek;
+ int oCount = other->count();
+ do {
+ oPeek = &other->fTs[oPeekIndex];
+ if (++oPeekIndex == oCount) {
+ success = false;
+ break;
+ }
+ } while (endPt != oPeek->fPt);
+ if (success) {
+ // make sure the matching point completes the coincidence span
+ success = false;
+ do {
+ if (oPeek->fOther == this) {
+ success = true;
+ break;
+ }
+ if (++oPeekIndex == oCount) {
+ break;
+ }
+ oPeek = &other->fTs[oPeekIndex];
+ } while (endPt == oPeek->fPt);
+ }
+ if (success) {
+ do {
+ if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
+ if (other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt)) {
+ break;
+ }
+ } else {
+ if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
+ return false;
+ }
+ }
+ oTest = &other->fTs[oIndex];
+ oTestPt = &oTest->fPt;
+ } while (endPt != *oTestPt);
}
}
- if (fVerb == SkPath::kCubic_Verb) {
- SkDCubic cubic;
- cubic.set(edge);
- *swap = sum > 0 && !cubic.monotonicInY();
- } else {
- SkDQuad quad;
- quad.set(edge);
- *swap = sum > 0 && !quad.monotonicInY();
+ int outCount = outsidePts.count();
+ if (!done() && outCount) {
+ addCoinOutsides(outsidePts[0], endPt, other);
}
- return sum <= 0;
+ if (!other->done() && oOutsidePts.count()) {
+ other->addCoinOutsides(oOutsidePts[0], endPt, this);
+ }
+ setCoincidentRange(startPt, endPt, other);
+ other->setCoincidentRange(startPt, endPt, this);
+ return true;
}
-void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
- SkOpAngle::IncludeType includeType) {
- const SkOpSegment* baseSegment = baseAngle->segment();
- int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
- int sumSuWinding;
- bool binary = includeType >= SkOpAngle::kBinarySingle;
- if (binary) {
- sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
- if (baseSegment->operand()) {
- SkTSwap<int>(sumMiWinding, sumSuWinding);
+// FIXME: this doesn't prevent the same span from being added twice
+// fix in caller, SkASSERT here?
+// FIXME: this may erroneously reject adds for cubic loops
+const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
+ const SkPoint& pt, const SkPoint& pt2) {
+ int tCount = fTs.count();
+ for (int tIndex = 0; tIndex < tCount; ++tIndex) {
+ const SkOpSpan& span = fTs[tIndex];
+ if (!approximately_negative(span.fT - t)) {
+ break;
+ }
+ if (span.fOther == other) {
+ bool tsMatch = approximately_equal(span.fT, t);
+ bool otherTsMatch = approximately_equal(span.fOtherT, otherT);
+ // FIXME: add cubic loop detecting logic here
+ // if fLoop bit is set on span, that could be enough if addOtherT copies the bit
+ // or if a new bit is added ala fOtherLoop
+ if (tsMatch || otherTsMatch) {
+#if DEBUG_ADD_T_PAIR
+ SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
+ __FUNCTION__, fID, t, other->fID, otherT);
+#endif
+ return NULL;
+ }
}
}
- SkOpSegment* nextSegment = nextAngle->segment();
- int maxWinding, sumWinding;
- SkOpSpanBase* last;
- if (binary) {
- int oppMaxWinding, oppSumWinding;
- nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
- &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
- last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
- nextAngle);
- } else {
- nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
- &maxWinding, &sumWinding);
- last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
+ int oCount = other->count();
+ for (int oIndex = 0; oIndex < oCount; ++oIndex) {
+ const SkOpSpan& oSpan = other->span(oIndex);
+ if (!approximately_negative(oSpan.fT - otherT)) {
+ break;
+ }
+ if (oSpan.fOther == this) {
+ bool otherTsMatch = approximately_equal(oSpan.fT, otherT);
+ bool tsMatch = approximately_equal(oSpan.fOtherT, t);
+ if (otherTsMatch || tsMatch) {
+#if DEBUG_ADD_T_PAIR
+ SkDebugf("%s addTPair other duplicate this=%d %1.9g other=%d %1.9g\n",
+ __FUNCTION__, fID, t, other->fID, otherT);
+#endif
+ return NULL;
+ }
+ }
}
- nextAngle->setLastMarked(last);
+#if DEBUG_ADD_T_PAIR
+ SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
+ __FUNCTION__, fID, t, other->fID, otherT);
+#endif
+ SkASSERT(other != this);
+ int insertedAt = addT(other, pt, t);
+ int otherInsertedAt = other->addT(this, pt2, otherT);
+ this->addOtherT(insertedAt, otherT, otherInsertedAt);
+ other->addOtherT(otherInsertedAt, t, insertedAt);
+ this->matchWindingValue(insertedAt, t, borrowWind);
+ other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
+ SkOpSpan& span = this->fTs[insertedAt];
+ if (pt != pt2) {
+ span.fNear = true;
+ 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;
}
-void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
- SkOpAngle::IncludeType includeType) {
- const SkOpSegment* baseSegment = baseAngle->segment();
- int sumMiWinding = baseSegment->updateWinding(baseAngle);
- int sumSuWinding;
- bool binary = includeType >= SkOpAngle::kBinarySingle;
- if (binary) {
- sumSuWinding = baseSegment->updateOppWinding(baseAngle);
- if (baseSegment->operand()) {
- SkTSwap<int>(sumMiWinding, sumSuWinding);
+const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
+ const SkPoint& pt) {
+ return addTPair(t, other, otherT, borrowWind, pt, pt);
+}
+
+bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
+ const SkPoint midPt = ptAtT(midT);
+ SkPathOpsBounds bounds;
+ bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
+ bounds.sort();
+ return bounds.almostContains(midPt);
+}
+
+bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const {
+ if (lesser > greater) {
+ SkTSwap<int>(lesser, greater);
+ }
+ return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
+}
+
+// in extreme cases (like the buffer overflow test) return false to abort
+// for now, if one t value represents two different points, then the values are too extreme
+// to generate meaningful results
+bool SkOpSegment::calcAngles() {
+ int spanCount = fTs.count();
+ if (spanCount <= 2) {
+ return spanCount == 2;
+ }
+ int index = 1;
+ const SkOpSpan* firstSpan = &fTs[index];
+ int activePrior = checkSetAngle(0);
+ const SkOpSpan* span = &fTs[0];
+ if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) {
+ index = findStartSpan(0); // curve start intersects
+ if (fTs[index].fT == 0) {
+ return false;
+ }
+ SkASSERT(index > 0);
+ if (activePrior >= 0) {
+ addStartSpan(index);
}
}
- SkOpSegment* nextSegment = nextAngle->segment();
- int maxWinding, sumWinding;
- SkOpSpanBase* last;
- if (binary) {
- int oppMaxWinding, oppSumWinding;
- nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
- &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
- last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
- nextAngle);
+ bool addEnd;
+ int endIndex = spanCount - 1;
+ span = &fTs[endIndex - 1];
+ if ((addEnd = span->fT == 1 || span->fTiny)) { // if curve end intersects
+ endIndex = findEndSpan(endIndex);
+ SkASSERT(endIndex > 0);
} else {
- nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
- &maxWinding, &sumWinding);
- last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
+ addEnd = fTs[endIndex].fOtherT != 0 || fTs[endIndex].fOther->multipleStarts();
}
- nextAngle->setLastMarked(last);
+ SkASSERT(endIndex >= index);
+ int prior = 0;
+ while (index < endIndex) {
+ const SkOpSpan& fromSpan = fTs[index]; // for each intermediate intersection
+ const SkOpSpan* lastSpan;
+ span = &fromSpan;
+ int start = index;
+ do {
+ lastSpan = span;
+ span = &fTs[++index];
+ SkASSERT(index < spanCount);
+ if (!precisely_negative(span->fT - lastSpan->fT) && !lastSpan->fTiny) {
+ break;
+ }
+ if (!SkDPoint::ApproximatelyEqual(lastSpan->fPt, span->fPt)) {
+ return false;
+ }
+ } while (true);
+ SkOpAngle* angle = NULL;
+ SkOpAngle* priorAngle;
+ if (activePrior >= 0) {
+ int pActive = firstActive(prior);
+ SkASSERT(pActive < start);
+ priorAngle = &fAngles.push_back();
+ priorAngle->set(this, start, pActive);
+ }
+ int active = checkSetAngle(start);
+ if (active >= 0) {
+ SkASSERT(active < index);
+ angle = &fAngles.push_back();
+ angle->set(this, active, index);
+ }
+ #if DEBUG_ANGLE
+ debugCheckPointsEqualish(start, index);
+ #endif
+ prior = start;
+ do {
+ const SkOpSpan* startSpan = &fTs[start - 1];
+ if (!startSpan->fSmall || isCanceled(start - 1) || startSpan->fFromAngle
+ || startSpan->fToAngle) {
+ break;
+ }
+ --start;
+ } while (start > 0);
+ do {
+ if (activePrior >= 0) {
+ SkASSERT(fTs[start].fFromAngle == NULL);
+ fTs[start].fFromAngle = priorAngle;
+ }
+ if (active >= 0) {
+ SkASSERT(fTs[start].fToAngle == NULL);
+ fTs[start].fToAngle = angle;
+ }
+ } while (++start < index);
+ activePrior = active;
+ }
+ if (addEnd && activePrior >= 0) {
+ addEndSpan(endIndex);
+ }
+ return true;
+}
+
+int SkOpSegment::checkSetAngle(int tIndex) const {
+ const SkOpSpan* span = &fTs[tIndex];
+ while (span->fTiny /* || span->fSmall */) {
+ span = &fTs[++tIndex];
+ }
+ return isCanceled(tIndex) ? -1 : tIndex;
}
// at this point, the span is already ordered, or unorderable
-int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
- SkOpAngle::IncludeType includeType) {
+int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
SkASSERT(includeType != SkOpAngle::kUnaryXor);
- SkOpAngle* firstAngle = this->spanToAngle(end, start);
+ SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
if (NULL == firstAngle || NULL == firstAngle->next()) {
return SK_NaN32;
}
@@ -614,7 +1898,7 @@ int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
baseAngle = NULL;
continue;
}
- int testWinding = angle->starter()->windSum();
+ int testWinding = angle->segment()->windSum(angle);
if (SK_MinS32 != testWinding) {
baseAngle = angle;
tryReverse = true;
@@ -622,10 +1906,10 @@ int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
}
if (baseAngle) {
ComputeOneSum(baseAngle, angle, includeType);
- baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
+ baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
}
} while (next != firstAngle);
- if (baseAngle && SK_MinS32 == firstAngle->starter()->windSum()) {
+ if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
firstAngle = baseAngle;
tryReverse = true;
}
@@ -641,41 +1925,137 @@ int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
baseAngle = NULL;
continue;
}
- int testWinding = angle->starter()->windSum();
+ int testWinding = angle->segment()->windSum(angle);
if (SK_MinS32 != testWinding) {
baseAngle = angle;
continue;
}
if (baseAngle) {
ComputeOneSumReverse(baseAngle, angle, includeType);
- baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
+ baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
}
} while (prior != firstAngle);
}
- return start->starter(end)->windSum();
+ int minIndex = SkMin32(startIndex, endIndex);
+ return windSum(minIndex);
+}
+
+void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+ SkOpAngle::IncludeType includeType) {
+ const SkOpSegment* baseSegment = baseAngle->segment();
+ int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
+ int sumSuWinding;
+ bool binary = includeType >= SkOpAngle::kBinarySingle;
+ if (binary) {
+ sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
+ if (baseSegment->operand()) {
+ SkTSwap<int>(sumMiWinding, sumSuWinding);
+ }
+ }
+ SkOpSegment* nextSegment = nextAngle->segment();
+ int maxWinding, sumWinding;
+ SkOpSpan* last;
+ if (binary) {
+ int oppMaxWinding, oppSumWinding;
+ nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
+ &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
+ last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
+ nextAngle);
+ } else {
+ nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
+ &maxWinding, &sumWinding);
+ last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
+ }
+ nextAngle->setLastMarked(last);
+}
+
+void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+ SkOpAngle::IncludeType includeType) {
+ const SkOpSegment* baseSegment = baseAngle->segment();
+ int sumMiWinding = baseSegment->updateWinding(baseAngle);
+ int sumSuWinding;
+ bool binary = includeType >= SkOpAngle::kBinarySingle;
+ if (binary) {
+ sumSuWinding = baseSegment->updateOppWinding(baseAngle);
+ if (baseSegment->operand()) {
+ SkTSwap<int>(sumMiWinding, sumSuWinding);
+ }
+ }
+ SkOpSegment* nextSegment = nextAngle->segment();
+ int maxWinding, sumWinding;
+ SkOpSpan* last;
+ if (binary) {
+ int oppMaxWinding, oppSumWinding;
+ nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
+ &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
+ last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
+ nextAngle);
+ } else {
+ nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
+ &maxWinding, &sumWinding);
+ last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
+ }
+ nextAngle->setLastMarked(last);
+}
+
+bool SkOpSegment::containsPt(const SkPoint& pt, int index, int endIndex) const {
+ int step = index < endIndex ? 1 : -1;
+ do {
+ const SkOpSpan& span = this->span(index);
+ if (span.fPt == pt) {
+ const SkOpSpan& endSpan = this->span(endIndex);
+ return span.fT == endSpan.fT && pt != endSpan.fPt;
+ }
+ index += step;
+ } while (index != endIndex);
+ return false;
+}
+
+bool SkOpSegment::containsT(double t, const SkOpSegment* other, double otherT) const {
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (t < span.fT) {
+ return false;
+ }
+ if (t == span.fT) {
+ if (other != span.fOther) {
+ continue;
+ }
+ if (other->fVerb != SkPath::kCubic_Verb) {
+ return true;
+ }
+ if (!other->fLoop) {
+ return true;
+ }
+ double otherMidT = (otherT + span.fOtherT) / 2;
+ SkPoint otherPt = other->ptAtT(otherMidT);
+ return SkDPoint::ApproximatelyEqual(span.fPt, otherPt);
+ }
+ }
+ return false;
}
-SkOpSpan* SkOpSegment::crossedSpanY(const SkPoint& basePt, double mid, bool opp, bool current,
- SkScalar* bestY, double* hitT, bool* hitSomething, bool* vertical) {
+int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
+ bool* hitSomething, double mid, bool opp, bool current) const {
SkScalar bottom = fBounds.fBottom;
- *vertical = false;
+ int bestTIndex = -1;
if (bottom <= *bestY) {
- return NULL;
+ return bestTIndex;
}
SkScalar top = fBounds.fTop;
if (top >= basePt.fY) {
- return NULL;
+ return bestTIndex;
}
if (fBounds.fLeft > basePt.fX) {
- return NULL;
+ return bestTIndex;
}
if (fBounds.fRight < basePt.fX) {
- return NULL;
+ return bestTIndex;
}
if (fBounds.fLeft == fBounds.fRight) {
// if vertical, and directly above test point, wait for another one
- *vertical = AlmostEqualUlps(basePt.fX, fBounds.fLeft);
- return NULL;
+ return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
}
// intersect ray starting at basePt with edge
SkIntersections intersections;
@@ -685,7 +2065,7 @@ SkOpSpan* SkOpSegment::crossedSpanY(const SkPoint& basePt, double mid, bool opp,
int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
(fPts, top, bottom, basePt.fX, false);
if (pts == 0 || (current && pts == 1)) {
- return NULL;
+ return bestTIndex;
}
if (current) {
SkASSERT(pts > 1);
@@ -713,73 +2093,933 @@ SkOpSpan* SkOpSegment::crossedSpanY(const SkPoint& basePt, double mid, bool opp,
continue;
}
if (pts > 1 && fVerb == SkPath::kLine_Verb) {
- *vertical = true;
- return NULL; // if the intersection is edge on, wait for another one
+ return SK_MinS32; // if the intersection is edge on, wait for another one
}
if (fVerb > SkPath::kLine_Verb) {
SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
if (approximately_zero(dx)) {
- *vertical = true;
- return NULL; // hit vertical, wait for another one
+ return SK_MinS32; // hit vertical, wait for another one
}
}
*bestY = testY;
bestT = foundT;
}
if (bestT < 0) {
- return NULL;
+ return bestTIndex;
}
SkASSERT(bestT >= 0);
- SkASSERT(bestT < 1);
- SkOpSpanBase* testTSpanBase = &this->fHead;
- SkOpSpanBase* nextTSpan;
- double endT = 0;
+ SkASSERT(bestT <= 1);
+ int start;
+ int end = 0;
do {
- nextTSpan = testTSpanBase->upCast()->next();
- endT = nextTSpan->t();
- if (endT >= bestT) {
- break;
- }
- testTSpanBase = nextTSpan;
- } while (testTSpanBase);
- SkOpSpan* bestTSpan = NULL;
- SkOpSpan* testTSpan = testTSpanBase->upCast();
- if (!testTSpan->isCanceled()) {
+ start = end;
+ end = nextSpan(start, 1);
+ } while (fTs[end].fT < bestT);
+ // FIXME: see next candidate for a better pattern to find the next start/end pair
+ while (start + 1 < end && fTs[start].fDone) {
+ ++start;
+ }
+ if (!isCanceled(start)) {
*hitT = bestT;
- bestTSpan = testTSpan;
+ bestTIndex = start;
*hitSomething = true;
}
- return bestTSpan;
+ return bestTIndex;
+}
+
+bool SkOpSegment::decrementSpan(SkOpSpan* span) {
+ SkASSERT(span->fWindValue > 0);
+ if (--(span->fWindValue) == 0) {
+ if (!span->fOppValue && !span->fDone) {
+ span->fDone = true;
+ ++fDoneSpans;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
+ SkASSERT(!span->fDone || span->fTiny || span->fSmall);
+ span->fWindValue += windDelta;
+ SkASSERT(span->fWindValue >= 0);
+ span->fOppValue += oppDelta;
+ SkASSERT(span->fOppValue >= 0);
+ if (fXor) {
+ span->fWindValue &= 1;
+ }
+ if (fOppXor) {
+ span->fOppValue &= 1;
+ }
+ if (!span->fWindValue && !span->fOppValue) {
+ if (!span->fDone) {
+ span->fDone = true;
+ ++fDoneSpans;
+ }
+ return true;
+ }
+ return false;
+}
+
+const SkOpSpan& SkOpSegment::firstSpan(const SkOpSpan& thisSpan) const {
+ const SkOpSpan* firstSpan = &thisSpan; // rewind to the start
+ const SkOpSpan* beginSpan = fTs.begin();
+ const SkPoint& testPt = thisSpan.fPt;
+ while (firstSpan > beginSpan && firstSpan[-1].fPt == testPt) {
+ --firstSpan;
+ }
+ return *firstSpan;
+}
+
+const SkOpSpan& SkOpSegment::lastSpan(const SkOpSpan& thisSpan) const {
+ const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
+ const SkOpSpan* lastSpan = &thisSpan; // find the end
+ const SkPoint& testPt = thisSpan.fPt;
+ while (lastSpan < endSpan && lastSpan[1].fPt == testPt) {
+ ++lastSpan;
+ }
+ return *lastSpan;
+}
+
+// with a loop, the comparison is move involved
+// scan backwards and forwards to count all matching points
+// (verify that there are twp scans marked as loops)
+// compare that against 2 matching scans for loop plus other results
+bool SkOpSegment::calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts) {
+ const SkOpSpan& firstSpan = this->firstSpan(thisSpan); // rewind to the start
+ const SkOpSpan& lastSpan = this->lastSpan(thisSpan); // find the end
+ double firstLoopT = -1, lastLoopT = -1;
+ const SkOpSpan* testSpan = &firstSpan - 1;
+ while (++testSpan <= &lastSpan) {
+ if (testSpan->fLoop) {
+ firstLoopT = testSpan->fT;
+ break;
+ }
+ }
+ testSpan = &lastSpan + 1;
+ while (--testSpan >= &firstSpan) {
+ if (testSpan->fLoop) {
+ lastLoopT = testSpan->fT;
+ break;
+ }
+ }
+ SkASSERT((firstLoopT == -1) == (lastLoopT == -1));
+ if (firstLoopT == -1) {
+ return false;
+ }
+ SkASSERT(firstLoopT < lastLoopT);
+ testSpan = &firstSpan - 1;
+ smallCounts[0] = smallCounts[1] = 0;
+ while (++testSpan <= &lastSpan) {
+ SkASSERT(approximately_equal(testSpan->fT, firstLoopT) +
+ approximately_equal(testSpan->fT, lastLoopT) == 1);
+ smallCounts[approximately_equal(testSpan->fT, lastLoopT)]++;
+ }
+ return true;
+}
+
+double SkOpSegment::calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
+ double hiEnd, const SkOpSegment* other, int thisStart) {
+ if (max >= hiEnd) {
+ return -1;
+ }
+ int end = findOtherT(hiEnd, ref);
+ if (end < 0) {
+ return -1;
+ }
+ double tHi = span(end).fT;
+ double tLo, refLo;
+ if (thisStart >= 0) {
+ tLo = span(thisStart).fT;
+ refLo = min;
+ } else {
+ int start1 = findOtherT(loEnd, ref);
+ SkASSERT(start1 >= 0);
+ tLo = span(start1).fT;
+ refLo = loEnd;
+ }
+ double missingT = (max - refLo) / (hiEnd - refLo);
+ missingT = tLo + missingT * (tHi - tLo);
+ return missingT;
+}
+
+double SkOpSegment::calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
+ double hiEnd, const SkOpSegment* other, int thisEnd) {
+ if (min <= loEnd) {
+ return -1;
+ }
+ int start = findOtherT(loEnd, ref);
+ if (start < 0) {
+ return -1;
+ }
+ double tLo = span(start).fT;
+ double tHi, refHi;
+ if (thisEnd >= 0) {
+ tHi = span(thisEnd).fT;
+ refHi = max;
+ } else {
+ int end1 = findOtherT(hiEnd, ref);
+ if (end1 < 0) {
+ return -1;
+ }
+ tHi = span(end1).fT;
+ refHi = hiEnd;
+ }
+ double missingT = (min - loEnd) / (refHi - loEnd);
+ missingT = tLo + missingT * (tHi - tLo);
+ return missingT;
+}
+
+// see if spans with two or more intersections have the same number on the other end
+void SkOpSegment::checkDuplicates() {
+ debugValidate();
+ SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
+ int index;
+ int endIndex = 0;
+ bool endFound;
+ do {
+ index = endIndex;
+ endIndex = nextExactSpan(index, 1);
+ if ((endFound = endIndex < 0)) {
+ endIndex = count();
+ }
+ int dupCount = endIndex - index;
+ if (dupCount < 2) {
+ continue;
+ }
+ do {
+ const SkOpSpan* thisSpan = &fTs[index];
+ if (thisSpan->fNear) {
+ continue;
+ }
+ SkOpSegment* other = thisSpan->fOther;
+ int oIndex = thisSpan->fOtherIndex;
+ int oStart = other->nextExactSpan(oIndex, -1) + 1;
+ int oEnd = other->nextExactSpan(oIndex, 1);
+ if (oEnd < 0) {
+ oEnd = other->count();
+ }
+ int oCount = oEnd - oStart;
+ // force the other to match its t and this pt if not on an end point
+ if (oCount != dupCount) {
+ MissingSpan& missing = missingSpans.push_back();
+ missing.fOther = NULL;
+ SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
+ missing.fPt = thisSpan->fPt;
+ const SkOpSpan& oSpan = other->span(oIndex);
+ if (oCount > dupCount) {
+ missing.fSegment = this;
+ missing.fT = thisSpan->fT;
+ other->checkLinks(&oSpan, &missingSpans);
+ } else {
+ missing.fSegment = other;
+ missing.fT = oSpan.fT;
+ checkLinks(thisSpan, &missingSpans);
+ }
+ if (!missingSpans.back().fOther) {
+ missingSpans.pop_back();
+ }
+ }
+ } while (++index < endIndex);
+ } while (!endFound);
+ int missingCount = missingSpans.count();
+ if (missingCount == 0) {
+ return;
+ }
+ SkSTArray<kMissingSpanCount, MissingSpan, true> missingCoincidence;
+ for (index = 0; index < missingCount; ++index) {
+ MissingSpan& missing = missingSpans[index];
+ SkOpSegment* missingOther = missing.fOther;
+ if (missing.fSegment == missing.fOther) {
+ continue;
+ }
+#if 0 // FIXME: this eliminates spurious data from skpwww_argus_presse_fr_41 but breaks
+ // skpwww_fashionscandal_com_94 -- calcAngles complains, but I don't understand why
+ if (missing.fSegment->containsT(missing.fT, missing.fOther, missing.fOtherT)) {
+#if DEBUG_DUPLICATES
+ SkDebugf("skip 1 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fSegment->fID,
+ missing.fT, missing.fOther->fID, missing.fOtherT);
+#endif
+ continue;
+ }
+ if (missing.fOther->containsT(missing.fOtherT, missing.fSegment, missing.fT)) {
+#if DEBUG_DUPLICATES
+ SkDebugf("skip 2 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fOther->fID,
+ missing.fOtherT, missing.fSegment->fID, missing.fT);
+#endif
+ continue;
+ }
+#endif
+ // skip if adding would insert point into an existing coincindent span
+ if (missing.fSegment->inCoincidentSpan(missing.fT, missingOther)
+ && missingOther->inCoincidentSpan(missing.fOtherT, this)) {
+ continue;
+ }
+ // skip if the created coincident spans are small
+ if (missing.fSegment->coincidentSmall(missing.fPt, missing.fT, missingOther)
+ && missingOther->coincidentSmall(missing.fPt, missing.fOtherT, missing.fSegment)) {
+ continue;
+ }
+ const SkOpSpan* added = missing.fSegment->addTPair(missing.fT, missingOther,
+ missing.fOtherT, false, missing.fPt);
+ if (added && added->fSmall) {
+ missing.fSegment->checkSmallCoincidence(*added, &missingCoincidence);
+ }
+ }
+ for (index = 0; index < missingCount; ++index) {
+ MissingSpan& missing = missingSpans[index];
+ missing.fSegment->fixOtherTIndex();
+ missing.fOther->fixOtherTIndex();
+ }
+ for (index = 0; index < missingCoincidence.count(); ++index) {
+ MissingSpan& missing = missingCoincidence[index];
+ missing.fSegment->fixOtherTIndex();
+ }
+ debugValidate();
+}
+
+// look to see if the curve end intersects an intermediary that intersects the other
+bool SkOpSegment::checkEnds() {
+ debugValidate();
+ SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
+ int count = fTs.count();
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ double otherT = span.fOtherT;
+ if (otherT != 0 && otherT != 1) { // only check ends
+ continue;
+ }
+ const SkOpSegment* other = span.fOther;
+ // peek start/last describe the range of spans that match the other t of this span
+ int peekStart = span.fOtherIndex;
+ while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT)
+ ;
+ int otherCount = other->fTs.count();
+ int peekLast = span.fOtherIndex;
+ while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT)
+ ;
+ if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do
+ continue;
+ }
+ // t start/last describe the range of spans that match the t of this span
+ double t = span.fT;
+ double tBottom = -1;
+ int tStart = -1;
+ int tLast = count;
+ bool lastSmall = false;
+ double afterT = t;
+ for (int inner = 0; inner < count; ++inner) {
+ double innerT = fTs[inner].fT;
+ if (innerT <= t && innerT > tBottom) {
+ if (innerT < t || !lastSmall) {
+ tStart = inner - 1;
+ }
+ tBottom = innerT;
+ }
+ if (innerT > afterT) {
+ if (t == afterT && lastSmall) {
+ afterT = innerT;
+ } else {
+ tLast = inner;
+ break;
+ }
+ }
+ lastSmall = innerT <= t ? fTs[inner].fSmall : false;
+ }
+ for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) {
+ if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span
+ continue;
+ }
+ const SkOpSpan& peekSpan = other->fTs[peekIndex];
+ SkOpSegment* match = peekSpan.fOther;
+ if (match->done()) {
+ continue; // if the edge has already been eaten (likely coincidence), ignore it
+ }
+ const double matchT = peekSpan.fOtherT;
+ // see if any of the spans match the other spans
+ for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
+ const SkOpSpan& tSpan = fTs[tIndex];
+ if (tSpan.fOther == match) {
+ if (tSpan.fOtherT == matchT) {
+ goto nextPeekIndex;
+ }
+ double midT = (tSpan.fOtherT + matchT) / 2;
+ if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
+ goto nextPeekIndex;
+ }
+ }
+ }
+ if (missingSpans.count() > 0) {
+ const MissingSpan& lastMissing = missingSpans.back();
+ if (lastMissing.fT == t
+ && lastMissing.fOther == match
+ && lastMissing.fOtherT == matchT) {
+ SkASSERT(SkDPoint::ApproximatelyEqual(lastMissing.fPt, peekSpan.fPt));
+ continue;
+ }
+ }
+ 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
+ // this segment is missing a entry that the other contains
+ // remember so we can add the missing one and recompute the indices
+ {
+ MissingSpan& missing = missingSpans.push_back();
+ SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
+ missing.fT = t;
+ SkASSERT(this != match);
+ missing.fOther = match;
+ missing.fOtherT = matchT;
+ missing.fPt = peekSpan.fPt;
+ }
+ break;
+nextPeekIndex:
+ ;
+ }
+ }
+ if (missingSpans.count() == 0) {
+ debugValidate();
+ return true;
+ }
+ debugValidate();
+ int missingCount = missingSpans.count();
+ for (int index = 0; index < missingCount; ++index) {
+ MissingSpan& missing = missingSpans[index];
+ if (this != missing.fOther) {
+ addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
+ }
+ }
+ fixOtherTIndex();
+ // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
+ // avoid this
+ for (int index = 0; index < missingCount; ++index) {
+ missingSpans[index].fOther->fixOtherTIndex();
+ }
+ debugValidate();
+ return true;
+}
+
+void SkOpSegment::checkLinks(const SkOpSpan* base,
+ SkTArray<MissingSpan, true>* missingSpans) const {
+ const SkOpSpan* first = fTs.begin();
+ const SkOpSpan* last = fTs.end() - 1;
+ SkASSERT(base >= first && last >= base);
+ const SkOpSegment* other = base->fOther;
+ const SkOpSpan* oFirst = other->fTs.begin();
+ const SkOpSpan* oLast = other->fTs.end() - 1;
+ const SkOpSpan* oSpan = &other->fTs[base->fOtherIndex];
+ const SkOpSpan* test = base;
+ const SkOpSpan* missing = NULL;
+ while (test > first && (--test)->fPt == base->fPt) {
+ if (this == test->fOther) {
+ continue;
+ }
+ CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
+ }
+ test = base;
+ while (test < last && (++test)->fPt == base->fPt) {
+ SkASSERT(this != test->fOther || test->fLoop);
+ CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
+ }
+}
+
+// see if spans with two or more intersections all agree on common t and point values
+void SkOpSegment::checkMultiples() {
+ debugValidate();
+ int index;
+ int end = 0;
+ while (fTs[++end].fT == 0)
+ ;
+ while (fTs[end].fT < 1) {
+ int start = index = end;
+ end = nextExactSpan(index, 1);
+ if (end <= index) {
+ return; // buffer overflow example triggers this
+ }
+ if (index + 1 == end) {
+ continue;
+ }
+ // force the duplicates to agree on t and pt if not on the end
+ SkOpSpan& span = fTs[index];
+ double thisT = span.fT;
+ const SkPoint& thisPt = span.fPt;
+ span.fMultiple = true;
+ bool aligned = false;
+ while (++index < end) {
+ aligned |= alignSpan(index, thisT, thisPt);
+ }
+ if (aligned) {
+ alignSpanState(start, end);
+ }
+ fMultiples = true;
+ }
+ debugValidate();
+}
+
+void SkOpSegment::CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
+ const SkOpSpan* oFirst, const SkOpSpan* oLast, const SkOpSpan** missingPtr,
+ SkTArray<MissingSpan, true>* missingSpans) {
+ SkASSERT(oSpan->fPt == test->fPt);
+ const SkOpSpan* oTest = oSpan;
+ while (oTest > oFirst && (--oTest)->fPt == test->fPt) {
+ if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
+ return;
+ }
+ }
+ oTest = oSpan;
+ while (oTest < oLast && (++oTest)->fPt == test->fPt) {
+ if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
+ return;
+ }
+ }
+ if (*missingPtr) {
+ missingSpans->push_back();
+ }
+ MissingSpan& lastMissing = missingSpans->back();
+ if (*missingPtr) {
+ lastMissing = missingSpans->end()[-2];
+ }
+ *missingPtr = test;
+ lastMissing.fOther = test->fOther;
+ lastMissing.fOtherT = test->fOtherT;
+}
+
+bool SkOpSegment::checkSmall(int index) const {
+ if (fTs[index].fSmall) {
+ return true;
+ }
+ double tBase = fTs[index].fT;
+ while (index > 0 && precisely_negative(tBase - fTs[--index].fT))
+ ;
+ return fTs[index].fSmall;
+}
+
+// a pair of curves may turn into coincident lines -- small may be a hint that that happened
+// if a cubic contains a loop, the counts must be adjusted
+void SkOpSegment::checkSmall() {
+ SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
+ const SkOpSpan* beginSpan = fTs.begin();
+ const SkOpSpan* thisSpan = beginSpan - 1;
+ const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
+ while (++thisSpan < endSpan) {
+ if (!thisSpan->fSmall) {
+ continue;
+ }
+ if (!thisSpan->fWindValue) {
+ continue;
+ }
+ const SkOpSpan& firstSpan = this->firstSpan(*thisSpan);
+ const SkOpSpan& lastSpan = this->lastSpan(*thisSpan);
+ const SkOpSpan* nextSpan = &firstSpan + 1;
+ ptrdiff_t smallCount = &lastSpan - &firstSpan + 1;
+ SkASSERT(1 <= smallCount && smallCount < count());
+ if (smallCount <= 1 && !nextSpan->fSmall) {
+ SkASSERT(1 == smallCount);
+ checkSmallCoincidence(firstSpan, NULL);
+ continue;
+ }
+ // at this point, check for missing computed intersections
+ const SkPoint& testPt = firstSpan.fPt;
+ thisSpan = &firstSpan - 1;
+ SkOpSegment* other = NULL;
+ while (++thisSpan <= &lastSpan) {
+ other = thisSpan->fOther;
+ if (other != this) {
+ break;
+ }
+ }
+ SkASSERT(other != this);
+ int oIndex = thisSpan->fOtherIndex;
+ const SkOpSpan& oSpan = other->span(oIndex);
+ const SkOpSpan& oFirstSpan = other->firstSpan(oSpan);
+ const SkOpSpan& oLastSpan = other->lastSpan(oSpan);
+ ptrdiff_t oCount = &oLastSpan - &oFirstSpan + 1;
+ if (fLoop) {
+ int smallCounts[2];
+ SkASSERT(!other->fLoop); // FIXME: we need more complicated logic for pair of loops
+ if (calcLoopSpanCount(*thisSpan, smallCounts)) {
+ if (smallCounts[0] && oCount != smallCounts[0]) {
+ SkASSERT(0); // FIXME: need a working test case to properly code & debug
+ }
+ if (smallCounts[1] && oCount != smallCounts[1]) {
+ SkASSERT(0); // FIXME: need a working test case to properly code & debug
+ }
+ goto nextSmallCheck;
+ }
+ }
+ if (other->fLoop) {
+ int otherCounts[2];
+ if (other->calcLoopSpanCount(other->span(oIndex), otherCounts)) {
+ if (otherCounts[0] && otherCounts[0] != smallCount) {
+ SkASSERT(0); // FIXME: need a working test case to properly code & debug
+ }
+ if (otherCounts[1] && otherCounts[1] != smallCount) {
+ SkASSERT(0); // FIXME: need a working test case to properly code & debug
+ }
+ goto nextSmallCheck;
+ }
+ }
+ if (oCount != smallCount) { // check if number of pts in this match other
+ MissingSpan& missing = missingSpans.push_back();
+ missing.fOther = NULL;
+ SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
+ missing.fPt = testPt;
+ const SkOpSpan& oSpan = other->span(oIndex);
+ if (oCount > smallCount) {
+ missing.fSegment = this;
+ missing.fT = thisSpan->fT;
+ other->checkLinks(&oSpan, &missingSpans);
+ } else {
+ missing.fSegment = other;
+ missing.fT = oSpan.fT;
+ checkLinks(thisSpan, &missingSpans);
+ }
+ if (!missingSpans.back().fOther || missing.fSegment->done()) {
+ missingSpans.pop_back();
+ }
+ }
+nextSmallCheck:
+ thisSpan = &lastSpan;
+ }
+ int missingCount = missingSpans.count();
+ for (int index = 0; index < missingCount; ++index) {
+ MissingSpan& missing = missingSpans[index];
+ SkOpSegment* missingOther = missing.fOther;
+ // note that add t pair may edit span arrays, so prior pointers to spans are no longer valid
+ if (!missing.fSegment->addTPair(missing.fT, missingOther, missing.fOtherT, false,
+ missing.fPt)) {
+ continue;
+ }
+ int otherTIndex = missingOther->findT(missing.fOtherT, missing.fPt, missing.fSegment);
+ 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) {
+ const SkOpSpan* priorSpan = &otherSpan;
+ do {
+ --priorSpan;
+ } while (priorSpan->fT == otherSpan.fT);
+ if (priorSpan->fSmall) {
+ missing.fSegment->addTCancel(missing.fPt, priorSpan->fPt, missingOther);
+ }
+ }
+ }
+ // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
+ // avoid this
+ for (int index = 0; index < missingCount; ++index) {
+ MissingSpan& missing = missingSpans[index];
+ missing.fSegment->fixOtherTIndex();
+ missing.fOther->fixOtherTIndex();
+ }
+ debugValidate();
+}
+
+void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span,
+ SkTArray<MissingSpan, true>* checkMultiple) {
+ SkASSERT(span.fSmall);
+ if (0 && !span.fWindValue) {
+ return;
+ }
+ SkASSERT(&span < fTs.end() - 1);
+ const SkOpSpan* next = &span + 1;
+ SkASSERT(!next->fSmall || checkMultiple);
+ if (checkMultiple) {
+ while (next->fSmall) {
+ ++next;
+ SkASSERT(next < fTs.end());
+ }
+ }
+ SkOpSegment* other = span.fOther;
+ while (other != next->fOther) {
+ if (!checkMultiple) {
+ return;
+ }
+ const SkOpSpan* test = next + 1;
+ if (test == fTs.end()) {
+ return;
+ }
+ if (test->fPt != next->fPt || !precisely_equal(test->fT, next->fT)) {
+ return;
+ }
+ next = test;
+ }
+ SkASSERT(span.fT < next->fT);
+ int oStartIndex = other->findExactT(span.fOtherT, this);
+ int oEndIndex = other->findExactT(next->fOtherT, this);
+ // FIXME: be overly conservative by limiting this to the caller that allows multiple smalls
+ if (!checkMultiple || fVerb != SkPath::kLine_Verb || other->fVerb != SkPath::kLine_Verb) {
+ SkPoint mid = ptAtT((span.fT + next->fT) / 2);
+ const SkOpSpan& oSpanStart = other->fTs[oStartIndex];
+ const SkOpSpan& oSpanEnd = other->fTs[oEndIndex];
+ SkPoint oMid = other->ptAtT((oSpanStart.fT + oSpanEnd.fT) / 2);
+ if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
+ return;
+ }
+ }
+ // FIXME: again, be overly conservative to avoid breaking existing tests
+ const SkOpSpan& oSpan = oStartIndex < oEndIndex ? other->fTs[oStartIndex]
+ : other->fTs[oEndIndex];
+ if (checkMultiple && !oSpan.fSmall) {
+ return;
+ }
+// SkASSERT(oSpan.fSmall);
+ if (oStartIndex < oEndIndex) {
+ SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, other));
+ } else {
+ addTCancel(span.fPt, next->fPt, other);
+ }
+ if (!checkMultiple) {
+ return;
+ }
+ // check to see if either segment is coincident with a third segment -- if it is, and if
+ // the opposite segment is not already coincident with the third, make it so
+ // OPTIMIZE: to make this check easier, add coincident and cancel could set a coincident bit
+ if (span.fWindValue != 1 || span.fOppValue != 0) {
+// start here;
+ // iterate through the spans, looking for the third coincident case
+ // if we find one, we need to return state to the caller so that the indices can be fixed
+ // this also suggests that all of this function is fragile since it relies on a valid index
+ }
+ // probably should make this a common function rather than copy/paste code
+ if (oSpan.fWindValue != 1 || oSpan.fOppValue != 0) {
+ const SkOpSpan* oTest = &oSpan;
+ while (--oTest >= other->fTs.begin()) {
+ if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
+ break;
+ }
+ SkOpSegment* testOther = oTest->fOther;
+ SkASSERT(testOther != this);
+ // look in both directions to see if there is a coincident span
+ const SkOpSpan* tTest = testOther->fTs.begin();
+ for (int testIndex = 0; testIndex < testOther->count(); ++testIndex) {
+ if (tTest->fPt != span.fPt) {
+ ++tTest;
+ continue;
+ }
+ if (testOther->verb() != SkPath::kLine_Verb
+ || other->verb() != SkPath::kLine_Verb) {
+ SkPoint mid = ptAtT((span.fT + next->fT) / 2);
+ SkPoint oMid = other->ptAtT((oTest->fOtherT + tTest->fT) / 2);
+ if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
+ continue;
+ }
+ }
+#if DEBUG_CONCIDENT
+ SkDebugf("%s coincident found=%d %1.9g %1.9g\n", __FUNCTION__, testOther->fID,
+ oTest->fOtherT, tTest->fT);
+#endif
+ if (tTest->fT < oTest->fOtherT) {
+ SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, testOther));
+ } else {
+ addTCancel(span.fPt, next->fPt, testOther);
+ }
+ MissingSpan missing;
+ missing.fSegment = testOther;
+ checkMultiple->push_back(missing);
+ break;
+ }
+ }
+ oTest = &oSpan;
+ while (++oTest < other->fTs.end()) {
+ if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
+ break;
+ }
+
+ }
+ }
}
-void SkOpSegment::detach(const SkOpSpan* span) {
- if (span->done()) {
- --this->fDoneCount;
+// if pair of spans on either side of tiny have the same end point and mid point, mark
+// them as parallel
+void SkOpSegment::checkTiny() {
+ SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
+ SkOpSpan* thisSpan = fTs.begin() - 1;
+ const SkOpSpan* endSpan = fTs.end() - 1; // last can't be tiny
+ while (++thisSpan < endSpan) {
+ if (!thisSpan->fTiny) {
+ continue;
+ }
+ SkOpSpan* nextSpan = thisSpan + 1;
+ double thisT = thisSpan->fT;
+ double nextT = nextSpan->fT;
+ if (thisT == nextT) {
+ continue;
+ }
+ SkASSERT(thisT < nextT);
+ SkASSERT(thisSpan->fPt == nextSpan->fPt);
+ SkOpSegment* thisOther = thisSpan->fOther;
+ SkOpSegment* nextOther = nextSpan->fOther;
+ int oIndex = thisSpan->fOtherIndex;
+ for (int oStep = -1; oStep <= 1; oStep += 2) {
+ int oEnd = thisOther->nextExactSpan(oIndex, oStep);
+ if (oEnd < 0) {
+ continue;
+ }
+ const SkOpSpan& oSpan = thisOther->span(oEnd);
+ int nIndex = nextSpan->fOtherIndex;
+ for (int nStep = -1; nStep <= 1; nStep += 2) {
+ int nEnd = nextOther->nextExactSpan(nIndex, nStep);
+ if (nEnd < 0) {
+ continue;
+ }
+ const SkOpSpan& nSpan = nextOther->span(nEnd);
+ if (oSpan.fPt != nSpan.fPt) {
+ continue;
+ }
+ double oMidT = (thisSpan->fOtherT + oSpan.fT) / 2;
+ const SkPoint& oPt = thisOther->ptAtT(oMidT);
+ double nMidT = (nextSpan->fOtherT + nSpan.fT) / 2;
+ const SkPoint& nPt = nextOther->ptAtT(nMidT);
+ if (!AlmostEqualUlps(oPt, nPt)) {
+ continue;
+ }
+#if DEBUG_CHECK_TINY
+ SkDebugf("%s [%d] add coincidence [%d] [%d]\n", __FUNCTION__, fID,
+ thisOther->fID, nextOther->fID);
+#endif
+ // this segment is missing a entry that the other contains
+ // remember so we can add the missing one and recompute the indices
+ MissingSpan& missing = missingSpans.push_back();
+ SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
+ missing.fSegment = thisOther;
+ missing.fT = thisSpan->fOtherT;
+ SkASSERT(this != nextOther);
+ missing.fOther = nextOther;
+ missing.fOtherT = nextSpan->fOtherT;
+ missing.fPt = thisSpan->fPt;
+ }
+ }
+ }
+ int missingCount = missingSpans.count();
+ if (!missingCount) {
+ return;
+ }
+ for (int index = 0; index < missingCount; ++index) {
+ MissingSpan& missing = missingSpans[index];
+ if (missing.fSegment != missing.fOther) {
+ missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false,
+ missing.fPt);
+ }
+ }
+ // OPTIMIZE: consolidate to avoid multiple calls to fix index
+ for (int index = 0; index < missingCount; ++index) {
+ MissingSpan& missing = missingSpans[index];
+ missing.fSegment->fixOtherTIndex();
+ missing.fOther->fixOtherTIndex();
}
- --this->fCount;
}
-double SkOpSegment::distSq(double t, SkOpAngle* oppAngle) {
- SkDPoint testPt = this->dPtAtT(t);
- SkDLine testPerp = {{ testPt, testPt }};
- SkDVector slope = this->dSlopeAtT(t);
- testPerp[1].fX += slope.fY;
- testPerp[1].fY -= slope.fX;
- SkIntersections i;
- SkOpSegment* oppSegment = oppAngle->segment();
- int oppPtCount = SkPathOpsVerbToPoints(oppSegment->verb());
- (*CurveIntersectRay[oppPtCount])(oppSegment->pts(), testPerp, &i);
- double closestDistSq = SK_ScalarInfinity;
- for (int index = 0; index < i.used(); ++index) {
- if (!between(oppAngle->start()->t(), i[0][index], oppAngle->end()->t())) {
+bool SkOpSegment::coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const {
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = this->span(index);
+ if (span.fOther != other) {
+ continue;
+ }
+ if (span.fPt == pt) {
+ continue;
+ }
+ if (!AlmostEqualUlps(span.fPt, pt)) {
continue;
}
- double testDistSq = testPt.distanceSquared(i.pt(index));
- if (closestDistSq > testDistSq) {
- closestDistSq = testDistSq;
+ if (fVerb != SkPath::kCubic_Verb) {
+ return true;
}
+ double tInterval = t - span.fT;
+ double tMid = t - tInterval / 2;
+ SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
+ return midPt.approximatelyEqual(xyAtT(t));
}
- return closestDistSq;
+ return false;
+}
+
+bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart,
+ int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const {
+ SkASSERT(span->fT == 0 || span->fT == 1);
+ SkASSERT(span->fOtherT == 0 || span->fOtherT == 1);
+ const SkOpSpan* otherSpan = &other->span(oEnd);
+ double refT = otherSpan->fT;
+ const SkPoint& refPt = otherSpan->fPt;
+ const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0);
+ do {
+ const SkOpSegment* match = span->fOther;
+ if (match == otherSpan->fOther) {
+ // find start of respective spans and see if both have winding
+ int startIndex, endIndex;
+ if (span->fOtherT == 1) {
+ endIndex = span->fOtherIndex;
+ startIndex = match->nextExactSpan(endIndex, -1);
+ } else {
+ startIndex = span->fOtherIndex;
+ endIndex = match->nextExactSpan(startIndex, 1);
+ }
+ const SkOpSpan& startSpan = match->span(startIndex);
+ if (startSpan.fWindValue != 0) {
+ // draw ray from endSpan.fPt perpendicular to end tangent and measure distance
+ // to other segment.
+ const SkOpSpan& endSpan = match->span(endIndex);
+ SkDLine ray;
+ SkVector dxdy;
+ if (span->fOtherT == 1) {
+ ray.fPts[0].set(startSpan.fPt);
+ dxdy = match->dxdy(startIndex);
+ } else {
+ ray.fPts[0].set(endSpan.fPt);
+ dxdy = match->dxdy(endIndex);
+ }
+ ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY;
+ ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX;
+ SkIntersections i;
+ int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray);
+ for (int index = 0; index < roots; ++index) {
+ if (ray.fPts[0].approximatelyEqual(i.pt(index))) {
+ double matchMidT = (match->span(startIndex).fT
+ + match->span(endIndex).fT) / 2;
+ SkPoint matchMidPt = match->ptAtT(matchMidT);
+ double otherMidT = (i[0][index] + other->span(oStart).fT) / 2;
+ SkPoint otherMidPt = other->ptAtT(otherMidT);
+ if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) {
+ *startPt = startSpan.fPt;
+ *endPt = endSpan.fPt;
+ *endT = endSpan.fT;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+ if (otherSpan == lastSpan) {
+ break;
+ }
+ otherSpan += step;
+ } while (otherSpan->fT == refT || otherSpan->fPt == refPt);
+ return false;
+}
+
+int SkOpSegment::findEndSpan(int endIndex) const {
+ const SkOpSpan* span = &fTs[--endIndex];
+ const SkPoint& lastPt = span->fPt;
+ double endT = span->fT;
+ do {
+ span = &fTs[--endIndex];
+ } while (SkDPoint::ApproximatelyEqual(span->fPt, lastPt) && (span->fT == endT || span->fTiny));
+ return endIndex + 1;
}
/*
@@ -789,57 +3029,71 @@ double SkOpSegment::distSq(double t, SkOpAngle* oppAngle) {
The Opp variable name part designates that the value is for the Opposite operator.
Opposite values result from combining coincident spans.
*/
-SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
- SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op, int xorMiMask, int xorSuMask) {
- SkOpSpanBase* start = *nextStart;
- SkOpSpanBase* end = *nextEnd;
- SkASSERT(start != end);
- int step = start->step(end);
- SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
- if (other) {
+SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
+ bool* unsortable, SkPathOp op, const int xorMiMask,
+ const int xorSuMask) {
+ const int startIndex = *nextStart;
+ const int endIndex = *nextEnd;
+ SkASSERT(startIndex != endIndex);
+ SkDEBUGCODE(const int count = fTs.count());
+ SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
+ int step = SkSign32(endIndex - startIndex);
+ *nextStart = startIndex;
+ SkOpSegment* other = isSimple(nextStart, &step);
+ if (other)
+ {
// mark the smaller of startIndex, endIndex done, and all adjacent
// spans with the same T value (but not 'other' spans)
#if DEBUG_WINDING
SkDebugf("%s simple\n", __FUNCTION__);
#endif
- SkOpSpan* startSpan = start->starter(end);
- if (startSpan->done()) {
+ int min = SkMin32(startIndex, endIndex);
+ if (fTs[min].fDone) {
+ return NULL;
+ }
+ markDoneBinary(min);
+ double startT = other->fTs[*nextStart].fT;
+ *nextEnd = *nextStart;
+ do {
+ *nextEnd += step;
+ } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
+ SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
+ if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
+ *unsortable = true;
return NULL;
}
- markDone(startSpan);
- *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
return other;
}
- SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
- SkASSERT(endNear == end); // is this ever not end?
- SkASSERT(endNear);
- SkASSERT(start != endNear);
- SkASSERT((start->t() < endNear->t()) ^ (step < 0));
+ const int end = nextExactSpan(startIndex, step);
+ SkASSERT(end >= 0);
+ SkASSERT(startIndex - endIndex != 0);
+ SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
// more than one viable candidate -- measure angles to find best
- int calcWinding = computeSum(start, endNear, SkOpAngle::kBinaryOpp);
+
+ int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp);
bool sortable = calcWinding != SK_NaN32;
if (!sortable) {
*unsortable = true;
- markDone(start->starter(end));
+ markDoneBinary(SkMin32(startIndex, endIndex));
return NULL;
}
- SkOpAngle* angle = this->spanToAngle(end, start);
+ SkOpAngle* angle = spanToAngle(end, startIndex);
if (angle->unorderable()) {
*unsortable = true;
- markDone(start->starter(end));
+ markDoneBinary(SkMin32(startIndex, endIndex));
return NULL;
}
#if DEBUG_SORT
SkDebugf("%s\n", __FUNCTION__);
angle->debugLoop();
#endif
- int sumMiWinding = updateWinding(end, start);
+ int sumMiWinding = updateWinding(endIndex, startIndex);
if (sumMiWinding == SK_MinS32) {
*unsortable = true;
- markDone(start->starter(end));
+ markDoneBinary(SkMin32(startIndex, endIndex));
return NULL;
}
- int sumSuWinding = updateOppWinding(end, start);
+ int sumSuWinding = updateOppWinding(endIndex, startIndex);
if (operand()) {
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
@@ -856,6 +3110,11 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBa
if (activeAngle) {
++activeCount;
if (!foundAngle || (foundDone && activeCount & 1)) {
+ if (nextSegment->isTiny(nextAngle)) {
+ *unsortable = true;
+ markDoneBinary(SkMin32(startIndex, endIndex));
+ return NULL;
+ }
foundAngle = nextAngle;
foundDone = nextSegment->done(nextAngle);
}
@@ -863,24 +3122,30 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBa
if (nextSegment->done()) {
continue;
}
+ if (nextSegment->isTiny(nextAngle)) {
+ continue;
+ }
if (!activeAngle) {
- (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
+ (void) nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
}
- SkOpSpanBase* last = nextAngle->lastMarked();
+ SkOpSpan* last = nextAngle->lastMarked();
if (last) {
SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
*chase->append() = last;
#if DEBUG_WINDING
- SkDebugf("%s chase.append segment=%d span=%d", __FUNCTION__,
- last->segment()->debugID(), last->debugID());
- if (!last->final()) {
- SkDebugf(" windSum=%d", last->upCast()->windSum());
- }
- SkDebugf("\n");
+ SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
+ last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
+ last->fSmall);
#endif
}
} while ((nextAngle = nextAngle->next()) != angle);
- start->segment()->markDone(start->starter(end));
+#if DEBUG_ANGLE
+ if (foundAngle) {
+ foundAngle->debugSameAs(foundAngle);
+ }
+#endif
+
+ markDoneBinary(SkMin32(startIndex, endIndex));
if (!foundAngle) {
return NULL;
}
@@ -894,55 +3159,62 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBa
return nextSegment;
}
-SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
- SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable) {
- SkOpSpanBase* start = *nextStart;
- SkOpSpanBase* end = *nextEnd;
- SkASSERT(start != end);
- int step = start->step(end);
- SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
- if (other) {
+SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart,
+ int* nextEnd, bool* unsortable) {
+ const int startIndex = *nextStart;
+ const int endIndex = *nextEnd;
+ SkASSERT(startIndex != endIndex);
+ SkDEBUGCODE(const int count = fTs.count());
+ SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
+ int step = SkSign32(endIndex - startIndex);
+ *nextStart = startIndex;
+ SkOpSegment* other = isSimple(nextStart, &step);
+ if (other)
+ {
// mark the smaller of startIndex, endIndex done, and all adjacent
// spans with the same T value (but not 'other' spans)
#if DEBUG_WINDING
SkDebugf("%s simple\n", __FUNCTION__);
#endif
- SkOpSpan* startSpan = start->starter(end);
- if (startSpan->done()) {
+ int min = SkMin32(startIndex, endIndex);
+ if (fTs[min].fDone) {
+ return NULL;
+ }
+ markDoneUnary(min);
+ double startT = other->fTs[*nextStart].fT;
+ *nextEnd = *nextStart;
+ do {
+ *nextEnd += step;
+ } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
+ SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
+ if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
+ *unsortable = true;
return NULL;
}
- markDone(startSpan);
- *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
return other;
}
- SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
- SkASSERT(endNear == end); // is this ever not end?
- SkASSERT(endNear);
- SkASSERT(start != endNear);
- SkASSERT((start->t() < endNear->t()) ^ (step < 0));
+ const int end = nextExactSpan(startIndex, step);
+ SkASSERT(end >= 0);
+ SkASSERT(startIndex - endIndex != 0);
+ SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
// more than one viable candidate -- measure angles to find best
- int calcWinding = computeSum(start, endNear, SkOpAngle::kUnaryWinding);
+
+ int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding);
bool sortable = calcWinding != SK_NaN32;
if (!sortable) {
*unsortable = true;
- markDone(start->starter(end));
- return NULL;
- }
- SkOpAngle* angle = this->spanToAngle(end, start);
- if (angle->unorderable()) {
- *unsortable = true;
- markDone(start->starter(end));
+ markDoneUnary(SkMin32(startIndex, endIndex));
return NULL;
}
+ SkOpAngle* angle = spanToAngle(end, startIndex);
#if DEBUG_SORT
SkDebugf("%s\n", __FUNCTION__);
angle->debugLoop();
#endif
- int sumWinding = updateWinding(end, start);
+ int sumWinding = updateWinding(endIndex, startIndex);
SkOpAngle* nextAngle = angle->next();
const SkOpAngle* foundAngle = NULL;
bool foundDone = false;
- // iterate through the angle, and compute everyone's winding
SkOpSegment* nextSegment;
int activeCount = 0;
do {
@@ -952,6 +3224,11 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
if (activeAngle) {
++activeCount;
if (!foundAngle || (foundDone && activeCount & 1)) {
+ if (nextSegment->isTiny(nextAngle)) {
+ *unsortable = true;
+ markDoneUnary(SkMin32(startIndex, endIndex));
+ return NULL;
+ }
foundAngle = nextAngle;
foundDone = nextSegment->done(nextAngle);
}
@@ -959,24 +3236,24 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
if (nextSegment->done()) {
continue;
}
+ if (nextSegment->isTiny(nextAngle)) {
+ continue;
+ }
if (!activeAngle) {
- (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
+ nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
}
- SkOpSpanBase* last = nextAngle->lastMarked();
+ SkOpSpan* last = nextAngle->lastMarked();
if (last) {
SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
*chase->append() = last;
#if DEBUG_WINDING
- SkDebugf("%s chase.append segment=%d span=%d", __FUNCTION__,
- last->segment()->debugID(), last->debugID());
- if (!last->final()) {
- SkDebugf(" windSum=%d", last->upCast()->windSum());
- }
- SkDebugf("\n");
+ SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
+ last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
+ last->fSmall);
#endif
}
} while ((nextAngle = nextAngle->next()) != angle);
- start->segment()->markDone(start->starter(end));
+ markDoneUnary(SkMin32(startIndex, endIndex));
if (!foundAngle) {
return NULL;
}
@@ -990,39 +3267,57 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
return nextSegment;
}
-SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd,
- bool* unsortable) {
- SkOpSpanBase* start = *nextStart;
- SkOpSpanBase* end = *nextEnd;
- SkASSERT(start != end);
- int step = start->step(end);
- SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
- if (other) {
- // mark the smaller of startIndex, endIndex done, and all adjacent
- // spans with the same T value (but not 'other' spans)
+SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsortable) {
+ const int startIndex = *nextStart;
+ const int endIndex = *nextEnd;
+ SkASSERT(startIndex != endIndex);
+ SkDEBUGCODE(int count = fTs.count());
+ SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
+ int step = SkSign32(endIndex - startIndex);
+// Detect cases where all the ends canceled out (e.g.,
+// there is no angle) and therefore there's only one valid connection
+ *nextStart = startIndex;
+ SkOpSegment* other = isSimple(nextStart, &step);
+ if (other)
+ {
#if DEBUG_WINDING
SkDebugf("%s simple\n", __FUNCTION__);
#endif
- SkOpSpan* startSpan = start->starter(end);
- if (startSpan->done()) {
+ int min = SkMin32(startIndex, endIndex);
+ if (fTs[min].fDone) {
return NULL;
}
- markDone(startSpan);
- *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
+ markDone(min, 1);
+ double startT = other->fTs[*nextStart].fT;
+ // FIXME: I don't know why the logic here is difference from the winding case
+ SkDEBUGCODE(bool firstLoop = true;)
+ if ((approximately_less_than_zero(startT) && step < 0)
+ || (approximately_greater_than_one(startT) && step > 0)) {
+ step = -step;
+ SkDEBUGCODE(firstLoop = false;)
+ }
+ do {
+ *nextEnd = *nextStart;
+ do {
+ *nextEnd += step;
+ } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
+ if (other->fTs[SkMin32(*nextStart, *nextEnd)].fWindValue) {
+ break;
+ }
+ SkASSERT(firstLoop);
+ SkDEBUGCODE(firstLoop = false;)
+ step = -step;
+ } while (true);
+ SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
return other;
}
- SkDEBUGCODE(SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() \
- : (*nextStart)->prev());
- SkASSERT(endNear == end); // is this ever not end?
- SkASSERT(endNear);
- SkASSERT(start != endNear);
- SkASSERT((start->t() < endNear->t()) ^ (step < 0));
- SkOpAngle* angle = this->spanToAngle(end, start);
- if (angle->unorderable()) {
- *unsortable = true;
- markDone(start->starter(end));
- return NULL;
- }
+ SkASSERT(startIndex - endIndex != 0);
+ SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+ // parallel block above with presorted version
+ int end = nextExactSpan(startIndex, step);
+ SkASSERT(end >= 0);
+ SkOpAngle* angle = spanToAngle(end, startIndex);
+ SkASSERT(angle);
#if DEBUG_SORT
SkDebugf("%s\n", __FUNCTION__);
angle->debugLoop();
@@ -1030,13 +3325,16 @@ SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** n
SkOpAngle* nextAngle = angle->next();
const SkOpAngle* foundAngle = NULL;
bool foundDone = false;
- // iterate through the angle, and compute everyone's winding
SkOpSegment* nextSegment;
int activeCount = 0;
do {
nextSegment = nextAngle->segment();
++activeCount;
if (!foundAngle || (foundDone && activeCount & 1)) {
+ if (nextSegment->isTiny(nextAngle)) {
+ *unsortable = true;
+ return NULL;
+ }
foundAngle = nextAngle;
if (!(foundDone = nextSegment->done(nextAngle))) {
break;
@@ -1044,7 +3342,7 @@ SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** n
}
nextAngle = nextAngle->next();
} while (nextAngle != angle);
- start->segment()->markDone(start->starter(end));
+ markDone(SkMin32(startIndex, endIndex), 1);
if (!foundAngle) {
return NULL;
}
@@ -1058,39 +3356,105 @@ SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** n
return nextSegment;
}
-SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
- bool* unsortable, SkChunkAlloc* allocator) {
+int SkOpSegment::findStartSpan(int startIndex) const {
+ int index = startIndex;
+ const SkOpSpan* span = &fTs[index];
+ const SkPoint& firstPt = span->fPt;
+ double firstT = span->fT;
+ const SkOpSpan* prior;
+ do {
+ prior = span;
+ span = &fTs[++index];
+ } while (SkDPoint::ApproximatelyEqual(span->fPt, firstPt)
+ && (span->fT == firstT || prior->fTiny));
+ return index;
+}
+
+int SkOpSegment::findExactT(double t, const SkOpSegment* match) const {
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (span.fT == t && span.fOther == match) {
+ return index;
+ }
+ }
+ SkASSERT(0);
+ return -1;
+}
+
+
+
+int SkOpSegment::findOtherT(double t, const SkOpSegment* match) const {
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (span.fOtherT == t && span.fOther == match) {
+ return index;
+ }
+ }
+ return -1;
+}
+
+int SkOpSegment::findT(double t, const SkPoint& pt, const SkOpSegment* match) const {
+ int count = this->count();
+ // prefer exact matches over approximate matches
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (span.fT == t && span.fOther == match) {
+ return index;
+ }
+ }
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (approximately_equal_orderable(span.fT, t) && span.fOther == match) {
+ return index;
+ }
+ }
+ // Usually, the pair of ts are an exact match. It's possible that the t values have
+ // been adjusted to make multiple intersections align. In this rare case, look for a
+ // matching point / match pair instead.
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (span.fPt == pt && span.fOther == match) {
+ return index;
+ }
+ }
+ SkASSERT(0);
+ return -1;
+}
+
+SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable,
+ bool firstPass) {
// iterate through T intersections and return topmost
// topmost tangent from y-min to first pt is closer to horizontal
SkASSERT(!done());
- SkOpSpanBase* firstT = NULL;
- (void) this->activeLeftTop(&firstT);
- if (!firstT) {
+ int firstT = -1;
+ /* SkPoint topPt = */ activeLeftTop(&firstT);
+ if (firstT < 0) {
*unsortable = !firstPass;
- firstT = &fHead;
- while (firstT->upCast()->done()) {
- firstT = firstT->upCast()->next();
+ firstT = 0;
+ while (fTs[firstT].fDone) {
+ SkASSERT(firstT < fTs.count());
+ ++firstT;
}
- *startPtr = firstT;
- *endPtr = firstT->upCast()->next();
+ *tIndexPtr = firstT;
+ *endIndexPtr = nextExactSpan(firstT, 1);
return this;
}
// sort the edges to find the leftmost
int step = 1;
- SkOpSpanBase* end;
- if (firstT->final() || firstT->upCast()->done()) {
+ int end;
+ if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) {
step = -1;
- end = firstT->prev();
- SkASSERT(end);
- } else {
- end = firstT->upCast()->next();
+ end = nextSpan(firstT, step);
+ SkASSERT(end != -1);
}
// if the topmost T is not on end, or is three-way or more, find left
// look for left-ness from tLeft to firstT (matching y of other)
- SkASSERT(firstT != end);
+ SkASSERT(firstT - end != 0);
SkOpAngle* markAngle = spanToAngle(firstT, end);
if (!markAngle) {
- markAngle = addSingletonAngles(step, allocator);
+ markAngle = addSingletonAngles(step);
}
markAngle->markStops();
const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
@@ -1103,7 +3467,7 @@ SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpS
const SkOpAngle* angle = baseAngle;
do {
if (!angle->unorderable()) {
- const SkOpSegment* next = angle->segment();
+ SkOpSegment* next = angle->segment();
SkPathOpsBounds bounds;
next->subDivideBounds(angle->end(), angle->start(), &bounds);
bool nearSame = AlmostEqualUlps(top, bounds.top());
@@ -1131,10 +3495,9 @@ SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpS
*unsortable = angle->unorderable();
if (firstPass || !*unsortable) {
leftSegment = angle->segment();
- *startPtr = angle->end();
- *endPtr = angle->start();
- const SkOpSpan* firstSpan = (*startPtr)->starter(*endPtr);
- if (!firstSpan->done()) {
+ *tIndexPtr = angle->end();
+ *endIndexPtr = angle->start();
+ if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
break;
}
}
@@ -1145,52 +3508,157 @@ SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpS
return NULL;
}
if (leftSegment->verb() >= SkPath::kQuad_Verb) {
- SkOpSpanBase* start = *startPtr;
- SkOpSpanBase* end = *endPtr;
+ const int tIndex = *tIndexPtr;
+ const int endIndex = *endIndexPtr;
bool swap;
- if (!leftSegment->clockwise(start, end, &swap)) {
+ if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
#if DEBUG_SWAP_TOP
- SkDebugf("%s swap=%d inflections=%d monotonic=%d\n",
+ SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
__FUNCTION__,
- swap, leftSegment->debugInflections(start, end),
- leftSegment->monotonicInY(start, end));
+ swap, leftSegment->debugInflections(tIndex, endIndex),
+ leftSegment->serpentine(tIndex, endIndex),
+ leftSegment->controlsContainedByEnds(tIndex, endIndex),
+ leftSegment->monotonicInY(tIndex, endIndex));
#endif
if (swap) {
// FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
// sorted but merely the first not already processed (i.e., not done)
- SkTSwap(*startPtr, *endPtr);
+ SkTSwap(*tIndexPtr, *endIndexPtr);
}
}
}
+ SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
return leftSegment;
}
-SkOpGlobalState* SkOpSegment::globalState() const {
- return contour()->globalState();
+int SkOpSegment::firstActive(int tIndex) const {
+ while (fTs[tIndex].fTiny) {
+ SkASSERT(!isCanceled(tIndex));
+ ++tIndex;
+ }
+ return tIndex;
+}
+
+// FIXME: not crazy about this
+// when the intersections are performed, the other index is into an
+// incomplete array. As the array grows, the indices become incorrect
+// while the following fixes the indices up again, it isn't smart about
+// skipping segments whose indices are already correct
+// assuming we leave the code that wrote the index in the first place
+// FIXME: if called after remove, this needs to correct tiny
+void SkOpSegment::fixOtherTIndex() {
+ int iCount = fTs.count();
+ for (int i = 0; i < iCount; ++i) {
+ SkOpSpan& iSpan = fTs[i];
+ double oT = iSpan.fOtherT;
+ SkOpSegment* other = iSpan.fOther;
+ int oCount = other->fTs.count();
+ SkDEBUGCODE(iSpan.fOtherIndex = -1);
+ for (int o = 0; o < oCount; ++o) {
+ SkOpSpan& oSpan = other->fTs[o];
+ if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) {
+ iSpan.fOtherIndex = o;
+ oSpan.fOtherIndex = i;
+ break;
+ }
+ }
+ SkASSERT(iSpan.fOtherIndex >= 0);
+ }
+}
+
+bool SkOpSegment::inCoincidentSpan(double t, const SkOpSegment* other) const {
+ int foundEnds = 0;
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ const SkOpSpan& span = this->span(index);
+ if (span.fCoincident) {
+ foundEnds |= (span.fOther == other) << ((t > span.fT) + (t >= span.fT));
+ }
+ }
+ SkASSERT(foundEnds != 7);
+ 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);
}
-void SkOpSegment::init(SkPoint pts[], SkOpContour* contour, SkPath::Verb verb) {
- fContour = contour;
- fNext = NULL;
+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;
+ fXor = evenOdd;
fPts = pts;
fVerb = verb;
- fCount = 0;
- fDoneCount = 0;
- fVisited = false;
- SkOpSpan* zeroSpan = &fHead;
- zeroSpan->init(this, NULL, 0, fPts[0]);
- SkOpSpanBase* oneSpan = &fTail;
- zeroSpan->setNext(oneSpan);
- oneSpan->initBase(this, zeroSpan, 1, fPts[SkPathOpsVerbToPoints(fVerb)]);
- PATH_OPS_DEBUG_CODE(fID = globalState()->nextSegmentID());
-}
-
-void SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end,
- SkOpAngle::IncludeType angleIncludeType) {
- int local = SkOpSegment::SpanSign(start, end);
+ fLoop = fMultiples = fSmall = fTiny = false;
+}
+
+void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
+ int local = spanSign(start, end);
SkDEBUGCODE(bool success);
if (angleIncludeType == SkOpAngle::kBinarySingle) {
- int oppLocal = SkOpSegment::OppSign(start, end);
+ int oppLocal = oppSign(start, end);
SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, oppLocal, NULL);
// OPTIMIZATION: the reverse mark and chase could skip the first marking
SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, oppLocal, NULL);
@@ -1211,13 +3679,12 @@ 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.
*/
-bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHit,
- int winding, SkScalar hitDx, int oppWind, SkScalar hitOppDx) {
- SkASSERT(this == start->segment());
+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;
-// SkASSERT(dx);
- int windVal = start->starter(end)->windValue();
+ SkASSERT(dx);
+ int windVal = windValue(SkMin32(start, end));
#if DEBUG_WINDING_AT_T
SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), winding,
hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
@@ -1226,9 +3693,9 @@ bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHi
if (abs(winding) < abs(sideWind)) {
winding = sideWind;
}
- SkDEBUGCODE(int oppLocal = SkOpSegment::OppSign(start, end));
+ SkDEBUGCODE(int oppLocal = oppSign(start, end));
SkASSERT(hitOppDx || !oppWind || !oppLocal);
- int oppWindVal = start->starter(end)->oppValue();
+ int oppWindVal = oppValue(SkMin32(start, end));
if (!oppWind) {
oppWind = dx < 0 ? oppWindVal : -oppWindVal;
} else if (hitOppDx * dx >= 0) {
@@ -1247,57 +3714,149 @@ bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHi
return success;
}
-bool SkOpSegment::isClose(double t, const SkOpSegment* opp) const {
- SkDPoint cPt = this->dPtAtT(t);
- int pts = SkPathOpsVerbToPoints(this->verb());
- SkDVector dxdy = (*CurveDSlopeAtT[pts])(this->pts(), t);
- SkDLine perp = {{ cPt, {cPt.fX + dxdy.fY, cPt.fY - dxdy.fX} }};
- SkIntersections i;
- int oppPts = SkPathOpsVerbToPoints(opp->verb());
- (*CurveIntersectRay[oppPts])(opp->pts(), perp, &i);
- int used = i.used();
- for (int index = 0; index < used; ++index) {
- if (cPt.roughlyEqual(i.pt(index))) {
- return true;
+bool SkOpSegment::inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const {
+ if (!baseAngle->inLoop()) {
+ return false;
+ }
+ int index = *indexPtr;
+ SkOpAngle* from = fTs[index].fFromAngle;
+ SkOpAngle* to = fTs[index].fToAngle;
+ while (++index < spanCount) {
+ SkOpAngle* nextFrom = fTs[index].fFromAngle;
+ SkOpAngle* nextTo = fTs[index].fToAngle;
+ if (from != nextFrom || to != nextTo) {
+ break;
+ }
+ }
+ *indexPtr = index;
+ return true;
+}
+
+// OPTIMIZE: successive calls could start were the last leaves off
+// or calls could specialize to walk forwards or backwards
+bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
+ int tCount = fTs.count();
+ for (int index = 0; index < tCount; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (approximately_zero(startT - span.fT) && pt == span.fPt) {
+ return false;
}
}
+ return true;
+}
+
+
+SkOpSegment* SkOpSegment::isSimple(int* end, int* step) {
+ return nextChase(end, step, NULL, NULL);
+}
+
+bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
+ int start = angle->start();
+ int end = angle->end();
+ const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
+ return mSpan.fTiny;
+}
+
+bool SkOpSegment::isTiny(int index) const {
+ return fTs[index].fTiny;
+}
+
+// look pair of active edges going away from coincident edge
+// one of them should be the continuation of other
+// if both are active, look to see if they both the connect to another coincident pair
+// if at least one is a line, then make the pair coincident
+// if neither is a line, test for coincidence
+bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt,
+ int step, bool cancel) {
+ int otherTIndex = other->findT(otherT, otherPt, this);
+ int next = other->nextExactSpan(otherTIndex, step);
+ int otherMin = SkMin32(otherTIndex, next);
+ int otherWind = other->span(otherMin).fWindValue;
+ if (otherWind == 0) {
+ return false;
+ }
+ 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];
+ SkASSERT(test->fT == 0);
+ if (test->fOther == other || test->fOtherT != 1) {
+ continue;
+ }
+ SkPoint startPt, endPt;
+ double endT;
+ if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) {
+ SkOpSegment* match = test->fOther;
+ if (cancel) {
+ match->addTCancel(startPt, endPt, other);
+ } else {
+ if (!match->addTCoincident(startPt, endPt, endT, other)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ } while (fTs[++tIndex].fT == 0);
return false;
}
-bool SkOpSegment::isXor() const {
- return fContour->isXor();
+// this span is excluded by the winding rule -- chase the ends
+// as long as they are unambiguous to mark connections as done
+// and give them the same winding value
+
+SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
+ int step = SkSign32(endIndex - index);
+ int min = SkMin32(index, endIndex);
+ markDoneBinary(min);
+ SkOpSpan* last = NULL;
+ SkOpSegment* other = this;
+ while ((other = other->nextChase(&index, &step, &min, &last))) {
+ if (other->done()) {
+ SkASSERT(!last);
+ break;
+ }
+ other->markDoneBinary(min);
+ }
+ return last;
}
-SkOpSpanBase* SkOpSegment::markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end) {
- int step = start->step(end);
- SkOpSpan* minSpan = start->starter(end);
- markDone(minSpan);
- SkOpSpanBase* last = NULL;
+SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
+ int step = SkSign32(endIndex - index);
+ int min = SkMin32(index, endIndex);
+ markDoneUnary(min);
+ SkOpSpan* last = NULL;
SkOpSegment* other = this;
- while ((other = other->nextChase(&start, &step, &minSpan, &last))) {
+ while ((other = other->nextChase(&index, &step, &min, &last))) {
if (other->done()) {
SkASSERT(!last);
break;
}
- other->markDone(minSpan);
+ other->markDoneUnary(min);
}
return last;
}
-bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
- SkOpSpanBase** lastPtr) {
- SkOpSpan* spanStart = start->starter(end);
- int step = start->step(end);
- bool success = markWinding(spanStart, winding);
- SkOpSpanBase* last = NULL;
+bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, SkOpSpan** lastPtr) {
+ int index = angle->start();
+ int endIndex = angle->end();
+ return markAndChaseWinding(index, endIndex, winding, lastPtr);
+}
+
+bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, SkOpSpan** lastPtr) {
+ int min = SkMin32(index, endIndex);
+ int step = SkSign32(endIndex - index);
+ bool success = markWinding(min, winding);
+ SkOpSpan* last = NULL;
SkOpSegment* other = this;
- while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
- if (spanStart->windSum() != SK_MinS32) {
- SkASSERT(spanStart->windSum() == winding);
+ while ((other = other->nextChase(&index, &step, &min, &last))) {
+ if (other->fTs[min].fWindSum != SK_MinS32) {
+ SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
SkASSERT(!last);
break;
}
- (void) other->markWinding(spanStart, winding);
+ (void) other->markWinding(min, winding);
}
if (lastPtr) {
*lastPtr = last;
@@ -1305,32 +3864,37 @@ bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, in
return success;
}
-bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end,
- int winding, int oppWinding, SkOpSpanBase** lastPtr) {
- SkOpSpan* spanStart = start->starter(end);
- int step = start->step(end);
- bool success = markWinding(spanStart, winding, oppWinding);
- SkOpSpanBase* last = NULL;
+bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding,
+ SkOpSpan** lastPtr) {
+ int min = SkMin32(index, endIndex);
+ int step = SkSign32(endIndex - index);
+ bool success = markWinding(min, winding, oppWinding);
+ SkOpSpan* last = NULL;
SkOpSegment* other = this;
- while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
- if (spanStart->windSum() != SK_MinS32) {
- if (this->operand() == other->operand()) {
- SkASSERT(spanStart->windSum() == winding);
- if (spanStart->oppSum() != oppWinding) {
- this->globalState()->setWindingFailed();
- return false;
+ while ((other = other->nextChase(&index, &step, &min, &last))) {
+ if (other->fTs[min].fWindSum != SK_MinS32) {
+#ifdef SK_DEBUG
+ if (!other->fTs[min].fLoop) {
+ if (fOperand == other->fOperand) {
+// FIXME: this is probably a bug -- rects4 asserts here
+// SkASSERT(other->fTs[min].fWindSum == winding);
+// FIXME: this is probably a bug -- rects3 asserts here
+// SkASSERT(other->fTs[min].fOppSum == oppWinding);
+ } else {
+// FIXME: this is probably a bug -- issue414409b asserts here
+// SkASSERT(other->fTs[min].fWindSum == oppWinding);
+// FIXME: this is probably a bug -- skpwww_joomla_org_23 asserts here
+// SkASSERT(other->fTs[min].fOppSum == winding);
}
- } else {
- SkASSERT(spanStart->windSum() == oppWinding);
- SkASSERT(spanStart->oppSum() == winding);
}
SkASSERT(!last);
+#endif
break;
}
- if (this->operand() == other->operand()) {
- (void) other->markWinding(spanStart, winding, oppWinding);
+ if (fOperand == other->fOperand) {
+ (void) other->markWinding(min, winding, oppWinding);
} else {
- (void) other->markWinding(spanStart, oppWinding, winding);
+ (void) other->markWinding(min, oppWinding, winding);
}
}
if (lastPtr) {
@@ -1339,29 +3903,33 @@ bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end,
return success;
}
-SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
+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, lastPtr);
+}
+
+SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
SkASSERT(angle->segment() == this);
if (UseInnerWinding(maxWinding, sumWinding)) {
maxWinding = sumWinding;
}
- SkOpSpanBase* last;
- (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, &last);
+ SkOpSpan* last;
+ SkAssertResult(markAndChaseWinding(angle, maxWinding, &last));
#if DEBUG_WINDING
if (last) {
- SkDebugf("%s last seg=%d span=%d", __FUNCTION__,
- last->segment()->debugID(), last->debugID());
- if (!last->final()) {
- SkDebugf(" windSum=");
- SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
- }
- SkDebugf("\n");
+ SkDebugf("%s last id=%d windSum=", __FUNCTION__,
+ last->fOther->fTs[last->fOtherIndex].fOther->debugID());
+ SkPathOpsDebug::WindingPrintf(last->fWindSum);
+ SkDebugf(" small=%d\n", last->fSmall);
}
#endif
return last;
}
-SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
- int oppSumWinding, const SkOpAngle* angle) {
+SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
+ int oppSumWinding, const SkOpAngle* angle) {
SkASSERT(angle->segment() == this);
if (UseInnerWinding(maxWinding, sumWinding)) {
maxWinding = sumWinding;
@@ -1369,161 +3937,440 @@ SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxW
if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
oppMaxWinding = oppSumWinding;
}
- SkOpSpanBase* last = NULL;
+ SkOpSpan* last;
// caller doesn't require that this marks anything
- (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, oppMaxWinding, &last);
+ (void) markAndChaseWinding(angle, maxWinding, oppMaxWinding, &last);
#if DEBUG_WINDING
if (last) {
- SkDebugf("%s last segment=%d span=%d", __FUNCTION__,
- last->segment()->debugID(), last->debugID());
- if (!last->final()) {
- SkDebugf(" windSum=");
- SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
- }
- SkDebugf(" \n");
+ SkDebugf("%s last id=%d windSum=", __FUNCTION__,
+ last->fOther->fTs[last->fOtherIndex].fOther->debugID());
+ SkPathOpsDebug::WindingPrintf(last->fWindSum);
+ SkDebugf(" small=%d\n", last->fSmall);
}
#endif
return last;
}
-void SkOpSegment::markDone(SkOpSpan* span) {
- SkASSERT(this == span->segment());
- if (span->done()) {
- return;
+// FIXME: this should also mark spans with equal (x,y)
+// This may be called when the segment is already marked done. While this
+// wastes time, it shouldn't do any more than spin through the T spans.
+// OPTIMIZATION: abort on first done found (assuming that this code is
+// always called to mark segments done).
+void SkOpSegment::markDone(int index, int winding) {
+ // SkASSERT(!done());
+ SkASSERT(winding);
+ double referenceT = fTs[index].fT;
+ int lesser = index;
+ while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+ markOneDone(__FUNCTION__, lesser, winding);
}
-#if DEBUG_MARK_DONE
- debugShowNewWinding(__FUNCTION__, span, span->windSum(), span->oppSum());
-#endif
- span->setDone(true);
- ++fDoneCount;
+ do {
+ markOneDone(__FUNCTION__, index, winding);
+ } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
debugValidate();
}
-bool SkOpSegment::markWinding(SkOpSpan* span, int winding) {
- SkASSERT(this == span->segment());
- SkASSERT(winding);
- if (span->done()) {
+void SkOpSegment::markDoneBinary(int index) {
+ double referenceT = fTs[index].fT;
+ int lesser = index;
+ while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+ markOneDoneBinary(__FUNCTION__, lesser);
+ }
+ do {
+ markOneDoneBinary(__FUNCTION__, index);
+ } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+ 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;
+ while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+ markOneDoneUnary(__FUNCTION__, lesser);
+ }
+ do {
+ markOneDoneUnary(__FUNCTION__, index);
+ } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+ debugValidate();
+}
+
+void SkOpSegment::markOneDone(const char* funName, int tIndex, int winding) {
+ SkOpSpan* span;
+ (void) markOneWinding(funName, tIndex, winding, &span); // allowed to do nothing
+ if (span->fDone) {
+ return;
+ }
+ span->fDone = true;
+ ++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) {
+ SkOpSpan* span = verifyOneWinding(funName, tIndex);
+ if (!span) {
+ return;
+ }
+ SkASSERT(!span->fDone);
+ span->fDone = true;
+ ++fDoneSpans;
+}
+
+void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
+ SkOpSpan* span = verifyOneWindingU(funName, tIndex);
+ if (!span) {
+ return;
+ }
+ if (span->fWindSum == SK_MinS32) {
+ SkDebugf("%s uncomputed\n", __FUNCTION__);
+ }
+ SkASSERT(!span->fDone);
+ span->fDone = true;
+ ++fDoneSpans;
+}
+
+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(__FUNCTION__, span, winding);
+ debugShowNewWinding(funName, *span, winding);
#endif
- span->setWindSum(winding);
- debugValidate();
+ 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 true;
}
-bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
- SkASSERT(this == span->segment());
- SkASSERT(winding || oppWinding);
- if (span->done()) {
+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(__FUNCTION__, span, winding, oppWinding);
+ debugShowNewWinding(funName, *span, winding, oppWinding);
#endif
- span->setWindSum(winding);
- span->setOppSum(oppWinding);
+ 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);
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(oppWinding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
+ span->fOppSum = oppWinding;
debugValidate();
+ if (lastPtr) {
+ *lastPtr = span;
+ }
return true;
}
-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;
+// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
+bool SkOpSegment::clockwise(int tStart, int tEnd, bool* swap) const {
+ SkASSERT(fVerb != SkPath::kLine_Verb);
+ SkPoint edge[4];
+ subDivide(tStart, tEnd, edge);
+ int points = SkPathOpsVerbToPoints(fVerb);
+ double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
+ bool sumSet = false;
+ if (fVerb == SkPath::kCubic_Verb) {
+ SkDCubic cubic;
+ cubic.set(edge);
+ double inflectionTs[2];
+ int inflections = cubic.findInflections(inflectionTs);
+ // FIXME: this fixes cubicOp114 and breaks cubicOp58d
+ // the trouble is that cubics with inflections confuse whether the curve breaks towards
+ // or away, which in turn is used to determine if it is on the far right or left.
+ // Probably a totally different approach is in order. At one time I tried to project a
+ // horizontal ray to determine winding, but was confused by how to map the vertically
+ // oriented winding computation over.
+ if (0 && inflections) {
+ double tLo = this->span(tStart).fT;
+ double tHi = this->span(tEnd).fT;
+ double tLoStart = tLo;
+ for (int index = 0; index < inflections; ++index) {
+ if (between(tLo, inflectionTs[index], tHi)) {
+ tLo = inflectionTs[index];
+ }
+ }
+ if (tLo != tLoStart && tLo != tHi) {
+ SkDPoint sub[2];
+ sub[0] = cubic.ptAtT(tLo);
+ sub[1].set(edge[3]);
+ SkDPoint ctrl[2];
+ SkDCubic::SubDivide(fPts, sub[0], sub[1], tLo, tHi, ctrl);
+ edge[0] = sub[0].asSkPoint();
+ edge[1] = ctrl[0].asSkPoint();
+ edge[2] = ctrl[1].asSkPoint();
+ sum = (edge[0].fX - edge[3].fX) * (edge[0].fY + edge[3].fY);
+ }
+ }
+ SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
+ if (edge[1].fY < lesser && edge[2].fY < lesser) {
+ SkDLine tangent1 = {{ {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} }};
+ SkDLine tangent2 = {{ {edge[2].fX, edge[2].fY}, {edge[3].fX, edge[3].fY} }};
+ if (SkIntersections::Test(tangent1, tangent2)) {
+ SkPoint topPt = cubic_top(fPts, fTs[tStart].fT, fTs[tEnd].fT);
+ sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
+ sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
+ sumSet = true;
+ }
+ }
}
- if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
- return false;
+ if (!sumSet) {
+ for (int idx = 0; idx < points; ++idx){
+ sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
+ }
}
- return !ptsDisjoint(base->fT, base->fPt, testT, testPt);
-}
-
-static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
- if (last) {
- *last = endSpan;
+ if (fVerb == SkPath::kCubic_Verb) {
+ SkDCubic cubic;
+ cubic.set(edge);
+ *swap = sum > 0 && !cubic.monotonicInY() && !cubic.serpentine();
+ } else {
+ SkDQuad quad;
+ quad.set(edge);
+ *swap = sum > 0 && !quad.monotonicInY();
}
- return NULL;
+ return sum <= 0;
}
-bool SkOpSegment::monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
+bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
SkASSERT(fVerb != SkPath::kLine_Verb);
if (fVerb == SkPath::kQuad_Verb) {
- SkDQuad dst = SkDQuad::SubDivide(fPts, start->t(), end->t());
+ SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
return dst.monotonicInY();
}
SkASSERT(fVerb == SkPath::kCubic_Verb);
- SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
+ SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
return dst.monotonicInY();
}
-bool SkOpSegment::NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start,
- SkOpSpanBase** end) {
- while (span->final() || span->upCast()->done()) {
- if (span->final()) {
+bool SkOpSegment::serpentine(int tStart, int tEnd) const {
+ if (fVerb != SkPath::kCubic_Verb) {
+ return false;
+ }
+ SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
+ return dst.serpentine();
+}
+
+SkOpSpan* SkOpSegment::verifyOneWinding(const char* funName, int tIndex) {
+ SkOpSpan& span = fTs[tIndex];
+ if (span.fDone) {
+ return NULL;
+ }
+#if DEBUG_MARK_DONE
+ debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
+#endif
+// If the prior angle in the sort is unorderable, the winding sum may not be computable.
+// To enable the assert, the 'prior is unorderable' state could be
+// piped down to this test, but not sure it's worth it.
+// (Once the sort order is stored in the span, this test may be feasible.)
+// SkASSERT(span.fWindSum != SK_MinS32);
+// SkASSERT(span.fOppSum != SK_MinS32);
+ return &span;
+}
+
+SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
+ SkOpSpan& span = fTs[tIndex];
+ if (span.fDone) {
+ return NULL;
+ }
+#if DEBUG_MARK_DONE
+ debugShowNewWinding(funName, span, span.fWindSum);
+#endif
+// If the prior angle in the sort is unorderable, the winding sum may not be computable.
+// To enable the assert, the 'prior is unorderable' state could be
+// piped down to this test, but not sure it's worth it.
+// (Once the sort order is stored in the span, this test may be feasible.)
+// SkASSERT(span.fWindSum != SK_MinS32);
+ return &span;
+}
+
+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)) {
+ success |= markOneWinding(__FUNCTION__, lesser, winding, NULL);
+ }
+ do {
+ success |= markOneWinding(__FUNCTION__, index, winding, NULL);
+ } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+ debugValidate();
+ return success;
+}
+
+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)) {
+ success |= markOneWinding(__FUNCTION__, lesser, winding, oppWinding, NULL);
+ }
+ do {
+ 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) {
+ int nextDoorWind = SK_MaxS32;
+ int nextOppWind = SK_MaxS32;
+ // prefer exact matches
+ if (tIndex > 0) {
+ const SkOpSpan& below = fTs[tIndex - 1];
+ if (below.fT == t) {
+ nextDoorWind = below.fWindValue;
+ nextOppWind = below.fOppValue;
+ }
+ }
+ if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
+ const SkOpSpan& above = fTs[tIndex + 1];
+ if (above.fT == t) {
+ nextDoorWind = above.fWindValue;
+ nextOppWind = above.fOppValue;
+ }
+ }
+ if (nextDoorWind == SK_MaxS32 && tIndex > 0) {
+ const SkOpSpan& below = fTs[tIndex - 1];
+ if (approximately_negative(t - below.fT)) {
+ nextDoorWind = below.fWindValue;
+ nextOppWind = below.fOppValue;
+ }
+ }
+ if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
+ const SkOpSpan& above = fTs[tIndex + 1];
+ if (approximately_negative(above.fT - t)) {
+ nextDoorWind = above.fWindValue;
+ nextOppWind = above.fOppValue;
+ }
+ }
+ if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
+ const SkOpSpan& below = fTs[tIndex - 1];
+ nextDoorWind = below.fWindValue;
+ nextOppWind = below.fOppValue;
+ }
+ if (nextDoorWind != SK_MaxS32) {
+ SkOpSpan& newSpan = fTs[tIndex];
+ newSpan.fWindValue = nextDoorWind;
+ newSpan.fOppValue = nextOppWind;
+ if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
+ newSpan.fDone = true;
+ ++fDoneSpans;
+ }
+ }
+}
+
+bool SkOpSegment::nextCandidate(int* start, int* end) const {
+ while (fTs[*end].fDone) {
+ if (fTs[*end].fT == 1) {
return false;
}
- span = span->upCast()->next();
+ ++(*end);
}
- *start = span;
- *end = span->upCast()->next();
+ *start = *end;
+ *end = nextExactSpan(*start, 1);
return true;
}
-SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpSpan** minPtr,
- SkOpSpanBase** last) const {
- SkOpSpanBase* origStart = *startPtr;
+static SkOpSegment* set_last(SkOpSpan** last, const SkOpSpan* endSpan) {
+ if (last && !endSpan->fSmall) {
+ *last = const_cast<SkOpSpan*>(endSpan); // FIXME: get rid of cast
+ }
+ return NULL;
+}
+
+SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr,
+ SkOpSpan** last) const {
+ int origIndex = *indexPtr;
int step = *stepPtr;
- SkOpSpanBase* endSpan = step > 0 ? origStart->upCast()->next() : origStart->prev();
- SkASSERT(endSpan);
- SkOpAngle* angle = step > 0 ? endSpan->fromAngle() : endSpan->upCast()->toAngle();
- SkOpSpanBase* foundSpan;
- SkOpSpanBase* otherEnd;
+ int end = nextExactSpan(origIndex, step);
+ SkASSERT(end >= 0);
+ const SkOpSpan& endSpan = this->span(end);
+ SkOpAngle* angle = step > 0 ? endSpan.fFromAngle : endSpan.fToAngle;
+ int foundIndex;
+ int otherEnd;
SkOpSegment* other;
if (angle == NULL) {
- if (endSpan->t() != 0 && endSpan->t() != 1) {
+ if (endSpan.fT != 0 && endSpan.fT != 1) {
return NULL;
}
- SkOpPtT* otherPtT = endSpan->ptT()->next();
- other = otherPtT->segment();
- foundSpan = otherPtT->span();
- otherEnd = step > 0 ? foundSpan->upCast()->next() : foundSpan->prev();
+ other = endSpan.fOther;
+ foundIndex = endSpan.fOtherIndex;
+ otherEnd = other->nextExactSpan(foundIndex, step);
} else {
int loopCount = angle->loopCount();
if (loopCount > 2) {
- return set_last(last, endSpan);
+ return set_last(last, &endSpan);
}
const SkOpAngle* next = angle->next();
if (NULL == next) {
return NULL;
}
+ if (angle->sign() != next->sign()) {
#if DEBUG_WINDING
- if (angle->sign() != next->sign() && !angle->segment()->contour()->isXor()
- && !next->segment()->contour()->isXor()) {
SkDebugf("%s mismatched signs\n", __FUNCTION__);
- }
#endif
+ // return set_last(last, &endSpan);
+ }
other = next->segment();
- foundSpan = endSpan = next->start();
+ foundIndex = end = next->start();
otherEnd = next->end();
}
- int foundStep = foundSpan->step(otherEnd);
+ int foundStep = foundIndex < otherEnd ? 1 : -1;
if (*stepPtr != foundStep) {
- return set_last(last, endSpan);
+ return set_last(last, &endSpan);
}
- SkASSERT(*startPtr);
- if (!otherEnd) {
+ SkASSERT(*indexPtr >= 0);
+ if (otherEnd < 0) {
return NULL;
}
// SkASSERT(otherEnd >= 0);
- SkOpSpan* origMin = step < 0 ? origStart->prev() : origStart->upCast();
- SkOpSpan* foundMin = foundSpan->starter(otherEnd);
- if (foundMin->windValue() != origMin->windValue()
- || foundMin->oppValue() != origMin->oppValue()) {
- return set_last(last, endSpan);
+#if 1
+ int origMin = origIndex + (step < 0 ? step : 0);
+ const SkOpSpan& orig = this->span(origMin);
+#endif
+ int foundMin = SkMin32(foundIndex, otherEnd);
+#if 1
+ const SkOpSpan& found = other->span(foundMin);
+ if (found.fWindValue != orig.fWindValue || found.fOppValue != orig.fOppValue) {
+ return set_last(last, &endSpan);
}
- *startPtr = foundSpan;
+#endif
+ *indexPtr = foundIndex;
*stepPtr = foundStep;
if (minPtr) {
*minPtr = foundMin;
@@ -1531,217 +4378,101 @@ SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpS
return other;
}
-static void clear_visited(SkOpSpan* span) {
- // reset visited flag back to false
- do {
- SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
- while ((ptT = ptT->next()) != stopPtT) {
- SkOpSegment* opp = ptT->segment();
- opp->resetVisited();
+// This has callers for two different situations: one establishes the end
+// of the current span, and one establishes the beginning of the next span
+// (thus the name). When this is looking for the end of the current span,
+// coincidence is found when the beginning Ts contain -step and the end
+// contains step. When it is looking for the beginning of the next, the
+// first Ts found can be ignored and the last Ts should contain -step.
+// OPTIMIZATION: probably should split into two functions
+int SkOpSegment::nextSpan(int from, int step) const {
+ const SkOpSpan& fromSpan = fTs[from];
+ int count = fTs.count();
+ int to = from;
+ while (step > 0 ? ++to < count : --to >= 0) {
+ const SkOpSpan& span = fTs[to];
+ if (approximately_zero(span.fT - fromSpan.fT)) {
+ continue;
}
- } while ((span = span->next()->upCastable()));
+ return to;
+ }
+ return -1;
}
-// 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
-void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
- if (this->verb() != SkPath::kLine_Verb) {
- return;
- }
- SkOpSpan* prior = NULL;
- SkOpSpan* span = &fHead;
- do {
- SkOpPtT* ptT = span->ptT(), * spanStopPtT = ptT;
- SkASSERT(ptT->span() == span);
- while ((ptT = ptT->next()) != spanStopPtT) {
- SkOpSegment* opp = ptT->span()->segment();
- if (opp->setVisited()) {
- continue;
- }
- if (opp->verb() == SkPath::kLine_Verb) {
- continue;
- }
- if (span->containsCoincidence(opp)) { // FIXME: this assumes that if the opposite
- // segment is coincident then no more coincidence
- // needs to be detected. This may not be true.
+// FIXME
+// this returns at any difference in T, vs. a preset minimum. It may be
+// that all callers to nextSpan should use this instead.
+int SkOpSegment::nextExactSpan(int from, int step) const {
+ int to = from;
+ if (step < 0) {
+ const SkOpSpan& fromSpan = fTs[from];
+ while (--to >= 0) {
+ const SkOpSpan& span = fTs[to];
+ if (precisely_negative(fromSpan.fT - span.fT) || span.fTiny) {
continue;
}
- if (span->containsCoinEnd(opp)) {
- continue;
- }
- // if already visited and visited again, check for coin
- if (span == &fHead) {
- continue;
- }
- SkOpPtT* priorPtT = NULL, * priorStopPtT;
- // find prior span containing opp segment
- SkOpSegment* priorOpp = NULL;
- prior = span;
- while (!priorOpp && (prior = prior->prev())) {
- priorStopPtT = priorPtT = prior->ptT();
- while ((priorPtT = priorPtT->next()) != priorStopPtT) {
- SkOpSegment* segment = priorPtT->span()->segment();
- if (segment == opp) {
- priorOpp = opp;
- break;
- }
- }
- }
- if (!priorOpp) {
- continue;
- }
- SkOpPtT* oppStart = prior->ptT();
- SkOpPtT* oppEnd = span->ptT();
- bool swapped = priorPtT->fT > ptT->fT;
- if (swapped) {
- SkTSwap(priorPtT, ptT);
- SkTSwap(oppStart, oppEnd);
- }
- bool flipped = oppStart->fT > oppEnd->fT;
- bool coincident;
- if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
- goto swapBack;
- }
- {
- // average t, find mid pt
- double midT = (prior->t() + span->t()) / 2;
- SkPoint midPt = this->ptAtT(midT);
- coincident = true;
- // 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)) {
- coincident = false;
- SkIntersections i;
- int ptCount = SkPathOpsVerbToPoints(this->verb());
- SkVector dxdy = (*CurveSlopeAtT[ptCount])(pts(), midT);
- SkDLine ray = {{{midPt.fX, midPt.fY},
- {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}};
- int oppPtCount = SkPathOpsVerbToPoints(opp->verb());
- (*CurveIntersectRay[oppPtCount])(opp->pts(), ray, &i);
- // measure distance and see if it's small enough to denote coincidence
- for (int index = 0; index < i.used(); ++index) {
- SkDPoint oppPt = i.pt(index);
- if (oppPt.approximatelyEqual(midPt)) {
- SkVector oppDxdy = (*CurveSlopeAtT[oppPtCount])(opp->pts(),
- i[index][0]);
- oppDxdy.normalize();
- dxdy.normalize();
- SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON);
- coincident |= flatness < 5000; // FIXME: replace with tuned value
- }
- }
- }
- }
- if (coincident) {
- // mark coincidence
- coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator);
- clear_visited(&fHead);
- missingCoincidence(coincidences, allocator);
- return;
- }
- swapBack:
- if (swapped) {
- SkTSwap(priorPtT, ptT);
- }
+ return to;
}
- } while ((span = span->next()->upCastable()));
- clear_visited(&fHead);
-}
-
-// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
-bool SkOpSegment::moveNearby() {
- debugValidate();
- SkOpSpanBase* spanS = &fHead;
- do {
- SkOpSpanBase* test = spanS->upCast()->next();
- SkOpSpanBase* next;
- if (spanS->contains(test)) {
- if (!test->final()) {
- test->upCast()->detach(spanS->ptT());
- continue;
- } else if (spanS != &fHead) {
- spanS->upCast()->detach(test->ptT());
- spanS = test;
+ } else {
+ while (fTs[from].fTiny) {
+ from++;
+ }
+ const SkOpSpan& fromSpan = fTs[from];
+ int count = fTs.count();
+ while (++to < count) {
+ const SkOpSpan& span = fTs[to];
+ if (precisely_negative(span.fT - fromSpan.fT)) {
continue;
}
+ return to;
}
- do { // iterate through all spans associated with start
- SkOpPtT* startBase = spanS->ptT();
- next = test->final() ? NULL : 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 true; // if this span has collapsed, remove it from parent
- }
- this->fTail.merge(spanS->upCast());
- debugValidate();
- return true;
- }
- spanS->merge(test->upCast());
- spanS->upCast()->setNext(next);
- 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());
- debugValidate();
- return true;
-}
-
-bool SkOpSegment::operand() const {
- return fContour->operand();
+ }
+ return -1;
}
-bool SkOpSegment::oppXor() const {
- return fContour->oppXor();
+void SkOpSegment::pinT(const SkPoint& pt, double* t) {
+ if (pt == fPts[0]) {
+ *t = 0;
+ }
+ int count = SkPathOpsVerbToPoints(fVerb);
+ if (pt == fPts[count]) {
+ *t = 1;
+ }
}
-bool SkOpSegment::ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const {
- if (fVerb == SkPath::kLine_Verb) {
- return false;
+bool SkOpSegment::reversePoints(const SkPoint& p1, const SkPoint& p2) const {
+ SkASSERT(p1 != p2);
+ int spanCount = count();
+ int p1IndexMin = -1;
+ int p2IndexMax = spanCount;
+ for (int index = 0; index < spanCount; ++index) {
+ const SkOpSpan& span = fTs[index];
+ if (span.fPt == p1) {
+ if (p1IndexMin < 0) {
+ p1IndexMin = index;
+ }
+ } else if (span.fPt == p2) {
+ p2IndexMax = index;
+ }
}
- // quads (and cubics) can loop back to nearly a line so that an opposite curve
- // hits in two places with very different t values.
- // OPTIMIZATION: curves could be preflighted so that, for example, something like
- // 'controls contained by ends' could avoid this check for common curves
- // 'ends are extremes in x or y' is cheaper to compute and real-world common
- // on the other hand, the below check is relatively inexpensive
- double midT = (t1 + t2) / 2;
- SkPoint midPt = this->ptAtT(midT);
- double seDistSq = SkTMax(pt1.distanceToSqd(pt2) * 2, FLT_EPSILON * 2);
- return midPt.distanceToSqd(pt1) > seDistSq || midPt.distanceToSqd(pt2) > seDistSq;
+ return p1IndexMin > p2IndexMax;
}
-void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
- int* maxWinding, int* sumWinding) {
- int deltaSum = SpanSign(start, end);
- *maxWinding = *sumMiWinding;
- *sumWinding = *sumMiWinding -= deltaSum;
- SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+void SkOpSegment::setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt,
+ SkOpSegment* other) {
+ int count = this->count();
+ for (int index = 0; index < count; ++index) {
+ SkOpSpan &span = fTs[index];
+ if ((startPt == span.fPt || endPt == span.fPt) && other == span.fOther) {
+ span.fCoincident = true;
+ }
+ }
}
-void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
- int* sumSuWinding, int* maxWinding, int* sumWinding, int* oppMaxWinding,
- int* oppSumWinding) {
- int deltaSum = SpanSign(start, end);
- int oppDeltaSum = OppSign(start, end);
+void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
+ int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
+ int deltaSum = spanSign(index, endIndex);
+ int oppDeltaSum = oppSign(index, endIndex);
if (operand()) {
*maxWinding = *sumSuWinding;
*sumWinding = *sumSuWinding -= deltaSum;
@@ -1753,94 +4484,130 @@ void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sum
*oppMaxWinding = *sumSuWinding;
*oppSumWinding = *sumSuWinding -= oppDeltaSum;
}
- SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
- SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+ SkASSERT(abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
+}
+
+void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
+ int* maxWinding, int* sumWinding) {
+ int deltaSum = spanSign(index, endIndex);
+ *maxWinding = *sumMiWinding;
+ *sumWinding = *sumMiWinding -= deltaSum;
+#if DEBUG_LIMIT_WIND_SUM
+ SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+#endif
}
void SkOpSegment::sortAngles() {
- SkOpSpanBase* span = &this->fHead;
+ int spanCount = fTs.count();
+ if (spanCount <= 2) {
+ return;
+ }
+ int index = 0;
do {
- SkOpAngle* fromAngle = span->fromAngle();
- SkOpAngle* toAngle = span->final() ? NULL : span->upCast()->toAngle();
+ SkOpAngle* fromAngle = fTs[index].fFromAngle;
+ SkOpAngle* toAngle = fTs[index].fToAngle;
if (!fromAngle && !toAngle) {
+ index += 1;
continue;
}
+ SkOpAngle* baseAngle = NULL;
+ if (fromAngle) {
+ baseAngle = fromAngle;
+ if (inLoop(baseAngle, spanCount, &index)) {
+ continue;
+ }
+ }
#if DEBUG_ANGLE
bool wroteAfterHeader = false;
#endif
- SkOpAngle* baseAngle = fromAngle;
- if (fromAngle && toAngle) {
+ if (toAngle) {
+ if (!baseAngle) {
+ baseAngle = toAngle;
+ if (inLoop(baseAngle, spanCount, &index)) {
+ continue;
+ }
+ } else {
+ SkDEBUGCODE(int newIndex = index);
+ SkASSERT(!inLoop(baseAngle, spanCount, &newIndex) && newIndex == index);
#if DEBUG_ANGLE
- SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), span->t(),
- span->debugID());
- wroteAfterHeader = true;
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
+ index);
+ wroteAfterHeader = true;
#endif
- fromAngle->insert(toAngle);
- } else if (!fromAngle) {
- baseAngle = toAngle;
+ baseAngle->insert(toAngle);
+ }
}
- SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
+ SkOpAngle* nextFrom, * nextTo;
+ int firstIndex = index;
do {
- SkOpSpanBase* oSpan = ptT->span();
- if (oSpan == span) {
- continue;
- }
- SkOpAngle* oAngle = oSpan->fromAngle();
+ SkOpSpan& span = fTs[index];
+ SkOpSegment* other = span.fOther;
+ SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
+ SkOpAngle* oAngle = oSpan.fFromAngle;
if (oAngle) {
#if DEBUG_ANGLE
if (!wroteAfterHeader) {
- SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
- span->t(), span->debugID());
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
+ index);
wroteAfterHeader = true;
}
#endif
- if (!oAngle->loopContains(baseAngle)) {
+ if (!oAngle->loopContains(*baseAngle)) {
baseAngle->insert(oAngle);
}
}
- if (!oSpan->final()) {
- oAngle = oSpan->upCast()->toAngle();
- if (oAngle) {
+ oAngle = oSpan.fToAngle;
+ if (oAngle) {
#if DEBUG_ANGLE
- if (!wroteAfterHeader) {
- SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
- span->t(), span->debugID());
- wroteAfterHeader = true;
- }
+ if (!wroteAfterHeader) {
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
+ index);
+ wroteAfterHeader = true;
+ }
#endif
- if (!oAngle->loopContains(baseAngle)) {
- baseAngle->insert(oAngle);
- }
+ if (!oAngle->loopContains(*baseAngle)) {
+ baseAngle->insert(oAngle);
}
}
- } while ((ptT = ptT->next()) != stopPtT);
- if (baseAngle->loopCount() == 1) {
- span->setFromAngle(NULL);
- if (toAngle) {
- span->upCast()->setToAngle(NULL);
+ if (++index == spanCount) {
+ break;
}
+ nextFrom = fTs[index].fFromAngle;
+ nextTo = fTs[index].fToAngle;
+ } while (fromAngle == nextFrom && toAngle == nextTo);
+ if (baseAngle && baseAngle->loopCount() == 1) {
+ index = firstIndex;
+ do {
+ SkOpSpan& span = fTs[index];
+ span.fFromAngle = span.fToAngle = NULL;
+ if (++index == spanCount) {
+ break;
+ }
+ nextFrom = fTs[index].fFromAngle;
+ nextTo = fTs[index].fToAngle;
+ } while (fromAngle == nextFrom && toAngle == nextTo);
baseAngle = NULL;
}
#if DEBUG_SORT
SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
#endif
- } while (!span->final() && (span = span->upCast()->next()));
+ } while (index < spanCount);
}
// return true if midpoints were computed
-bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
- SkPoint edge[4]) const {
+bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
SkASSERT(start != end);
- const SkOpPtT& startPtT = *start->ptT();
- const SkOpPtT& endPtT = *end->ptT();
- edge[0] = startPtT.fPt;
+ edge[0] = fTs[start].fPt;
int points = SkPathOpsVerbToPoints(fVerb);
- edge[points] = endPtT.fPt;
+ edge[points] = fTs[end].fPt;
if (fVerb == SkPath::kLine_Verb) {
return false;
}
- double startT = startPtT.fT;
- double endT = endPtT.fT;
+ double startT = fTs[start].fT;
+ double endT = fTs[end].fT;
if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
// don't compute midpoints if we already have them
if (fVerb == SkPath::kQuad_Verb) {
@@ -1870,19 +4637,17 @@ bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
return true;
}
-bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
- SkDCubic* result) const {
+// return true if midpoints were computed
+bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
SkASSERT(start != end);
- const SkOpPtT& startPtT = *start->ptT();
- const SkOpPtT& endPtT = *end->ptT();
- (*result)[0].set(startPtT.fPt);
+ (*result)[0].set(fTs[start].fPt);
int points = SkPathOpsVerbToPoints(fVerb);
- (*result)[points].set(endPtT.fPt);
+ (*result)[points].set(fTs[end].fPt);
if (fVerb == SkPath::kLine_Verb) {
return false;
}
- double startT = startPtT.fT;
- double endT = endPtT.fT;
+ double startT = fTs[start].fT;
+ double endT = fTs[end].fT;
if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
// don't compute midpoints if we already have them
if (fVerb == SkPath::kQuad_Verb) {
@@ -1890,7 +4655,7 @@ bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
return false;
}
SkASSERT(fVerb == SkPath::kCubic_Verb);
- if (startT == 0) {
+ if (start < end) {
(*result)[1].set(fPts[1]);
(*result)[2].set(fPts[2]);
return false;
@@ -1908,29 +4673,49 @@ bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
return true;
}
-void SkOpSegment::subDivideBounds(const SkOpSpanBase* start, const SkOpSpanBase* end,
- SkPathOpsBounds* bounds) const {
+void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
SkPoint edge[4];
subDivide(start, end, edge);
(bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
}
-void SkOpSegment::undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end) {
- SkOpSpan* span = this->head();
- do {
- if (!span->done()) {
+void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt,
+ const SkPoint& startPt) {
+ int outCount = outsidePts->count();
+ if (outCount == 0 || endPt != (*outsidePts)[outCount - 2]) {
+ outsidePts->push_back(endPt);
+ outsidePts->push_back(startPt);
+ }
+}
+
+void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoint& startPt) {
+ int outCount = outsidePts->count();
+ if (outCount == 0 || startPt != (*outsidePts)[outCount - 1]) {
+ outsidePts->push_back(startPt);
+ }
+}
+
+void SkOpSegment::undoneSpan(int* start, int* end) {
+ int tCount = fTs.count();
+ int index;
+ for (index = 0; index < tCount; ++index) {
+ if (!fTs[index].fDone) {
break;
}
- } while ((span = span->next()->upCastable()));
- SkASSERT(span);
- *start = span;
- *end = span->next();
+ }
+ SkASSERT(index < tCount - 1);
+ *start = index;
+ double startT = fTs[index].fT;
+ while (approximately_negative(fTs[++index].fT - startT))
+ SkASSERT(index < tCount);
+ SkASSERT(index < tCount);
+ *end = index;
}
-int SkOpSegment::updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
- const SkOpSpan* lesser = start->starter(end);
- int oppWinding = lesser->oppSum();
- int oppSpanWinding = SkOpSegment::OppSign(start, end);
+int SkOpSegment::updateOppWinding(int index, int endIndex) const {
+ int lesser = SkMin32(index, endIndex);
+ int oppWinding = oppSum(lesser);
+ int oppSpanWinding = oppSign(index, endIndex);
if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
&& oppWinding != SK_MaxS32) {
oppWinding -= oppSpanWinding;
@@ -1939,24 +4724,24 @@ int SkOpSegment::updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase*
}
int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
- const SkOpSpanBase* startSpan = angle->start();
- const SkOpSpanBase* endSpan = angle->end();
- return updateOppWinding(endSpan, startSpan);
+ int startIndex = angle->start();
+ int endIndex = angle->end();
+ return updateOppWinding(endIndex, startIndex);
}
int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
- const SkOpSpanBase* startSpan = angle->start();
- const SkOpSpanBase* endSpan = angle->end();
- return updateOppWinding(startSpan, endSpan);
+ int startIndex = angle->start();
+ int endIndex = angle->end();
+ return updateOppWinding(startIndex, endIndex);
}
-int SkOpSegment::updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
- const SkOpSpan* lesser = start->starter(end);
- int winding = lesser->windSum();
+int SkOpSegment::updateWinding(int index, int endIndex) const {
+ int lesser = SkMin32(index, endIndex);
+ int winding = windSum(lesser);
if (winding == SK_MinS32) {
return winding;
}
- int spanWinding = SkOpSegment::SpanSign(start, end);
+ int spanWinding = spanSign(index, endIndex);
if (winding && UseInnerWinding(winding - spanWinding, winding)
&& winding != SK_MaxS32) {
winding -= spanWinding;
@@ -1965,15 +4750,26 @@ int SkOpSegment::updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* en
}
int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
- const SkOpSpanBase* startSpan = angle->start();
- const SkOpSpanBase* endSpan = angle->end();
- return updateWinding(endSpan, startSpan);
+ int startIndex = angle->start();
+ int endIndex = angle->end();
+ return updateWinding(endIndex, startIndex);
+}
+
+int SkOpSegment::updateWindingReverse(int index, int endIndex) const {
+ int lesser = SkMin32(index, endIndex);
+ int winding = windSum(lesser);
+ int spanWinding = spanSign(endIndex, index);
+ if (winding && UseInnerWindingReverse(winding - spanWinding, winding)
+ && winding != SK_MaxS32) {
+ winding -= spanWinding;
+ }
+ return winding;
}
int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
- const SkOpSpanBase* startSpan = angle->start();
- const SkOpSpanBase* endSpan = angle->end();
- return updateWinding(startSpan, endSpan);
+ int startIndex = angle->start();
+ int endIndex = angle->end();
+ return updateWindingReverse(endIndex, startIndex);
}
// OPTIMIZATION: does the following also work, and is it any faster?
@@ -1988,17 +4784,25 @@ bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
return result;
}
-int SkOpSegment::windingAtT(double tHit, const SkOpSpan* span, bool crossOpp,
- SkScalar* dx) const {
- if (approximately_zero(tHit - span->t())) { // if we hit the end of a span, disregard
+bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) {
+ SkASSERT(outerWinding != SK_MaxS32);
+ SkASSERT(innerWinding != SK_MaxS32);
+ int absOut = abs(outerWinding);
+ int absIn = abs(innerWinding);
+ bool result = absOut == absIn ? true : absOut < absIn;
+ return result;
+}
+
+int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const {
+ if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
return SK_MinS32;
}
- int winding = crossOpp ? span->oppSum() : span->windSum();
+ int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
SkASSERT(winding != SK_MinS32);
- int windVal = crossOpp ? span->oppValue() : span->windValue();
+ int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
#if DEBUG_WINDING_AT_T
SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
- debugID(), crossOpp, tHit, span->t(), winding, windVal);
+ debugID(), crossOpp, tHit, t(tIndex), winding, windVal);
#endif
// see if a + change in T results in a +/- change in X (compute x'(T))
*dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
@@ -2024,6 +4828,20 @@ int SkOpSegment::windingAtT(double tHit, const SkOpSpan* span, bool crossOpp,
}
int SkOpSegment::windSum(const SkOpAngle* angle) const {
- const SkOpSpan* minSpan = angle->start()->starter(angle->end());
- return minSpan->windSum();
+ int start = angle->start();
+ int end = angle->end();
+ int index = SkMin32(start, end);
+ return windSum(index);
+}
+
+void SkOpSegment::zeroSpan(SkOpSpan* span) {
+ SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
+ span->fWindValue = 0;
+ span->fOppValue = 0;
+ if (span->fTiny || span->fSmall) {
+ return;
+ }
+ SkASSERT(!span->fDone);
+ span->fDone = true;
+ ++fDoneSpans;
}
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index c1c6e696fe..b4da929d99 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -9,261 +9,159 @@
#include "SkOpAngle.h"
#include "SkOpSpan.h"
-#include "SkOpTAllocator.h"
#include "SkPathOpsBounds.h"
#include "SkPathOpsCurve.h"
+#include "SkTArray.h"
+#include "SkTDArray.h"
-class SkOpCoincidence;
-class SkOpContour;
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+#include "SkThread.h"
+#endif
+
+struct SkCoincidence;
class SkPathWriter;
class SkOpSegment {
public:
- enum AllowAlias {
- kAllowAlias,
- kNoAlias
- };
+ SkOpSegment() {
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+ fID = sk_atomic_inc(&SkPathOpsDebug::gSegmentID);
+#endif
+ }
bool operator<(const SkOpSegment& rh) const {
return fBounds.fTop < rh.fBounds.fTop;
}
- SkOpAngle* activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
- bool* done, bool* sortable);
- SkOpAngle* activeAngleInner(SkOpSpanBase* start, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr, bool* done, bool* sortable);
- SkOpAngle* activeAngleOther(SkOpSpanBase* start, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr, bool* done, bool* sortable);
- bool activeOp(SkOpSpanBase* start, SkOpSpanBase* end, int xorMiMask, int xorSuMask,
- SkPathOp op);
- bool activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end, SkPathOp op,
- int* sumMiWinding, int* sumSuWinding);
-
- SkPoint activeLeftTop(SkOpSpanBase** firstT);
-
- bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end);
- bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding);
+ struct AlignedSpan {
+ double fOldT;
+ double fT;
+ SkPoint fOldPt;
+ SkPoint fPt;
+ const SkOpSegment* fSegment;
+ const SkOpSegment* fOther1;
+ const SkOpSegment* fOther2;
+ };
- void addCubic(SkPoint pts[4], SkOpContour* parent) {
- init(pts, parent, SkPath::kCubic_Verb);
- fBounds.setCubicBounds(pts);
+ const SkPathOpsBounds& bounds() const {
+ return fBounds;
}
- void addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path,
- bool active) const;
-
- SkOpAngle* addEndSpan(SkChunkAlloc* allocator) {
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- angle->set(&fTail, fTail.prev());
- fTail.setFromAngle(angle);
- return angle;
+ // OPTIMIZE
+ // when the edges are initially walked, they don't automatically get the prior and next
+ // edges assigned to positions t=0 and t=1. Doing that would remove the need for this check,
+ // and would additionally remove the need for similar checks in condition edges. It would
+ // also allow intersection code to assume end of segment intersections (maybe?)
+ bool complete() const {
+ int count = fTs.count();
+ return count > 1 && fTs[0].fT == 0 && fTs[--count].fT == 1;
}
- void addLine(SkPoint pts[2], SkOpContour* parent) {
- init(pts, parent, SkPath::kLine_Verb);
- fBounds.set(pts, 2);
+ int count() const {
+ return fTs.count();
}
- SkOpPtT* addMissing(double t, SkOpSegment* opp, SkChunkAlloc* );
- SkOpAngle* addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** , SkChunkAlloc* );
- SkOpAngle* addSingletonAngles(int step, SkChunkAlloc* );
- SkOpAngle* addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** , SkChunkAlloc* );
-
- SkOpAngle* addStartSpan(SkChunkAlloc* allocator) {
- SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
- angle->set(&fHead, fHead.next());
- fHead.setToAngle(angle);
- return angle;
+ bool done() const {
+ SkASSERT(fDoneSpans <= fTs.count());
+ return fDoneSpans == fTs.count();
}
- void addQuad(SkPoint pts[3], SkOpContour* parent) {
- init(pts, parent, SkPath::kQuad_Verb);
- fBounds.setQuadBounds(pts);
+ bool done(int min) const {
+ return fTs[min].fDone;
}
- SkOpPtT* addT(double t, AllowAlias , SkChunkAlloc* );
-
- void align();
- static bool BetweenTs(const SkOpSpanBase* lesser, double testT, const SkOpSpanBase* greater);
-
- const SkPathOpsBounds& bounds() const {
- return fBounds;
+ bool done(const SkOpAngle* angle) const {
+ return done(SkMin32(angle->start(), angle->end()));
}
- void bumpCount() {
- ++fCount;
+ SkDPoint dPtAtT(double mid) const {
+ return (*CurveDPointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
}
- void calcAngles(SkChunkAlloc*);
- void checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
- void checkNearCoincidence(SkOpAngle* );
- bool clockwise(const SkOpSpanBase* start, const SkOpSpanBase* end, bool* swap) const;
- static void ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
- SkOpAngle::IncludeType );
- static void ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
- SkOpAngle::IncludeType );
- int computeSum(SkOpSpanBase* start, SkOpSpanBase* end, SkOpAngle::IncludeType includeType);
-
- SkOpContour* contour() const {
- return fContour;
+ SkVector dxdy(int index) const {
+ return (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[index].fT);
}
- int count() const {
- return fCount;
+ SkScalar dy(int index) const {
+ return dxdy(index).fY;
}
- SkOpSpan* crossedSpanY(const SkPoint& basePt, double mid, bool opp, bool current,
- SkScalar* bestY, double* hitT, bool* hitSomething, bool* vertical);
-
- void debugAddAngle(double startT, double endT, SkChunkAlloc*);
- const SkOpAngle* debugAngle(int id) const;
- SkOpContour* debugContour(int id);
-
- int debugID() const {
- return PATH_OPS_DEBUG_RELEASE(fID, -1);
+ bool hasMultiples() const {
+ return fMultiples;
}
-#if DEBUG_SWAP_TOP
- int debugInflections(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
-#endif
-
- SkOpAngle* debugLastAngle();
- const SkOpPtT* debugPtT(int id) const;
- void debugReset();
- const SkOpSegment* debugSegment(int id) const;
-
-#if DEBUG_ACTIVE_SPANS
- void debugShowActiveSpans() const;
-#endif
-#if DEBUG_MARK_DONE
- void debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding);
- void debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding, int oppWinding);
-#endif
-
- const SkOpSpanBase* debugSpan(int id) const;
- void debugValidate() const;
- void detach(const SkOpSpan* );
- double distSq(double t, SkOpAngle* opp);
-
- bool done() const {
- SkASSERT(fDoneCount <= fCount);
- return fDoneCount == fCount;
+ bool hasSmall() const {
+ return fSmall;
}
- bool done(const SkOpAngle* angle) const {
- return angle->start()->starter(angle->end())->done();
+ bool hasTiny() const {
+ return fTiny;
}
- SkDPoint dPtAtT(double mid) const {
- return (*CurveDPointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
+ bool intersected() const {
+ return fTs.count() > 0;
}
- SkDVector dSlopeAtT(double mid) const {
- return (*CurveDSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
+ bool isCanceled(int tIndex) const {
+ return fTs[tIndex].fWindValue == 0 && fTs[tIndex].fOppValue == 0;
}
- void dump() const;
- void dumpAll() const;
- void dumpAngles() const;
- void dumpCoin() const;
- void dumpPts() const;
-
- SkOpSegment* findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
- SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op,
- int xorMiMask, int xorSuMask);
- SkOpSegment* findNextWinding(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
- SkOpSpanBase** nextEnd, bool* unsortable);
- SkOpSegment* findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable);
- SkOpSegment* findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
- bool* unsortable, SkChunkAlloc* );
- SkOpGlobalState* globalState() const;
-
- const SkOpSpan* head() const {
- return &fHead;
- }
-
- SkOpSpan* head() {
- return &fHead;
- }
-
- void init(SkPoint pts[], SkOpContour* parent, SkPath::Verb verb);
- void initWinding(SkOpSpanBase* start, SkOpSpanBase* end,
- SkOpAngle::IncludeType angleIncludeType);
- bool initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHit, int winding,
- SkScalar hitDx, int oppWind, SkScalar hitOppDx);
-
- SkOpSpan* insert(SkOpSpan* prev, SkChunkAlloc* allocator) {
- SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(allocator);
- SkOpSpanBase* next = prev->next();
- result->setPrev(prev);
- prev->setNext(result);
- SkDEBUGCODE(result->ptT()->fT = 0);
- result->setNext(next);
- if (next) {
- next->setPrev(result);
- }
- return result;
+ bool isConnected(int startIndex, int endIndex) const {
+ return fTs[startIndex].fWindSum != SK_MinS32 || fTs[endIndex].fWindSum != SK_MinS32;
}
- bool isClose(double t, const SkOpSegment* opp) const;
-
bool isHorizontal() const {
return fBounds.fTop == fBounds.fBottom;
}
- SkOpSegment* isSimple(SkOpSpanBase** end, int* step) {
- return nextChase(end, step, NULL, NULL);
- }
-
bool isVertical() const {
return fBounds.fLeft == fBounds.fRight;
}
- bool isVertical(SkOpSpanBase* start, SkOpSpanBase* end) const {
- return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start->t(), end->t());
+ bool isVertical(int start, int end) const {
+ return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start, end);
}
- bool isXor() const;
+ bool operand() const {
+ return fOperand;
+ }
- const SkPoint& lastPt() const {
- return fPts[SkPathOpsVerbToPoints(fVerb)];
+ int oppSign(const SkOpAngle* angle) const {
+ SkASSERT(angle->segment() == this);
+ return oppSign(angle->start(), angle->end());
}
- SkOpSpanBase* markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end);
- bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
- SkOpSpanBase** lastPtr);
- bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
- int oppWinding, SkOpSpanBase** lastPtr);
- SkOpSpanBase* markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle);
- SkOpSpanBase* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
- const SkOpAngle* angle);
- 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;
- void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
- bool monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
- bool moveNearby();
+ int oppSign(int startIndex, int endIndex) const {
+ int result = startIndex < endIndex ? -fTs[startIndex].fOppValue : fTs[endIndex].fOppValue;
+#if DEBUG_WIND_BUMP
+ SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
+#endif
+ return result;
+ }
- SkOpSegment* next() const {
- return fNext;
+ int oppSum(int tIndex) const {
+ return fTs[tIndex].fOppSum;
}
- static bool NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start, SkOpSpanBase** end);
- SkOpSegment* nextChase(SkOpSpanBase** , int* step, SkOpSpan** , SkOpSpanBase** last) const;
- bool operand() const;
+ int oppSum(const SkOpAngle* angle) const {
+ int lesser = SkMin32(angle->start(), angle->end());
+ return fTs[lesser].fOppSum;
+ }
- static int OppSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
- int result = start->t() < end->t() ? -start->upCast()->oppValue()
- : end->upCast()->oppValue();
- return result;
+ int oppValue(int tIndex) const {
+ return fTs[tIndex].fOppValue;
}
- bool oppXor() const;
+ int oppValue(const SkOpAngle* angle) const {
+ int lesser = SkMin32(angle->start(), angle->end());
+ return fTs[lesser].fOppValue;
+ }
- const SkOpSegment* prev() const {
- return fPrev;
+#if DEBUG_VALIDATE
+ bool oppXor() const {
+ return fOppXor;
}
+#endif
SkPoint ptAtT(double mid) const {
return (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
@@ -273,113 +171,399 @@ public:
return fPts;
}
- bool ptsDisjoint(const SkOpPtT& span, const SkOpPtT& test) const {
- return ptsDisjoint(span.fT, span.fPt, test.fT, test.fPt);
+ void reset() {
+ init(NULL, (SkPath::Verb) -1, false, false);
+ fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
+ fTs.reset();
}
- bool ptsDisjoint(const SkOpPtT& span, double t, const SkPoint& pt) const {
- return ptsDisjoint(span.fT, span.fPt, t, pt);
+ bool reversePoints(const SkPoint& p1, const SkPoint& p2) const;
+
+ void setOppXor(bool isOppXor) {
+ fOppXor = isOppXor;
}
- bool ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const;
+ void setUpWinding(int index, int endIndex, int* maxWinding, int* sumWinding) {
+ int deltaSum = spanSign(index, endIndex);
+ *maxWinding = *sumWinding;
+ *sumWinding -= deltaSum;
+ }
- void resetVisited() {
- fVisited = false;
+ const SkOpSpan& span(int tIndex) const {
+ return fTs[tIndex];
}
- void setContour(SkOpContour* contour) {
- fContour = contour;
+ const SkOpAngle* spanToAngle(int tStart, int tEnd) const {
+ SkASSERT(tStart != tEnd);
+ const SkOpSpan& span = fTs[tStart];
+ return tStart < tEnd ? span.fToAngle : span.fFromAngle;
}
- void setNext(SkOpSegment* next) {
- fNext = next;
+ // FIXME: create some sort of macro or template that avoids casting
+ SkOpAngle* spanToAngle(int tStart, int tEnd) {
+ const SkOpAngle* cAngle = (const_cast<const SkOpSegment*>(this))->spanToAngle(tStart, tEnd);
+ return const_cast<SkOpAngle*>(cAngle);
}
- void setPrev(SkOpSegment* prev) {
- fPrev = prev;
+ int spanSign(const SkOpAngle* angle) const {
+ SkASSERT(angle->segment() == this);
+ return spanSign(angle->start(), angle->end());
}
- bool setVisited() {
- if (fVisited) {
- return false;
- }
- return (fVisited = true);
+ int spanSign(int startIndex, int endIndex) const {
+ int result = startIndex < endIndex ? -fTs[startIndex].fWindValue : fTs[endIndex].fWindValue;
+#if DEBUG_WIND_BUMP
+ SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
+#endif
+ return result;
}
- void setUpWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* maxWinding, int* sumWinding) {
- int deltaSum = SpanSign(start, end);
- *maxWinding = *sumWinding;
- *sumWinding -= deltaSum;
+ double t(int tIndex) const {
+ return fTs[tIndex].fT;
}
- void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
- int* maxWinding, int* sumWinding);
- void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding, int* sumSuWinding,
- int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding);
- void sortAngles();
+ double tAtMid(int start, int end, double mid) const {
+ return fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
+ }
- static int SpanSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
- int result = start->t() < end->t() ? -start->upCast()->windValue()
- : end->upCast()->windValue();
- return result;
+ void updatePts(const SkPoint pts[]) {
+ fPts = pts;
}
- SkOpAngle* spanToAngle(SkOpSpanBase* start, SkOpSpanBase* end) {
- SkASSERT(start != end);
- return start->t() < end->t() ? start->upCast()->toAngle() : start->fromAngle();
+ SkPath::Verb verb() const {
+ return fVerb;
}
- bool subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPoint edge[4]) const;
- bool subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end, SkDCubic* result) const;
- void subDivideBounds(const SkOpSpanBase* start, const SkOpSpanBase* end,
- SkPathOpsBounds* bounds) const;
+ int windSum(int tIndex) const {
+ return fTs[tIndex].fWindSum;
+ }
- const SkOpSpanBase* tail() const {
- return &fTail;
+ int windValue(int tIndex) const {
+ return fTs[tIndex].fWindValue;
}
- SkOpSpanBase* tail() {
- return &fTail;
+#if defined(SK_DEBUG) || DEBUG_WINDING
+ SkScalar xAtT(int index) const {
+ return xAtT(&fTs[index]);
}
+#endif
- static double TAtMid(const SkOpSpanBase* start, const SkOpSpanBase* end, double mid) {
- return start->t() * (1 - mid) + end->t() * mid;
+#if DEBUG_VALIDATE
+ bool _xor() const { // FIXME: used only by SkOpAngle::debugValidateLoop()
+ return fXor;
}
+#endif
- void undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end);
- int updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
- int updateOppWinding(const SkOpAngle* angle) const;
- int updateOppWindingReverse(const SkOpAngle* angle) const;
- int updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
- int updateWinding(const SkOpAngle* angle) const;
- int updateWindingReverse(const SkOpAngle* angle) const;
+ const SkPoint& xyAtT(const SkOpSpan* span) const {
+ return span->fPt;
+ }
- static bool UseInnerWinding(int outerWinding, int innerWinding);
+ const SkPoint& xyAtT(int index) const {
+ return xyAtT(&fTs[index]);
+ }
- SkPath::Verb verb() const {
- return fVerb;
+#if defined(SK_DEBUG) || DEBUG_WINDING
+ SkScalar yAtT(int index) const {
+ return yAtT(&fTs[index]);
}
+#endif
- int windingAtT(double tHit, const SkOpSpan* span, bool crossOpp, SkScalar* dx) const;
+ const SkOpAngle* activeAngle(int index, int* start, int* end, bool* done,
+ bool* sortable) const;
+ SkPoint activeLeftTop(int* firstT) const;
+ bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op);
+ bool activeWinding(int index, int endIndex);
+ void addCubic(const SkPoint pts[4], bool operand, bool evenOdd);
+ void addCurveTo(int start, int end, SkPathWriter* path, bool active) const;
+ void addEndSpan(int endIndex);
+ void addLine(const SkPoint pts[2], bool operand, bool evenOdd);
+ void addOtherT(int index, double otherT, int otherIndex);
+ void addQuad(const SkPoint pts[3], bool operand, bool evenOdd);
+ void addSimpleAngle(int endIndex);
+ int addSelfT(const SkPoint& pt, double newT);
+ void addStartSpan(int endIndex);
+ int addT(SkOpSegment* other, const SkPoint& pt, double newT);
+ void addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
+ bool addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
+ SkOpSegment* other);
+ const SkOpSpan* addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
+ const SkPoint& pt);
+ const SkOpSpan* addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
+ const SkPoint& pt, const SkPoint& oPt);
+ void alignMultiples(SkTDArray<AlignedSpan>* aligned);
+ bool alignSpan(int index, double thisT, const SkPoint& thisPt);
+ void alignSpanState(int start, int end);
+ bool betweenTs(int lesser, double testT, int greater) const;
+ void blindCancel(const SkCoincidence& coincidence, SkOpSegment* other);
+ void blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other);
+ bool calcAngles();
+ double calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
+ double hiEnd, const SkOpSegment* other, int thisEnd);
+ double calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
+ double hiEnd, const SkOpSegment* other, int thisEnd);
+ void checkDuplicates();
+ bool checkEnds();
+ void checkMultiples();
+ void checkSmall();
+ bool checkSmall(int index) const;
+ void checkTiny();
+ int computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType);
+ bool containsPt(const SkPoint& , int index, int endIndex) const;
+ int crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT, bool* hitSomething,
+ double mid, bool opp, bool current) const;
+ bool findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart, int oEnd,
+ int step, SkPoint* startPt, SkPoint* endPt, double* endT) const;
+ SkOpSegment* findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
+ bool* unsortable, SkPathOp op, int xorMiMask, int xorSuMask);
+ SkOpSegment* findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
+ bool* unsortable);
+ SkOpSegment* findNextXor(int* nextStart, int* nextEnd, bool* unsortable);
+ int findExactT(double t, const SkOpSegment* ) const;
+ int findOtherT(double t, const SkOpSegment* ) const;
+ 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);
+ 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;
+ bool joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt, int step,
+ bool cancel);
+ SkOpSpan* markAndChaseDoneBinary(int index, int endIndex);
+ SkOpSpan* markAndChaseDoneUnary(int index, int endIndex);
+ 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;
+ void pinT(const SkPoint& pt, double* t);
+ void setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
+ int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding);
+ void sortAngles();
+ bool subDivide(int start, int end, SkPoint edge[4]) const;
+ bool subDivide(int start, int end, SkDCubic* result) const;
+ void undoneSpan(int* start, int* end);
+ int updateOppWindingReverse(const SkOpAngle* angle) const;
+ int updateWindingReverse(const SkOpAngle* angle) const;
+ static bool UseInnerWinding(int outerWinding, int innerWinding);
+ static bool UseInnerWindingReverse(int outerWinding, int innerWinding);
+ int windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const;
int windSum(const SkOpAngle* angle) const;
-
- SkPoint* writablePt(bool end) {
- return &fPts[end ? SkPathOpsVerbToPoints(fVerb) : 0];
+// available for testing only
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+ int debugID() const {
+ return fID;
+ }
+#else
+ int debugID() const {
+ return -1;
}
+#endif
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
+ void debugShowActiveSpans() const;
+#endif
+#if DEBUG_CONCIDENT
+ void debugShowTs(const char* prefix) const;
+#endif
+#if DEBUG_SHOW_WINDING
+ int debugShowWindingValues(int slotCount, int ofInterest) const;
+#endif
+ const SkTDArray<SkOpSpan>& debugSpans() const;
+ void debugValidate() const;
+ // available to testing only
+ const SkOpAngle* debugLastAngle() const;
+ void dumpAngles() const;
+ void dumpContour(int firstID, int lastID) const;
+ void dumpPts() const;
+ void dumpSpans() const;
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* fPts; // pointer into array of points owned by edge builder that may be tweaked
- SkPathOpsBounds fBounds; // tight bounds
- int fCount; // number of spans (one for a non-intersecting segment)
- int fDoneCount; // number of processed spans (zero initially)
+ struct MissingSpan {
+ double fT;
+ double fEndT;
+ SkOpSegment* fSegment;
+ SkOpSegment* fOther;
+ double fOtherT;
+ SkPoint fPt;
+ };
+
+ const SkOpAngle* activeAngleInner(int index, int* start, int* end, bool* done,
+ bool* sortable) const;
+ const SkOpAngle* activeAngleOther(int index, int* start, int* end, bool* done,
+ bool* sortable) const;
+ bool activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
+ int* sumMiWinding, int* sumSuWinding);
+ bool activeWinding(int index, int endIndex, int* sumWinding);
+ void addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
+ void addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
+ 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);
+ void bumpCoincidentOBlind(int index, int last);
+ 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,
+ int* less, int* more) const;
+ void checkLinks(const SkOpSpan* ,
+ SkTArray<MissingSpan, true>* missingSpans) const;
+ static void CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
+ const SkOpSpan* oFirst, const SkOpSpan* oLast,
+ const SkOpSpan** missingPtr,
+ SkTArray<MissingSpan, true>* missingSpans);
+ int checkSetAngle(int tIndex) const;
+ void checkSmallCoincidence(const SkOpSpan& span, SkTArray<MissingSpan, true>* );
+ bool coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const;
+ bool clockwise(int tStart, int tEnd, bool* swap) const;
+ static void ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+ SkOpAngle::IncludeType );
+ static void ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+ SkOpAngle::IncludeType );
+ bool containsT(double t, const SkOpSegment* other, double otherT) const;
+ bool decrementSpan(SkOpSpan* span);
+ int findEndSpan(int endIndex) const;
+ int findStartSpan(int startIndex) const;
+ int firstActive(int tIndex) const;
+ 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;
+#else
+ SkOpSegment* isSimple(int* end, int* step);
+#endif
+ bool isTiny(int index) const;
+ const SkOpSpan& lastSpan(const SkOpSpan& thisSpan) const;
+ 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);
+ 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);
+ 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) 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);
+ void subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const;
+ static void TrackOutsidePair(SkTArray<SkPoint, true>* outsideTs, const SkPoint& endPt,
+ const SkPoint& startPt);
+ static void TrackOutside(SkTArray<SkPoint, true>* outsideTs, const SkPoint& startPt);
+ int updateOppWinding(int index, int endIndex) const;
+ int updateOppWinding(const SkOpAngle* angle) const;
+ int updateWinding(int index, int endIndex) const;
+ int updateWinding(const SkOpAngle* angle) const;
+ int updateWindingReverse(int index, int endIndex) const;
+ SkOpSpan* verifyOneWinding(const char* funName, int tIndex);
+ SkOpSpan* verifyOneWindingU(const char* funName, int tIndex);
+
+ SkScalar xAtT(const SkOpSpan* span) const {
+ return xyAtT(span).fX;
+ }
+
+ SkScalar yAtT(const SkOpSpan* span) const {
+ return xyAtT(span).fY;
+ }
+
+ void zeroSpan(SkOpSpan* span);
+
+#if DEBUG_SWAP_TOP
+ bool controlsContainedByEnds(int tStart, int tEnd) const;
+#endif
+ void debugAddAngle(int start, int end);
+#if DEBUG_CONCIDENT
+ void debugAddTPair(double t, const SkOpSegment& other, double otherT) const;
+#endif
+#if DEBUG_ANGLE
+ void debugCheckPointsEqualish(int tStart, int tEnd) const;
+#endif
+#if DEBUG_SWAP_TOP
+ int debugInflections(int index, int endIndex) const;
+#endif
+#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
+ void debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding);
+ void debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding, int oppWinding);
+#endif
+#if DEBUG_WINDING
+ static char as_digit(int value) {
+ return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
+ }
+#endif
+ // available to testing only
+ void debugConstruct();
+ void debugConstructCubic(SkPoint shortQuad[4]);
+ void debugConstructLine(SkPoint shortQuad[2]);
+ void debugConstructQuad(SkPoint shortQuad[3]);
+ void debugReset();
+ void dumpDPts() const;
+ void dumpHexPts() const;
+ void dumpSpan(int index) const;
+
+ const SkPoint* fPts;
+ SkPathOpsBounds fBounds;
+ // FIXME: can't convert to SkTArray because it uses insert
+ SkTDArray<SkOpSpan> fTs; // 2+ (always includes t=0 t=1) -- at least (number of spans) + 1
+ SkOpAngleSet fAngles; // empty or 2+ -- (number of non-zero spans) * 2
+ // OPTIMIZATION: could pack donespans, verb, operand, xor into 1 int-sized value
+ int fDoneSpans; // quick check that segment is finished
+ // OPTIMIZATION: force the following to be byte-sized
SkPath::Verb fVerb;
- bool fVisited; // used by missing coincidence check
- PATH_OPS_DEBUG_CODE(int fID);
+ bool fLoop; // set if cubic intersects itself
+ bool fMultiples; // set if curve intersects multiple other curves at one interior point
+ bool fOperand;
+ bool fXor; // set if original contour had even-odd fill
+ bool fOppXor; // set if opposite operand had even-odd fill
+ bool fSmall; // set if some span is small
+ bool fTiny; // set if some span is tiny
+#if defined(SK_DEBUG) || !FORCE_RELEASE
+ int fID;
+#endif
+
+ friend class PathOpsSegmentTester;
};
#endif
diff --git a/src/pathops/SkOpSpan.cpp b/src/pathops/SkOpSpan.cpp
deleted file mode 100755
index 37d5120019..0000000000
--- a/src/pathops/SkOpSpan.cpp
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SkOpCoincidence.h"
-#include "SkOpContour.h"
-#include "SkOpSegment.h"
-#include "SkPathWriter.h"
-
-bool SkOpPtT::alias() const {
- return this->span()->ptT() != this;
-}
-
-SkOpContour* SkOpPtT::contour() const {
- return segment()->contour();
-}
-
-SkOpGlobalState* SkOpPtT::globalState() const {
- return contour()->globalState();
-}
-
-void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
- fT = t;
- fPt = pt;
- fSpan = span;
- fNext = this;
- fDuplicatePt = duplicate;
- fDeleted = false;
- PATH_OPS_DEBUG_CODE(fID = span->globalState()->nextPtTID());
-}
-
-bool SkOpPtT::onEnd() const {
- const SkOpSpanBase* span = this->span();
- if (span->ptT() != this) {
- return false;
- }
- const SkOpSegment* segment = this->segment();
- return span == segment->head() || span == segment->tail();
-}
-
-SkOpPtT* SkOpPtT::prev() {
- SkOpPtT* result = this;
- SkOpPtT* next = this;
- while ((next = next->fNext) != this) {
- result = next;
- }
- SkASSERT(result->fNext == this);
- return result;
-}
-
-SkOpPtT* SkOpPtT::remove() {
- SkOpPtT* prev = this;
- do {
- SkOpPtT* next = prev->fNext;
- if (next == this) {
- prev->removeNext(this);
- SkASSERT(prev->fNext != prev);
- fDeleted = true;
- return prev;
- }
- prev = next;
- } while (prev != this);
- SkASSERT(0);
- return NULL;
-}
-
-void SkOpPtT::removeNext(SkOpPtT* kept) {
- SkASSERT(this->fNext);
- SkOpPtT* next = this->fNext;
- SkASSERT(this != next->fNext);
- this->fNext = next->fNext;
- SkOpSpanBase* span = next->span();
- next->setDeleted();
- if (span->ptT() == next) {
- span->upCast()->detach(kept);
- }
-}
-
-const SkOpSegment* SkOpPtT::segment() const {
- return span()->segment();
-}
-
-SkOpSegment* SkOpPtT::segment() {
- return span()->segment();
-}
-
-// find the starting or ending span with an existing loop of angles
-// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
-// FIXME? assert that only one other span has a valid windValue or oppValue
-void SkOpSpanBase::addSimpleAngle(bool checkFrom, SkChunkAlloc* allocator) {
- SkOpAngle* angle;
- if (checkFrom) {
- SkASSERT(this->final());
- if (this->fromAngle()) {
- SkASSERT(this->fromAngle()->loopCount() == 2);
- return;
- }
- angle = this->segment()->addEndSpan(allocator);
- } else {
- SkASSERT(this->t() == 0);
- SkOpSpan* span = this->upCast();
- if (span->toAngle()) {
- SkASSERT(span->toAngle()->loopCount() == 2);
- SkASSERT(!span->fromAngle());
- span->setFromAngle(span->toAngle()->next());
- return;
- }
- angle = this->segment()->addStartSpan(allocator);
- }
- SkOpPtT* ptT = this->ptT();
- SkOpSpanBase* oSpanBase;
- SkOpSpan* oSpan;
- SkOpSegment* other;
- do {
- ptT = ptT->next();
- oSpanBase = ptT->span();
- oSpan = oSpanBase->upCastable();
- other = oSpanBase->segment();
- if (oSpan && oSpan->windValue()) {
- break;
- }
- if (oSpanBase->t() == 0) {
- continue;
- }
- SkOpSpan* oFromSpan = oSpanBase->prev();
- SkASSERT(oFromSpan->t() < 1);
- if (oFromSpan->windValue()) {
- break;
- }
- } while (ptT != this->ptT());
- SkOpAngle* oAngle;
- if (checkFrom) {
- oAngle = other->addStartSpan(allocator);
- SkASSERT(oSpan && !oSpan->final());
- SkASSERT(oAngle == oSpan->toAngle());
- } else {
- oAngle = other->addEndSpan(allocator);
- SkASSERT(oAngle == oSpanBase->fromAngle());
- }
- angle->insert(oAngle);
-}
-
-void SkOpSpanBase::align() {
- if (this->fAligned) {
- 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]);
- }
- return;
- }
- }
- 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;
- }
- if (!zero_or_one(test->fT)) {
- continue;
- }
- *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);
-}
-
-bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
- const SkOpPtT* start = &fPtT;
- const SkOpPtT* check = &span->fPtT;
- SkASSERT(start != check);
- const SkOpPtT* walk = start;
- while ((walk = walk->next()) != start) {
- if (walk == check) {
- return true;
- }
- }
- return false;
-}
-
-SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) {
- SkOpPtT* start = &fPtT;
- SkOpPtT* walk = start;
- while ((walk = walk->next()) != start) {
- if (walk->segment() == segment) {
- return walk;
- }
- }
- return NULL;
-}
-
-bool SkOpSpanBase::containsCoinEnd(const SkOpSegment* segment) const {
- SkASSERT(this->segment() != segment);
- const SkOpSpanBase* next = this;
- while ((next = next->fCoinEnd) != this) {
- if (next->segment() == segment) {
- return true;
- }
- }
- return false;
-}
-
-SkOpContour* SkOpSpanBase::contour() const {
- return segment()->contour();
-}
-
-SkOpGlobalState* SkOpSpanBase::globalState() const {
- return contour()->globalState();
-}
-
-void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
- fSegment = segment;
- fPtT.init(this, t, pt, false);
- fCoinEnd = this;
- fFromAngle = NULL;
- fPrev = prev;
- fAligned = true;
- fChased = false;
- PATH_OPS_DEBUG_CODE(fCount = 1);
- PATH_OPS_DEBUG_CODE(fID = globalState()->nextSpanID());
-}
-
-// this pair of spans share a common t value or point; merge them and eliminate duplicates
-// this does not compute the best t or pt value; this merely moves all data into a single list
-void SkOpSpanBase::merge(SkOpSpan* span) {
- SkOpPtT* spanPtT = span->ptT();
- SkASSERT(this->t() != spanPtT->fT);
- SkASSERT(!zero_or_one(spanPtT->fT));
- span->detach(this->ptT());
- SkOpPtT* remainder = spanPtT->next();
- ptT()->insert(spanPtT);
- while (remainder != spanPtT) {
- SkOpPtT* next = remainder->next();
- SkOpPtT* compare = spanPtT->next();
- while (compare != spanPtT) {
- SkOpPtT* nextC = compare->next();
- if (nextC->span() == remainder->span() && nextC->fT == remainder->fT) {
- goto tryNextRemainder;
- }
- compare = nextC;
- }
- spanPtT->insert(remainder);
-tryNextRemainder:
- remainder = next;
- }
-}
-
-void SkOpSpan::applyCoincidence(SkOpSpan* opp) {
- SkASSERT(!final());
- SkASSERT(0); // incomplete
-}
-
-bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
- SkASSERT(this->segment() != segment);
- const SkOpSpan* next = fCoincident;
- do {
- if (next->segment() == segment) {
- return true;
- }
- } while ((next = next->fCoincident) != this);
- return false;
-}
-
-void SkOpSpan::detach(SkOpPtT* kept) {
- SkASSERT(!final());
- SkOpSpan* prev = this->prev();
- SkASSERT(prev);
- SkOpSpanBase* next = this->next();
- SkASSERT(next);
- prev->setNext(next);
- next->setPrev(prev);
- this->segment()->detach(this);
- this->globalState()->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 = NULL;
- fWindSum = fOppSum = SK_MinS32;
- fWindValue = 1;
- fOppValue = 0;
- fChased = fDone = false;
- segment->bumpCount();
-}
-
-void SkOpSpan::setOppSum(int oppSum) {
- SkASSERT(!final());
- if (fOppSum != SK_MinS32 && fOppSum != oppSum) {
- this->globalState()->setWindingFailed();
- return;
- }
- SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(oppSum) <= DEBUG_LIMIT_WIND_SUM);
- fOppSum = oppSum;
-}
diff --git a/src/pathops/SkOpSpan.h b/src/pathops/SkOpSpan.h
index 9e5939a5e1..d9ce44eb77 100644
--- a/src/pathops/SkOpSpan.h
+++ b/src/pathops/SkOpSpan.h
@@ -7,460 +7,36 @@
#ifndef SkOpSpan_DEFINED
#define SkOpSpan_DEFINED
-#include "SkPathOpsDebug.h"
#include "SkPoint.h"
-class SkChunkAlloc;
-struct SkOpAngle;
-class SkOpContour;
-class SkOpGlobalState;
+class SkOpAngle;
class SkOpSegment;
-class SkOpSpanBase;
-class SkOpSpan;
-// subset of op span used by terminal span (when t is equal to one)
-class SkOpPtT {
-public:
- enum {
- kIsAlias = 1,
- kIsDuplicate = 1
- };
-
- void addOpp(SkOpPtT* opp) {
- // find the fOpp ptr to opp
- SkOpPtT* oppPrev = opp->fNext;
- if (oppPrev == this) {
- return;
- }
- while (oppPrev->fNext != opp) {
- oppPrev = oppPrev->fNext;
- if (oppPrev == this) {
- return;
- }
- }
-
- SkOpPtT* oldNext = this->fNext;
- SkASSERT(this != opp);
- this->fNext = opp;
- SkASSERT(oppPrev != oldNext);
- oppPrev->fNext = oldNext;
- }
-
- bool alias() const;
- SkOpContour* contour() const;
-
- int debugID() const {
- return PATH_OPS_DEBUG_RELEASE(fID, -1);
- }
-
- const SkOpAngle* debugAngle(int id) const;
- SkOpContour* debugContour(int id);
- int debugLoopLimit(bool report) const;
- bool debugMatchID(int id) const;
- const SkOpPtT* debugPtT(int id) const;
- const SkOpSegment* debugSegment(int id) const;
- const SkOpSpanBase* debugSpan(int id) const;
- SkOpGlobalState* globalState() const;
- void debugValidate() const;
-
- bool deleted() const {
- return fDeleted;
- }
-
- bool duplicate() const {
- return fDuplicatePt;
- }
-
- void dump() const; // available to testing only
- void dumpAll() const;
- void dumpBase() const;
-
- void init(SkOpSpanBase* , double t, const SkPoint& , bool dup);
-
- void insert(SkOpPtT* span) {
- SkASSERT(span != this);
- span->fNext = fNext;
- fNext = span;
- }
-
- const SkOpPtT* next() const {
- return fNext;
- }
-
- SkOpPtT* next() {
- return fNext;
- }
-
- bool onEnd() const;
- SkOpPtT* prev();
- SkOpPtT* remove();
- void removeNext(SkOpPtT* kept);
-
- const SkOpSegment* segment() const;
- SkOpSegment* segment();
-
- void setDeleted() {
- SkASSERT(!fDeleted);
- fDeleted = true;
- }
-
- const SkOpSpanBase* span() const {
- return fSpan;
- }
-
- SkOpSpanBase* span() {
- return fSpan;
- }
-
- 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 fDuplicatePt; // set if identical pt is somewhere in the next loop
- PATH_OPS_DEBUG_CODE(int fID);
-};
-
-class SkOpSpanBase {
-public:
- void addSimpleAngle(bool checkFrom , SkChunkAlloc* );
- void align();
-
- bool aligned() const {
- return fAligned;
- }
-
- void alignEnd(double t, const SkPoint& pt);
-
- bool chased() const {
- return fChased;
- }
-
- void clearCoinEnd() {
- SkASSERT(fCoinEnd != this);
- fCoinEnd = this;
- }
-
- const SkOpSpanBase* coinEnd() const {
- return fCoinEnd;
- }
-
- bool contains(const SkOpSpanBase* ) const;
- SkOpPtT* contains(const SkOpSegment* );
-
- bool containsCoinEnd(const SkOpSpanBase* coin) const {
- SkASSERT(this != coin);
- const SkOpSpanBase* next = this;
- while ((next = next->fCoinEnd) != this) {
- if (next == coin) {
- return true;
- }
- }
- return false;
- }
-
- bool containsCoinEnd(const SkOpSegment* ) const;
- SkOpContour* contour() const;
-
- int debugBumpCount() {
- return PATH_OPS_DEBUG_RELEASE(++fCount, -1);
- }
-
- int debugID() const {
- return PATH_OPS_DEBUG_RELEASE(fID, -1);
- }
-
- const SkOpAngle* debugAngle(int id) const;
- bool debugCoinEndLoopCheck() const;
- SkOpContour* debugContour(int id);
- const SkOpPtT* debugPtT(int id) const;
- const SkOpSegment* debugSegment(int id) const;
- const SkOpSpanBase* debugSpan(int id) const;
- SkOpGlobalState* globalState() const;
- void debugValidate() const;
-
- bool deleted() const {
- return fPtT.deleted();
- }
-
- void dump() const; // available to testing only
- void dumpCoin() const;
- void dumpAll() const;
- void dumpBase() const;
-
- bool final() const {
- return fPtT.fT == 1;
- }
-
- SkOpAngle* fromAngle() const {
- return fFromAngle;
- }
-
- void initBase(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
-
- void insertCoinEnd(SkOpSpanBase* coin) {
- if (containsCoinEnd(coin)) {
- SkASSERT(coin->containsCoinEnd(this));
- return;
- }
- debugValidate();
- SkASSERT(this != coin);
- SkOpSpanBase* coinNext = coin->fCoinEnd;
- coin->fCoinEnd = this->fCoinEnd;
- this->fCoinEnd = coinNext;
- debugValidate();
- }
-
- void merge(SkOpSpan* span);
-
- SkOpSpan* prev() const {
- return fPrev;
- }
-
- const SkPoint& pt() const {
- return fPtT.fPt;
- }
-
- const SkOpPtT* ptT() const {
- return &fPtT;
- }
-
- SkOpPtT* ptT() {
- return &fPtT;
- }
-
- SkOpSegment* segment() const {
- return fSegment;
- }
-
- void setChased(bool chased) {
- fChased = chased;
- }
-
- SkOpPtT* setCoinEnd(SkOpSpanBase* oldCoinEnd, SkOpSegment* oppSegment);
-
- void setFromAngle(SkOpAngle* angle) {
- fFromAngle = angle;
- }
-
- void setPrev(SkOpSpan* prev) {
- fPrev = prev;
- }
-
- bool simple() const {
- fPtT.debugValidate();
- return fPtT.next()->next() == &fPtT;
- }
-
- const SkOpSpan* starter(const SkOpSpanBase* end) const {
- const SkOpSpanBase* result = t() < end->t() ? this : end;
- return result->upCast();
- }
-
- SkOpSpan* starter(SkOpSpanBase* end) {
- SkASSERT(this->segment() == end->segment());
- SkOpSpanBase* result = t() < end->t() ? this : end;
- return result->upCast();
- }
-
- SkOpSpan* starter(SkOpSpanBase** endPtr) {
- SkOpSpanBase* end = *endPtr;
- SkASSERT(this->segment() == end->segment());
- SkOpSpanBase* result;
- if (t() < end->t()) {
- result = this;
- } else {
- result = end;
- *endPtr = this;
- }
- return result->upCast();
- }
-
- int step(const SkOpSpanBase* end) const {
- return t() < end->t() ? 1 : -1;
- }
-
- double t() const {
- return fPtT.fT;
- }
-
- void unaligned() {
- fAligned = false;
- }
-
- SkOpSpan* upCast() {
- SkASSERT(!final());
- return (SkOpSpan*) this;
- }
-
- const SkOpSpan* upCast() const {
- SkASSERT(!final());
- return (const SkOpSpan*) this;
- }
-
- SkOpSpan* upCastable() {
- return final() ? NULL : upCast();
- }
-
- const SkOpSpan* upCastable() const {
- return final() ? NULL : upCast();
- }
-
-private:
- void alignInner();
-
-protected: // no direct access to internals to avoid treating a span base as a span
- SkOpPtT fPtT; // list of points and t values associated with the start of this span
- SkOpSegment* fSegment; // segment that contains this span
- SkOpSpanBase* fCoinEnd; // linked list of coincident spans that end here (may point to itself)
- SkOpAngle* fFromAngle; // points to next angle from span start to end
- SkOpSpan* fPrev; // previous intersection point
- bool fAligned;
- bool fChased; // set after span has been added to chase array
- PATH_OPS_DEBUG_CODE(int fCount); // number of pt/t pairs added
- PATH_OPS_DEBUG_CODE(int fID);
-};
-
-class SkOpSpan : public SkOpSpanBase {
-public:
- void applyCoincidence(SkOpSpan* opp);
-
- bool clearCoincident() {
- SkASSERT(!final());
- if (fCoincident == this) {
- return false;
- }
- fCoincident = this;
- return true;
- }
-
- bool containsCoincidence(const SkOpSegment* ) const;
-
- bool containsCoincidence(const SkOpSpan* coin) const {
- SkASSERT(this != coin);
- const SkOpSpan* next = this;
- while ((next = next->fCoincident) != this) {
- if (next == coin) {
- return true;
- }
- }
- return false;
- }
-
- bool debugCoinLoopCheck() const;
- void detach(SkOpPtT* );
-
- bool done() const {
- SkASSERT(!final());
- return fDone;
- }
-
- void dumpCoin() const;
- bool dumpSpan() const;
- void init(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
-
- void insertCoincidence(SkOpSpan* coin) {
- if (containsCoincidence(coin)) {
- SkASSERT(coin->containsCoincidence(this));
- return;
- }
- debugValidate();
- SkASSERT(this != coin);
- SkOpSpan* coinNext = coin->fCoincident;
- coin->fCoincident = this->fCoincident;
- this->fCoincident = coinNext;
- debugValidate();
- }
-
- bool isCanceled() const {
- SkASSERT(!final());
- return fWindValue == 0 && fOppValue == 0;
- }
-
- bool isCoincident() const {
- SkASSERT(!final());
- return fCoincident != this;
- }
-
- SkOpSpanBase* next() const {
- SkASSERT(!final());
- return fNext;
- }
-
- int oppSum() const {
- SkASSERT(!final());
- return fOppSum;
- }
-
- int oppValue() const {
- SkASSERT(!final());
- return fOppValue;
- }
-
- SkOpPtT* setCoinStart(SkOpSpan* oldCoinStart, SkOpSegment* oppSegment);
-
- void setDone(bool done) {
- SkASSERT(!final());
- fDone = done;
- }
-
- void setNext(SkOpSpanBase* nextT) {
- SkASSERT(!final());
- fNext = nextT;
- }
-
- void setOppSum(int oppSum);
-
- void setOppValue(int oppValue) {
- SkASSERT(!final());
- SkASSERT(fOppSum == SK_MinS32);
- fOppValue = oppValue;
- }
-
- void setToAngle(SkOpAngle* angle) {
- SkASSERT(!final());
- fToAngle = angle;
- }
-
- void setWindSum(int windSum) {
- SkASSERT(!final());
- SkASSERT(fWindSum == SK_MinS32 || fWindSum == windSum);
- SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(windSum) <= DEBUG_LIMIT_WIND_SUM);
- fWindSum = windSum;
- }
-
- void setWindValue(int windValue) {
- SkASSERT(!final());
- SkASSERT(windValue >= 0);
- SkASSERT(fWindSum == SK_MinS32);
- fWindValue = windValue;
- }
-
- SkOpAngle* toAngle() const {
- SkASSERT(!final());
- return fToAngle;
- }
-
- int windSum() const {
- SkASSERT(!final());
- return fWindSum;
- }
-
- int windValue() const {
- SkASSERT(!final());
- return fWindValue;
- }
-
-private: // no direct access to internals to avoid treating a span base as a span
- SkOpSpan* fCoincident; // linked list of spans coincident with this one (may point to itself)
- SkOpAngle* fToAngle; // points to next angle from span start to end
- SkOpSpanBase* fNext; // next intersection point
+struct SkOpSpan {
+ SkPoint fPt; // computed when the curves are intersected
+ double fT;
+ double fOtherT; // value at fOther[fOtherIndex].fT
+ SkOpSegment* fOther;
+ SkOpAngle* fFromAngle; // (if t > 0) index into segment's angle array going negative in t
+ SkOpAngle* fToAngle; // (if t < 1) index into segment's angle array going positive in t
+ int fOtherIndex; // can't be used during intersection
int fWindSum; // accumulated from contours surrounding this one.
int fOppSum; // for binary operators: the opposite winding sum
int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here
+ bool fChased; // set after span has been added to chase array
+ bool fCoincident; // set if span is bumped -- if set additional points aren't inserted
bool fDone; // if set, this span to next higher T has been processed
+ bool fLoop; // set when a cubic loops back to this point
+ bool fMultiple; // set if this is one of mutiple spans with identical t and pt values
+ bool fNear; // set if opposite end point is near but not equal to this one
+ bool fSmall; // if set, consecutive points are almost equal
+ bool fTiny; // if set, consecutive points are equal but consecutive ts are not precisely equal
+
+ // available to testing only
+ const SkOpSegment* debugToSegment(ptrdiff_t* ) const;
+ void dump() const;
+ void dumpOne() const;
};
#endif
diff --git a/src/pathops/SkOpTAllocator.h b/src/pathops/SkOpTAllocator.h
index e8835f02e6..c80c12f63b 100644
--- a/src/pathops/SkOpTAllocator.h
+++ b/src/pathops/SkOpTAllocator.h
@@ -19,12 +19,6 @@ public:
return record;
}
- static T* AllocateArray(SkChunkAlloc* allocator, int count) {
- void* ptr = allocator->allocThrow(sizeof(T) * count);
- T* record = (T*) ptr;
- return record;
- }
-
static T* New(SkChunkAlloc* allocator) {
return new (Allocate(allocator)) T();
}
diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp
index b0fd822a9d..1a5bfc1889 100644
--- a/src/pathops/SkPathOpsCommon.cpp
+++ b/src/pathops/SkPathOpsCommon.cpp
@@ -5,25 +5,47 @@
* found in the LICENSE file.
*/
#include "SkAddIntersections.h"
-#include "SkOpCoincidence.h"
#include "SkOpEdgeBuilder.h"
#include "SkPathOpsCommon.h"
#include "SkPathWriter.h"
#include "SkTSort.h"
-static int contourRangeCheckY(const SkTDArray<SkOpContour* >& contourList,
- SkOpSegment** currentPtr, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
- double* bestHit, SkScalar* bestDx, bool* tryAgain, double* midPtr, bool opp) {
- SkOpSpanBase* start = *startPtr;
- SkOpSpanBase* end = *endPtr;
+static void alignMultiples(SkTArray<SkOpContour*, true>* contourList,
+ SkTDArray<SkOpSegment::AlignedSpan>* aligned) {
+ int contourCount = (*contourList).count();
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ SkOpContour* contour = (*contourList)[cTest];
+ if (contour->hasMultiples()) {
+ contour->alignMultiples(aligned);
+ }
+ }
+}
+
+static void alignCoincidence(SkTArray<SkOpContour*, true>* contourList,
+ const SkTDArray<SkOpSegment::AlignedSpan>& aligned) {
+ int contourCount = (*contourList).count();
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ SkOpContour* contour = (*contourList)[cTest];
+ int count = aligned.count();
+ for (int index = 0; index < count; ++index) {
+ contour->alignCoincidence(aligned[index]);
+ }
+ }
+}
+
+static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, SkOpSegment** currentPtr,
+ int* indexPtr, int* endIndexPtr, double* bestHit, SkScalar* bestDx,
+ bool* tryAgain, double* midPtr, bool opp) {
+ const int index = *indexPtr;
+ const int endIndex = *endIndexPtr;
const double mid = *midPtr;
const SkOpSegment* current = *currentPtr;
- double tAtMid = SkOpSegment::TAtMid(start, end, mid);
+ double tAtMid = current->tAtMid(index, endIndex, mid);
SkPoint basePt = current->ptAtT(tAtMid);
int contourCount = contourList.count();
SkScalar bestY = SK_ScalarMin;
SkOpSegment* bestSeg = NULL;
- SkOpSpan* bestTSpan = NULL;
+ int bestTIndex = 0;
bool bestOpp;
bool hitSomething = false;
for (int cTest = 0; cTest < contourCount; ++cTest) {
@@ -35,38 +57,37 @@ static int contourRangeCheckY(const SkTDArray<SkOpContour* >& contourList,
if (bestY > contour->bounds().fBottom) {
continue;
}
- SkOpSegment* testSeg = contour->first();
- SkASSERT(testSeg);
- do {
+ int segmentCount = contour->segments().count();
+ for (int test = 0; test < segmentCount; ++test) {
+ SkOpSegment* testSeg = &contour->segments()[test];
SkScalar testY = bestY;
double testHit;
- bool vertical;
- SkOpSpan* testTSpan = testSeg->crossedSpanY(basePt, tAtMid, testOpp,
- testSeg == current, &testY, &testHit, &hitSomething, &vertical);
- if (!testTSpan) {
- if (vertical) {
+ int testTIndex = testSeg->crossedSpanY(basePt, &testY, &testHit, &hitSomething, tAtMid,
+ testOpp, testSeg == current);
+ if (testTIndex < 0) {
+ if (testTIndex == SK_MinS32) {
hitSomething = true;
bestSeg = NULL;
goto abortContours; // vertical encountered, return and try different point
}
continue;
}
- if (testSeg == current && SkOpSegment::BetweenTs(start, testHit, end)) {
- double baseT = start->t();
- double endT = end->t();
+ if (testSeg == current && current->betweenTs(index, testHit, endIndex)) {
+ double baseT = current->t(index);
+ double endT = current->t(endIndex);
double newMid = (testHit - baseT) / (endT - baseT);
#if DEBUG_WINDING
- double midT = SkOpSegment::TAtMid(start, end, mid);
- SkPoint midXY = current->ptAtT(midT);
- double newMidT = SkOpSegment::TAtMid(start, end, newMid);
- SkPoint newXY = current->ptAtT(newMidT);
+ double midT = current->tAtMid(index, endIndex, mid);
+ SkPoint midXY = current->xyAtT(midT);
+ double newMidT = current->tAtMid(index, endIndex, newMid);
+ SkPoint newXY = current->xyAtT(newMidT);
SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)"
" n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__,
current->debugID(), mid, newMid,
- baseT, start->pt().fX, start->pt().fY,
+ baseT, current->xAtT(index), current->yAtT(index),
baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
- endT, end->pt().fX, end->pt().fY);
+ endT, current->xAtT(endIndex), current->yAtT(endIndex));
#endif
*midPtr = newMid * 2; // calling loop with divide by 2 before continuing
return SK_MinS32;
@@ -74,39 +95,38 @@ static int contourRangeCheckY(const SkTDArray<SkOpContour* >& contourList,
bestSeg = testSeg;
*bestHit = testHit;
bestOpp = testOpp;
- bestTSpan = testTSpan;
+ bestTIndex = testTIndex;
bestY = testY;
- } while ((testSeg = testSeg->next()));
+ }
}
abortContours:
int result;
if (!bestSeg) {
result = hitSomething ? SK_MinS32 : 0;
} else {
- if (bestTSpan->windSum() == SK_MinS32) {
+ if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
*currentPtr = bestSeg;
- *startPtr = bestTSpan;
- *endPtr = bestTSpan->next();
- SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
+ *indexPtr = bestTIndex;
+ *endIndexPtr = bestSeg->nextSpan(bestTIndex, 1);
+ SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
*tryAgain = true;
return 0;
}
- result = bestSeg->windingAtT(*bestHit, bestTSpan, bestOpp, bestDx);
+ result = bestSeg->windingAtT(*bestHit, bestTIndex, bestOpp, bestDx);
SkASSERT(result == SK_MinS32 || *bestDx);
}
- double baseT = (*startPtr)->t();
- double endT = (*endPtr)->t();
+ double baseT = current->t(index);
+ double endT = current->t(endIndex);
*bestHit = baseT + mid * (endT - baseT);
return result;
}
-SkOpSegment* FindUndone(SkTDArray<SkOpContour* >& contourList, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr) {
+SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end) {
int contourCount = contourList.count();
SkOpSegment* result;
for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
SkOpContour* contour = contourList[cIndex];
- result = contour->undoneSegment(startPtr, endPtr);
+ result = contour->undoneSegment(start, end);
if (result) {
return result;
}
@@ -114,23 +134,20 @@ SkOpSegment* FindUndone(SkTDArray<SkOpContour* >& contourList, SkOpSpanBase** st
return NULL;
}
-SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr) {
+SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex) {
while (chase->count()) {
- SkOpSpanBase* span;
+ SkOpSpan* span;
chase->pop(&span);
- SkOpSegment* segment = span->segment();
- *startPtr = span->ptT()->next()->span();
+ const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
+ SkOpSegment* segment = backPtr.fOther;
+ *tIndex = backPtr.fOtherIndex;
bool sortable = true;
bool done = true;
- *endPtr = NULL;
- if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done,
+ *endIndex = -1;
+ if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
&sortable)) {
- if (last->unorderable()) {
- continue;
- }
- *startPtr = last->start();
- *endPtr = last->end();
+ *tIndex = last->start();
+ *endIndex = last->end();
#if TRY_ROTATE
*chase->insert(0) = span;
#else
@@ -145,58 +162,65 @@ SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
continue;
}
// find first angle, initialize winding to computed wind sum
- const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr);
- if (!angle) {
- continue;
- }
- const SkOpAngle* firstAngle = angle;
- bool loop = false;
- int winding = SK_MinS32;
+ const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
+ const SkOpAngle* firstAngle;
+ SkDEBUGCODE(firstAngle = angle);
+ SkDEBUGCODE(bool loop = false);
+ int winding;
do {
angle = angle->next();
- if (angle == firstAngle && loop) {
- break; // if we get here, there's no winding, loop is unorderable
- }
- loop |= angle == firstAngle;
+ SkASSERT(angle != firstAngle || !loop);
+ SkDEBUGCODE(loop |= angle == firstAngle);
segment = angle->segment();
winding = segment->windSum(angle);
} while (winding == SK_MinS32);
- if (winding == SK_MinS32) {
- continue;
- }
- int sumWinding = segment->updateWindingReverse(angle);
- SkOpSegment* first = NULL;
+ int spanWinding = segment->spanSign(angle->start(), angle->end());
+ #if DEBUG_WINDING
+ SkDebugf("%s winding=%d spanWinding=%d\n", __FUNCTION__, winding, spanWinding);
+ #endif
+ // turn span winding into contour winding
+ if (spanWinding * winding < 0) {
+ winding += spanWinding;
+ }
+ // we care about first sign and whether wind sum indicates this
+ // edge is inside or outside. Maybe need to pass span winding
+ // or first winding or something into this function?
+ // advance to first undone angle, then return it and winding
+ // (to set whether edges are active or not)
firstAngle = angle;
+ winding -= firstAngle->segment()->spanSign(firstAngle);
while ((angle = angle->next()) != firstAngle) {
segment = angle->segment();
- SkOpSpanBase* start = angle->start();
- SkOpSpanBase* end = angle->end();
- int maxWinding;
- segment->setUpWinding(start, end, &maxWinding, &sumWinding);
- if (!segment->done(angle)) {
- if (!first) {
- first = segment;
- *startPtr = start;
- *endPtr = end;
+ int maxWinding = winding;
+ winding -= segment->spanSign(angle);
+ #if DEBUG_SORT
+ SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
+ segment->debugID(), maxWinding, winding, angle->sign());
+ #endif
+ *tIndex = angle->start();
+ *endIndex = angle->end();
+ int lesser = SkMin32(*tIndex, *endIndex);
+ const SkOpSpan& nextSpan = segment->span(lesser);
+ if (!nextSpan.fDone) {
+ // FIXME: this be wrong? assign startWinding if edge is in
+ // same direction. If the direction is opposite, winding to
+ // assign is flipped sign or +/- 1?
+ if (SkOpSegment::UseInnerWinding(maxWinding, winding)) {
+ maxWinding = winding;
}
- // OPTIMIZATION: should this also add to the chase?
- (void) segment->markAngle(maxWinding, sumWinding, angle);
+ // allowed to do nothing
+ (void) segment->markAndChaseWinding(angle, maxWinding, 0, NULL);
+ break;
}
}
- if (first) {
- #if TRY_ROTATE
- *chase->insert(0) = span;
- #else
- *chase->append() = span;
- #endif
- return first;
- }
+ *chase->insert(0) = span;
+ return segment;
}
return NULL;
}
-#if DEBUG_ACTIVE_SPANS
-void DebugShowActiveSpans(SkTDArray<SkOpContour* >& contourList) {
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
+void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList) {
int index;
for (index = 0; index < contourList.count(); ++ index) {
contourList[index]->debugShowActiveSpans();
@@ -204,12 +228,11 @@ void DebugShowActiveSpans(SkTDArray<SkOpContour* >& contourList) {
}
#endif
-static SkOpSegment* findTopSegment(const SkTDArray<SkOpContour* >& contourList,
- bool firstPass, SkOpSpanBase** start, SkOpSpanBase** end, SkPoint* topLeft,
- bool* unsortable, bool* done, SkChunkAlloc* allocator) {
+static SkOpSegment* findTopSegment(const SkTArray<SkOpContour*, true>& contourList, int* index,
+ int* endIndex, SkPoint* topLeft, bool* unsortable, bool* done, bool firstPass) {
SkOpSegment* result;
const SkOpSegment* lastTopStart = NULL;
- SkOpSpanBase* lastStart = NULL, * lastEnd = NULL;
+ int lastIndex = -1, lastEndIndex = -1;
do {
SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
int contourCount = contourList.count();
@@ -238,27 +261,27 @@ static SkOpSegment* findTopSegment(const SkTDArray<SkOpContour* >& contourList,
return NULL;
}
*topLeft = bestXY;
- result = topStart->findTop(firstPass, start, end, unsortable, allocator);
+ result = topStart->findTop(index, endIndex, unsortable, firstPass);
if (!result) {
- if (lastTopStart == topStart && lastStart == *start && lastEnd == *end) {
+ if (lastTopStart == topStart && lastIndex == *index && lastEndIndex == *endIndex) {
*done = true;
return NULL;
}
lastTopStart = topStart;
- lastStart = *start;
- lastEnd = *end;
+ lastIndex = *index;
+ lastEndIndex = *endIndex;
}
} while (!result);
return result;
}
-static int rightAngleWinding(const SkTDArray<SkOpContour* >& contourList,
- SkOpSegment** currentPtr, SkOpSpanBase** start, SkOpSpanBase** end, double* tHit,
+static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList,
+ SkOpSegment** currentPtr, int* indexPtr, int* endIndexPtr, double* tHit,
SkScalar* hitDx, bool* tryAgain, bool* onlyVertical, bool opp) {
double test = 0.9;
int contourWinding;
do {
- contourWinding = contourRangeCheckY(contourList, currentPtr, start, end,
+ contourWinding = contourRangeCheckY(contourList, currentPtr, indexPtr, endIndexPtr,
tHit, hitDx, tryAgain, &test, opp);
if (contourWinding != SK_MinS32 || *tryAgain) {
return contourWinding;
@@ -273,9 +296,9 @@ static int rightAngleWinding(const SkTDArray<SkOpContour* >& contourList,
return contourWinding;
}
-static void skipVertical(const SkTDArray<SkOpContour* >& contourList,
- SkOpSegment** current, SkOpSpanBase** start, SkOpSpanBase** end) {
- if (!(*current)->isVertical(*start, *end)) {
+static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
+ SkOpSegment** current, int* index, int* endIndex) {
+ if (!(*current)->isVertical(*index, *endIndex)) {
return;
}
int contourCount = contourList.count();
@@ -284,7 +307,7 @@ static void skipVertical(const SkTDArray<SkOpContour* >& contourList,
if (contour->done()) {
continue;
}
- SkOpSegment* nonVertical = contour->nonVerticalSegment(start, end);
+ SkOpSegment* nonVertical = contour->nonVerticalSegment(index, endIndex);
if (nonVertical) {
*current = nonVertical;
return;
@@ -293,41 +316,41 @@ static void skipVertical(const SkTDArray<SkOpContour* >& contourList,
return;
}
-struct SortableTop2 { // error if local in pre-C++11
- SkOpSpanBase* fStart;
- SkOpSpanBase* fEnd;
+struct SortableTop { // error if local in pre-C++11
+ SkOpSegment* fSegment;
+ int fIndex;
+ int fEndIndex;
};
-SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool firstPass,
- SkOpAngle::IncludeType angleIncludeType, bool* firstContour, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical,
- SkChunkAlloc* allocator) {
- SkOpSegment* current = findTopSegment(contourList, firstPass, startPtr, endPtr, topLeft,
- unsortable, done, allocator);
+SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
+ SkOpAngle::IncludeType angleIncludeType, bool* firstContour, int* indexPtr,
+ int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical,
+ bool firstPass) {
+ SkOpSegment* current = findTopSegment(contourList, indexPtr, endIndexPtr, topLeft, unsortable,
+ done, firstPass);
if (!current) {
return NULL;
}
- SkOpSpanBase* start = *startPtr;
- SkOpSpanBase* end = *endPtr;
- SkASSERT(current == start->segment());
+ const int startIndex = *indexPtr;
+ const int endIndex = *endIndexPtr;
if (*firstContour) {
- current->initWinding(start, end, angleIncludeType);
+ current->initWinding(startIndex, endIndex, angleIncludeType);
*firstContour = false;
return current;
}
- SkOpSpan* minSpan = start->starter(end);
- int sumWinding = minSpan->windSum();
+ int minIndex = SkMin32(startIndex, endIndex);
+ int sumWinding = current->windSum(minIndex);
if (sumWinding == SK_MinS32) {
- SkOpSpanBase* iSpan = end;
- SkOpSpanBase* oSpan = start;
- do {
- bool checkFrom = oSpan->t() < iSpan->t();
- if ((checkFrom ? iSpan->fromAngle() : iSpan->upCast()->toAngle()) == NULL) {
- iSpan->addSimpleAngle(checkFrom, allocator);
+ int index = endIndex;
+ int oIndex = startIndex;
+ do {
+ const SkOpSpan& span = current->span(index);
+ if ((oIndex < index ? span.fFromAngle : span.fToAngle) == NULL) {
+ current->addSimpleAngle(index);
}
- sumWinding = current->computeSum(oSpan, iSpan, angleIncludeType);
- SkTSwap(iSpan, oSpan);
- } while (sumWinding == SK_MinS32 && iSpan == start);
+ sumWinding = current->computeSum(oIndex, index, angleIncludeType);
+ SkTSwap(index, oIndex);
+ } while (sumWinding == SK_MinS32 && index == startIndex);
}
if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) {
return current;
@@ -341,28 +364,26 @@ SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool f
SkScalar hitDx = 0;
SkScalar hitOppDx = 0;
// keep track of subsequent returns to detect infinite loops
- SkTDArray<SortableTop2> sortableTops;
+ 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
- SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
- SkASSERT(current == (*startPtr)->segment());
- skipVertical(contourList, &current, startPtr, endPtr);
+ SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
+ skipVertical(contourList, &current, indexPtr, endIndexPtr);
SkASSERT(current); // FIXME: if null, all remaining are vertical
- SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
- SkASSERT(current == (*startPtr)->segment());
+ SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
tryAgain = false;
- contourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &tHit,
+ contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
&hitDx, &tryAgain, onlyVertical, false);
- SkASSERT(current == (*startPtr)->segment());
if (tryAgain) {
bool giveUp = false;
int count = sortableTops.count();
for (int index = 0; index < count; ++index) {
- const SortableTop2& prev = sortableTops[index];
+ const SortableTop& prev = sortableTops[index];
if (giveUp) {
- prev.fStart->segment()->markDone(prev.fStart->starter(prev.fEnd));
- } else if (prev.fStart == *startPtr || prev.fEnd == *endPtr) {
+ 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;
@@ -374,13 +395,14 @@ SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool f
return NULL;
}
}
- SortableTop2* sortableTop = sortableTops.append();
- sortableTop->fStart = *startPtr;
- sortableTop->fEnd = *endPtr;
+ 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(), (*startPtr)->debugID(), (*endPtr)->debugID(),
- tHit, hitDx, tryAgain, *onlyVertical);
+ __FUNCTION__, current->debugID(), *indexPtr, *endIndexPtr, tHit, hitDx, tryAgain,
+ *onlyVertical);
#endif
if (*onlyVertical) {
return current;
@@ -391,35 +413,127 @@ SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool f
if (angleIncludeType < SkOpAngle::kBinarySingle) {
break;
}
- oppContourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &tHit,
+ oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
&hitOppDx, &tryAgain, NULL, true);
- SkASSERT(current == (*startPtr)->segment());
} while (tryAgain);
- bool success = current->initWinding(*startPtr, *endPtr, tHit, contourWinding, hitDx,
+ 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
- SkOpSpan* minSpan = (*startPtr)->t() < (*endPtr)->t() ? (*startPtr)->upCast()
- : (*endPtr)->upCast();
- if (minSpan->windSum() == SK_MinS32) {
+ int min = SkTMin(*indexPtr, *endIndexPtr);
+ const SkOpSpan& span = current->span(min);
+ if (span.fWindSum == SK_MinS32) {
return NULL;
}
}
return current;
}
-void MakeContourList(SkOpContour* contour, SkTDArray<SkOpContour* >& list,
- bool evenOdd, bool oppEvenOdd) {
- do {
- if (contour->count()) {
- contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
- *list.append() = contour;
+static bool calcAngles(SkTArray<SkOpContour*, true>* contourList) {
+ int contourCount = (*contourList).count();
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ SkOpContour* contour = (*contourList)[cTest];
+ if (!contour->calcAngles()) {
+ return false;
}
- } while ((contour = contour->next()));
- if (list.count() < 2) {
+ }
+ return true;
+}
+
+static void checkDuplicates(SkTArray<SkOpContour*, true>* contourList) {
+ int contourCount = (*contourList).count();
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ SkOpContour* contour = (*contourList)[cTest];
+ contour->checkDuplicates();
+ }
+}
+
+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];
+ if (!contour->checkEnds()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool checkMultiples(SkTArray<SkOpContour*, true>* contourList) {
+ bool hasMultiples = false;
+ int contourCount = (*contourList).count();
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ SkOpContour* contour = (*contourList)[cTest];
+ contour->checkMultiples();
+ hasMultiples |= contour->hasMultiples();
+ }
+ return hasMultiples;
+}
+
+// A small interval of a pair of curves may collapse to lines for each, triggering coincidence
+static void checkSmall(SkTArray<SkOpContour*, true>* contourList) {
+ int contourCount = (*contourList).count();
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ SkOpContour* contour = (*contourList)[cTest];
+ contour->checkSmall();
+ }
+}
+
+// A tiny interval may indicate an undiscovered coincidence. Find and fix.
+static void checkTiny(SkTArray<SkOpContour*, true>* contourList) {
+ int contourCount = (*contourList).count();
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ SkOpContour* contour = (*contourList)[cTest];
+ contour->checkTiny();
+ }
+}
+
+static void fixOtherTIndex(SkTArray<SkOpContour*, true>* contourList) {
+ int contourCount = (*contourList).count();
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ SkOpContour* contour = (*contourList)[cTest];
+ contour->fixOtherTIndex();
+ }
+}
+
+static void joinCoincidence(SkTArray<SkOpContour*, true>* contourList) {
+ int contourCount = (*contourList).count();
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ SkOpContour* contour = (*contourList)[cTest];
+ contour->joinCoincidence();
+ }
+}
+
+static void sortAngles(SkTArray<SkOpContour*, true>* contourList) {
+ int contourCount = (*contourList).count();
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ SkOpContour* contour = (*contourList)[cTest];
+ contour->sortAngles();
+ }
+}
+
+static void sortSegments(SkTArray<SkOpContour*, true>* contourList) {
+ int contourCount = (*contourList).count();
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ SkOpContour* contour = (*contourList)[cTest];
+ contour->sortSegments();
+ }
+}
+
+void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
+ bool evenOdd, bool oppEvenOdd) {
+ int count = contours.count();
+ if (count == 0) {
return;
}
+ for (int index = 0; index < count; ++index) {
+ SkOpContour& contour = contours[index];
+ contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
+ list.push_back(&contour);
+ }
SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
}
@@ -440,22 +554,19 @@ public:
reassemble contour pieces into new path
*/
void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
- SkOpContour contour;
- SkOpGlobalState globalState(NULL PATH_OPS_DEBUG_PARAMS(&contour));
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("%s\n", __FUNCTION__);
#endif
- SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
- SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
- builder.finish(&allocator);
- SkTDArray<const SkOpContour* > runs; // indices of partial contours
- const SkOpContour* eContour = builder.head();
- do {
- if (!eContour->count()) {
- continue;
- }
- const SkPoint& eStart = eContour->start();
- const SkPoint& eEnd = eContour->end();
+ SkTArray<SkOpContour> contours;
+ SkOpEdgeBuilder builder(path, contours);
+ builder.finish();
+ int count = contours.count();
+ int outer;
+ SkTArray<int, true> runs(count); // indices of partial contours
+ for (outer = 0; outer < count; ++outer) {
+ const SkOpContour& eContour = contours[outer];
+ const SkPoint& eStart = eContour.start();
+ const SkPoint& eEnd = eContour.end();
#if DEBUG_ASSEMBLE
SkDebugf("%s contour", __FUNCTION__);
if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
@@ -467,42 +578,44 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
#endif
if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
- eContour->toPath(simple);
+ eContour.toPath(simple);
continue;
}
- *runs.append() = eContour;
- } while ((eContour = eContour->next()));
- int count = runs.count();
+ runs.push_back(outer);
+ }
+ count = runs.count();
if (count == 0) {
return;
}
- SkTDArray<int> sLink, eLink;
- sLink.append(count);
- eLink.append(count);
+ SkTArray<int, true> sLink, eLink;
+ sLink.push_back_n(count);
+ eLink.push_back_n(count);
int rIndex, iIndex;
for (rIndex = 0; rIndex < count; ++rIndex) {
sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
}
const int ends = count * 2; // all starts and ends
const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
- SkTDArray<double> distances;
- distances.append(entries);
+ SkTArray<double, true> distances;
+ distances.push_back_n(entries);
for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
- const SkOpContour* oContour = runs[rIndex >> 1];
- const SkPoint& oPt = rIndex & 1 ? oContour->end() : oContour->start();
+ outer = runs[rIndex >> 1];
+ const SkOpContour& oContour = contours[outer];
+ const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
* ends - rIndex - 1;
for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
- const SkOpContour* iContour = runs[iIndex >> 1];
- const SkPoint& iPt = iIndex & 1 ? iContour->end() : iContour->start();
+ int inner = runs[iIndex >> 1];
+ const SkOpContour& iContour = contours[inner];
+ const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
double dx = iPt.fX - oPt.fX;
double dy = iPt.fY - oPt.fY;
double dist = dx * dx + dy * dy;
distances[row + iIndex] = dist; // oStart distance from iStart
}
}
- SkTDArray<int> sortedDist;
- sortedDist.append(entries);
+ SkTArray<int, true> sortedDist;
+ sortedDist.push_back_n(entries);
for (rIndex = 0; rIndex < entries; ++rIndex) {
sortedDist[rIndex] = rIndex;
}
@@ -565,16 +678,17 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
eIndex < 0 ? ~eIndex : eIndex);
#endif
do {
- const SkOpContour* contour = runs[rIndex];
+ outer = runs[rIndex];
+ const SkOpContour& contour = contours[outer];
if (first) {
first = false;
- const SkPoint* startPtr = &contour->start();
+ const SkPoint* startPtr = &contour.start();
simple->deferredMove(startPtr[0]);
}
if (forward) {
- contour->toPartialForward(simple);
+ contour.toPartialForward(simple);
} else {
- contour->toPartialBackward(simple);
+ contour.toPartialBackward(simple);
}
#if DEBUG_ASSEMBLE
SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
@@ -628,88 +742,36 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
#endif
}
-static void align(SkTDArray<SkOpContour* >* contourList) {
- int contourCount = (*contourList).count();
- for (int cTest = 0; cTest < contourCount; ++cTest) {
- SkOpContour* contour = (*contourList)[cTest];
- contour->align();
- }
-}
-
-static void calcAngles(SkTDArray<SkOpContour* >* contourList, SkChunkAlloc* allocator) {
- int contourCount = (*contourList).count();
- for (int cTest = 0; cTest < contourCount; ++cTest) {
- SkOpContour* contour = (*contourList)[cTest];
- contour->calcAngles(allocator);
- }
-}
-
-static void missingCoincidence(SkTDArray<SkOpContour* >* contourList,
- SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
- int contourCount = (*contourList).count();
- for (int cTest = 0; cTest < contourCount; ++cTest) {
- SkOpContour* contour = (*contourList)[cTest];
- contour->missingCoincidence(coincidence, allocator);
- }
-}
-
-static bool moveNearby(SkTDArray<SkOpContour* >* contourList) {
- int contourCount = (*contourList).count();
- for (int cTest = 0; cTest < contourCount; ++cTest) {
- SkOpContour* contour = (*contourList)[cTest];
- if (!contour->moveNearby()) {
- return false;
- }
- }
- return true;
-}
-
-static void sortAngles(SkTDArray<SkOpContour* >* contourList) {
- int contourCount = (*contourList).count();
- for (int cTest = 0; cTest < contourCount; ++cTest) {
- SkOpContour* contour = (*contourList)[cTest];
- contour->sortAngles();
- }
-}
-
-static void sortSegments(SkTDArray<SkOpContour* >* contourList) {
- int contourCount = (*contourList).count();
- for (int cTest = 0; cTest < contourCount; ++cTest) {
- SkOpContour* contour = (*contourList)[cTest];
- contour->sortSegments();
- }
-}
-
-bool HandleCoincidence(SkTDArray<SkOpContour* >* contourList, SkOpCoincidence* coincidence,
- SkChunkAlloc* allocator, SkOpGlobalState* globalState) {
- // move t values and points together to eliminate small/tiny gaps
- if (!moveNearby(contourList)) {
+bool HandleCoincidence(SkTArray<SkOpContour*, true>* contourList, int total) {
+#if DEBUG_SHOW_WINDING
+ SkOpContour::debugShowWindingValues(contourList);
+#endif
+ if (!CoincidenceCheck(contourList, total)) {
return false;
}
- align(contourList); // give all span members common values
-#if DEBUG_VALIDATE
- globalState->setPhase(SkOpGlobalState::kIntersecting);
-#endif
- coincidence->addMissing(allocator);
-#if DEBUG_VALIDATE
- globalState->setPhase(SkOpGlobalState::kWalking);
+#if DEBUG_SHOW_WINDING
+ SkOpContour::debugShowWindingValues(contourList);
#endif
- coincidence->expand(); // check to see if, loosely, coincident ranges may be expanded
- coincidence->mark(); // mark spans of coincident segments as coincident
- missingCoincidence(contourList, coincidence, allocator); // look for coincidence missed earlier
- if (!coincidence->apply()) { // adjust the winding value to account for coincident edges
+ fixOtherTIndex(contourList);
+ 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) {
+ alignMultiples(contourList, &aligned); // align pairs of identical points
+ alignCoincidence(contourList, aligned);
+ }
+ checkDuplicates(contourList); // check if spans have the same number on the other end
+ checkTiny(contourList); // if pair have the same end points, mark them as parallel
+ checkSmall(contourList); // a pair of curves with a small span may turn into coincident lines
+ joinCoincidence(contourList); // join curves that connect to a coincident pair
sortSegments(contourList);
- calcAngles(contourList, allocator);
- sortAngles(contourList);
- if (globalState->angleCoincidence()) {
- missingCoincidence(contourList, coincidence, allocator);
- if (!coincidence->apply()) {
- return false;
- }
+ if (!calcAngles(contourList)) {
+ return false;
}
-#if DEBUG_ACTIVE_SPANS
+ sortAngles(contourList);
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
DebugShowActiveSpans(*contourList);
#endif
return true;
diff --git a/src/pathops/SkPathOpsCommon.h b/src/pathops/SkPathOpsCommon.h
index 1bf17919ad..0d8cfc42f9 100644
--- a/src/pathops/SkPathOpsCommon.h
+++ b/src/pathops/SkPathOpsCommon.h
@@ -8,28 +8,24 @@
#define SkPathOpsCommon_DEFINED
#include "SkOpAngle.h"
+#include "SkOpContour.h"
#include "SkTDArray.h"
-class SkOpCoincidence;
-class SkOpContour;
class SkPathWriter;
void Assemble(const SkPathWriter& path, SkPathWriter* simple);
-SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr);
-SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour*>& , bool firstPass,
- SkOpAngle::IncludeType , bool* firstContour, SkOpSpanBase** index,
- SkOpSpanBase** endIndex, SkPoint* topLeft, bool* unsortable,
- bool* done, bool* onlyVertical, SkChunkAlloc* );
-SkOpSegment* FindUndone(SkTDArray<SkOpContour*>& contourList, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr);
-void MakeContourList(SkOpContour* , SkTDArray<SkOpContour*>& list,
+// FIXME: find chase uses insert, so it can't be converted to SkTArray yet
+SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex);
+SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& , SkOpAngle::IncludeType ,
+ bool* firstContour, int* index, int* endIndex, SkPoint* topLeft,
+ bool* unsortable, bool* done, bool* onlyVertical, bool firstPass);
+SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end);
+void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
bool evenOdd, bool oppEvenOdd);
-bool HandleCoincidence(SkTDArray<SkOpContour*>* , SkOpCoincidence* , SkChunkAlloc* ,
- SkOpGlobalState* );
+bool HandleCoincidence(SkTArray<SkOpContour*, true>* , int );
-#if DEBUG_ACTIVE_SPANS
-void DebugShowActiveSpans(SkTDArray<SkOpContour*>& contourList);
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
+void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList);
#endif
#endif
diff --git a/src/pathops/SkPathOpsCubic.cpp b/src/pathops/SkPathOpsCubic.cpp
index d4a5898a1d..9d70d58ec1 100644
--- a/src/pathops/SkPathOpsCubic.cpp
+++ b/src/pathops/SkPathOpsCubic.cpp
@@ -4,7 +4,6 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-#include "SkGeometry.h"
#include "SkLineParameters.h"
#include "SkPathOpsCubic.h"
#include "SkPathOpsLine.h"
@@ -27,8 +26,8 @@ double SkDCubic::binarySearch(double min, double max, double axisIntercept,
double priorT = t - step;
SkASSERT(priorT >= min);
SkDPoint lessPt = ptAtT(priorT);
- if (approximately_equal_half(lessPt.fX, cubicAtT.fX)
- && approximately_equal_half(lessPt.fY, cubicAtT.fY)) {
+ if (approximately_equal(lessPt.fX, cubicAtT.fX)
+ && approximately_equal(lessPt.fY, cubicAtT.fY)) {
return -1; // binary search found no point at this axis intercept
}
double lessDist = (&lessPt.fX)[xAxis] - axisIntercept;
@@ -42,12 +41,10 @@ double SkDCubic::binarySearch(double min, double max, double axisIntercept,
t = priorT;
} else {
double nextT = t + lastStep;
- if (nextT > max) {
- return -1;
- }
+ SkASSERT(nextT <= max);
SkDPoint morePt = ptAtT(nextT);
- if (approximately_equal_half(morePt.fX, cubicAtT.fX)
- && approximately_equal_half(morePt.fY, cubicAtT.fY)) {
+ if (approximately_equal(morePt.fX, cubicAtT.fX)
+ && approximately_equal(morePt.fY, cubicAtT.fY)) {
return -1; // binary search found no point at this axis intercept
}
double moreDist = (&morePt.fX)[xAxis] - axisIntercept;
@@ -91,6 +88,35 @@ void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C,
*C -= 3 * *D; // C = -3*a + 3*b
}
+bool SkDCubic::controlsContainedByEnds() const {
+ SkDVector startTan = fPts[1] - fPts[0];
+ if (startTan.fX == 0 && startTan.fY == 0) {
+ startTan = fPts[2] - fPts[0];
+ }
+ SkDVector endTan = fPts[2] - fPts[3];
+ if (endTan.fX == 0 && endTan.fY == 0) {
+ endTan = fPts[1] - fPts[3];
+ }
+ if (startTan.dot(endTan) >= 0) {
+ return false;
+ }
+ SkDLine startEdge = {{fPts[0], fPts[0]}};
+ startEdge[1].fX -= startTan.fY;
+ startEdge[1].fY += startTan.fX;
+ SkDLine endEdge = {{fPts[3], fPts[3]}};
+ endEdge[1].fX -= endTan.fY;
+ endEdge[1].fY += endTan.fX;
+ double leftStart1 = startEdge.isLeft(fPts[1]);
+ if (leftStart1 * startEdge.isLeft(fPts[2]) < 0) {
+ return false;
+ }
+ double leftEnd1 = endEdge.isLeft(fPts[1]);
+ if (leftEnd1 * endEdge.isLeft(fPts[2]) < 0) {
+ return false;
+ }
+ return leftStart1 * leftEnd1 >= 0;
+}
+
bool SkDCubic::endsAreExtremaInXOrY() const {
return (between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
&& between(fPts[0].fX, fPts[2].fX, fPts[3].fX))
@@ -98,120 +124,17 @@ bool SkDCubic::endsAreExtremaInXOrY() const {
&& between(fPts[0].fY, fPts[2].fY, fPts[3].fY));
}
-// Do a quick reject by rotating all points relative to a line formed by
-// a pair of one cubic's points. If the 2nd cubic's points
-// are on the line or on the opposite side from the 1st cubic's 'odd man', the
-// curves at most intersect at the endpoints.
-/* if returning true, check contains true if cubic's hull collapsed, making the cubic linear
- if returning false, check contains true if the the cubic pair have only the end point in common
-*/
-bool SkDCubic::hullIntersects(const SkDCubic& c2, bool* isLinear) const {
- bool linear = true;
- char hullOrder[4];
- int hullCount = convexHull(hullOrder);
- int end1 = hullOrder[0];
- int hullIndex = 0;
- const SkDPoint* endPt[2];
- endPt[0] = &fPts[end1];
- do {
- hullIndex = (hullIndex + 1) % hullCount;
- int end2 = hullOrder[hullIndex];
- endPt[1] = &fPts[end2];
- double origX = endPt[0]->fX;
- double origY = endPt[0]->fY;
- double adj = endPt[1]->fX - origX;
- double opp = endPt[1]->fY - origY;
- int oddManMask = other_two(end1, end2);
- int oddMan = end1 ^ oddManMask;
- double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
- int oddMan2 = end2 ^ oddManMask;
- double sign2 = (fPts[oddMan2].fY - origY) * adj - (fPts[oddMan2].fX - origX) * opp;
- if (sign * sign2 < 0) {
- continue;
- }
- if (approximately_zero(sign)) {
- sign = sign2;
- if (approximately_zero(sign)) {
- continue;
- }
- }
- linear = false;
- bool foundOutlier = false;
- for (int n = 0; n < kPointCount; ++n) {
- double test = (c2[n].fY - origY) * adj - (c2[n].fX - origX) * opp;
- if (test * sign > 0 && !precisely_zero(test)) {
- foundOutlier = true;
- break;
- }
- }
- if (!foundOutlier) {
- return false;
- }
- endPt[0] = endPt[1];
- end1 = end2;
- } while (hullIndex);
- *isLinear = linear;
- return true;
-}
-
bool SkDCubic::isLinear(int startIndex, int endIndex) const {
SkLineParameters lineParameters;
lineParameters.cubicEndPoints(*this, startIndex, endIndex);
// FIXME: maybe it's possible to avoid this and compare non-normalized
lineParameters.normalize();
- double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
- fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY);
- double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
- fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY);
- largest = SkTMax(largest, -tiniest);
double distance = lineParameters.controlPtDistance(*this, 1);
- if (!approximately_zero_when_compared_to(distance, largest)) {
+ if (!approximately_zero(distance)) {
return false;
}
distance = lineParameters.controlPtDistance(*this, 2);
- return approximately_zero_when_compared_to(distance, largest);
-}
-
-bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
- SkScalar d[3];
- SkCubicType cubicType = SkClassifyCubic(pointsPtr, d);
- if (cubicType == kLoop_SkCubicType) {
- // crib code from gpu path utils that finds t values where loop self-intersects
- // use it to find mid of t values which should be a friendly place to chop
- SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]);
- SkScalar ls = d[1] - tempSqrt;
- SkScalar lt = 2.f * d[0];
- SkScalar ms = d[1] + tempSqrt;
- SkScalar mt = 2.f * d[0];
- if (between(0, ls, lt) || between(0, ms, mt)) {
- ls = ls / lt;
- ms = ms / mt;
- SkScalar smaller = SkTMax(0.f, SkTMin(ls, ms));
- SkScalar larger = SkTMin(1.f, SkTMax(ls, ms));
- *t = (smaller + larger) / 2;
- return *t > 0 && *t < 1;
- }
- } else if (cubicType == kSerpentine_SkCubicType) {
- SkDCubic cubic;
- cubic.set(pointsPtr);
- double inflectionTs[2];
- int infTCount = cubic.findInflections(inflectionTs);
- if (infTCount == 2) {
- double maxCurvature[3];
- int roots = cubic.findMaxCurvature(maxCurvature);
- for (int index = 0; index < roots; ++index) {
- if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
- *t = maxCurvature[index];
- return true;
- }
- }
- } else if (infTCount == 1) {
- *t = inflectionTs[0];
- return *t > 0 && *t < 1;
- }
- return false;
- }
- return false;
+ return approximately_zero(distance);
}
bool SkDCubic::monotonicInY() const {
@@ -219,13 +142,6 @@ bool SkDCubic::monotonicInY() const {
&& between(fPts[0].fY, fPts[2].fY, fPts[3].fY);
}
-void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const {
- int offset = (int) !SkToBool(index);
- o1Pts[0] = &fPts[offset];
- o1Pts[1] = &fPts[++offset];
- o1Pts[2] = &fPts[++offset];
-}
-
int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept,
SearchAxis xAxis, double* validRoots) const {
extrema += findInflections(&extremeTs[extrema]);
@@ -247,6 +163,26 @@ int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept
return validCount;
}
+bool SkDCubic::serpentine() const {
+#if 0 // FIXME: enabling this fixes cubicOp114 but breaks cubicOp58d and cubicOp53d
+ double tValues[2];
+ // OPTIMIZATION : another case where caching the present of cubic inflections would be useful
+ return findInflections(tValues) > 1;
+#endif
+ if (!controlsContainedByEnds()) {
+ return false;
+ }
+ double wiggle = (fPts[0].fX - fPts[2].fX) * (fPts[0].fY + fPts[2].fY);
+ for (int idx = 0; idx < 2; ++idx) {
+ wiggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY);
+ }
+ double waggle = (fPts[1].fX - fPts[3].fX) * (fPts[1].fY + fPts[3].fY);
+ for (int idx = 1; idx < 3; ++idx) {
+ waggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY);
+ }
+ return wiggle * waggle < 0;
+}
+
// cubic roots
static const double PI = 3.141592653589793;
@@ -569,10 +505,25 @@ void SkDCubic::align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const {
void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d,
double t1, double t2, SkDPoint dst[2]) const {
SkASSERT(t1 != t2);
+#if 0
+ double ex = interp_cubic_coords(&fPts[0].fX, (t1 * 2 + t2) / 3);
+ double ey = interp_cubic_coords(&fPts[0].fY, (t1 * 2 + t2) / 3);
+ double fx = interp_cubic_coords(&fPts[0].fX, (t1 + t2 * 2) / 3);
+ double fy = interp_cubic_coords(&fPts[0].fY, (t1 + t2 * 2) / 3);
+ double mx = ex * 27 - a.fX * 8 - d.fX;
+ double my = ey * 27 - a.fY * 8 - d.fY;
+ double nx = fx * 27 - a.fX - d.fX * 8;
+ double ny = fy * 27 - a.fY - d.fY * 8;
+ /* bx = */ dst[0].fX = (mx * 2 - nx) / 18;
+ /* by = */ dst[0].fY = (my * 2 - ny) / 18;
+ /* cx = */ dst[1].fX = (nx * 2 - mx) / 18;
+ /* cy = */ dst[1].fY = (ny * 2 - my) / 18;
+#else
// this approach assumes that the control points computed directly are accurate enough
SkDCubic sub = subDivide(t1, t2);
dst[0] = sub[1] + (a - sub[0]);
dst[1] = sub[2] + (d - sub[3]);
+#endif
if (t1 == 0 || t2 == 0) {
align(0, 1, t1 == 0 ? &dst[0] : &dst[1]);
}
diff --git a/src/pathops/SkPathOpsCubic.h b/src/pathops/SkPathOpsCubic.h
index 9932e1d1bc..1037cae4f7 100644
--- a/src/pathops/SkPathOpsCubic.h
+++ b/src/pathops/SkPathOpsCubic.h
@@ -10,6 +10,7 @@
#include "SkPath.h"
#include "SkPathOpsPoint.h"
+#include "SkTArray.h"
struct SkDCubicPair {
const SkDCubic& first() const { return (const SkDCubic&) pts[0]; }
@@ -18,33 +19,13 @@ struct SkDCubicPair {
};
struct SkDCubic {
- static const int kPointCount = 4;
- static const int kPointLast = kPointCount - 1;
- static const int kMaxIntersections = 9;
-
enum SearchAxis {
kXAxis,
kYAxis
};
- bool collapsed() const {
- return fPts[0].approximatelyEqual(fPts[1]) && fPts[0].approximatelyEqual(fPts[2])
- && fPts[0].approximatelyEqual(fPts[3]);
- }
-
- bool controlsInside() const {
- SkDVector v01 = fPts[0] - fPts[1];
- SkDVector v02 = fPts[0] - fPts[2];
- SkDVector v03 = fPts[0] - fPts[3];
- SkDVector v13 = fPts[1] - fPts[3];
- SkDVector v23 = fPts[2] - fPts[3];
- return v03.dot(v01) > 0 && v03.dot(v02) > 0 && v03.dot(v13) > 0 && v03.dot(v23) > 0;
- }
-
- static bool IsCubic() { return true; }
-
- const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
- SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
+ const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < 4); return fPts[n]; }
+ SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < 4); return fPts[n]; }
void align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const;
double binarySearch(double min, double max, double axisIntercept, SearchAxis xAxis) const;
@@ -52,35 +33,30 @@ struct SkDCubic {
SkDCubicPair chopAt(double t) const;
bool clockwise() const;
static void Coefficients(const double* cubic, double* A, double* B, double* C, double* D);
- static bool ComplexBreak(const SkPoint pts[4], SkScalar* t);
- int convexHull(char order[kPointCount]) const;
- void dump() const; // callable from the debugger when the implementation code is linked in
- void dumpID(int id) const;
- void dumpInner() const;
+ bool controlsContainedByEnds() const;
SkDVector dxdyAtT(double t) const;
bool endsAreExtremaInXOrY() const;
static int FindExtrema(double a, double b, double c, double d, double tValue[2]);
int findInflections(double tValues[2]) const;
- static int FindInflections(const SkPoint a[kPointCount], double tValues[2]) {
+ static int FindInflections(const SkPoint a[4], double tValues[2]) {
SkDCubic cubic;
cubic.set(a);
return cubic.findInflections(tValues);
}
int findMaxCurvature(double tValues[]) const;
- bool hullIntersects(const SkDCubic& c2, bool* isLinear) const;
bool isLinear(int startIndex, int endIndex) const;
bool monotonicInY() const;
- void otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const;
SkDPoint ptAtT(double t) const;
static int RootsReal(double A, double B, double C, double D, double t[3]);
static int RootsValidT(const double A, const double B, const double C, double D, double s[3]);
int searchRoots(double extremes[6], int extrema, double axisIntercept,
SearchAxis xAxis, double* validRoots) const;
+ bool serpentine() const;
- void set(const SkPoint pts[kPointCount]) {
+ void set(const SkPoint pts[4]) {
fPts[0] = pts[0];
fPts[1] = pts[1];
fPts[2] = pts[2];
@@ -89,7 +65,7 @@ struct SkDCubic {
SkDCubic subDivide(double t1, double t2) const;
- static SkDCubic SubDivide(const SkPoint a[kPointCount], double t1, double t2) {
+ static SkDCubic SubDivide(const SkPoint a[4], double t1, double t2) {
SkDCubic cubic;
cubic.set(a);
return cubic.subDivide(t1, t2);
@@ -97,7 +73,7 @@ struct SkDCubic {
void subDivide(const SkDPoint& a, const SkDPoint& d, double t1, double t2, SkDPoint p[2]) const;
- static void SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& d, double t1,
+ static void SubDivide(const SkPoint pts[4], const SkDPoint& a, const SkDPoint& d, double t1,
double t2, SkDPoint p[2]) {
SkDCubic cubic;
cubic.set(pts);
@@ -105,29 +81,16 @@ struct SkDCubic {
}
SkDPoint top(double startT, double endT) const;
+ void toQuadraticTs(double precision, SkTArray<double, true>* ts) const;
SkDQuad toQuad() const;
+ // utilities callable by the user from the debugger when the implementation code is linked in
+ void dump() const;
+ void dumpNumber() const;
+
static const int gPrecisionUnit;
- SkDPoint fPts[kPointCount];
+ SkDPoint fPts[4];
};
-/* Given the set [0, 1, 2, 3], and two of the four members, compute an XOR mask
- that computes the other two. Note that:
-
- one ^ two == 3 for (0, 3), (1, 2)
- one ^ two < 3 for (0, 1), (0, 2), (1, 3), (2, 3)
- 3 - (one ^ two) is either 0, 1, or 2
- 1 >> (3 - (one ^ two)) is either 0 or 1
-thus:
- returned == 2 for (0, 3), (1, 2)
- returned == 3 for (0, 1), (0, 2), (1, 3), (2, 3)
-given that:
- (0, 3) ^ 2 -> (2, 1) (1, 2) ^ 2 -> (3, 0)
- (0, 1) ^ 3 -> (3, 2) (0, 2) ^ 3 -> (3, 1) (1, 3) ^ 3 -> (2, 0) (2, 3) ^ 3 -> (1, 0)
-*/
-inline int other_two(int one, int two) {
- return 1 >> (3 - (one ^ two)) ^ 3;
-}
-
#endif
diff --git a/src/pathops/SkPathOpsCubicSect.h b/src/pathops/SkPathOpsCubicSect.h
new file mode 100644
index 0000000000..d7634449b6
--- /dev/null
+++ b/src/pathops/SkPathOpsCubicSect.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkCubicSpan_DEFINE
+#define SkCubicSpan_DEFINE
+
+#include "SkChunkAlloc.h"
+#include "SkPathOpsRect.h"
+#include "SkPathOpsCubic.h"
+#include "SkTArray.h"
+
+class SkIntersections;
+
+class SkCubicCoincident {
+public:
+ bool isCoincident() const {
+ return fCoincident;
+ }
+
+ void init() {
+ fCoincident = false;
+ SkDEBUGCODE(fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN);
+ SkDEBUGCODE(fPerpT = SK_ScalarNaN);
+ }
+
+ void markCoincident() {
+ if (!fCoincident) {
+ fPerpT = -1;
+ }
+ fCoincident = true;
+ }
+
+ const SkDPoint& perpPt() const {
+ return fPerpPt;
+ }
+
+ double perpT() const {
+ return fPerpT;
+ }
+
+ void setPerp(const SkDCubic& cubic1, double t, const SkDPoint& qPt, const SkDCubic& cubic2);
+
+private:
+ SkDPoint fPerpPt;
+ double fPerpT; // perpendicular intersection on opposite Cubic
+ bool fCoincident;
+};
+
+class SkCubicSect; // used only by debug id
+
+class SkCubicSpan {
+public:
+ void init(const SkDCubic& Cubic);
+ void initBounds(const SkDCubic& Cubic);
+
+ bool contains(double t) const {
+ return !! const_cast<SkCubicSpan*>(this)->innerFind(t);
+ }
+
+ bool contains(const SkCubicSpan* span) const;
+
+ SkCubicSpan* find(double t) {
+ SkCubicSpan* result = innerFind(t);
+ SkASSERT(result);
+ return result;
+ }
+
+ bool intersects(const SkCubicSpan* span) const;
+
+ const SkCubicSpan* next() const {
+ return fNext;
+ }
+
+ void reset() {
+ fBounded.reset();
+ }
+
+ bool split(SkCubicSpan* work) {
+ return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
+ }
+
+ bool splitAt(SkCubicSpan* work, double t);
+ bool tightBoundsIntersects(const SkCubicSpan* span) const;
+
+ // implementation is for testing only
+ void dump() const;
+
+private:
+ bool hullIntersects(const SkDCubic& ) const;
+ SkCubicSpan* innerFind(double t);
+ bool linearIntersects(const SkDCubic& ) const;
+
+ // implementation is for testing only
+#if DEBUG_BINARY_CUBIC
+ int debugID(const SkCubicSect* ) const { return fDebugID; }
+#else
+ int debugID(const SkCubicSect* ) const;
+#endif
+ void dump(const SkCubicSect* ) const;
+ void dumpID(const SkCubicSect* ) const;
+
+#if DEBUG_BINARY_CUBIC
+ void validate() const;
+#endif
+
+ SkDCubic fPart;
+ SkCubicCoincident fCoinStart;
+ SkCubicCoincident fCoinEnd;
+ SkSTArray<4, SkCubicSpan*, true> fBounded;
+ SkCubicSpan* fPrev;
+ SkCubicSpan* fNext;
+ SkDRect fBounds;
+ double fStartT;
+ double fEndT;
+ double fBoundsMax;
+ bool fCollapsed;
+ bool fHasPerp;
+ mutable bool fIsLinear;
+#if DEBUG_BINARY_CUBIC
+ int fDebugID;
+ bool fDebugDeleted;
+#endif
+ friend class SkCubicSect;
+};
+
+class SkCubicSect {
+public:
+ SkCubicSect(const SkDCubic& Cubic PATH_OPS_DEBUG_PARAMS(int id));
+ static void BinarySearch(SkCubicSect* sect1, SkCubicSect* sect2, SkIntersections* intersections);
+
+ // for testing only
+ void dumpCubics() const;
+private:
+ SkCubicSpan* addOne();
+ bool binarySearchCoin(const SkCubicSect& , double tStart, double tStep, double* t,
+ double* oppT);
+ SkCubicSpan* boundsMax() const;
+ void coincidentCheck(SkCubicSect* sect2);
+ bool intersects(const SkCubicSpan* span, const SkCubicSect* opp, const SkCubicSpan* oppSpan) const;
+ void onCurveCheck(SkCubicSect* sect2, SkCubicSpan* first, SkCubicSpan* last);
+ void recoverCollapsed();
+ void removeSpan(SkCubicSpan* span);
+ void removeOne(const SkCubicSpan* test, SkCubicSpan* span);
+ void removeSpans(SkCubicSpan* span, SkCubicSect* opp);
+ void setPerp(const SkDCubic& opp, SkCubicSpan* first, SkCubicSpan* last);
+ void trim(SkCubicSpan* span, SkCubicSect* opp);
+
+ // for testing only
+ void dump() const;
+ void dumpBoth(const SkCubicSect& opp) const;
+ void dumpBoth(const SkCubicSect* opp) const;
+
+#if DEBUG_BINARY_CUBIC
+ int debugID() const { return fDebugID; }
+ void validate() const;
+#else
+ int debugID() const { return 0; }
+#endif
+ const SkDCubic& fCubic;
+ SkChunkAlloc fHeap;
+ SkCubicSpan* fHead;
+ SkCubicSpan* fDeleted;
+ int fActiveCount;
+#if DEBUG_BINARY_CUBIC
+ int fDebugID;
+ int fDebugCount;
+ int fDebugAllocatedCount;
+#endif
+ friend class SkCubicSpan; // only used by debug id
+};
+
+#endif
diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp
index 0331f34e8a..7db93f5e96 100644
--- a/src/pathops/SkPathOpsDebug.cpp
+++ b/src/pathops/SkPathOpsDebug.cpp
@@ -7,13 +7,6 @@
#include "SkPathOpsDebug.h"
#include "SkPath.h"
-#if DEBUG_ANGLE
-#include "SkString.h"
-#endif
-
-#if DEBUG_VALIDATE
-extern bool FLAGS_runFail;
-#endif
#if defined SK_DEBUG || !FORCE_RELEASE
@@ -33,10 +26,10 @@ int SkPathOpsDebug::gSortCount;
const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
#endif
-bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
- const SkOpSpanBase* span) {
+bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpan *>& chaseArray,
+ const SkOpSpan* span) {
for (int index = 0; index < chaseArray.count(); ++index) {
- const SkOpSpanBase* entry = chaseArray[index];
+ const SkOpSpan* entry = chaseArray[index];
if (entry == span) {
return true;
}
@@ -72,8 +65,6 @@ void SkPathOpsDebug::WindingPrintf(int wind) {
SkDebugf("%d", wind);
}
}
-#endif // defined SK_DEBUG || !FORCE_RELEASE
-
#if DEBUG_SHOW_TEST_NAME
void* SkPathOpsDebug::CreateNameStr() {
@@ -106,368 +97,470 @@ void SkPathOpsDebug::ShowPath(const SkPath& one, const SkPath& two, SkPathOp op,
}
#endif
+#endif // defined SK_DEBUG || !FORCE_RELEASE
+
#include "SkOpAngle.h"
#include "SkOpSegment.h"
+#if DEBUG_SORT
+void SkOpAngle::debugLoop() const {
+ const SkOpAngle* first = this;
+ const SkOpAngle* next = this;
+ do {
+ next->dumpOne(true);
+ SkDebugf("\n");
+ next = next->fNext;
+ } while (next && next != first);
+}
+#endif
+
+#if DEBUG_ANGLE
+void SkOpAngle::debugSameAs(const SkOpAngle* compare) const {
+ SK_ALWAYSBREAK(fSegment == compare->fSegment);
+ const SkOpSpan& startSpan = fSegment->span(fStart);
+ const SkOpSpan& oStartSpan = fSegment->span(compare->fStart);
+ SK_ALWAYSBREAK(startSpan.fToAngle == oStartSpan.fToAngle);
+ SK_ALWAYSBREAK(startSpan.fFromAngle == oStartSpan.fFromAngle);
+ const SkOpSpan& endSpan = fSegment->span(fEnd);
+ const SkOpSpan& oEndSpan = fSegment->span(compare->fEnd);
+ SK_ALWAYSBREAK(endSpan.fToAngle == oEndSpan.fToAngle);
+ SK_ALWAYSBREAK(endSpan.fFromAngle == oEndSpan.fFromAngle);
+}
+#endif
+
+#if DEBUG_VALIDATE
+void SkOpAngle::debugValidateNext() const {
+ const SkOpAngle* first = this;
+ const SkOpAngle* next = first;
+ SkTDArray<const SkOpAngle*>(angles);
+ do {
+// SK_ALWAYSBREAK(next->fSegment->debugContains(next));
+ angles.push(next);
+ next = next->next();
+ if (next == first) {
+ break;
+ }
+ SK_ALWAYSBREAK(!angles.contains(next));
+ if (!next) {
+ return;
+ }
+ } while (true);
+}
+
+void SkOpAngle::debugValidateLoop() const {
+ const SkOpAngle* first = this;
+ const SkOpAngle* next = first;
+ SK_ALWAYSBREAK(first->next() != first);
+ int signSum = 0;
+ int oppSum = 0;
+ bool firstOperand = fSegment->operand();
+ bool unorderable = false;
+ do {
+ unorderable |= next->fUnorderable;
+ const SkOpSegment* segment = next->fSegment;
+ bool operandsMatch = firstOperand == segment->operand();
+ signSum += operandsMatch ? segment->spanSign(next) : segment->oppSign(next);
+ oppSum += operandsMatch ? segment->oppSign(next) : segment->spanSign(next);
+ const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
+ if (segment->_xor()) {
+// SK_ALWAYSBREAK(span.fWindValue == 1);
+// SK_ALWAYSBREAK(span.fWindSum == SK_MinS32 || span.fWindSum == 1);
+ }
+ if (segment->oppXor()) {
+ SK_ALWAYSBREAK(span.fOppValue == 0 || abs(span.fOppValue) == 1);
+// SK_ALWAYSBREAK(span.fOppSum == SK_MinS32 || span.fOppSum == 0 || abs(span.fOppSum) == 1);
+ }
+ next = next->next();
+ if (!next) {
+ return;
+ }
+ } while (next != first);
+ if (unorderable) {
+ return;
+ }
+ SK_ALWAYSBREAK(!signSum || fSegment->_xor());
+ SK_ALWAYSBREAK(!oppSum || fSegment->oppXor());
+ int lastWinding;
+ int lastOppWinding;
+ int winding;
+ int oppWinding;
+ do {
+ const SkOpSegment* segment = next->fSegment;
+ const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
+ winding = span.fWindSum;
+ if (winding != SK_MinS32) {
+// SK_ALWAYSBREAK(winding != 0);
+ SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
+ lastWinding = winding;
+ int diffWinding = segment->spanSign(next);
+ if (!segment->_xor()) {
+ SK_ALWAYSBREAK(diffWinding != 0);
+ bool sameSign = (winding > 0) == (diffWinding > 0);
+ winding -= sameSign ? diffWinding : -diffWinding;
+ SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
+ SK_ALWAYSBREAK(abs(winding) <= abs(lastWinding));
+ if (!sameSign) {
+ SkTSwap(winding, lastWinding);
+ }
+ }
+ lastOppWinding = oppWinding = span.fOppSum;
+ if (oppWinding != SK_MinS32 && !segment->oppXor()) {
+ int oppDiffWinding = segment->oppSign(next);
+// SK_ALWAYSBREAK(abs(oppDiffWinding) <= abs(diffWinding) || segment->_xor());
+ if (oppDiffWinding) {
+ bool oppSameSign = (oppWinding > 0) == (oppDiffWinding > 0);
+ oppWinding -= oppSameSign ? oppDiffWinding : -oppDiffWinding;
+ SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
+ SK_ALWAYSBREAK(abs(oppWinding) <= abs(lastOppWinding));
+ if (!oppSameSign) {
+ SkTSwap(oppWinding, lastOppWinding);
+ }
+ }
+ }
+ firstOperand = segment->operand();
+ break;
+ }
+ SK_ALWAYSBREAK(span.fOppSum == SK_MinS32);
+ next = next->next();
+ } while (next != first);
+ if (winding == SK_MinS32) {
+ return;
+ }
+ SK_ALWAYSBREAK(oppWinding == SK_MinS32 || SkPathOpsDebug::ValidWind(oppWinding));
+ first = next;
+ next = next->next();
+ do {
+ const SkOpSegment* segment = next->fSegment;
+ lastWinding = winding;
+ lastOppWinding = oppWinding;
+ bool operandsMatch = firstOperand == segment->operand();
+ if (operandsMatch) {
+ if (!segment->_xor()) {
+ winding -= segment->spanSign(next);
+ SK_ALWAYSBREAK(winding != lastWinding);
+ SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
+ }
+ if (!segment->oppXor()) {
+ int oppDiffWinding = segment->oppSign(next);
+ if (oppWinding != SK_MinS32) {
+ oppWinding -= oppDiffWinding;
+ SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
+ } else {
+ SK_ALWAYSBREAK(oppDiffWinding == 0);
+ }
+ }
+ } else {
+ if (!segment->oppXor()) {
+ winding -= segment->oppSign(next);
+ SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
+ }
+ if (!segment->_xor()) {
+ oppWinding -= segment->spanSign(next);
+ SK_ALWAYSBREAK(oppWinding != lastOppWinding);
+ SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
+ }
+ }
+ bool useInner = SkOpSegment::UseInnerWinding(lastWinding, winding);
+ int sumWinding = useInner ? winding : lastWinding;
+ bool oppUseInner = SkOpSegment::UseInnerWinding(lastOppWinding, oppWinding);
+ int oppSumWinding = oppUseInner ? oppWinding : lastOppWinding;
+ if (!operandsMatch) {
+ SkTSwap(useInner, oppUseInner);
+ SkTSwap(sumWinding, oppSumWinding);
+ }
+ const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
+ if (winding == -lastWinding) {
+ if (span.fWindSum != SK_MinS32) {
+ SkDebugf("%s useInner=%d spanSign=%d lastWinding=%d winding=%d windSum=%d\n",
+ __FUNCTION__,
+ useInner, segment->spanSign(next), lastWinding, winding, span.fWindSum);
+ }
+ }
+ if (oppWinding != SK_MinS32) {
+ if (span.fOppSum != SK_MinS32) {
+ SK_ALWAYSBREAK(span.fOppSum == oppSumWinding || segment->oppXor() || segment->_xor());
+ }
+ } else {
+ SK_ALWAYSBREAK(!firstOperand);
+ SK_ALWAYSBREAK(!segment->operand());
+ SK_ALWAYSBREAK(!span.fOppValue);
+ }
+ next = next->next();
+ } while (next != first);
+}
+#endif
+
#if DEBUG_SWAP_TOP
-int SkOpSegment::debugInflections(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
+bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const {
if (fVerb != SkPath::kCubic_Verb) {
return false;
}
- SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
+ SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
+ return dst.controlsContainedByEnds();
+}
+#endif
+
+#if DEBUG_CONCIDENT
+// SK_ALWAYSBREAK if pair has not already been added
+void SkOpSegment::debugAddTPair(double t, const SkOpSegment& other, double otherT) const {
+ for (int i = 0; i < fTs.count(); ++i) {
+ if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
+ return;
+ }
+ }
+ SK_ALWAYSBREAK(0);
+}
+#endif
+
+#if DEBUG_ANGLE
+void SkOpSegment::debugCheckPointsEqualish(int tStart, int tEnd) const {
+ const SkPoint& basePt = fTs[tStart].fPt;
+ while (++tStart < tEnd) {
+ const SkPoint& cmpPt = fTs[tStart].fPt;
+ SK_ALWAYSBREAK(SkDPoint::ApproximatelyEqual(basePt, cmpPt));
+ }
+}
+#endif
+
+#if DEBUG_SWAP_TOP
+int SkOpSegment::debugInflections(int tStart, int tEnd) const {
+ if (fVerb != SkPath::kCubic_Verb) {
+ return false;
+ }
+ SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
double inflections[2];
return dst.findInflections(inflections);
}
#endif
-SkOpAngle* SkOpSegment::debugLastAngle() {
- SkOpAngle* result = NULL;
- SkOpSpan* span = this->head();
- do {
- if (span->toAngle()) {
+const SkOpAngle* SkOpSegment::debugLastAngle() const {
+ const SkOpAngle* result = NULL;
+ for (int index = 0; index < count(); ++index) {
+ const SkOpSpan& span = this->span(index);
+ if (span.fToAngle) {
SkASSERT(!result);
- result = span->toAngle();
+ result = span.fToAngle;
}
- } while ((span = span->next()->upCastable()));
+ }
SkASSERT(result);
return result;
}
void SkOpSegment::debugReset() {
- this->init(this->fPts, this->contour(), this->verb());
+ fTs.reset();
+ fAngles.reset();
+}
+
+#if DEBUG_CONCIDENT
+void SkOpSegment::debugShowTs(const char* prefix) const {
+ SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID);
+ int lastWind = -1;
+ int lastOpp = -1;
+ double lastT = -1;
+ int i;
+ for (i = 0; i < fTs.count(); ++i) {
+ bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
+ || lastOpp != fTs[i].fOppValue;
+ if (change && lastWind >= 0) {
+ SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
+ lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
+ }
+ if (change) {
+ SkDebugf(" [o=%d", fTs[i].fOther->fID);
+ lastWind = fTs[i].fWindValue;
+ lastOpp = fTs[i].fOppValue;
+ lastT = fTs[i].fT;
+ } else {
+ SkDebugf(",%d", fTs[i].fOther->fID);
+ }
+ }
+ if (i <= 0) {
+ return;
+ }
+ SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
+ lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
+ if (fOperand) {
+ SkDebugf(" operand");
+ }
+ if (done()) {
+ SkDebugf(" done");
+ }
+ SkDebugf("\n");
}
+#endif
-#if DEBUG_ACTIVE_SPANS
+#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
void SkOpSegment::debugShowActiveSpans() const {
debugValidate();
if (done()) {
return;
}
+#if DEBUG_ACTIVE_SPANS_SHORT_FORM
int lastId = -1;
double lastT = -1;
- const SkOpSpan* span = &fHead;
- do {
- if (span->done()) {
+#endif
+ for (int i = 0; i < fTs.count(); ++i) {
+ if (fTs[i].fDone) {
continue;
}
- if (lastId == fID && lastT == span->t()) {
+ SK_ALWAYSBREAK(i < fTs.count() - 1);
+#if DEBUG_ACTIVE_SPANS_SHORT_FORM
+ if (lastId == fID && lastT == fTs[i].fT) {
continue;
}
lastId = fID;
- lastT = span->t();
+ lastT = fTs[i].fT;
+#endif
SkDebugf("%s id=%d", __FUNCTION__, fID);
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
- 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(" windSum=");
- if (span->windSum() == SK_MinS32) {
+ const SkOpSpan* span = &fTs[i];
+ SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span));
+ int iEnd = i + 1;
+ while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) {
+ ++iEnd;
+ }
+ SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT);
+ const SkOpSegment* other = fTs[i].fOther;
+ SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
+ other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
+ if (fTs[i].fWindSum == SK_MinS32) {
SkDebugf("?");
} else {
- SkDebugf("%d", span->windSum());
+ SkDebugf("%d", fTs[i].fWindSum);
}
- SkDebugf(" windValue=%d oppValue=%d", span->windValue(), span->oppValue());
- SkDebugf("\n");
- } while ((span = span->next()->upCastable()));
+ SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
+ }
}
#endif
-#if DEBUG_MARK_DONE
-void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
- const SkPoint& pt = span->ptT()->fPt;
+#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
+void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) {
+ const SkPoint& pt = xyAtT(&span);
SkDebugf("%s id=%d", fun, fID);
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
- SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
- span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
- if (winding == SK_MinS32) {
+ SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
+ fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
+ SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d windSum=",
+ span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
+ (&span)[1].fT, winding);
+ if (span.fWindSum == SK_MinS32) {
SkDebugf("?");
} else {
- SkDebugf("%d", winding);
+ SkDebugf("%d", span.fWindSum);
}
- SkDebugf(" windSum=");
- if (span->windSum() == SK_MinS32) {
- SkDebugf("?");
- } else {
- SkDebugf("%d", span->windSum());
- }
- SkDebugf(" windValue=%d\n", span->windValue());
+ SkDebugf(" windValue=%d\n", span.fWindValue);
}
-void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
+void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding,
int oppWinding) {
- const SkPoint& pt = span->ptT()->fPt;
+ const SkPoint& pt = xyAtT(&span);
SkDebugf("%s id=%d", fun, fID);
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
- SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
- span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
- if (winding == SK_MinS32) {
- SkDebugf("?");
- } else {
- SkDebugf("%d", winding);
- }
- SkDebugf(" newOppSum=");
- if (oppWinding == SK_MinS32) {
- SkDebugf("?");
- } else {
- SkDebugf("%d", oppWinding);
- }
- SkDebugf(" oppSum=");
- if (span->oppSum() == SK_MinS32) {
+ SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
+ fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
+ SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=",
+ span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
+ (&span)[1].fT, winding, oppWinding);
+ if (span.fOppSum == SK_MinS32) {
SkDebugf("?");
} else {
- SkDebugf("%d", span->oppSum());
+ SkDebugf("%d", span.fOppSum);
}
SkDebugf(" windSum=");
- if (span->windSum() == SK_MinS32) {
+ if (span.fWindSum == SK_MinS32) {
SkDebugf("?");
} else {
- SkDebugf("%d", span->windSum());
+ SkDebugf("%d", span.fWindSum);
}
- SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
+ SkDebugf(" windValue=%d oppValue=%d\n", span.fWindValue, span.fOppValue);
}
-
#endif
-#if DEBUG_ANGLE
-SkString SkOpAngle::debugPart() const {
- SkString result;
- switch (this->segment()->verb()) {
- case SkPath::kLine_Verb:
- result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart),
- this->segment()->debugID());
- break;
- case SkPath::kQuad_Verb:
- result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart),
- this->segment()->debugID());
- break;
- case SkPath::kCubic_Verb:
- result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fCurvePart),
- this->segment()->debugID());
- break;
- default:
- SkASSERT(0);
- }
- return result;
-}
-#endif
-
-#if DEBUG_SORT
-void SkOpAngle::debugLoop() const {
- const SkOpAngle* first = this;
- const SkOpAngle* next = this;
- do {
- next->dumpOne(true);
- SkDebugf("\n");
- next = next->fNext;
- } while (next && next != first);
- next = first;
- do {
- next->debugValidate();
- next = next->fNext;
- } while (next && next != first);
-}
-#endif
-
-void SkOpAngle::debugValidate() const {
-#if DEBUG_VALIDATE
- const SkOpAngle* first = this;
- const SkOpAngle* next = this;
- int wind = 0;
- int opp = 0;
- int lastXor = -1;
- int lastOppXor = -1;
- do {
- if (next->unorderable()) {
- return;
- }
- const SkOpSpan* minSpan = next->start()->starter(next->end());
- if (minSpan->windValue() == SK_MinS32) {
- return;
- }
- bool op = next->segment()->operand();
- bool isXor = next->segment()->isXor();
- bool oppXor = next->segment()->oppXor();
- SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
- SkASSERT(!DEBUG_LIMIT_WIND_SUM
- || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
- bool useXor = op ? oppXor : isXor;
- SkASSERT(lastXor == -1 || lastXor == (int) useXor);
- lastXor = (int) useXor;
- wind += next->sign() * (op ? minSpan->oppValue() : minSpan->windValue());
- if (useXor) {
- wind &= 1;
- }
- useXor = op ? isXor : oppXor;
- SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
- lastOppXor = (int) useXor;
- opp += next->sign() * (op ? minSpan->windValue() : minSpan->oppValue());
- if (useXor) {
- opp &= 1;
- }
- next = next->fNext;
- } while (next && next != first);
- SkASSERT(wind == 0);
- SkASSERT(opp == 0 || !FLAGS_runFail);
-#endif
+#if DEBUG_SHOW_WINDING
+int SkOpSegment::debugShowWindingValues(int slotCount, int ofInterest) const {
+ if (!(1 << fID & ofInterest)) {
+ return 0;
+ }
+ int sum = 0;
+ SkTArray<char, true> slots(slotCount * 2);
+ memset(slots.begin(), ' ', slotCount * 2);
+ for (int i = 0; i < fTs.count(); ++i) {
+ // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
+ // continue;
+ // }
+ sum += fTs[i].fWindValue;
+ slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
+ sum += fTs[i].fOppValue;
+ slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
+ }
+ SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
+ slots.begin() + slotCount);
+ return sum;
}
-
-void SkOpAngle::debugValidateNext() const {
-#if !FORCE_RELEASE
- const SkOpAngle* first = this;
- const SkOpAngle* next = first;
- SkTDArray<const SkOpAngle*>(angles);
- do {
-// SK_ALWAYSBREAK(next->fSegment->debugContains(next));
- angles.push(next);
- next = next->next();
- if (next == first) {
- break;
- }
- SK_ALWAYSBREAK(!angles.contains(next));
- if (!next) {
- return;
- }
- } while (true);
#endif
-}
void SkOpSegment::debugValidate() const {
#if DEBUG_VALIDATE
- const SkOpSpanBase* span = &fHead;
- double lastT = -1;
- const SkOpSpanBase* prev = NULL;
- int count = 0;
+ int count = fTs.count();
+ SK_ALWAYSBREAK(count >= 2);
+ SK_ALWAYSBREAK(fTs[0].fT == 0);
+ SK_ALWAYSBREAK(fTs[count - 1].fT == 1);
int done = 0;
- do {
- if (!span->final()) {
- ++count;
- done += span->upCast()->done() ? 1 : 0;
- }
- SkASSERT(span->segment() == this);
- SkASSERT(!prev || prev->upCast()->next() == span);
- SkASSERT(!prev || prev == span->prev());
- prev = span;
- double t = span->ptT()->fT;
- SkASSERT(lastT < t);
- lastT = t;
- span->debugValidate();
- } while (!span->final() && (span = span->upCast()->next()));
- SkASSERT(count == fCount);
- SkASSERT(done == fDoneCount);
- SkASSERT(span->final());
- span->debugValidate();
-#endif
-}
-
-bool SkOpSpanBase::debugCoinEndLoopCheck() const {
- int loop = 0;
- const SkOpSpanBase* next = this;
- SkOpSpanBase* nextCoin;
- do {
- nextCoin = next->fCoinEnd;
- SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
- for (int check = 1; check < loop - 1; ++check) {
- const SkOpSpanBase* checkCoin = this->fCoinEnd;
- const SkOpSpanBase* innerCoin = checkCoin;
- for (int inner = check + 1; inner < loop; ++inner) {
- innerCoin = innerCoin->fCoinEnd;
- if (checkCoin == innerCoin) {
- SkDebugf("*** bad coincident end loop ***\n");
- return false;
- }
- }
- }
- ++loop;
- } while ((next = nextCoin) && next != this);
- return true;
-}
-
-void SkOpSpanBase::debugValidate() const {
-#if DEBUG_VALIDATE
- const SkOpPtT* ptT = &fPtT;
- SkASSERT(ptT->span() == this);
- do {
-// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
- ptT->debugValidate();
- ptT = ptT->next();
- } while (ptT != &fPtT);
- SkASSERT(this->debugCoinEndLoopCheck());
- if (!this->final()) {
- SkASSERT(this->upCast()->debugCoinLoopCheck());
- }
- if (fFromAngle) {
- fFromAngle->debugValidate();
- }
- if (!this->final() && this->upCast()->toAngle()) {
- this->upCast()->toAngle()->debugValidate();
- }
+ double t = -1;
+ const SkOpSpan* last = NULL;
+ bool tinyTFound = false;
+ bool hasLoop = false;
+ for (int i = 0; i < count; ++i) {
+ const SkOpSpan& span = fTs[i];
+ SK_ALWAYSBREAK(t <= span.fT);
+ t = span.fT;
+ int otherIndex = span.fOtherIndex;
+ const SkOpSegment* other = span.fOther;
+ SK_ALWAYSBREAK(other != this || fVerb == SkPath::kCubic_Verb);
+ const SkOpSpan& otherSpan = other->fTs[otherIndex];
+ SK_ALWAYSBREAK(otherSpan.fPt == span.fPt);
+ SK_ALWAYSBREAK(otherSpan.fOtherT == t);
+ SK_ALWAYSBREAK(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]);
+ done += span.fDone;
+ if (last) {
+ SK_ALWAYSBREAK(last->fT != span.fT || last->fOther != span.fOther);
+ bool tsEqual = last->fT == span.fT;
+ bool tsPreciselyEqual = precisely_equal(last->fT, span.fT);
+ SK_ALWAYSBREAK(!tsEqual || tsPreciselyEqual);
+ bool pointsEqual = last->fPt == span.fPt;
+ bool pointsNearlyEqual = AlmostEqualUlps(last->fPt, span.fPt);
+#if 0 // bufferOverflow test triggers this
+ SK_ALWAYSBREAK(!tsPreciselyEqual || pointsNearlyEqual);
#endif
-}
-
-bool SkOpSpan::debugCoinLoopCheck() const {
- int loop = 0;
- const SkOpSpan* next = this;
- SkOpSpan* nextCoin;
- do {
- nextCoin = next->fCoincident;
- SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
- for (int check = 1; check < loop - 1; ++check) {
- const SkOpSpan* checkCoin = this->fCoincident;
- const SkOpSpan* innerCoin = checkCoin;
- for (int inner = check + 1; inner < loop; ++inner) {
- innerCoin = innerCoin->fCoincident;
- if (checkCoin == innerCoin) {
- SkDebugf("*** bad coincident loop ***\n");
- return false;
- }
- }
- }
- ++loop;
- } while ((next = nextCoin) && next != this);
- return true;
-}
-
-#include "SkOpContour.h"
-
-int SkOpPtT::debugLoopLimit(bool report) const {
- int loop = 0;
- const SkOpPtT* next = this;
- do {
- for (int check = 1; check < loop - 1; ++check) {
- const SkOpPtT* checkPtT = this->fNext;
- const SkOpPtT* innerPtT = checkPtT;
- for (int inner = check + 1; inner < loop; ++inner) {
- innerPtT = innerPtT->fNext;
- if (checkPtT == innerPtT) {
- if (report) {
- SkDebugf("*** bad ptT loop ***\n");
- }
- return loop;
- }
+// SK_ALWAYSBREAK(!last->fTiny || !tsPreciselyEqual || span.fTiny || tinyTFound);
+ SK_ALWAYSBREAK(last->fTiny || tsPreciselyEqual || !pointsEqual || hasLoop);
+ SK_ALWAYSBREAK(!last->fTiny || pointsEqual);
+ SK_ALWAYSBREAK(!last->fTiny || last->fDone);
+ SK_ALWAYSBREAK(!last->fSmall || pointsNearlyEqual);
+ SK_ALWAYSBREAK(!last->fSmall || last->fDone);
+// SK_ALWAYSBREAK(!last->fSmall || last->fTiny);
+// SK_ALWAYSBREAK(last->fTiny || !pointsEqual || last->fDone == span.fDone);
+ if (last->fTiny) {
+ tinyTFound |= !tsPreciselyEqual;
+ } else {
+ tinyTFound = false;
}
}
- ++loop;
- } while ((next = next->fNext) && next != this);
- return 0;
-}
-
-void SkOpPtT::debugValidate() const {
-#if DEBUG_VALIDATE
- if (contour()->globalState()->phase() == SkOpGlobalState::kIntersecting) {
- return;
+ last = &span;
+ hasLoop |= last->fLoop;
}
- SkASSERT(fNext);
- SkASSERT(fNext != this);
- SkASSERT(fNext->fNext);
- SkASSERT(debugLoopLimit(false) == 0);
+ SK_ALWAYSBREAK(done == fDoneSpans);
+// if (fAngles.count() ) {
+// fAngles.begin()->debugValidateLoop();
+// }
#endif
}
diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h
index 72a9ea528f..5770aefec5 100644
--- a/src/pathops/SkPathOpsDebug.h
+++ b/src/pathops/SkPathOpsDebug.h
@@ -39,22 +39,35 @@
#define DEBUG_ACTIVE_OP 0
#define DEBUG_ACTIVE_SPANS 0
+#define DEBUG_ACTIVE_SPANS_FIRST_ONLY 0
+#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
#define DEBUG_ADD_INTERSECTING_TS 0
-#define DEBUG_ADD_T 0
+#define DEBUG_ADD_T_PAIR 0
#define DEBUG_ANGLE 0
+#define DEBUG_AS_C_CODE 1
#define DEBUG_ASSEMBLE 0
+#define DEBUG_CHECK_ALIGN 0
+#define DEBUG_CHECK_TINY 0
+#define DEBUG_CONCIDENT 0
+#define DEBUG_CROSS 0
#define DEBUG_CUBIC_BINARY_SEARCH 0
+#define DEBUG_DUPLICATES 0
+#define DEBUG_FLAT_QUADS 0
#define DEBUG_FLOW 0
#define DEBUG_LIMIT_WIND_SUM 0
#define DEBUG_MARK_DONE 0
#define DEBUG_PATH_CONSTRUCTION 0
-#define DEBUG_PERP 0
#define DEBUG_SHOW_TEST_NAME 0
+#define DEBUG_SHOW_TEST_PROGRESS 0
+#define DEBUG_SHOW_WINDING 0
#define DEBUG_SORT 0
+#define DEBUG_SORT_COMPACT 0
+#define DEBUG_SORT_RAW 0
+#define DEBUG_SORT_SINGLE 0
#define DEBUG_SWAP_TOP 0
-#define DEBUG_T_SECT 0
-#define DEBUG_T_SECT_DUMP 0
+#define DEBUG_UNSORTABLE 0
#define DEBUG_VALIDATE 0
+#define DEBUG_WIND_BUMP 0
#define DEBUG_WINDING 0
#define DEBUG_WINDING_AT_T 0
@@ -62,56 +75,51 @@
#define DEBUG_ACTIVE_OP 1
#define DEBUG_ACTIVE_SPANS 1
+#define DEBUG_ACTIVE_SPANS_FIRST_ONLY 0
+#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
#define DEBUG_ADD_INTERSECTING_TS 1
-#define DEBUG_ADD_T 1
+#define DEBUG_ADD_T_PAIR 1
#define DEBUG_ANGLE 1
+#define DEBUG_AS_C_CODE 1
#define DEBUG_ASSEMBLE 1
+#define DEBUG_CHECK_ALIGN 1
+#define DEBUG_CHECK_TINY 1
+#define DEBUG_CONCIDENT 1
+#define DEBUG_CROSS 01
#define DEBUG_CUBIC_BINARY_SEARCH 0
+#define DEBUG_DUPLICATES 1
+#define DEBUG_FLAT_QUADS 0
#define DEBUG_FLOW 1
-#define DEBUG_LIMIT_WIND_SUM 5
+#define DEBUG_LIMIT_WIND_SUM 4
#define DEBUG_MARK_DONE 1
#define DEBUG_PATH_CONSTRUCTION 1
-#define DEBUG_PERP 0
#define DEBUG_SHOW_TEST_NAME 1
+#define DEBUG_SHOW_TEST_PROGRESS 1
+#define DEBUG_SHOW_WINDING 0
#define DEBUG_SORT 1
+#define DEBUG_SORT_COMPACT 0
+#define DEBUG_SORT_RAW 0
+#define DEBUG_SORT_SINGLE 0
#define DEBUG_SWAP_TOP 1
-#define DEBUG_T_SECT 1
-#define DEBUG_T_SECT_DUMP 02
-#define DEBUG_VALIDATE 1
+#define DEBUG_UNSORTABLE 1
+#define DEBUG_VALIDATE 0
+#define DEBUG_WIND_BUMP 0
#define DEBUG_WINDING 1
#define DEBUG_WINDING_AT_T 1
#endif
-#ifdef SK_RELEASE
- #define PATH_OPS_DEBUG_RELEASE(a, b) b
- #define PATH_OPS_DEBUG_CODE(...)
- #define PATH_OPS_DEBUG_PARAMS(...)
-#else
- #define PATH_OPS_DEBUG_RELEASE(a, b) a
- #define PATH_OPS_DEBUG_CODE(...) __VA_ARGS__
- #define PATH_OPS_DEBUG_PARAMS(...) , __VA_ARGS__
-#endif
-
-#if DEBUG_T_SECT == 0
- #define PATH_OPS_DEBUG_T_SECT_RELEASE(a, b) b
- #define PATH_OPS_DEBUG_T_SECT_PARAMS(...)
- #define PATH_OPS_DEBUG_T_SECT_CODE(...)
+#if DEBUG_AS_C_CODE
+#define CUBIC_DEBUG_STR "{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}"
+#define QUAD_DEBUG_STR "{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}"
+#define LINE_DEBUG_STR "{{%1.9g,%1.9g}, {%1.9g,%1.9g}}"
+#define PT_DEBUG_STR "{{%1.9g,%1.9g}}"
#else
- #define PATH_OPS_DEBUG_T_SECT_RELEASE(a, b) a
- #define PATH_OPS_DEBUG_T_SECT_PARAMS(...) , __VA_ARGS__
- #define PATH_OPS_DEBUG_T_SECT_CODE(...) __VA_ARGS__
+#define CUBIC_DEBUG_STR "(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
+#define QUAD_DEBUG_STR "(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
+#define LINE_DEBUG_STR "(%1.9g,%1.9g %1.9g,%1.9g)"
+#define PT_DEBUG_STR "(%1.9g,%1.9g)"
#endif
-
-#if DEBUG_T_SECT_DUMP > 1
- extern int gDumpTSectNum;
-#endif
-
-#define CUBIC_DEBUG_STR "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
-#define QUAD_DEBUG_STR "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
-#define LINE_DEBUG_STR "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
-#define PT_DEBUG_STR "{{%1.9g,%1.9g}}"
-
#define T_DEBUG_STR(t, n) #t "[" #n "]=%1.9g"
#define TX_DEBUG_STR(t) #t "[%d]=%1.9g"
#define CUBIC_DEBUG_DATA(c) c[0].fX, c[0].fY, c[1].fX, c[1].fY, c[2].fX, c[2].fY, c[3].fX, c[3].fY
@@ -127,6 +135,7 @@
#include "SkTLS.h"
#endif
+#include "SkTArray.h"
#include "SkTDArray.h"
class SkPathOpsDebug {
@@ -147,6 +156,7 @@ public:
static const char* kPathOpStr[];
#endif
+ static bool ChaseContains(const SkTDArray<struct SkOpSpan *>& , const struct SkOpSpan * );
static void MathematicaIze(char* str, size_t bufferSize);
static bool ValidWind(int winding);
static void WindingPrintf(int winding);
@@ -161,96 +171,66 @@ public:
#endif
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);
-
- static bool ChaseContains(const SkTDArray<class SkOpSpanBase*>& , const class SkOpSpanBase* );
-
- 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 struct 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* 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 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 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);
- static const class SkOpSpanBase* DebugSpanSpan(const class SkOpSpanBase*, int id);
-
- static void DumpContours(SkTDArray<class SkOpContour* >* contours);
- static void DumpContoursAll(SkTDArray<class SkOpContour* >* contours);
- static void DumpContoursAngles(const SkTDArray<class SkOpContour* >* contours);
- static void DumpContoursPt(const SkTDArray<class SkOpContour* >* contours, int id);
- static void DumpContoursPts(const SkTDArray<class SkOpContour* >* contours);
- static void DumpContoursSegment(const SkTDArray<class SkOpContour* >* contours, int id);
- static void DumpContoursSpan(const SkTDArray<class SkOpContour* >* contours, int id);
- static void DumpContoursSpans(const SkTDArray<class SkOpContour* >* contours);
+ static void DumpCoincidence(const SkTArray<class SkOpContour, true>& contours);
+ static void DumpCoincidence(const SkTArray<class SkOpContour* , true>& contours);
+ static void DumpContours(const SkTArray<class SkOpContour, true>& contours);
+ static void DumpContours(const SkTArray<class SkOpContour* , true>& contours);
+ static void DumpContourAngles(const SkTArray<class SkOpContour, true>& contours);
+ static void DumpContourAngles(const SkTArray<class SkOpContour* , true>& contours);
+ static void DumpContourPt(const SkTArray<class SkOpContour, true>& contours, int id);
+ static void DumpContourPt(const SkTArray<class SkOpContour* , true>& contours, int id);
+ static void DumpContourPts(const SkTArray<class SkOpContour, true>& contours);
+ static void DumpContourPts(const SkTArray<class SkOpContour* , true>& contours);
+ static void DumpContourSpan(const SkTArray<class SkOpContour, true>& contours, int id);
+ static void DumpContourSpan(const SkTArray<class SkOpContour* , true>& contours, int id);
+ static void DumpContourSpans(const SkTArray<class SkOpContour, true>& contours);
+ static void DumpContourSpans(const SkTArray<class SkOpContour* , true>& contours);
+ static void DumpSpans(const SkTDArray<struct SkOpSpan *>& );
+ static void DumpSpans(const SkTDArray<struct SkOpSpan *>* );
};
// shorthand for calling from debugger
-template<typename TCurve> class SkTSect;
-template<typename TCurve> class SkTSpan;
-
-struct SkDQuad;
-struct SkDCubic;
-
-const SkTSpan<SkDCubic>* DebugSpan(const SkTSect<SkDCubic>* , int id);
-const SkTSpan<SkDQuad>* DebugSpan(const SkTSect<SkDQuad>* , int id);
-const SkTSpan<SkDCubic>* DebugT(const SkTSect<SkDCubic>* , double t);
-const SkTSpan<SkDQuad>* DebugT(const SkTSect<SkDQuad>* , double t);
-
-const SkTSpan<SkDCubic>* DebugSpan(const SkTSpan<SkDCubic>* , int id);
-const SkTSpan<SkDQuad>* DebugSpan(const SkTSpan<SkDQuad>* , int id);
-const SkTSpan<SkDCubic>* DebugT(const SkTSpan<SkDCubic>* , double t);
-const SkTSpan<SkDQuad>* DebugT(const SkTSpan<SkDQuad>* , double t);
-
-void Dump(const SkTSect<SkDCubic>* );
-void Dump(const SkTSect<SkDQuad>* );
-void Dump(const SkTSpan<SkDCubic>* , const SkTSect<SkDCubic>* = NULL);
-void Dump(const SkTSpan<SkDQuad>* , const SkTSect<SkDQuad>* = NULL);
-void DumpBoth(SkTSect<SkDCubic>* sect1, SkTSect<SkDCubic>* sect2);
-void DumpBoth(SkTSect<SkDQuad>* sect1, SkTSect<SkDQuad>* sect2);
-void DumpCoin(SkTSect<SkDCubic>* sect1);
-void DumpCoin(SkTSect<SkDQuad>* sect1);
-void DumpCoinCurves(SkTSect<SkDCubic>* sect1);
-void DumpCoinCurves(SkTSect<SkDQuad>* sect1);
-void DumpCurves(const SkTSpan<SkDCubic>* );
-void DumpCurves(const SkTSpan<SkDQuad>* );
+void Dump(const SkTArray<class SkOpContour, true>& contours);
+void Dump(const SkTArray<class SkOpContour* , true>& contours);
+void Dump(const SkTArray<class SkOpContour, true>* contours);
+void Dump(const SkTArray<class SkOpContour* , true>* contours);
+
+void Dump(const SkTDArray<SkOpSpan* >& chase);
+void Dump(const SkTDArray<SkOpSpan* >* chase);
+
+void DumpAngles(const SkTArray<class SkOpContour, true>& contours);
+void DumpAngles(const SkTArray<class SkOpContour* , true>& contours);
+void DumpAngles(const SkTArray<class SkOpContour, true>* contours);
+void DumpAngles(const SkTArray<class SkOpContour* , true>* contours);
+
+void DumpCoin(const SkTArray<class SkOpContour, true>& contours);
+void DumpCoin(const SkTArray<class SkOpContour* , true>& contours);
+void DumpCoin(const SkTArray<class SkOpContour, true>* contours);
+void DumpCoin(const SkTArray<class SkOpContour* , true>* contours);
+
+void DumpPts(const SkTArray<class SkOpContour, true>& contours);
+void DumpPts(const SkTArray<class SkOpContour* , true>& contours);
+void DumpPts(const SkTArray<class SkOpContour, true>* contours);
+void DumpPts(const SkTArray<class SkOpContour* , true>* contours);
+
+void DumpPt(const SkTArray<class SkOpContour, true>& contours, int segmentID);
+void DumpPt(const SkTArray<class SkOpContour* , true>& contours, int segmentID);
+void DumpPt(const SkTArray<class SkOpContour, true>* contours, int segmentID);
+void DumpPt(const SkTArray<class SkOpContour* , true>* contours, int segmentID);
+
+void DumpSpans(const SkTArray<class SkOpContour, true>& contours);
+void DumpSpans(const SkTArray<class SkOpContour* , true>& contours);
+void DumpSpans(const SkTArray<class SkOpContour, true>* contours);
+void DumpSpans(const SkTArray<class SkOpContour* , true>* contours);
+
+void DumpSpan(const SkTArray<class SkOpContour, true>& contours, int segmentID);
+void DumpSpan(const SkTArray<class SkOpContour* , true>& contours, int segmentID);
+void DumpSpan(const SkTArray<class SkOpContour, true>* contours, int segmentID);
+void DumpSpan(const SkTArray<class SkOpContour* , true>* contours, int segmentID);
// generates tools/path_sorter.htm and path_visualizer.htm compatible data
-void DumpQ(const SkDQuad& quad1, const SkDQuad& quad2, int testNo);
-void DumpT(const SkDQuad& quad, double t);
+void DumpQ(const struct SkDQuad& quad1, const struct SkDQuad& quad2, int testNo);
-const struct SkOpAngle* DebugAngle(const SkTDArray<class SkOpContour* >* contours, int id);
-class SkOpContour* DebugContour(const SkTDArray<class SkOpContour* >* contours, int id);
-const class SkOpPtT* DebugPtT(const SkTDArray<class SkOpContour* >* contours, int id);
-const class SkOpSegment* DebugSegment(const SkTDArray<class SkOpContour* >* contours, int id);
-const class SkOpSpanBase* DebugSpan(const SkTDArray<class SkOpContour* >* contours, int id);
+void DumpT(const struct SkDQuad& quad, double t);
-void Dump(const SkTDArray<class SkOpContour* >* contours);
-void DumpAll(SkTDArray<class SkOpContour* >* contours);
-void DumpAngles(const SkTDArray<class SkOpContour* >* contours);
-void DumpCoin(const SkTDArray<class SkOpContour* >* contours);
-void DumpPt(const SkTDArray<class SkOpContour* >* contours, int segmentID);
-void DumpPts(const SkTDArray<class SkOpContour* >* contours);
-void DumpSegment(const SkTDArray<class SkOpContour* >* contours, int segmentID);
-void DumpSpan(const SkTDArray<class SkOpContour* >* contours, int spanID);
-void DumpSpans(const SkTDArray<class SkOpContour* >* contours);
#endif
diff --git a/src/pathops/SkPathOpsLine.cpp b/src/pathops/SkPathOpsLine.cpp
index 70f2e12472..e4fc97bc61 100644
--- a/src/pathops/SkPathOpsLine.cpp
+++ b/src/pathops/SkPathOpsLine.cpp
@@ -6,6 +6,14 @@
*/
#include "SkPathOpsLine.h"
+SkDLine SkDLine::subDivide(double t1, double t2) const {
+ SkDVector delta = tangent();
+ SkDLine dst = {{{
+ fPts[0].fX - t1 * delta.fX, fPts[0].fY - t1 * delta.fY}, {
+ fPts[0].fX - t2 * delta.fX, fPts[0].fY - t2 * delta.fY}}};
+ return dst;
+}
+
// may have this below somewhere else already:
// copying here because I thought it was clever
@@ -20,7 +28,6 @@
// Point with coordinates {float x, y;}
//===================================================================
-// (only used by testing)
// isLeft(): tests if a point is Left|On|Right of an infinite line.
// Input: three points P0, P1, and P2
// Return: >0 for P2 left of the line through P0 and P1
@@ -103,6 +110,19 @@ bool SkDLine::nearRay(const SkDPoint& xy) const {
return RoughlyEqualUlps(largest, largest + dist); // is the dist within ULPS tolerance?
}
+// Returns true if a ray from (0,0) to (x1,y1) is coincident with a ray (0,0) to (x2,y2)
+// OPTIMIZE: a specialty routine could speed this up -- may not be called very often though
+bool SkDLine::NearRay(double x1, double y1, double x2, double y2) {
+ double denom1 = x1 * x1 + y1 * y1;
+ double denom2 = x2 * x2 + y2 * y2;
+ SkDLine line = {{{0, 0}, {x1, y1}}};
+ SkDPoint pt = {x2, y2};
+ if (denom2 > denom1) {
+ SkTSwap(line[1], pt);
+ }
+ return line.nearRay(pt);
+}
+
double SkDLine::ExactPointH(const SkDPoint& xy, double left, double right, double y) {
if (xy.fY == y) {
if (xy.fX == left) {
diff --git a/src/pathops/SkPathOpsLine.h b/src/pathops/SkPathOpsLine.h
index bb25162860..74eb615348 100644
--- a/src/pathops/SkPathOpsLine.h
+++ b/src/pathops/SkPathOpsLine.h
@@ -20,20 +20,27 @@ struct SkDLine {
fPts[1] = pts[1];
}
+ static SkDLine SubDivide(const SkPoint a[2], double t1, double t2) {
+ SkDLine line;
+ line.set(a);
+ return line.subDivide(t1, t2);
+ }
+
double exactPoint(const SkDPoint& xy) const;
static double ExactPointH(const SkDPoint& xy, double left, double right, double y);
static double ExactPointV(const SkDPoint& xy, double top, double bottom, double x);
-
- // only used by testing
double isLeft(const SkDPoint& pt) const;
-
double nearPoint(const SkDPoint& xy, bool* unequal) const;
bool nearRay(const SkDPoint& xy) const;
static double NearPointH(const SkDPoint& xy, double left, double right, double y);
static double NearPointV(const SkDPoint& xy, double top, double bottom, double x);
+ static bool NearRay(double dx1, double dy1, double dx2, double dy2);
SkDPoint ptAtT(double t) const;
+ SkDLine subDivide(double t1, double t2) const;
void dump() const;
+private:
+ SkDVector tangent() const { return fPts[0] - fPts[1]; }
};
#endif
diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp
index 77ae2de778..f2b25c04ec 100644
--- a/src/pathops/SkPathOpsOp.cpp
+++ b/src/pathops/SkPathOpsOp.cpp
@@ -5,29 +5,27 @@
* found in the LICENSE file.
*/
#include "SkAddIntersections.h"
-#include "SkOpCoincidence.h"
#include "SkOpEdgeBuilder.h"
#include "SkPathOpsCommon.h"
#include "SkPathWriter.h"
-static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr,
- SkOpSpanBase** endPtr) {
+static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* endIndex) {
while (chase.count()) {
- SkOpSpanBase* span;
+ SkOpSpan* span;
chase.pop(&span);
- // OPTIMIZE: prev makes this compatible with old code -- but is it necessary?
- *startPtr = span->ptT()->prev()->span();
- SkOpSegment* segment = (*startPtr)->segment();
+ const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
+ SkOpSegment* segment = backPtr.fOther;
+ *tIndex = backPtr.fOtherIndex;
bool sortable = true;
bool done = true;
- *endPtr = NULL;
- if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done,
+ *endIndex = -1;
+ if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
&sortable)) {
if (last->unorderable()) {
continue;
}
- *startPtr = last->start();
- *endPtr = last->end();
+ *tIndex = last->start();
+ *endIndex = last->end();
#if TRY_ROTATE
*chase.insert(0) = span;
#else
@@ -42,7 +40,7 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase**
continue;
}
// find first angle, initialize winding to computed fWindSum
- const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr);
+ const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
if (!angle) {
continue;
}
@@ -67,25 +65,33 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase**
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
SkOpSegment* first = NULL;
- firstAngle = angle;
- while ((angle = angle->next()) != firstAngle) {
+ bool badData = false;
+ while ((angle = angle->next()) != firstAngle && !badData) {
segment = angle->segment();
- SkOpSpanBase* start = angle->start();
- SkOpSpanBase* end = angle->end();
+ int start = angle->start();
+ int end = angle->end();
int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
&maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
if (!segment->done(angle)) {
if (!first) {
first = segment;
- *startPtr = start;
- *endPtr = end;
+ *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;
@@ -98,8 +104,36 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase**
return NULL;
}
-static bool bridgeOp(SkTDArray<SkOpContour* >& contourList, const SkPathOp op,
- const int xorMask, const int xorOpMask, SkPathWriter* simple, SkChunkAlloc* allocator) {
+/*
+static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
+ bool windingIsOp, PathOp op) {
+ bool active = windingIsActive(winding, spanWinding);
+ if (!active) {
+ return false;
+ }
+ if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
+ switch (op) {
+ case kIntersect_Op:
+ case kUnion_Op:
+ return true;
+ case kDifference_Op: {
+ int absSpan = abs(spanWinding);
+ int absOpp = abs(oppSpanWinding);
+ return windingIsOp ? absSpan < absOpp : absSpan > absOpp;
+ }
+ case kXor_Op:
+ return spanWinding != oppSpanWinding;
+ default:
+ SkASSERT(0);
+ }
+ }
+ bool opActive = oppWinding != 0;
+ return gOpLookup[op][opActive][windingIsOp];
+}
+*/
+
+static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp op,
+ const int xorMask, const int xorOpMask, SkPathWriter* simple) {
bool firstContour = true;
bool unsortable = false;
bool topUnsortable = false;
@@ -107,14 +141,12 @@ static bool bridgeOp(SkTDArray<SkOpContour* >& contourList, const SkPathOp op,
SkPoint lastTopLeft;
SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
do {
- SkOpSpanBase* start;
- SkOpSpanBase* end;
+ int index, endIndex;
bool topDone;
bool onlyVertical = false;
lastTopLeft = topLeft;
- SkOpSegment* current = FindSortableTop(contourList, firstPass, SkOpAngle::kBinarySingle,
- &firstContour, &start, &end, &topLeft, &topUnsortable, &topDone, &onlyVertical,
- allocator);
+ SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kBinarySingle, &firstContour,
+ &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
if (!current) {
if ((!topUnsortable || firstPass) && !topDone) {
SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
@@ -133,65 +165,69 @@ static bool bridgeOp(SkTDArray<SkOpContour* >& contourList, const SkPathOp op,
break;
}
firstPass = !topUnsortable || lastTopLeft != topLeft;
- SkTDArray<SkOpSpanBase*> chase;
+ SkTDArray<SkOpSpan*> chase;
do {
- if (current->activeOp(start, end, xorMask, xorOpMask, op)) {
+ if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
do {
if (!unsortable && current->done()) {
break;
}
SkASSERT(unsortable || !current->done());
- SkOpSpanBase* nextStart = start;
- SkOpSpanBase* nextEnd = end;
+ int nextStart = index;
+ int nextEnd = endIndex;
SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd,
&unsortable, op, xorMask, xorOpMask);
if (!next) {
if (!unsortable && simple->hasMove()
&& current->verb() != SkPath::kLine_Verb
&& !simple->isClosed()) {
- current->addCurveTo(start, end, simple, true);
+ current->addCurveTo(index, endIndex, simple, true);
#if DEBUG_ACTIVE_SPANS
if (!simple->isClosed()) {
DebugShowActiveSpans(contourList);
}
#endif
+// SkASSERT(simple->isClosed());
}
break;
}
#if DEBUG_FLOW
- SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
- current->debugID(), start->pt().fX, start->pt().fY,
- end->pt().fX, end->pt().fY);
+ SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
+ current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
+ current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
#endif
- current->addCurveTo(start, end, simple, true);
+ current->addCurveTo(index, endIndex, simple, true);
current = next;
- start = nextStart;
- end = nextEnd;
- } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
- if (current->activeWinding(start, end) && !simple->isClosed()) {
- SkOpSpan* spanStart = start->starter(end);
- if (!spanStart->done()) {
- current->addCurveTo(start, end, simple, true);
- current->markDone(spanStart);
+ index = nextStart;
+ endIndex = nextEnd;
+ } while (!simple->isClosed() && (!unsortable
+ || !current->done(SkMin32(index, endIndex))));
+ if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
+ // FIXME : add to simplify, xor cpaths
+ int min = SkMin32(index, endIndex);
+ if (!unsortable && !simple->isEmpty()) {
+ unsortable = current->checkSmall(min);
+ }
+ if (!current->done(min)) {
+ current->addCurveTo(index, endIndex, simple, true);
+ current->markDoneBinary(min);
}
}
simple->close();
} else {
- SkOpSpanBase* last = current->markAndChaseDone(start, end);
- if (last && !last->chased()) {
- last->setChased(true);
+ SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
+ if (last && !last->fChased && !last->fLoop) {
+ last->fChased = true;
SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
*chase.append() = last;
#if DEBUG_WINDING
- SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
- if (!last->final()) {
- SkDebugf(" windSum=%d", last->upCast()->windSum());
- }
- SkDebugf("\n");
+ SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
+ last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
+ last->fSmall);
#endif
}
}
- current = findChaseOp(chase, &start, &end);
+ current = findChaseOp(chase, &index, &endIndex);
#if DEBUG_ACTIVE_SPANS
DebugShowActiveSpans(contourList);
#endif
@@ -255,19 +291,16 @@ 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
bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
- SkOpContour contour;
- SkOpCoincidence coincidence;
- SkOpGlobalState globalState(&coincidence PATH_OPS_DEBUG_PARAMS(&contour));
#if DEBUGGING_PATHOPS_FROM_HOST
dump_op(one, two, op);
-#endif
-#if 0 && DEBUG_SHOW_TEST_NAME
+#endif
+#if DEBUG_SHOW_TEST_NAME
char* debugName = DEBUG_FILENAME_STRING;
if (debugName && debugName[0]) {
SkPathOpsDebug::BumpTestName(debugName);
@@ -288,54 +321,53 @@ bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
// turn path into list of segments
- SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tune
- SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState);
+ SkTArray<SkOpContour> contours;
+ // FIXME: add self-intersecting cubics' T values to segment
+ SkOpEdgeBuilder builder(*minuend, contours);
if (builder.unparseable()) {
return false;
}
const int xorMask = builder.xorMask();
builder.addOperand(*subtrahend);
- if (!builder.finish(&allocator)) {
+ if (!builder.finish()) {
return false;
}
-#if !FORCE_RELEASE
- contour.dumpSegments(op);
-#endif
-
result->reset();
result->setFillType(fillType);
const int xorOpMask = builder.xorMask();
- SkTDArray<SkOpContour* > contourList;
- MakeContourList(&contour, contourList, xorMask == kEvenOdd_PathOpsMask,
+ SkTArray<SkOpContour*, true> contourList;
+ MakeContourList(contours, contourList, xorMask == kEvenOdd_PathOpsMask,
xorOpMask == kEvenOdd_PathOpsMask);
SkOpContour** currentPtr = contourList.begin();
if (!currentPtr) {
return true;
}
- if ((*currentPtr)->count() == 0) {
- SkASSERT((*currentPtr)->next() == NULL);
- return true;
- }
SkOpContour** listEnd = contourList.end();
// find all intersections between segments
do {
SkOpContour** nextPtr = currentPtr;
SkOpContour* current = *currentPtr++;
+ if (current->containsCubics()) {
+ AddSelfIntersectTs(current);
+ }
SkOpContour* next;
do {
next = *nextPtr++;
- } while (AddIntersectTs(current, next, &coincidence, &allocator) && nextPtr != listEnd);
+ } while (AddIntersectTs(current, next) && nextPtr != listEnd);
} while (currentPtr != listEnd);
-#if DEBUG_VALIDATE
- globalState.setPhase(SkOpGlobalState::kWalking);
-#endif
// eat through coincident edges
- if (!HandleCoincidence(&contourList, &coincidence, &allocator, &globalState)) {
+
+ int total = 0;
+ int index;
+ for (index = 0; index < contourList.count(); ++index) {
+ total += contourList[index]->segments().count();
+ }
+ if (!HandleCoincidence(&contourList, total)) {
return false;
}
// construct closed contours
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);
diff --git a/src/pathops/SkPathOpsPoint.h b/src/pathops/SkPathOpsPoint.h
index 2d07427783..7ddfbfb5d1 100644
--- a/src/pathops/SkPathOpsPoint.h
+++ b/src/pathops/SkPathOpsPoint.h
@@ -25,25 +25,21 @@ struct SkDVector {
friend SkDPoint operator+(const SkDPoint& a, const SkDVector& b);
- // only used by testing
void operator+=(const SkDVector& v) {
fX += v.fX;
fY += v.fY;
}
- // only called by nearestT, which is currently only used by testing
void operator-=(const SkDVector& v) {
fX -= v.fX;
fY -= v.fY;
}
- // only used by testing
void operator/=(const double s) {
fX /= s;
fY /= s;
}
- // only used by testing
void operator*=(const double s) {
fX *= s;
fY *= s;
@@ -54,7 +50,6 @@ struct SkDVector {
return v;
}
- // only used by testing
double cross(const SkDVector& a) const {
return fX * a.fY - fY * a.fX;
}
@@ -103,13 +98,11 @@ struct SkDPoint {
fY = pt.fY;
}
- // only used by testing
void operator+=(const SkDVector& v) {
fX += v.fX;
fY += v.fY;
}
- // only used by testing
void operator-=(const SkDVector& v) {
fX -= v.fX;
fY -= v.fY;
@@ -129,7 +122,7 @@ struct SkDPoint {
double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
largest = SkTMax(largest, -tiniest);
- return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
+ return AlmostBequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
}
bool approximatelyEqual(const SkPoint& a) const {
@@ -152,10 +145,44 @@ 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 AlmostBequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
+ }
+
+ static bool RoughlyEqual(const SkPoint& a, const SkPoint& b) {
+ if (approximately_equal(a.fX, b.fX) && approximately_equal(a.fY, b.fY)) {
+ return true;
+ }
+ return RoughlyEqualUlps(a.fX, b.fX) && RoughlyEqualUlps(a.fY, b.fY);
+ }
+
+ bool approximatelyPEqual(const SkDPoint& a) const {
+ if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
+ return true;
+ }
+ if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
+ return false;
+ }
+ double dist = distance(a); // OPTIMIZATION: can we compare against distSq instead ?
+ double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
+ double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
+ largest = SkTMax(largest, -tiniest);
+ return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
+ }
+
+ bool approximatelyDEqual(const SkDPoint& a) const {
+ if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
+ return true;
+ }
+ if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
+ return false;
+ }
+ double dist = distance(a); // OPTIMIZATION: can we compare against distSq instead ?
+ double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
+ double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
+ largest = SkTMax(largest, -tiniest);
+ return AlmostDequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
}
- // only used by testing
bool approximatelyZero() const {
return approximately_zero(fX) && approximately_zero(fY);
}
@@ -182,7 +209,7 @@ struct SkDPoint {
return result;
}
- bool roughlyEqual(const SkDPoint& a) const {
+ bool moreRoughlyEqual(const SkDPoint& a) const {
if (roughly_equal(fX, a.fX) && roughly_equal(fY, a.fY)) {
return true;
}
@@ -193,6 +220,10 @@ struct SkDPoint {
return RoughlyEqualUlps(largest, largest + dist); // is the dist within ULPS tolerance?
}
+ bool roughlyEqual(const SkDPoint& a) const {
+ return roughly_equal(a.fY, fY) && roughly_equal(a.fX, fX);
+ }
+
// utilities callable by the user from the debugger when the implementation code is linked in
void dump() const;
static void Dump(const SkPoint& pt);
diff --git a/src/pathops/SkPathOpsPostSect.cpp b/src/pathops/SkPathOpsPostSect.cpp
index eb2d1ab3ab..15a1900ce3 100755..100644
--- a/src/pathops/SkPathOpsPostSect.cpp
+++ b/src/pathops/SkPathOpsPostSect.cpp
@@ -17,8 +17,8 @@ SkOpContour* SkOpPtT::contour() const {
return segment()->contour();
}
-SkOpGlobalState* SkOpPtT::globalState() const {
- return PATH_OPS_DEBUG_RELEASE(contour()->globalState(), NULL);
+SkOpDebugState* SkOpPtT::debugState() const {
+ return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL);
}
void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
@@ -28,7 +28,7 @@ void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplica
fNext = this;
fDuplicatePt = duplicate;
fDeleted = false;
- PATH_OPS_DEBUG_CODE(fID = ++span->globalState()->fPtTID);
+ PATH_OPS_DEBUG_CODE(fID = ++span->debugState()->fPtTID);
}
bool SkOpPtT::onEnd() const {
@@ -45,7 +45,7 @@ SkOpPtT* SkOpPtT::remove() {
do {
SkOpPtT* next = prev->fNext;
if (next == this) {
- prev->removeNext(this);
+ prev->removeNext();
fDeleted = true;
return prev;
}
@@ -55,14 +55,14 @@ SkOpPtT* SkOpPtT::remove() {
return NULL;
}
-void SkOpPtT::removeNext(SkOpPtT* kept) {
+void SkOpPtT::removeNext() {
SkASSERT(this->fNext);
SkOpPtT* next = this->fNext;
this->fNext = next->fNext;
SkOpSpanBase* span = next->span();
next->setDeleted();
if (span->ptT() == next) {
- span->upCast()->detach(kept);
+ span->upCast()->detach();
}
}
@@ -199,7 +199,7 @@ void SkOpSpanBase::alignInner() {
// omit aliases that alignment makes redundant
if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
SkASSERT(test->alias());
- prev->removeNext(ptT);
+ prev->removeNext();
test = prev;
} else {
SkASSERT(ptT->alias());
@@ -239,8 +239,8 @@ SkOpContour* SkOpSpanBase::contour() const {
return segment()->contour();
}
-SkOpGlobalState* SkOpSpanBase::globalState() const {
- return PATH_OPS_DEBUG_RELEASE(contour()->globalState(), NULL);
+SkOpDebugState* SkOpSpanBase::debugState() const {
+ return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL);
}
void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
@@ -252,7 +252,7 @@ void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, cons
fAligned = true;
fChased = false;
PATH_OPS_DEBUG_CODE(fCount = 1);
- PATH_OPS_DEBUG_CODE(fID = ++globalState()->fSpanID);
+ PATH_OPS_DEBUG_CODE(fID = ++debugState()->fSpanID);
}
// this pair of spans share a common t value or point; merge them and eliminate duplicates
@@ -261,7 +261,7 @@ void SkOpSpanBase::merge(SkOpSpan* span) {
SkOpPtT* spanPtT = span->ptT();
SkASSERT(this->t() != spanPtT->fT);
SkASSERT(!zero_or_one(spanPtT->fT));
- span->detach(this->ptT());
+ span->detach();
SkOpPtT* remainder = spanPtT->next();
ptT()->insert(spanPtT);
while (remainder != spanPtT) {
@@ -304,7 +304,7 @@ bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
return false;
}
-void SkOpSpan::detach(SkOpPtT* kept) {
+void SkOpSpan::detach() {
SkASSERT(!final());
SkOpSpan* prev = this->prev();
SkASSERT(prev);
@@ -313,9 +313,6 @@ void SkOpSpan::detach(SkOpPtT* kept) {
prev->setNext(next);
next->setPrev(prev);
this->segment()->detach(this);
- if (this->coincident()) {
- this->globalState()->fCoincidence->fixUp(this->ptT(), kept);
- }
this->ptT()->setDeleted();
}
diff --git a/src/pathops/SkPathOpsQuad.cpp b/src/pathops/SkPathOpsQuad.cpp
index 4913c9f9f3..c1d068af34 100644
--- a/src/pathops/SkPathOpsQuad.cpp
+++ b/src/pathops/SkPathOpsQuad.cpp
@@ -8,61 +8,7 @@
#include "SkLineParameters.h"
#include "SkPathOpsCubic.h"
#include "SkPathOpsQuad.h"
-
-/* started with at_most_end_pts_in_common from SkDQuadIntersection.cpp */
-// Do a quick reject by rotating all points relative to a line formed by
-// a pair of one quad's points. If the 2nd quad's points
-// are on the line or on the opposite side from the 1st quad's 'odd man', the
-// curves at most intersect at the endpoints.
-/* if returning true, check contains true if quad's hull collapsed, making the cubic linear
- if returning false, check contains true if the the quad pair have only the end point in common
-*/
-bool SkDQuad::hullIntersects(const SkDQuad& q2, bool* isLinear) const {
- bool linear = true;
- for (int oddMan = 0; oddMan < kPointCount; ++oddMan) {
- const SkDPoint* endPt[2];
- this->otherPts(oddMan, endPt);
- double origX = endPt[0]->fX;
- double origY = endPt[0]->fY;
- double adj = endPt[1]->fX - origX;
- double opp = endPt[1]->fY - origY;
- double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
- if (approximately_zero(sign)) {
- continue;
- }
- linear = false;
- bool foundOutlier = false;
- for (int n = 0; n < kPointCount; ++n) {
- double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
- if (test * sign > 0 && !precisely_zero(test)) {
- foundOutlier = true;
- break;
- }
- }
- if (!foundOutlier) {
- return false;
- }
- }
- *isLinear = linear;
- return true;
-}
-
-/* bit twiddling for finding the off curve index (x&~m is the pair in [0,1,2] excluding oddMan)
-oddMan opp x=oddMan^opp x=x-oddMan m=x>>2 x&~m
- 0 1 1 1 0 1
- 2 2 2 0 2
- 1 1 0 -1 -1 0
- 2 3 2 0 2
- 2 1 3 1 0 1
- 2 0 -2 -1 0
-*/
-void SkDQuad::otherPts(int oddMan, const SkDPoint* endPt[2]) const {
- for (int opp = 1; opp < kPointCount; ++opp) {
- int end = (oddMan ^ opp) - oddMan; // choose a value not equal to oddMan
- end &= ~(end >> 2); // if the value went negative, set it to zero
- endPt[opp - 1] = &fPts[end];
- }
-}
+#include "SkPathOpsTriangle.h"
// from http://blog.gludion.com/2009/08/distance-to-quadratic-bezier-curve.html
// (currently only used by testing)
@@ -97,6 +43,10 @@ double SkDQuad::nearestT(const SkDPoint& pt) const {
return d0 < d2 ? 0 : 1;
}
+bool SkDQuad::pointInHull(const SkDPoint& pt) const {
+ return ((const SkDTriangle&) fPts).contains(pt);
+}
+
SkDPoint SkDQuad::top(double startT, double endT) const {
SkDQuad sub = subDivide(startT, endT);
SkDPoint topPt = sub[0];
@@ -190,12 +140,7 @@ bool SkDQuad::isLinear(int startIndex, int endIndex) const {
// FIXME: maybe it's possible to avoid this and compare non-normalized
lineParameters.normalize();
double distance = lineParameters.controlPtDistance(*this);
- double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
- fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
- double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
- fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
- largest = SkTMax(largest, -tiniest);
- return approximately_zero_when_compared_to(distance, largest);
+ return approximately_zero(distance);
}
SkDCubic SkDQuad::toCubic() const {
@@ -295,6 +240,13 @@ void SkDQuad::align(int endIndex, SkDPoint* dstPt) const {
SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const {
SkASSERT(t1 != t2);
SkDPoint b;
+#if 0
+ // this approach assumes that the control point computed directly is accurate enough
+ double dx = interp_quad_coords(&fPts[0].fX, (t1 + t2) / 2);
+ double dy = interp_quad_coords(&fPts[0].fY, (t1 + t2) / 2);
+ b.fX = 2 * dx - (a.fX + c.fX) / 2;
+ b.fY = 2 * dy - (a.fY + c.fY) / 2;
+#else
SkDQuad sub = subDivide(t1, t2);
SkDLine b0 = {{a, sub[1] + (a - sub[0])}};
SkDLine b1 = {{c, sub[1] + (c - sub[2])}};
@@ -306,6 +258,7 @@ SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, dou
SkASSERT(i.used() <= 2);
b = SkDPoint::Mid(b0[1], b1[1]);
}
+#endif
if (t1 == 0 || t2 == 0) {
align(0, &b);
}
diff --git a/src/pathops/SkPathOpsQuad.h b/src/pathops/SkPathOpsQuad.h
index 81638cf0bc..932c5fbe75 100644
--- a/src/pathops/SkPathOpsQuad.h
+++ b/src/pathops/SkPathOpsQuad.h
@@ -17,61 +17,43 @@ struct SkDQuadPair {
};
struct SkDQuad {
- static const int kPointCount = 3;
- static const int kPointLast = kPointCount - 1;
- static const int kMaxIntersections = 4;
-
- SkDPoint fPts[kPointCount];
-
- bool collapsed() const {
- return fPts[0].approximatelyEqual(fPts[1]) && fPts[0].approximatelyEqual(fPts[2]);
- }
-
- bool controlsInside() const {
- SkDVector v01 = fPts[0] - fPts[1];
- SkDVector v02 = fPts[0] - fPts[2];
- SkDVector v12 = fPts[1] - fPts[2];
- return v02.dot(v01) > 0 && v02.dot(v12) > 0;
- }
+ SkDPoint fPts[3];
SkDQuad flip() const {
SkDQuad result = {{fPts[2], fPts[1], fPts[0]}};
return result;
}
- static bool IsCubic() { return false; }
-
- void set(const SkPoint pts[kPointCount]) {
+ void set(const SkPoint pts[3]) {
fPts[0] = pts[0];
fPts[1] = pts[1];
fPts[2] = pts[2];
}
- const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
- SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
+ const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < 3); return fPts[n]; }
+ SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < 3); return fPts[n]; }
static int AddValidTs(double s[], int realRoots, double* t);
void align(int endIndex, SkDPoint* dstPt) const;
SkDQuadPair chopAt(double t) const;
SkDVector dxdyAtT(double t) const;
static int FindExtrema(double a, double b, double c, double tValue[1]);
- bool hullIntersects(const SkDQuad& , bool* isLinear) const;
bool isLinear(int startIndex, int endIndex) const;
bool monotonicInY() const;
double nearestT(const SkDPoint&) const;
- void otherPts(int oddMan, const SkDPoint* endPt[2]) const;
+ bool pointInHull(const SkDPoint&) const;
SkDPoint ptAtT(double t) const;
static int RootsReal(double A, double B, double C, double t[2]);
static int RootsValidT(const double A, const double B, const double C, double s[2]);
static void SetABC(const double* quad, double* a, double* b, double* c);
SkDQuad subDivide(double t1, double t2) const;
- static SkDQuad SubDivide(const SkPoint a[kPointCount], double t1, double t2) {
+ static SkDQuad SubDivide(const SkPoint a[3], double t1, double t2) {
SkDQuad quad;
quad.set(a);
return quad.subDivide(t1, t2);
}
SkDPoint subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const;
- static SkDPoint SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& c,
+ static SkDPoint SubDivide(const SkPoint pts[3], const SkDPoint& a, const SkDPoint& c,
double t1, double t2) {
SkDQuad quad;
quad.set(pts);
@@ -82,8 +64,7 @@ struct SkDQuad {
// utilities callable by the user from the debugger when the implementation code is linked in
void dump() const;
- void dumpID(int id) const;
- void dumpInner() const;
+ void dumpComma(const char*) const;
private:
// static double Tangent(const double* quadratic, double t); // uncalled
diff --git a/src/pathops/SkPathOpsQuadSect.h b/src/pathops/SkPathOpsQuadSect.h
new file mode 100644
index 0000000000..57f1aa08d8
--- /dev/null
+++ b/src/pathops/SkPathOpsQuadSect.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkQuadSpan_DEFINE
+#define SkQuadSpan_DEFINE
+
+#include "SkChunkAlloc.h"
+#include "SkPathOpsRect.h"
+#include "SkPathOpsQuad.h"
+#include "SkTArray.h"
+
+class SkIntersections;
+
+class SkQuadCoincident {
+public:
+ bool isCoincident() const {
+ return fCoincident;
+ }
+
+ void init() {
+ fCoincident = false;
+ SkDEBUGCODE(fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN);
+ SkDEBUGCODE(fPerpT = SK_ScalarNaN);
+ }
+
+ void markCoincident() {
+ if (!fCoincident) {
+ fPerpT = -1;
+ }
+ fCoincident = true;
+ }
+
+ const SkDPoint& perpPt() const {
+ return fPerpPt;
+ }
+
+ double perpT() const {
+ return fPerpT;
+ }
+
+ void setPerp(const SkDQuad& quad1, double t, const SkDPoint& qPt, const SkDQuad& quad2);
+
+private:
+ SkDPoint fPerpPt;
+ double fPerpT; // perpendicular intersection on opposite quad
+ bool fCoincident;
+};
+
+class SkQuadSect; // used only by debug id
+
+class SkQuadSpan {
+public:
+ void init(const SkDQuad& quad);
+ void initBounds(const SkDQuad& quad);
+
+ bool contains(double t) const {
+ return !! const_cast<SkQuadSpan*>(this)->innerFind(t);
+ }
+
+ bool contains(const SkQuadSpan* span) const;
+
+ SkQuadSpan* find(double t) {
+ SkQuadSpan* result = innerFind(t);
+ SkASSERT(result);
+ return result;
+ }
+
+ bool intersects(const SkQuadSpan* span) const;
+
+ const SkQuadSpan* next() const {
+ return fNext;
+ }
+
+ void reset() {
+ fBounded.reset();
+ }
+
+ bool split(SkQuadSpan* work) {
+ return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
+ }
+
+ bool splitAt(SkQuadSpan* work, double t);
+ bool tightBoundsIntersects(const SkQuadSpan* span) const;
+
+ // implementation is for testing only
+ void dump() const;
+
+private:
+ bool hullIntersects(const SkDQuad& q2) const;
+ SkQuadSpan* innerFind(double t);
+ bool linearIntersects(const SkDQuad& q2) const;
+
+ // implementation is for testing only
+#if DEBUG_BINARY_QUAD
+ int debugID(const SkQuadSect* ) const { return fDebugID; }
+#else
+ int debugID(const SkQuadSect* ) const;
+#endif
+ void dump(const SkQuadSect* ) const;
+ void dumpID(const SkQuadSect* ) const;
+
+#if DEBUG_BINARY_QUAD
+ void validate() const;
+#endif
+
+ SkDQuad fPart;
+ SkQuadCoincident fCoinStart;
+ SkQuadCoincident fCoinEnd;
+ SkSTArray<4, SkQuadSpan*, true> fBounded;
+ SkQuadSpan* fPrev;
+ SkQuadSpan* fNext;
+ SkDRect fBounds;
+ double fStartT;
+ double fEndT;
+ double fBoundsMax;
+ bool fCollapsed;
+ bool fHasPerp;
+ mutable bool fIsLinear;
+#if DEBUG_BINARY_QUAD
+ int fDebugID;
+ bool fDebugDeleted;
+#endif
+ friend class SkQuadSect;
+};
+
+class SkQuadSect {
+public:
+ SkQuadSect(const SkDQuad& quad PATH_OPS_DEBUG_PARAMS(int id));
+ static void BinarySearch(SkQuadSect* sect1, SkQuadSect* sect2, SkIntersections* intersections);
+
+ // for testing only
+ void dumpQuads() const;
+private:
+ SkQuadSpan* addOne();
+ bool binarySearchCoin(const SkQuadSect& , double tStart, double tStep, double* t, double* oppT);
+ SkQuadSpan* boundsMax() const;
+ void coincidentCheck(SkQuadSect* sect2);
+ bool intersects(const SkQuadSpan* span, const SkQuadSect* opp, const SkQuadSpan* oppSpan) const;
+ void onCurveCheck(SkQuadSect* sect2, SkQuadSpan* first, SkQuadSpan* last);
+ void recoverCollapsed();
+ void removeSpan(SkQuadSpan* span);
+ void removeOne(const SkQuadSpan* test, SkQuadSpan* span);
+ void removeSpans(SkQuadSpan* span, SkQuadSect* opp);
+ void setPerp(const SkDQuad& opp, SkQuadSpan* first, SkQuadSpan* last);
+ const SkQuadSpan* tail() const;
+ void trim(SkQuadSpan* span, SkQuadSect* opp);
+
+ // for testing only
+ void dump() const;
+ void dumpBoth(const SkQuadSect& opp) const;
+ void dumpBoth(const SkQuadSect* opp) const;
+
+#if DEBUG_BINARY_QUAD
+ int debugID() const { return fDebugID; }
+ void validate() const;
+#else
+ int debugID() const { return 0; }
+#endif
+ const SkDQuad& fQuad;
+ SkChunkAlloc fHeap;
+ SkQuadSpan* fHead;
+ SkQuadSpan* fDeleted;
+ int fActiveCount;
+#if DEBUG_BINARY_QUAD
+ int fDebugID;
+ int fDebugCount;
+ int fDebugAllocatedCount;
+#endif
+ friend class SkQuadSpan; // only used by debug id
+};
+
+#endif
diff --git a/src/pathops/SkPathOpsRect.cpp b/src/pathops/SkPathOpsRect.cpp
index 5dd3d8def5..2ceed32900 100644
--- a/src/pathops/SkPathOpsRect.cpp
+++ b/src/pathops/SkPathOpsRect.cpp
@@ -9,6 +9,11 @@
#include "SkPathOpsQuad.h"
#include "SkPathOpsRect.h"
+void SkDRect::setBounds(const SkDLine& line) {
+ set(line[0]);
+ add(line[1]);
+}
+
void SkDRect::setBounds(const SkDQuad& quad) {
set(quad[0]);
add(quad[2]);
@@ -25,6 +30,13 @@ void SkDRect::setBounds(const SkDQuad& quad) {
}
}
+void SkDRect::setRawBounds(const SkDQuad& quad) {
+ set(quad[0]);
+ for (int x = 1; x < 3; ++x) {
+ add(quad[x]);
+ }
+}
+
static bool is_bounded_by_end_points(double a, double b, double c, double d) {
return between(a, b, d) && between(a, c, d);
}
@@ -44,3 +56,10 @@ void SkDRect::setBounds(const SkDCubic& c) {
add(c.ptAtT(tValues[x]));
}
}
+
+void SkDRect::setRawBounds(const SkDCubic& cubic) {
+ set(cubic[0]);
+ for (int x = 1; x < 4; ++x) {
+ add(cubic[x]);
+ }
+}
diff --git a/src/pathops/SkPathOpsRect.h b/src/pathops/SkPathOpsRect.h
index 2b37a5f098..2c47f43b88 100644
--- a/src/pathops/SkPathOpsRect.h
+++ b/src/pathops/SkPathOpsRect.h
@@ -13,10 +13,18 @@ struct SkDRect {
double fLeft, fTop, fRight, fBottom;
void add(const SkDPoint& pt) {
- fLeft = SkTMin(fLeft, pt.fX);
- fTop = SkTMin(fTop, pt.fY);
- fRight = SkTMax(fRight, pt.fX);
- fBottom = SkTMax(fBottom, pt.fY);
+ if (fLeft > pt.fX) {
+ fLeft = pt.fX;
+ }
+ if (fTop > pt.fY) {
+ fTop = pt.fY;
+ }
+ if (fRight < pt.fX) {
+ fRight = pt.fX;
+ }
+ if (fBottom < pt.fY) {
+ fBottom = pt.fY;
+ }
}
bool contains(const SkDPoint& pt) const {
@@ -24,15 +32,12 @@ struct SkDRect {
&& approximately_between(fTop, pt.fY, fBottom);
}
- bool intersects(const SkDRect& r) const {
- if (fLeft > fRight) {
- SkDebugf("!");
- }
+ bool intersects(SkDRect* r) const {
SkASSERT(fLeft <= fRight);
SkASSERT(fTop <= fBottom);
- SkASSERT(r.fLeft <= r.fRight);
- SkASSERT(r.fTop <= r.fBottom);
- return r.fLeft <= fRight && fLeft <= r.fRight && r.fTop <= fBottom && fTop <= r.fBottom;
+ SkASSERT(r->fLeft <= r->fRight);
+ SkASSERT(r->fTop <= r->fBottom);
+ return r->fLeft <= fRight && fLeft <= r->fRight && r->fTop <= fBottom && fTop <= r->fBottom;
}
void set(const SkDPoint& pt) {
@@ -48,8 +53,11 @@ struct SkDRect {
return fBottom - fTop;
}
+ void setBounds(const SkDLine&);
void setBounds(const SkDCubic&);
void setBounds(const SkDQuad&);
+ void setRawBounds(const SkDCubic&);
+ void setRawBounds(const SkDQuad&);
};
#endif
diff --git a/src/pathops/SkPathOpsSimplify.cpp b/src/pathops/SkPathOpsSimplify.cpp
index 7a234ecb01..57090ac423 100644
--- a/src/pathops/SkPathOpsSimplify.cpp
+++ b/src/pathops/SkPathOpsSimplify.cpp
@@ -5,13 +5,11 @@
* found in the LICENSE file.
*/
#include "SkAddIntersections.h"
-#include "SkOpCoincidence.h"
#include "SkOpEdgeBuilder.h"
#include "SkPathOpsCommon.h"
#include "SkPathWriter.h"
-static bool bridgeWinding(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simple,
- SkChunkAlloc* allocator) {
+static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
bool firstContour = true;
bool unsortable = false;
bool topUnsortable = false;
@@ -19,24 +17,15 @@ static bool bridgeWinding(SkTDArray<SkOpContour* >& contourList, SkPathWriter* s
SkPoint lastTopLeft;
SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
do {
- SkOpSpanBase* start;
- SkOpSpanBase* end;
+ int index, endIndex;
bool topDone;
bool onlyVertical = false;
lastTopLeft = topLeft;
- SkOpSegment* current = FindSortableTop(contourList, firstPass, SkOpAngle::kUnaryWinding,
- &firstContour, &start, &end, &topLeft, &topUnsortable, &topDone, &onlyVertical,
- allocator);
+ SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour,
+ &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
if (!current) {
if ((!topUnsortable || firstPass) && !topDone) {
SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
- if (lastTopLeft.fX == SK_ScalarMin && lastTopLeft.fY == SK_ScalarMin) {
- if (firstPass) {
- firstPass = false;
- } else {
- break;
- }
- }
topLeft.fX = topLeft.fY = SK_ScalarMin;
continue;
}
@@ -45,66 +34,62 @@ static bool bridgeWinding(SkTDArray<SkOpContour* >& contourList, SkPathWriter* s
break;
}
firstPass = !topUnsortable || lastTopLeft != topLeft;
- SkTDArray<SkOpSpanBase*> chase;
+ SkTDArray<SkOpSpan*> chase;
do {
- if (current->activeWinding(start, end)) {
+ if (current->activeWinding(index, endIndex)) {
do {
if (!unsortable && current->done()) {
break;
}
SkASSERT(unsortable || !current->done());
- SkOpSpanBase* nextStart = start;
- SkOpSpanBase* nextEnd = end;
+ int nextStart = index;
+ int nextEnd = endIndex;
SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
&unsortable);
if (!next) {
if (!unsortable && simple->hasMove()
&& current->verb() != SkPath::kLine_Verb
&& !simple->isClosed()) {
- current->addCurveTo(start, end, simple, true);
- #if DEBUG_ACTIVE_SPANS
- if (!simple->isClosed()) {
- DebugShowActiveSpans(contourList);
- }
- #endif
+ current->addCurveTo(index, endIndex, simple, true);
+ SkASSERT(simple->isClosed());
}
break;
}
#if DEBUG_FLOW
SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
- current->debugID(), start->pt().fX, start->pt().fY,
- end->pt().fX, end->pt().fY);
+ current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
+ current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
#endif
- current->addCurveTo(start, end, simple, true);
+ current->addCurveTo(index, endIndex, simple, true);
current = next;
- start = nextStart;
- end = nextEnd;
- } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
- if (current->activeWinding(start, end) && !simple->isClosed()) {
- SkOpSpan* spanStart = start->starter(end);
- if (!spanStart->done()) {
- current->addCurveTo(start, end, simple, true);
- current->markDone(spanStart);
+ index = nextStart;
+ endIndex = nextEnd;
+ } while (!simple->isClosed() && (!unsortable
+ || !current->done(SkMin32(index, endIndex))));
+ if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
+// SkASSERT(unsortable || simple->isEmpty());
+ int min = SkMin32(index, endIndex);
+ if (!current->done(min)) {
+ current->addCurveTo(index, endIndex, simple, true);
+ current->markDoneUnary(min);
}
}
simple->close();
} else {
- SkOpSpanBase* last = current->markAndChaseDone(start, end);
- if (last && !last->chased()) {
- last->setChased(true);
+ SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex);
+ if (last && !last->fChased && !last->fLoop) {
+ last->fChased = true;
SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
// assert that last isn't already in array
*chase.append() = last;
#if DEBUG_WINDING
- SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
- if (!last->final()) {
- SkDebugf(" windSum=%d", last->upCast()->windSum());
- }
- SkDebugf("\n");
+ SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
+ last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
+ last->fSmall);
#endif
}
}
- current = FindChase(&chase, &start, &end);
+ current = FindChase(&chase, &index, &endIndex);
#if DEBUG_ACTIVE_SPANS
DebugShowActiveSpans(contourList);
#endif
@@ -117,11 +102,9 @@ static bool bridgeWinding(SkTDArray<SkOpContour* >& contourList, SkPathWriter* s
}
// returns true if all edges were processed
-static bool bridgeXor(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simple,
- SkChunkAlloc* allocator) {
+static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
SkOpSegment* current;
- SkOpSpanBase* start;
- SkOpSpanBase* end;
+ int start, end;
bool unsortable = false;
bool closable = true;
while ((current = FindUndone(contourList, &start, &end))) {
@@ -132,38 +115,34 @@ static bool bridgeXor(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simpl
}
#endif
SkASSERT(unsortable || !current->done());
- SkOpSpanBase* nextStart = start;
- SkOpSpanBase* nextEnd = end;
+ int nextStart = start;
+ int nextEnd = end;
SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
if (!next) {
if (!unsortable && simple->hasMove()
&& current->verb() != SkPath::kLine_Verb
&& !simple->isClosed()) {
current->addCurveTo(start, end, simple, true);
- #if DEBUG_ACTIVE_SPANS
- if (!simple->isClosed()) {
- DebugShowActiveSpans(contourList);
- }
- #endif
+ SkASSERT(simple->isClosed());
}
break;
}
#if DEBUG_FLOW
SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
- current->debugID(), start->pt().fX, start->pt().fY,
- end->pt().fX, end->pt().fY);
+ current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
+ current->xyAtT(end).fX, current->xyAtT(end).fY);
#endif
current->addCurveTo(start, end, simple, true);
current = next;
start = nextStart;
end = nextEnd;
- } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
+ } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
if (!simple->isClosed()) {
SkASSERT(unsortable);
- SkOpSpan* spanStart = start->starter(end);
- if (!spanStart->done()) {
+ int min = SkMin32(start, end);
+ if (!current->done(min)) {
current->addCurveTo(start, end, simple, true);
- current->markDone(spanStart);
+ current->markDone(min, 1);
}
closable = false;
}
@@ -177,68 +156,52 @@ static bool bridgeXor(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simpl
// FIXME : add this as a member of SkPath
bool Simplify(const SkPath& path, SkPath* result) {
+#if DEBUG_SORT || DEBUG_SWAP_TOP
+ SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
+#endif
// returns 1 for evenodd, -1 for winding, regardless of inverse-ness
SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
: SkPath::kEvenOdd_FillType;
- if (path.isConvex()) {
- if (result != &path) {
- *result = path;
- }
- result->setFillType(fillType);
- return true;
- }
+
// turn path into list of segments
- SkOpCoincidence coincidence;
- SkOpContour contour;
- SkOpGlobalState globalState(&coincidence PATH_OPS_DEBUG_PARAMS(&contour));
-#if DEBUG_SORT || DEBUG_SWAP_TOP
- SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
-#endif
- SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
- SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
- if (!builder.finish(&allocator)) {
+ SkTArray<SkOpContour> contours;
+ SkOpEdgeBuilder builder(path, contours);
+ if (!builder.finish()) {
return false;
}
-#if !FORCE_RELEASE
- contour.dumpSegments((SkPathOp) -1);
-#endif
+ SkTArray<SkOpContour*, true> contourList;
+ MakeContourList(contours, contourList, false, false);
+ SkOpContour** currentPtr = contourList.begin();
result->reset();
result->setFillType(fillType);
- SkTDArray<SkOpContour* > contourList;
- MakeContourList(&contour, contourList, false, false);
- SkOpContour** currentPtr = contourList.begin();
if (!currentPtr) {
return true;
}
- if ((*currentPtr)->count() == 0) {
- SkASSERT((*currentPtr)->next() == NULL);
- return true;
- }
- SkOpContour** listEnd2 = contourList.end();
+ SkOpContour** listEnd = contourList.end();
// find all intersections between segments
do {
SkOpContour** nextPtr = currentPtr;
SkOpContour* current = *currentPtr++;
+ if (current->containsCubics()) {
+ AddSelfIntersectTs(current);
+ }
SkOpContour* next;
do {
next = *nextPtr++;
- } while (AddIntersectTs(current, next, &coincidence, &allocator) && nextPtr != listEnd2);
- } while (currentPtr != listEnd2);
-#if DEBUG_VALIDATE
- globalState.setPhase(SkOpGlobalState::kWalking);
-#endif
- if (!HandleCoincidence(&contourList, &coincidence, &allocator, &globalState)) {
+ } while (AddIntersectTs(current, next) && nextPtr != listEnd);
+ } while (currentPtr != listEnd);
+ if (!HandleCoincidence(&contourList, 0)) {
return false;
}
// construct closed contours
- SkPathWriter wrapper(*result);
- if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &wrapper, &allocator)
- : !bridgeXor(contourList, &wrapper, &allocator))
+ SkPathWriter simple(*result);
+ if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
+ : !bridgeXor(contourList, &simple))
{ // if some edges could not be resolved, assemble remaining fragments
SkPath temp;
temp.setFillType(fillType);
SkPathWriter assembled(temp);
- Assemble(wrapper, &assembled);
+ Assemble(simple, &assembled);
*result = *assembled.nativePath();
result->setFillType(fillType);
}
diff --git a/src/pathops/SkPathOpsTCubicSect.cpp b/src/pathops/SkPathOpsTCubicSect.cpp
index 10a84a3843..0b3ddd7d0d 100644
--- a/src/pathops/SkPathOpsTCubicSect.cpp
+++ b/src/pathops/SkPathOpsTCubicSect.cpp
@@ -7,9 +7,9 @@
#include "SkPathOpsTSect.h"
-int SkIntersections::intersect(const SkDCubic& cubic1, const SkDCubic& cubic2) {
- SkTSect<SkDCubic> sect1(cubic1 PATH_OPS_DEBUG_T_SECT_PARAMS(1));
- SkTSect<SkDCubic> sect2(cubic2 PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+int SkIntersections::intersectB(const SkDCubic& cubic1, const SkDCubic& cubic2) {
+ SkTSect<SkDCubic> sect1(cubic1 PATH_OPS_DEBUG_PARAMS(1));
+ SkTSect<SkDCubic> sect2(cubic2 PATH_OPS_DEBUG_PARAMS(2));
SkTSect<SkDCubic>::BinarySearch(&sect1, &sect2, this);
return used();
}
diff --git a/src/pathops/SkPathOpsTQuadSect.cpp b/src/pathops/SkPathOpsTQuadSect.cpp
index 06b5f2f8e9..46ce5cfdbb 100644
--- a/src/pathops/SkPathOpsTQuadSect.cpp
+++ b/src/pathops/SkPathOpsTQuadSect.cpp
@@ -7,9 +7,9 @@
#include "SkPathOpsTSect.h"
-int SkIntersections::intersect(const SkDQuad& quad1, const SkDQuad& quad2) {
- SkTSect<SkDQuad> sect1(quad1 PATH_OPS_DEBUG_T_SECT_PARAMS(1));
- SkTSect<SkDQuad> sect2(quad2 PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+int SkIntersections::intersectB(const SkDQuad& quad1, const SkDQuad& quad2) {
+ SkTSect<SkDQuad> sect1(quad1 PATH_OPS_DEBUG_PARAMS(1));
+ SkTSect<SkDQuad> sect2(quad2 PATH_OPS_DEBUG_PARAMS(2));
SkTSect<SkDQuad>::BinarySearch(&sect1, &sect2, this);
return used();
}
diff --git a/src/pathops/SkPathOpsTSect.h b/src/pathops/SkPathOpsTSect.h
index 5c76da7e83..4e7d3b1795 100644
--- a/src/pathops/SkPathOpsTSect.h
+++ b/src/pathops/SkPathOpsTSect.h
@@ -6,25 +6,15 @@
*/
#include "SkChunkAlloc.h"
-#include "SkPathOpsBounds.h"
#include "SkPathOpsRect.h"
#include "SkPathOpsQuad.h"
#include "SkIntersections.h"
-#include "SkTSort.h"
+#include "SkTArray.h"
/* TCurve is either SkDQuadratic or SkDCubic */
template<typename TCurve>
class SkTCoincident {
public:
- SkTCoincident()
- : fCoincident(false) {
- }
-
- void clear() {
- fPerpT = -1;
- fCoincident = false;
- }
-
bool isCoincident() const {
return fCoincident;
}
@@ -64,73 +54,41 @@ template<typename TCurve> class SkTSect;
template<typename TCurve>
class SkTSpan {
public:
- void addBounded(SkTSpan* );
+ void init(const TCurve& );
+ void initBounds(const TCurve& );
+
double closestBoundedT(const SkDPoint& pt) const;
- bool contains(double t) const;
- const SkTSect<TCurve>* debugOpp() const;
- const SkTSpan* debugSpan(int ) const;
- const SkTSpan* debugT(double t) const;
-#ifdef SK_DEBUG
- bool debugIsBefore(const SkTSpan* span) const;
-#endif
- void dump() const;
- void dumpBounds(int id) const;
+ bool contains(double t) const {
+ return !! const_cast<SkTSpan*>(this)->innerFind(t);
+ }
+
+ bool contains(const SkTSpan* span) const;
double endT() const {
return fEndT;
}
- SkTSpan* findOppSpan(const SkTSpan* opp) const;
-
- SkTSpan* findOppT(double t) const {
- SkTSpan* result = oppT(t);
+ SkTSpan* find(double t) {
+ SkTSpan* result = innerFind(t);
SkASSERT(result);
return result;
}
- bool hasOppT(double t) const {
- return SkToBool(oppT(t));
- }
-
- int hullsIntersect(SkTSpan* span, bool* start, bool* oppStart);
- void init(const TCurve& );
- void initBounds(const TCurve& );
-
- bool isBounded() const {
- return fBounded.count() > 0;
- }
-
- bool linearsIntersect(SkTSpan* span);
- double linearT(const SkDPoint& ) const;
-
- void markCoincident() {
- fCoinStart.markCoincident();
- fCoinEnd.markCoincident();
- }
+ bool intersects(const SkTSpan* span, bool* check);
const SkTSpan* next() const {
return fNext;
}
- bool onlyEndPointsInCommon(const SkTSpan* opp, bool* start, bool* oppStart, bool* ptsInCommon);
-
const TCurve& part() const {
return fPart;
}
- bool removeAllBounded();
- bool removeBounded(const SkTSpan* opp);
-
void reset() {
fBounded.reset();
}
- void resetBounds(const TCurve& curve) {
- fIsLinear = fIsLine = false;
- initBounds(curve);
- }
-
bool split(SkTSpan* work) {
return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
}
@@ -141,23 +99,29 @@ public:
return fStartT;
}
-private:
+ bool tightBoundsIntersects(const SkTSpan* span) const;
// implementation is for testing only
- int debugID() const {
- return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1);
+ void dump() const {
+ dump(NULL);
}
- void dumpID() const;
+private:
+ SkTSpan* innerFind(double t);
+ bool linearIntersects(const TCurve& ) const;
- int hullCheck(const SkTSpan* opp, bool* start, bool* oppStart);
- int linearIntersects(const TCurve& ) const;
- SkTSpan* oppT(double t) const;
+ // implementation is for testing only
+#if DEBUG_T_SECT
+ int debugID(const SkTSect<TCurve>* ) const { return fDebugID; }
+#else
+ int debugID(const SkTSect<TCurve>* ) const;
+#endif
+ void dump(const SkTSect<TCurve>* ) const;
+ void dumpID(const SkTSect<TCurve>* ) const;
+#if DEBUG_T_SECT
void validate() const;
- void validateBounded() const;
- void validatePerpT(double oppT) const;
- void validatePerpPt(double t, const SkDPoint& ) const;
+#endif
TCurve fPart;
SkTCoincident<TCurve> fCoinStart;
@@ -172,33 +136,23 @@ private:
bool fCollapsed;
bool fHasPerp;
bool fIsLinear;
- bool fIsLine;
- bool fDeleted;
- PATH_OPS_DEBUG_CODE(SkTSect<TCurve>* fDebugSect);
- PATH_OPS_DEBUG_T_SECT_CODE(int fID);
+#if DEBUG_T_SECT
+ int fDebugID;
+ bool fDebugDeleted;
+#endif
friend class SkTSect<TCurve>;
};
template<typename TCurve>
class SkTSect {
public:
- SkTSect(const TCurve& c PATH_OPS_DEBUG_T_SECT_PARAMS(int id));
+ SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id));
static void BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersections* intersections);
// for testing only
- bool debugHasBounded(const SkTSpan<TCurve>* ) const;
-
- const SkTSect* debugOpp() const {
- return PATH_OPS_DEBUG_RELEASE(fOppSect, NULL);
- }
-
- const SkTSpan<TCurve>* debugSpan(int id) const;
- const SkTSpan<TCurve>* debugT(double t) const;
void dump() const;
- void dumpBoth(SkTSect* ) const;
- void dumpBounds(int id) const;
- void dumpCoin() const;
- void dumpCoinCurves() const;
+ void dumpBoth(const SkTSect& opp) const;
+ void dumpBoth(const SkTSect* opp) const;
void dumpCurves() const;
private:
@@ -209,72 +163,36 @@ private:
kOneS2Set = 8
};
- SkTSpan<TCurve>* addFollowing(SkTSpan<TCurve>* prior);
- void addForPerp(SkTSpan<TCurve>* span, double t);
SkTSpan<TCurve>* addOne();
-
- SkTSpan<TCurve>* addSplitAt(SkTSpan<TCurve>* span, double t) {
- SkTSpan<TCurve>* result = this->addOne();
- result->splitAt(span, t);
- result->initBounds(fCurve);
- span->initBounds(fCurve);
- return result;
- }
-
- bool binarySearchCoin(SkTSect* , double tStart, double tStep, double* t, double* oppT);
+ bool binarySearchCoin(const SkTSect& , double tStart, double tStep, double* t, double* oppT);
SkTSpan<TCurve>* boundsMax() const;
void coincidentCheck(SkTSect* sect2);
- bool coincidentHasT(double t);
- void computePerpendiculars(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
- int countConsecutiveSpans(SkTSpan<TCurve>* first, SkTSpan<TCurve>** last) const;
-
- int debugID() const {
- return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1);
- }
-
- void deleteEmptySpans();
- void dumpCommon(const SkTSpan<TCurve>* ) const;
- void dumpCommonCurves(const SkTSpan<TCurve>* ) const;
static int EndsEqual(const SkTSect* sect1, const SkTSect* sect2, SkIntersections* );
- SkTSpan<TCurve>* extractCoincident(SkTSect* sect2, SkTSpan<TCurve>* first,
- SkTSpan<TCurve>* last);
- SkTSpan<TCurve>* findCoincidentRun(SkTSpan<TCurve>* first, SkTSpan<TCurve>** lastPtr,
- const SkTSect* sect2);
- int intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
- SkTSpan<TCurve>* oppSpan, int* oppResult) const;
- int linesIntersect(const SkTSpan<TCurve>* span, const SkTSect* opp,
- const SkTSpan<TCurve>* oppSpan, SkIntersections* ) const;
- void markSpanGone(SkTSpan<TCurve>* span);
- bool matchedDirection(double t, const SkTSect* sect2, double t2) const;
- void matchedDirCheck(double t, const SkTSect* sect2, double t2,
- bool* calcMatched, bool* oppMatched) const;
- void mergeCoincidence(SkTSect* sect2);
- SkTSpan<TCurve>* prev(SkTSpan<TCurve>* ) const;
- void removeByPerpendicular(SkTSect* opp);
+ bool intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
+ const SkTSpan<TCurve>* oppSpan) const;
+ void onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
void recoverCollapsed();
- void removeCoincident(SkTSpan<TCurve>* span, bool isBetween);
- void removeAllBut(const SkTSpan<TCurve>* keep, SkTSpan<TCurve>* span, SkTSect* opp);
void removeSpan(SkTSpan<TCurve>* span);
- void removeSpanRange(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
+ void removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span);
void removeSpans(SkTSpan<TCurve>* span, SkTSect* opp);
- SkTSpan<TCurve>* spanAtT(double t, SkTSpan<TCurve>** priorSpan);
- SkTSpan<TCurve>* tail();
+ void setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
+ const SkTSpan<TCurve>* tail() const;
void trim(SkTSpan<TCurve>* span, SkTSect* opp);
- void unlinkSpan(SkTSpan<TCurve>* span);
- bool updateBounded(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last, SkTSpan<TCurve>* oppFirst);
- void validate() const;
- void validateBounded() const;
+#if DEBUG_T_SECT
+ int debugID() const { return fDebugID; }
+ void validate() const;
+#else
+ int debugID() const { return 0; }
+#endif
const TCurve& fCurve;
SkChunkAlloc fHeap;
SkTSpan<TCurve>* fHead;
- SkTSpan<TCurve>* fCoincident;
SkTSpan<TCurve>* fDeleted;
int fActiveCount;
- PATH_OPS_DEBUG_CODE(SkTSect* fOppSect);
- PATH_OPS_DEBUG_T_SECT_CODE(int fID);
- PATH_OPS_DEBUG_T_SECT_CODE(int fDebugCount);
#if DEBUG_T_SECT
+ int fDebugID;
+ int fDebugCount;
int fDebugAllocatedCount;
#endif
friend class SkTSpan<TCurve>; // only used by debug id
@@ -290,8 +208,8 @@ void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t,
SkIntersections i;
int used = i.intersectRay(c2, perp);
// only keep closest
- if (used == 0 || used == 3) {
- this->clear();
+ if (used == 0) {
+ fPerpT = -1;
return;
}
fPerpT = i[0][0];
@@ -305,10 +223,6 @@ void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t,
fPerpPt = i.pt(1);
}
}
-#if DEBUG_T_SECT
- SkDebugf("%s cPt=(%1.9g,%1.9g) %s fPerpPt=(%1.9g,%1.9g)\n", __FUNCTION__, cPt.fX, cPt.fY,
- cPt.approximatelyEqual(fPerpPt) ? "==" : "!=", fPerpPt.fX, fPerpPt.fY);
-#endif
fCoincident = cPt.approximatelyEqual(fPerpPt);
#if DEBUG_T_SECT
if (fCoincident) {
@@ -318,55 +232,29 @@ void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t,
}
template<typename TCurve>
-void SkTSpan<TCurve>::addBounded(SkTSpan* span) {
- if (this->findOppSpan(span)) {
- return;
- }
- fBounded.push_back() = span;
+void SkTSpan<TCurve>::init(const TCurve& c) {
+ fPrev = fNext = NULL;
+ fIsLinear = false;
+ fStartT = 0;
+ fEndT = 1;
+ initBounds(c);
}
template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::addFollowing(SkTSpan<TCurve>* prior) {
- SkTSpan<TCurve>* result = this->addOne();
- result->fStartT = prior ? prior->fEndT : 0;
- SkTSpan<TCurve>* next = prior ? prior->fNext : fHead;
- result->fEndT = next ? next->fStartT : 1;
- result->fPrev = prior;
- result->fNext = next;
- if (prior) {
- prior->fNext = result;
- } else {
- fHead = result;
- }
- if (next) {
- next->fPrev = result;
+void SkTSpan<TCurve>::initBounds(const TCurve& c) {
+ fPart = c.subDivide(fStartT, fEndT);
+ fBounds.setBounds(fPart);
+ fCoinStart.init();
+ fCoinEnd.init();
+ fBoundsMax = SkTMax(fBounds.width(), fBounds.height());
+ fCollapsed = fPart.collapsed();
+ fHasPerp = false;
+#if DEBUG_T_SECT
+ fDebugDeleted = false;
+ if (fCollapsed) {
+ SkDebugf(""); // for convenient breakpoints
}
- result->resetBounds(fCurve);
- return result;
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::addForPerp(SkTSpan<TCurve>* span, double t) {
- if (!span->hasOppT(t)) {
- SkTSpan<TCurve>* priorSpan;
- SkTSpan<TCurve>* opp = this->spanAtT(t, &priorSpan);
- if (!opp) {
- opp = this->addFollowing(priorSpan);
-#if DEBUG_PERP
- SkDebugf("%s priorSpan=%d t=%1.9g opp=%d\n", __FUNCTION__, priorSpan->debugID(), t,
- opp->debugID());
#endif
- }
-#if DEBUG_PERP
- opp->dump(); SkDebugf("\n");
- SkDebugf("%s fBounded.push_back span=%d opp=%d\n", __FUNCTION__, priorSpan->debugID(),
- opp->debugID());
-#endif
- opp->fBounded.push_back(span);
- span->fBounded.push_back(opp);
- }
- this->validate();
- span->validatePerpT(t);
}
template<typename TCurve>
@@ -391,143 +279,55 @@ double SkTSpan<TCurve>::closestBoundedT(const SkDPoint& pt) const {
return result;
}
-#ifdef SK_DEBUG
template<typename TCurve>
-bool SkTSpan<TCurve>::debugIsBefore(const SkTSpan* span) const {
- const SkTSpan* work = this;
- do {
- if (span == work) {
+bool SkTSpan<TCurve>::contains(const SkTSpan* span) const {
+ int count = fBounded.count();
+ for (int index = 0; index < count; ++index) {
+ const SkTSpan* test = fBounded[index];
+ if (span == test) {
return true;
}
- } while ((work = work->fNext));
+ }
return false;
}
-#endif
template<typename TCurve>
-bool SkTSpan<TCurve>::contains(double t) const {
- const SkTSpan* work = this;
+SkTSpan<TCurve>* SkTSpan<TCurve>::innerFind(double t) {
+ SkTSpan* work = this;
do {
if (between(work->fStartT, t, work->fEndT)) {
- return true;
+ return work;
}
} while ((work = work->fNext));
- return false;
-}
-
-template<typename TCurve>
-const SkTSect<TCurve>* SkTSpan<TCurve>::debugOpp() const {
- return PATH_OPS_DEBUG_RELEASE(fDebugSect->debugOpp(), NULL);
-}
-
-template<typename TCurve>
-SkTSpan<TCurve>* SkTSpan<TCurve>::findOppSpan(const SkTSpan* opp) const {
- int count = fBounded.count();
- for (int index = 0; index < count; ++index) {
- SkTSpan* test = fBounded[index];
- if (opp == test) {
- return test;
- }
- }
return NULL;
}
-// returns 0 if no hull intersection
-// 1 if hulls intersect
-// 2 if hulls only share a common endpoint
-// -1 if linear and further checking is required
-template<typename TCurve>
-int SkTSpan<TCurve>::hullCheck(const SkTSpan* opp, bool* start, bool* oppStart) {
- if (fIsLinear) {
- return -1;
- }
- bool ptsInCommon;
- if (onlyEndPointsInCommon(opp, start, oppStart, &ptsInCommon)) {
- SkASSERT(ptsInCommon);
- return 2;
- }
- bool linear;
- if (fPart.hullIntersects(opp->fPart, &linear)) {
- if (!linear) { // check set true if linear
- return 1;
- }
- fIsLinear = true;
- fIsLine = fPart.controlsInside();
- return ptsInCommon ? 2 : -1;
- } else { // hull is not linear; check set true if intersected at the end points
- return ((int) ptsInCommon) << 1; // 0 or 2
- }
- return 0;
-}
-
// OPTIMIZE ? If at_most_end_pts_in_common detects that one quad is near linear,
// use line intersection to guess a better split than 0.5
// OPTIMIZE Once at_most_end_pts_in_common detects linear, mark span so all future splits are linear
template<typename TCurve>
-int SkTSpan<TCurve>::hullsIntersect(SkTSpan* opp, bool* start, bool* oppStart) {
- if (!fBounds.intersects(opp->fBounds)) {
- return 0;
+bool SkTSpan<TCurve>::intersects(const SkTSpan* span, bool* check) {
+ if (!fBounds.intersects(span->fBounds)) {
+ *check = false; // no need to check to see if the bounds have end points in common
+ return false;
}
- int hullSect = this->hullCheck(opp, start, oppStart);
- if (hullSect >= 0) {
- return hullSect;
+ if (!fIsLinear && fPart.hullIntersects(span->fPart, check)) {
+ if (!*check) {
+ return true;
+ }
+ fIsLinear = true;
}
- hullSect = opp->hullCheck(this, oppStart, start);
- if (hullSect >= 0) {
- return hullSect;
+ if (fIsLinear) {
+ *check = false;
+ return linearIntersects(span->fPart);
}
- return -1;
+ return *check;
}
template<typename TCurve>
-void SkTSpan<TCurve>::init(const TCurve& c) {
- fPrev = fNext = NULL;
- fStartT = 0;
- fEndT = 1;
- resetBounds(c);
-}
-
-template<typename TCurve>
-void SkTSpan<TCurve>::initBounds(const TCurve& c) {
- fPart = c.subDivide(fStartT, fEndT);
- fBounds.setBounds(fPart);
- fCoinStart.init();
- fCoinEnd.init();
- fBoundsMax = SkTMax(fBounds.width(), fBounds.height());
- fCollapsed = fPart.collapsed();
- fHasPerp = false;
- fDeleted = false;
-#if DEBUG_T_SECT
- if (fCollapsed) {
- SkDebugf(""); // for convenient breakpoints
- }
-#endif
-}
-
-template<typename TCurve>
-bool SkTSpan<TCurve>::linearsIntersect(SkTSpan* span) {
- int result = this->linearIntersects(span->fPart);
- if (result <= 1) {
- return SkToBool(result);
- }
- SkASSERT(span->fIsLinear);
- result = span->linearIntersects(this->fPart);
-// SkASSERT(result <= 1);
- return SkToBool(result);
-}
-
-template<typename TCurve>
-double SkTSpan<TCurve>::linearT(const SkDPoint& pt) const {
- SkDVector len = fPart[TCurve::kPointLast] - fPart[0];
- return fabs(len.fX) > fabs(len.fY)
- ? (pt.fX - fPart[0].fX) / len.fX
- : (pt.fY - fPart[0].fY) / len.fY;
-}
-
-template<typename TCurve>
-int SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const {
+bool SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const {
// looks like q1 is near-linear
- int start = 0, end = TCurve::kPointLast; // the outside points are usually the extremes
+ int start = 0, end = TCurve::kPointCount - 1; // the outside points are usually the extremes
if (!fPart.controlsInside()) {
double dist = 0; // if there's any question, compute distance to find best outsiders
for (int outer = 0; outer < TCurve::kPointCount - 1; ++outer) {
@@ -547,116 +347,20 @@ int SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const {
double origY = fPart[start].fY;
double adj = fPart[end].fX - origX;
double opp = fPart[end].fY - origY;
- double maxPart = SkTMax(fabs(adj), fabs(opp));
- double sign = 0; // initialization to shut up warning in release build
+ double sign;
for (int n = 0; n < TCurve::kPointCount; ++n) {
- double dx = q2[n].fY - origY;
- double dy = q2[n].fX - origX;
- double maxVal = SkTMax(maxPart, SkTMax(fabs(dx), fabs(dy)));
double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
- if (precisely_zero_when_compared_to(test, maxVal)) {
- return 1;
- }
- if (approximately_zero_when_compared_to(test, maxVal)) {
- return 3;
+ if (precisely_zero(test)) {
+ return true;
}
if (n == 0) {
sign = test;
continue;
}
if (test * sign < 0) {
- return 1;
- }
- }
- return 0;
-}
-
-template<typename TCurve>
-bool SkTSpan<TCurve>::onlyEndPointsInCommon(const SkTSpan* opp, bool* start, bool* oppStart,
- bool* ptsInCommon) {
- if (opp->fPart[0] == fPart[0]) {
- *start = *oppStart = true;
- } else if (opp->fPart[0] == fPart[TCurve::kPointLast]) {
- *start = false;
- *oppStart = true;
- } else if (opp->fPart[TCurve::kPointLast] == fPart[0]) {
- *start = true;
- *oppStart = false;
- } else if (opp->fPart[TCurve::kPointLast] == fPart[TCurve::kPointLast]) {
- *start = *oppStart = false;
- } else {
- *ptsInCommon = false;
- return false;
- }
- *ptsInCommon = true;
- const SkDPoint* o1Pts[TCurve::kPointCount - 1], * o2Pts[TCurve::kPointCount - 1];
- int baseIndex = *start ? 0 : TCurve::kPointLast;
- fPart.otherPts(baseIndex, o1Pts);
- opp->fPart.otherPts(*oppStart ? 0 : TCurve::kPointLast, o2Pts);
- const SkDPoint& base = fPart[baseIndex];
- for (int o1 = 0; o1 < (int) SK_ARRAY_COUNT(o1Pts); ++o1) {
- SkDVector v1 = *o1Pts[o1] - base;
- for (int o2 = 0; o2 < (int) SK_ARRAY_COUNT(o2Pts); ++o2) {
- SkDVector v2 = *o2Pts[o2] - base;
- if (v2.dot(v1) >= 0) {
- return false;
- }
- }
- }
- return true;
-}
-
-template<typename TCurve>
-SkTSpan<TCurve>* SkTSpan<TCurve>::oppT(double t) const {
- int count = fBounded.count();
- for (int index = 0; index < count; ++index) {
- SkTSpan* test = fBounded[index];
- if (between(test->fStartT, t, test->fEndT)) {
- return test;
- }
- }
- return NULL;
-}
-
-template<typename TCurve>
-bool SkTSpan<TCurve>::removeAllBounded() {
- bool deleteSpan = false;
- int count = fBounded.count();
- for (int index = 0; index < count; ++index) {
- SkTSpan* opp = fBounded[index];
- deleteSpan |= opp->removeBounded(this);
- }
- return deleteSpan;
-}
-
-template<typename TCurve>
-bool SkTSpan<TCurve>::removeBounded(const SkTSpan* opp) {
- int count = fBounded.count();
- if (fHasPerp) {
- bool foundStart = false;
- bool foundEnd = false;
- for (int index = 0; index < count; ++index) {
- const SkTSpan* test = fBounded[index];
- if (opp == test) {
- continue;
- }
- foundStart |= between(test->fStartT, fCoinStart.perpT(), test->fEndT);
- foundEnd |= between(test->fStartT, fCoinEnd.perpT(), test->fEndT);
- }
- if (!foundStart || !foundEnd) {
- fHasPerp = false;
- fCoinStart.init();
- fCoinEnd.init();
- }
- }
- for (int index = 0; index < count; ++index) {
- if (opp == fBounded[index]) {
- fBounded.removeShuffle(index);
- SkASSERT((count == 1) == (fBounded.count() == 0));
- return count == 1;
+ return true;
}
}
- SkASSERT(0);
return false;
}
@@ -676,8 +380,6 @@ bool SkTSpan<TCurve>::splitAt(SkTSpan* work, double t) {
fPrev = work;
fNext = work->fNext;
fIsLinear = work->fIsLinear;
- fIsLine = work->fIsLine;
-
work->fNext = this;
if (fNext) {
fNext->fPrev = this;
@@ -691,74 +393,102 @@ bool SkTSpan<TCurve>::splitAt(SkTSpan* work, double t) {
}
template<typename TCurve>
-void SkTSpan<TCurve>::validate() const {
-#if DEBUG_T_SECT
- SkASSERT(fNext == NULL || fNext != fPrev);
- SkASSERT(fNext == NULL || this == fNext->fPrev);
- SkASSERT(fPrev == NULL || this == fPrev->fNext);
- SkASSERT(fBounds.width() || fBounds.height() || fCollapsed);
- SkASSERT(fBoundsMax == SkTMax(fBounds.width(), fBounds.height()));
- SkASSERT(0 <= fStartT);
- SkASSERT(fEndT <= 1);
- SkASSERT(fStartT <= fEndT);
- SkASSERT(fBounded.count() > 0);
- this->validateBounded();
- if (fHasPerp) {
- if (fCoinStart.isCoincident()) {
- validatePerpT(fCoinStart.perpT());
- validatePerpPt(fCoinStart.perpT(), fCoinStart.perpPt());
+bool SkTSpan<TCurve>::tightBoundsIntersects(const SkTSpan* span) const {
+ // skew all to an axis
+ SkDVector v2_0 = fPart[TCurve::kPointLast] - fPart[0];
+ bool skewToXAxis = fabs(v2_0.fX) > fabs(v2_0.fY);
+ double ratio = skewToXAxis ? v2_0.fY / v2_0.fX : v2_0.fX / v2_0.fY;
+ TCurve r1 = fPart;
+ if (skewToXAxis) {
+ r1[1].fY -= (fPart[1].fX - r1[0].fX) * ratio;
+ if (TCurve::IsCubic()) {
+ r1[2].fY -= (fPart[2].fX - r1[0].fX) * ratio;
+ r1[3].fY = r1[0].fY;
+ } else {
+ r1[2].fY = r1[0].fY;
}
- if (fCoinEnd.isCoincident()) {
- validatePerpT(fCoinEnd.perpT());
- validatePerpPt(fCoinEnd.perpT(), fCoinEnd.perpPt());
+ } else {
+ r1[1].fX -= (fPart[1].fY - r1[0].fY) * ratio;
+ if (TCurve::IsCubic()) {
+ r1[2].fX -= (fPart[2].fY - r1[0].fY) * ratio;
+ r1[3].fX = r1[0].fX;
+ } else {
+ r1[2].fX = r1[0].fX;
+ }
+ }
+ // compute the tight skewed bounds
+ SkDRect bounds;
+ bounds.setBounds(r1);
+ // see if opposite ends are within range of tight skewed bounds
+ TCurve r2 = span->fPart;
+ for (int i = 0; i < TCurve::kPointCount; i += 2) {
+ if (skewToXAxis) {
+ r2[i].fY -= (r2[i].fX - r1[0].fX) * ratio;
+ if (between(bounds.fTop, r2[i].fY, bounds.fBottom)) {
+ return true;
+ }
+ } else {
+ r2[i].fX -= (r2[i].fY - r1[0].fY) * ratio;
+ if (between(bounds.fLeft, r2[i].fX, bounds.fRight)) {
+ return true;
+ }
}
}
-#endif
-}
-
-template<typename TCurve>
-void SkTSpan<TCurve>::validateBounded() const {
-#if DEBUG_VALIDATE
- for (int index = 0; index < fBounded.count(); ++index) {
- const SkTSpan* overlap = fBounded[index];
- SkASSERT(!overlap->fDeleted);
- SkASSERT(((this->debugID() ^ overlap->debugID()) & 1) == 1);
- SkASSERT(overlap->findOppSpan(this));
+ // see if opposite ends are on either side of tight skewed bounds
+ if ((skewToXAxis ? (r2[0].fY - r1[0].fY) * (r2[TCurve::kPointLast].fY - r1[0].fY)
+ : (r2[0].fX - r1[0].fX) * (r2[TCurve::kPointLast].fX - r1[0].fX)) < 0) {
+ return true;
+ }
+ // compute opposite tight skewed bounds
+ if (skewToXAxis) {
+ r2[1].fY -= (r2[1].fX - r1[0].fX) * ratio;
+ if (TCurve::IsCubic()) {
+ r2[2].fY -= (r2[2].fX - r1[0].fX) * ratio;
+ }
+ } else {
+ r2[1].fX -= (r2[1].fY - r1[0].fY) * ratio;
+ if (TCurve::IsCubic()) {
+ r2[2].fX -= (r2[2].fY - r1[0].fY) * ratio;
+ }
+ }
+ SkDRect sBounds;
+ sBounds.setBounds(r2);
+ // see if tight bounds overlap
+ if (skewToXAxis) {
+ return bounds.fTop <= sBounds.fBottom && sBounds.fTop <= bounds.fBottom;
+ } else {
+ return bounds.fLeft <= sBounds.fRight && sBounds.fLeft <= bounds.fRight;
}
-#endif
}
+#if DEBUG_T_SECT
template<typename TCurve>
-void SkTSpan<TCurve>::validatePerpT(double oppT) const {
-#if DEBUG_VALIDATE
+void SkTSpan<TCurve>::validate() const {
+ SkASSERT(fNext == NULL || fNext != fPrev);
+ SkASSERT(fNext == NULL || this == fNext->fPrev);
+ SkASSERT(fBounds.width() || fBounds.height());
+ SkASSERT(fBoundsMax == SkTMax(fBounds.width(), fBounds.height()));
+ SkASSERT(0 <= fStartT);
+ SkASSERT(fEndT <= 1);
+ SkASSERT(fStartT < fEndT);
+ SkASSERT(fBounded.count() > 0);
for (int index = 0; index < fBounded.count(); ++index) {
const SkTSpan* overlap = fBounded[index];
- if (between(overlap->fStartT, oppT, overlap->fEndT)) {
- return;
- }
+ SkASSERT(((fDebugID ^ overlap->fDebugID) & 1) == 1);
+ SkASSERT(overlap->contains(this));
}
- SkASSERT(0);
-#endif
}
-
-template<typename TCurve>
-void SkTSpan<TCurve>::validatePerpPt(double t, const SkDPoint& pt) const {
-#if DEBUG_T_SECT
- PATH_OPS_DEBUG_CODE(SkASSERT(fDebugSect->fOppSect->fCurve.ptAtT(t) == pt));
#endif
-}
-
template<typename TCurve>
-SkTSect<TCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_T_SECT_PARAMS(int id))
+SkTSect<TCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id))
: fCurve(c)
, fHeap(sizeof(SkTSpan<TCurve>) * 4)
- , fCoincident(NULL)
, fDeleted(NULL)
, fActiveCount(0)
- PATH_OPS_DEBUG_T_SECT_PARAMS(fID(id))
- PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugCount(0))
- PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugAllocatedCount(0))
+ PATH_OPS_DEBUG_PARAMS(fDebugID(id))
+ PATH_OPS_DEBUG_PARAMS(fDebugCount(0))
+ PATH_OPS_DEBUG_PARAMS(fDebugAllocatedCount(0))
{
fHead = addOne();
fHead->init(c);
@@ -778,22 +508,22 @@ SkTSpan<TCurve>* SkTSect<TCurve>::addOne() {
#endif
}
++fActiveCount;
- PATH_OPS_DEBUG_T_SECT_CODE(result->fID = fDebugCount++ * 2 + fID);
- PATH_OPS_DEBUG_CODE(result->fDebugSect = this);
+#if DEBUG_T_SECT
+ result->fDebugID = fDebugCount++ * 2 + fDebugID;
+#endif
return result;
}
template<typename TCurve>
-bool SkTSect<TCurve>::binarySearchCoin(SkTSect* sect2, double tStart, double tStep,
+bool SkTSect<TCurve>::binarySearchCoin(const SkTSect& sect2, double tStart, double tStep,
double* resultT, double* oppT) {
SkTSpan<TCurve> work;
double result = work.fStartT = work.fEndT = tStart;
- PATH_OPS_DEBUG_CODE(work.fDebugSect = this);
SkDPoint last = fCurve.ptAtT(tStart);
SkDPoint oppPt;
bool flip = false;
SkDEBUGCODE(bool down = tStep < 0);
- const TCurve& opp = sect2->fCurve;
+ const TCurve& opp = sect2.fCurve;
do {
tStep *= 0.5;
work.fStartT += tStep;
@@ -811,11 +541,8 @@ bool SkTSect<TCurve>::binarySearchCoin(SkTSect* sect2, double tStart, double tSt
last = work.fPart[0];
work.fCoinStart.setPerp(fCurve, work.fStartT, last, opp);
if (work.fCoinStart.isCoincident()) {
-#if DEBUG_T_SECT
- work.validatePerpPt(work.fCoinStart.perpT(), work.fCoinStart.perpPt());
-#endif
double oppTTest = work.fCoinStart.perpT();
- if (sect2->fHead->contains(oppTTest)) {
+ if (sect2.fHead->contains(oppTTest)) {
*oppT = oppTTest;
oppPt = work.fCoinStart.perpPt();
SkASSERT(down ? result > work.fStartT : result < work.fStartT);
@@ -847,472 +574,194 @@ template<typename TCurve>
SkTSpan<TCurve>* SkTSect<TCurve>::boundsMax() const {
SkTSpan<TCurve>* test = fHead;
SkTSpan<TCurve>* largest = fHead;
- bool lCollapsed = largest->fCollapsed;
+ bool largestCoin = largest->fCoinStart.isCoincident() && largest->fCoinEnd.isCoincident();
while ((test = test->fNext)) {
- bool tCollapsed = test->fCollapsed;
- if ((lCollapsed && !tCollapsed) || (lCollapsed == tCollapsed &&
- largest->fBoundsMax < test->fBoundsMax)) {
+ bool testCoin = test->fCoinStart.isCoincident() || test->fCoinEnd.isCoincident();
+ if ((largestCoin && !testCoin) || (largestCoin == testCoin
+ && (largest->fBoundsMax < test->fBoundsMax
+ || (largest->fCollapsed && !test->fCollapsed)))) {
largest = test;
+ largestCoin = testCoin;
}
}
- return largest;
+ return largestCoin ? NULL : largest;
}
template<typename TCurve>
void SkTSect<TCurve>::coincidentCheck(SkTSect* sect2) {
SkTSpan<TCurve>* first = fHead;
- SkTSpan<TCurve>* last, * next;
+ SkTSpan<TCurve>* next;
do {
- int consecutive = this->countConsecutiveSpans(first, &last);
- next = last->fNext;
+ int consecutive = 1;
+ SkTSpan<TCurve>* last = first;
+ do {
+ next = last->fNext;
+ if (!next) {
+ break;
+ }
+ if (next->fStartT > last->fEndT) {
+ break;
+ }
+ ++consecutive;
+ last = next;
+ } while (true);
if (consecutive < COINCIDENT_SPAN_COUNT) {
continue;
}
- this->validate();
- sect2->validate();
- this->computePerpendiculars(sect2, first, last);
- this->validate();
- sect2->validate();
+ setPerp(sect2->fCurve, first, last);
// check to see if a range of points are on the curve
- SkTSpan<TCurve>* coinStart = first;
- do {
- coinStart = this->extractCoincident(sect2, coinStart, last);
- } while (coinStart && !last->fDeleted);
- } while ((first = next));
-}
-
-template<typename TCurve>
-bool SkTSect<TCurve>::coincidentHasT(double t) {
- SkTSpan<TCurve>* test = fCoincident;
- while (test) {
- if (between(test->fStartT, t, test->fEndT)) {
- return true;
+ onCurveCheck(sect2, first, last);
+ SkTSpan<TCurve>* removalCandidate = NULL;
+ if (!first->fCoinStart.isCoincident()) {
+ SkTSpan<TCurve>* firstCoin = first->fNext;
+ removalCandidate = first;
+ first = firstCoin;
+ }
+ if (!first->fCoinStart.isCoincident()) {
+ continue;
}
- test = test->fNext;
- }
- return false;
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::computePerpendiculars(SkTSect* sect2, SkTSpan<TCurve>* first,
- SkTSpan<TCurve>* last) {
- const TCurve& opp = sect2->fCurve;
- SkTSpan<TCurve>* work = first;
- SkTSpan<TCurve>* prior = NULL;
- do {
- if (!work->fHasPerp && !work->fCollapsed) {
- if (prior) {
- work->fCoinStart = prior->fCoinEnd;
- } else {
- work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp);
- }
- if (work->fCoinStart.isCoincident()) {
- double perpT = work->fCoinStart.perpT();
- if (sect2->coincidentHasT(perpT)) {
- work->fCoinStart.clear();
- } else {
- sect2->addForPerp(work, perpT);
- }
- }
- work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp);
- if (work->fCoinEnd.isCoincident()) {
- double perpT = work->fCoinEnd.perpT();
- if (sect2->coincidentHasT(perpT)) {
- work->fCoinEnd.clear();
- } else {
- sect2->addForPerp(work, perpT);
- }
+ if (removalCandidate) {
+ removeSpans(removalCandidate, sect2);
+ }
+ if (!last->fCoinStart.isCoincident()) {
+ continue;
+ }
+ if (!last->fCoinEnd.isCoincident()) {
+ if (--consecutive < COINCIDENT_SPAN_COUNT) {
+ continue;
}
- work->fHasPerp = true;
+ last = last->fPrev;
+ SkASSERT(last->fCoinStart.isCoincident());
+ SkASSERT(last->fCoinEnd.isCoincident());
}
- if (work == last) {
- break;
+ SkASSERT(between(0, first->fCoinStart.perpT(), 1) || first->fCoinStart.perpT() == -1);
+ if (first->fCoinStart.perpT() < 0) {
+ first->fCoinStart.setPerp(fCurve, first->fStartT, first->fPart[0], sect2->fCurve);
}
- prior = work;
- work = work->fNext;
- SkASSERT(work);
- } while (true);
-}
-
-template<typename TCurve>
-int SkTSect<TCurve>::countConsecutiveSpans(SkTSpan<TCurve>* first,
- SkTSpan<TCurve>** lastPtr) const {
- int consecutive = 1;
- SkTSpan<TCurve>* last = first;
- do {
- SkTSpan<TCurve>* next = last->fNext;
- if (!next) {
- break;
+ SkASSERT(between(0, last->fCoinEnd.perpT(), 1) || last->fCoinEnd.perpT() == -1);
+ if (last->fCoinEnd.perpT() < 0) {
+ last->fCoinEnd.setPerp(fCurve, last->fEndT, last->fPart[TCurve::kPointLast],
+ sect2->fCurve);
}
- if (next->fStartT > last->fEndT) {
- break;
+ SkTSpan<TCurve>* removeMe = first->fNext;
+ while (removeMe != last) {
+ SkTSpan<TCurve>* removeNext = removeMe->fNext;
+ removeSpans(removeMe, sect2);
+ removeMe = removeNext;
}
- ++consecutive;
- last = next;
- } while (true);
- *lastPtr = last;
- return consecutive;
+ } while ((first = next));
}
template<typename TCurve>
-bool SkTSect<TCurve>::debugHasBounded(const SkTSpan<TCurve>* span) const {
- const SkTSpan<TCurve>* test = fHead;
- if (!test) {
+bool SkTSect<TCurve>::intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
+ const SkTSpan<TCurve>* oppSpan) const {
+ bool check; // we ignore whether the end points are in common or not
+ if (!span->intersects(oppSpan, &check)) {
return false;
}
- do {
- if (test->findOppSpan(span)) {
- return true;
- }
- } while ((test = test->next()));
- return false;
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::deleteEmptySpans() {
- SkTSpan<TCurve>* test;
- SkTSpan<TCurve>* next = fHead;
- while ((test = next)) {
- next = test->fNext;
- if (test->fBounded.count() == 0) {
- this->removeSpan(test);
- }
- }
-}
-
-template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::extractCoincident(SkTSect* sect2, SkTSpan<TCurve>* first,
- SkTSpan<TCurve>* last) {
- first = findCoincidentRun(first, &last, sect2);
- if (!first) {
- return NULL;
+ if (fActiveCount < COINCIDENT_SPAN_COUNT || opp->fActiveCount < COINCIDENT_SPAN_COUNT) {
+ return true;
}
- // march outwards to find limit of coincidence from here to previous and next spans
- double startT = first->fStartT;
- double oppStartT;
- double oppEndT SK_INIT_TO_AVOID_WARNING;
- SkTSpan<TCurve>* prev = first->fPrev;
- SkASSERT(first->fCoinStart.isCoincident());
- SkTSpan<TCurve>* oppFirst = first->findOppT(first->fCoinStart.perpT());
- SkASSERT(last->fCoinEnd.isCoincident());
- bool oppMatched = first->fCoinStart.perpT() < first->fCoinEnd.perpT();
- double coinStart;
- SkDEBUGCODE(double coinEnd);
- if (prev && prev->fEndT == startT
- && this->binarySearchCoin(sect2, startT, prev->fStartT - startT, &coinStart,
- &oppStartT)
- && prev->fStartT < coinStart && coinStart < startT) {
- oppFirst = prev->findOppT(oppStartT); // find opp start before splitting prev
- SkASSERT(oppFirst);
- first = this->addSplitAt(prev, coinStart);
- first->markCoincident();
- prev->fCoinEnd.markCoincident();
- if (oppFirst->fStartT < oppStartT && oppStartT < oppFirst->fEndT) {
- SkTSpan<TCurve>* oppHalf = sect2->addSplitAt(oppFirst, oppStartT);
- if (oppMatched) {
- oppFirst->fCoinEnd.markCoincident();
- oppHalf->markCoincident();
- oppFirst = oppHalf;
- } else {
- oppFirst->markCoincident();
- oppHalf->fCoinStart.markCoincident();
- }
- }
- } else {
- SkDEBUGCODE(coinStart = first->fStartT);
- SkDEBUGCODE(oppStartT = oppMatched ? oppFirst->fStartT : oppFirst->fEndT);
- }
- SkTSpan<TCurve>* oppLast;
- SkASSERT(last->fCoinEnd.isCoincident());
- oppLast = last->findOppT(last->fCoinEnd.perpT());
- SkDEBUGCODE(coinEnd = last->fEndT);
- SkDEBUGCODE(oppEndT = oppMatched ? oppLast->fEndT : oppLast->fStartT);
- if (!oppMatched) {
- SkTSwap(oppFirst, oppLast);
- SkTSwap(oppStartT, oppEndT);
- }
- SkASSERT(oppStartT < oppEndT);
- SkASSERT(coinStart == first->fStartT);
- SkASSERT(coinEnd == last->fEndT);
- SkASSERT(oppStartT == oppFirst->fStartT);
- SkASSERT(oppEndT == oppLast->fEndT);
- // reduce coincident runs to single entries
- this->validate();
- sect2->validate();
- bool deleteThisSpan = this->updateBounded(first, last, oppFirst);
- bool deleteSect2Span = sect2->updateBounded(oppFirst, oppLast, first);
- this->removeSpanRange(first, last);
- sect2->removeSpanRange(oppFirst, oppLast);
- first->fEndT = last->fEndT;
- first->resetBounds(this->fCurve);
- first->fCoinStart.setPerp(fCurve, first->fStartT, first->fPart[0], sect2->fCurve);
- first->fCoinEnd.setPerp(fCurve, first->fEndT, first->fPart[TCurve::kPointLast], sect2->fCurve);
- oppStartT = first->fCoinStart.perpT();
- oppEndT = first->fCoinEnd.perpT();
- if (between(0, oppStartT, 1) && between(0, oppEndT, 1)) {
- if (!oppMatched) {
- SkTSwap(oppStartT, oppEndT);
- }
- oppFirst->fStartT = oppStartT;
- oppFirst->fEndT = oppEndT;
- oppFirst->resetBounds(sect2->fCurve);
- }
- this->validateBounded();
- sect2->validateBounded();
- last = first->fNext;
- this->removeCoincident(first, false);
- sect2->removeCoincident(oppFirst, true);
- if (deleteThisSpan) {
- this->deleteEmptySpans();
- }
- if (deleteSect2Span) {
- sect2->deleteEmptySpans();
- }
- this->validate();
- sect2->validate();
- return last && !last->fDeleted ? last : NULL;
+ return span->tightBoundsIntersects(oppSpan);
}
template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::findCoincidentRun(SkTSpan<TCurve>* first,
- SkTSpan<TCurve>** lastPtr, const SkTSect* sect2) {
+void SkTSect<TCurve>::onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
SkTSpan<TCurve>* work = first;
- SkTSpan<TCurve>* lastCandidate = NULL;
first = NULL;
- // find the first fully coincident span
do {
if (work->fCoinStart.isCoincident()) {
- work->validatePerpT(work->fCoinStart.perpT());
- work->validatePerpPt(work->fCoinStart.perpT(), work->fCoinStart.perpPt());
- SkASSERT(work->hasOppT(work->fCoinStart.perpT()));
- if (!work->fCoinEnd.isCoincident()) {
- break;
- }
- lastCandidate = work;
if (!first) {
first = work;
}
- } else {
- lastCandidate = NULL;
- SkASSERT(!first);
+ } else if (first) {
+ break;
}
- if (work == *lastPtr) {
- return first;
+ if (work == last) {
+ break;
}
work = work->fNext;
SkASSERT(work);
} while (true);
- if (lastCandidate) {
- *lastPtr = lastCandidate;
+ if (!first) {
+ return;
}
- return first;
-}
-
-template<typename TCurve>
-int SkTSect<TCurve>::intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
- SkTSpan<TCurve>* oppSpan, int* oppResult) const {
- bool spanStart, oppStart;
- int hullResult = span->hullsIntersect(oppSpan, &spanStart, &oppStart);
- if (hullResult >= 0) {
- if (hullResult == 2) { // hulls have one point in common
- if (span->fBounded.count() <= 1) {
- SkASSERT(span->fBounded.count() == 0 || span->fBounded[0] == oppSpan);
- if (spanStart) {
- span->fEndT = span->fStartT;
- } else {
- span->fStartT = span->fEndT;
- }
- } else {
- hullResult = 1;
- }
- if (oppSpan->fBounded.count() <= 1) {
- SkASSERT(span->fBounded.count() == 0 || oppSpan->fBounded[0] == span);
- if (oppStart) {
- oppSpan->fEndT = oppSpan->fStartT;
+ // march outwards to find limit of coincidence from here to previous and next spans
+ double startT = first->fStartT;
+ double oppT;
+ SkTSpan<TCurve>* prev = first->fPrev;
+ if (prev) {
+ double coinStart;
+ if (binarySearchCoin(*sect2, startT, prev->fStartT - startT, &coinStart, &oppT)) {
+ if (coinStart < startT) {
+ SkASSERT(prev->fStartT < coinStart && coinStart < prev->fEndT);
+ SkTSpan<TCurve>* oppStart = sect2->fHead->find(oppT);
+ if (oppStart->fStartT < oppT && oppT < oppStart->fEndT) {
+ // split prev at coinStart if needed
+ SkTSpan<TCurve>* half2 = addOne();
+ half2->splitAt(prev, coinStart);
+ half2->initBounds(fCurve);
+ prev->initBounds(fCurve);
+ prev->fCoinEnd.markCoincident();
+ half2->fCoinStart.markCoincident();
+ half2->fCoinEnd.markCoincident();
+ // find span containing opposite t, and split that too
+ SkTSpan<TCurve>* oppHalf = sect2->addOne();
+ oppHalf->splitAt(oppStart, oppT);
+ oppHalf->initBounds(sect2->fCurve);
+ oppStart->initBounds(sect2->fCurve);
} else {
- oppSpan->fStartT = oppSpan->fEndT;
+ SkASSERT(oppStart->fStartT == oppT || oppT == oppStart->fEndT);
+ first->fStartT = coinStart;
+ prev->fEndT = coinStart;
+ first->initBounds(fCurve);
+ prev->initBounds(fCurve);
+ first->fCoinStart.markCoincident();
+ first->fCoinEnd.markCoincident();
}
- *oppResult = 2;
- } else {
- *oppResult = 1;
}
- } else {
- *oppResult = 1;
- }
- return hullResult;
- }
- if (span->fIsLine && oppSpan->fIsLine) {
- SkIntersections i;
- int sects = this->linesIntersect(span, opp, oppSpan, &i);
- if (!sects) {
- return -1;
}
- span->fStartT = span->fEndT = i[0][0];
- oppSpan->fStartT = oppSpan->fEndT = i[1][0];
- return *oppResult = 2;
- }
- if (span->fIsLinear || oppSpan->fIsLinear) {
- return *oppResult = (int) span->linearsIntersect(oppSpan);
}
- return *oppResult = 1;
-}
-
-// while the intersection points are sufficiently far apart:
-// construct the tangent lines from the intersections
-// find the point where the tangent line intersects the opposite curve
-template<typename TCurve>
-int SkTSect<TCurve>::linesIntersect(const SkTSpan<TCurve>* span, const SkTSect* opp,
- const SkTSpan<TCurve>* oppSpan, SkIntersections* i) const {
- SkIntersections thisRayI, oppRayI;
- SkDLine thisLine = {{ span->fPart[0], span->fPart[TCurve::kPointLast] }};
- SkDLine oppLine = {{ oppSpan->fPart[0], oppSpan->fPart[TCurve::kPointLast] }};
- int loopCount = 0;
- double bestDistSq = DBL_MAX;
- do {
- if (!thisRayI.intersectRay(opp->fCurve, thisLine)) {
- return 0;
- }
- if (!oppRayI.intersectRay(this->fCurve, oppLine)) {
- return 0;
- }
- // pick the closest pair of points
- double closest = DBL_MAX;
- int closeIndex SK_INIT_TO_AVOID_WARNING;
- int oppCloseIndex SK_INIT_TO_AVOID_WARNING;
- for (int index = 0; index < oppRayI.used(); ++index) {
- if (!roughly_between(span->fStartT, oppRayI[0][index], span->fEndT)) {
- continue;
- }
- for (int oIndex = 0; oIndex < thisRayI.used(); ++oIndex) {
- if (!roughly_between(oppSpan->fStartT, thisRayI[0][oIndex], oppSpan->fEndT)) {
- continue;
- }
- double distSq = thisRayI.pt(index).distanceSquared(oppRayI.pt(oIndex));
- if (closest > distSq) {
- closest = distSq;
- closeIndex = index;
- oppCloseIndex = oIndex;
+ if (!work->fCoinEnd.isCoincident()) {
+ if (work->fEndT == 1) {
+ SkDebugf("!");
+ }
+// SkASSERT(work->fEndT < 1);
+ startT = work->fStartT;
+ double coinEnd;
+ if (binarySearchCoin(*sect2, startT, work->fEndT - startT, &coinEnd, &oppT)) {
+ if (coinEnd > startT) {
+ SkTSpan<TCurve>* oppStart = sect2->fHead->find(oppT);
+ if (oppStart->fStartT < oppT && oppT < oppStart->fEndT) {
+ SkASSERT(coinEnd < work->fEndT);
+ // split prev at coinEnd if needed
+ SkTSpan<TCurve>* half2 = addOne();
+ half2->splitAt(work, coinEnd);
+ half2->initBounds(fCurve);
+ work->initBounds(fCurve);
+ work->fCoinStart.markCoincident();
+ work->fCoinEnd.markCoincident();
+ half2->fCoinStart.markCoincident();
+ SkTSpan<TCurve>* oppHalf = sect2->addOne();
+ oppHalf->splitAt(oppStart, oppT);
+ oppHalf->initBounds(sect2->fCurve);
+ oppStart->initBounds(sect2->fCurve);
+ } else {
+ SkASSERT(oppStart->fStartT == oppT || oppT == oppStart->fEndT);
+ SkTSpan<TCurve>* next = work->fNext;
+ bool hasNext = next && work->fEndT == next->fStartT;
+ work->fEndT = coinEnd;
+ work->initBounds(fCurve);
+ work->fCoinStart.markCoincident();
+ work->fCoinEnd.markCoincident();
+ if (hasNext) {
+ next->fStartT = coinEnd;
+ next->initBounds(fCurve);
+ }
}
}
}
- if (closest == DBL_MAX) {
- return 0;
- }
- const SkDPoint& oppIPt = thisRayI.pt(oppCloseIndex);
- const SkDPoint& iPt = oppRayI.pt(closeIndex);
- if (between(span->fStartT, oppRayI[0][closeIndex], span->fEndT)
- && between(oppSpan->fStartT, thisRayI[0][oppCloseIndex], oppSpan->fEndT)
- && oppIPt.approximatelyEqual(iPt)) {
- i->merge(oppRayI, closeIndex, thisRayI, oppCloseIndex);
- return i->used();
- }
- double distSq = oppIPt.distanceSquared(iPt);
- if (bestDistSq < distSq || ++loopCount > 5) {
- break;
- }
- bestDistSq = distSq;
- thisLine[0] = fCurve.ptAtT(oppRayI[0][closeIndex]);
- thisLine[1] = thisLine[0] + fCurve.dxdyAtT(oppRayI[0][closeIndex]);
- oppLine[0] = opp->fCurve.ptAtT(thisRayI[0][oppCloseIndex]);
- oppLine[1] = oppLine[0] + opp->fCurve.dxdyAtT(thisRayI[0][oppCloseIndex]);
- } while (true);
- return false;
-}
-
-
-template<typename TCurve>
-void SkTSect<TCurve>::markSpanGone(SkTSpan<TCurve>* span) {
- --fActiveCount;
- span->fNext = fDeleted;
- fDeleted = span;
- SkASSERT(!span->fDeleted);
- span->fDeleted = true;
-}
-
-template<typename TCurve>
-bool SkTSect<TCurve>::matchedDirection(double t, const SkTSect* sect2, double t2) const {
- SkDVector dxdy = this->fCurve.dxdyAtT(t);
- SkDVector dxdy2 = sect2->fCurve.dxdyAtT(t2);
- return dxdy.dot(dxdy2) >= 0;
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::matchedDirCheck(double t, const SkTSect* sect2, double t2,
- bool* calcMatched, bool* oppMatched) const {
- if (*calcMatched) {
- SkASSERT(*oppMatched == this->matchedDirection(t, sect2, t2));
- } else {
- *oppMatched = this->matchedDirection(t, sect2, t2);
- *calcMatched = true;
- }
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::mergeCoincidence(SkTSect* sect2) {
- double smallLimit = 0;
- do {
- // find the smallest unprocessed span
- SkTSpan<TCurve>* smaller = NULL;
- SkTSpan<TCurve>* test = fCoincident;
- do {
- if (test->fStartT < smallLimit) {
- continue;
- }
- if (smaller && smaller->fEndT < test->fStartT) {
- continue;
- }
- smaller = test;
- } while ((test = test->fNext));
- if (!smaller) {
- return;
- }
- smallLimit = smaller->fEndT;
- // find next larger span
- SkTSpan<TCurve>* prior = NULL;
- SkTSpan<TCurve>* larger = NULL;
- SkTSpan<TCurve>* largerPrior = NULL;
- test = fCoincident;
- do {
- if (test->fStartT < smaller->fEndT) {
- continue;
- }
- SkASSERT(test->fStartT != smaller->fEndT);
- if (larger && larger->fStartT < test->fStartT) {
- continue;
- }
- largerPrior = prior;
- larger = test;
- } while ((prior = test), (test = test->fNext));
- if (!larger) {
- continue;
- }
- // check middle t value to see if it is coincident as well
- double midT = (smaller->fEndT + larger->fStartT) / 2;
- SkDPoint midPt = fCurve.ptAtT(midT);
- SkTCoincident<TCurve> coin;
- coin.setPerp(fCurve, midT, midPt, sect2->fCurve);
- if (coin.isCoincident()) {
- smaller->fEndT = larger->fEndT;
- smaller->fCoinEnd = larger->fCoinEnd;
- if (largerPrior) {
- largerPrior->fNext = larger->fNext;
- } else {
- fCoincident = larger->fNext;
- }
- }
- } while (true);
-}
-
-template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::prev(SkTSpan<TCurve>* span) const {
- SkTSpan<TCurve>* result = NULL;
- SkTSpan<TCurve>* test = fHead;
- while (span != test) {
- result = test;
- test = test->fNext;
- SkASSERT(test);
}
- return result;
}
template<typename TCurve>
@@ -1333,84 +782,41 @@ void SkTSect<TCurve>::recoverCollapsed() {
}
template<typename TCurve>
-void SkTSect<TCurve>::removeAllBut(const SkTSpan<TCurve>* keep, SkTSpan<TCurve>* span,
- SkTSect* opp) {
- int count = span->fBounded.count();
- for (int index = 0; index < count; ++index) {
- SkTSpan<TCurve>* bounded = span->fBounded[0];
- if (bounded == keep) {
- continue;
- }
- if (bounded->fDeleted) { // may have been deleted when opp did 'remove all but'
- continue;
+void SkTSect<TCurve>::removeSpan(SkTSpan<TCurve>* span) {
+ SkTSpan<TCurve>* prev = span->fPrev;
+ SkTSpan<TCurve>* next = span->fNext;
+ if (prev) {
+ prev->fNext = next;
+ if (next) {
+ next->fPrev = prev;
}
- SkAssertResult(SkDEBUGCODE(!) span->removeBounded(bounded));
- if (bounded->removeBounded(span)) {
- opp->removeSpan(bounded);
+ } else {
+ fHead = next;
+ if (next) {
+ next->fPrev = NULL;
}
}
- SkASSERT(!span->fDeleted);
- SkASSERT(span->findOppSpan(keep));
- SkASSERT(keep->findOppSpan(span));
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::removeByPerpendicular(SkTSect<TCurve>* opp) {
- SkTSpan<TCurve>* test = fHead;
- SkTSpan<TCurve>* next;
- do {
- next = test->fNext;
- if (test->fCoinStart.perpT() < 0 || test->fCoinEnd.perpT() < 0) {
- continue;
- }
- SkDVector startV = test->fCoinStart.perpPt() - test->fPart[0];
- SkDVector endV = test->fCoinEnd.perpPt() - test->fPart[TCurve::kPointLast];
+ --fActiveCount;
+ span->fNext = fDeleted;
+ fDeleted = span;
#if DEBUG_T_SECT
- SkDebugf("%s startV=(%1.9g,%1.9g) endV=(%1.9g,%1.9g) dot=%1.9g\n", __FUNCTION__,
- startV.fX, startV.fY, endV.fX, endV.fY, startV.dot(endV));
+ SkASSERT(!span->fDebugDeleted);
+ span->fDebugDeleted = true;
#endif
- if (startV.dot(endV) <= 0) {
- continue;
- }
- this->removeSpans(test, opp);
- } while ((test = next));
}
template<typename TCurve>
-void SkTSect<TCurve>::removeCoincident(SkTSpan<TCurve>* span, bool isBetween) {
- this->unlinkSpan(span);
- if (isBetween || between(0, span->fCoinStart.perpT(), 1)) {
- --fActiveCount;
- span->fNext = fCoincident;
- fCoincident = span;
- } else {
- this->markSpanGone(span);
- }
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::removeSpan(SkTSpan<TCurve>* span) {
- this->unlinkSpan(span);
- this->markSpanGone(span);
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::removeSpanRange(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
- if (first == last) {
- return;
- }
- SkTSpan<TCurve>* span = first;
- SkASSERT(span);
- SkTSpan<TCurve>* final = last->fNext;
- SkTSpan<TCurve>* next = span->fNext;
- while ((span = next) && span != final) {
- next = span->fNext;
- this->markSpanGone(span);
- }
- if (final) {
- final->fPrev = first;
+void SkTSect<TCurve>::removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span) {
+ int last = span->fBounded.count() - 1;
+ for (int index = 0; index <= last; ++index) {
+ if (span->fBounded[index] == test) {
+ span->fBounded.removeShuffle(index);
+ if (!last) {
+ removeSpan(span);
+ }
+ return;
+ }
}
- first->fNext = final;
}
template<typename TCurve>
@@ -1418,33 +824,38 @@ void SkTSect<TCurve>::removeSpans(SkTSpan<TCurve>* span, SkTSect<TCurve>* opp) {
int count = span->fBounded.count();
for (int index = 0; index < count; ++index) {
SkTSpan<TCurve>* bounded = span->fBounded[0];
- if (span->removeBounded(bounded)) { // shuffles last into position 0
- this->removeSpan(span);
- }
- if (bounded->removeBounded(span)) {
- opp->removeSpan(bounded);
- }
- SkASSERT(!span->fDeleted || !opp->debugHasBounded(span));
-
+ removeOne(bounded, span); // shuffles last into position 0
+ opp->removeOne(span, bounded);
}
}
template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::spanAtT(double t, SkTSpan<TCurve>** priorSpan) {
- SkTSpan<TCurve>* test = fHead;
- SkTSpan<TCurve>* prev = NULL;
- while (test && test->fEndT < t) {
- prev = test;
- test = test->fNext;
+void SkTSect<TCurve>::setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
+ SkTSpan<TCurve>* work = first;
+ if (!work->fHasPerp) {
+ work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp);
}
- *priorSpan = prev;
- return test && test->fStartT <= t ? test : NULL;
+ do {
+ if (!work->fHasPerp) {
+ work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp);
+ work->fHasPerp = true;
+ }
+ if (work == last) {
+ break;
+ }
+ SkTSpan<TCurve>* last = work;
+ work = work->fNext;
+ SkASSERT(work);
+ if (!work->fHasPerp) {
+ work->fCoinStart = last->fCoinEnd;
+ }
+ } while (true);
}
template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::tail() {
- SkTSpan<TCurve>* result = fHead;
- SkTSpan<TCurve>* next = fHead;
+const SkTSpan<TCurve>* SkTSect<TCurve>::tail() const {
+ const SkTSpan<TCurve>* result = fHead;
+ const SkTSpan<TCurve>* next = fHead;
while ((next = next->fNext)) {
if (next->fEndT > result->fEndT) {
result = next;
@@ -1458,75 +869,32 @@ SkTSpan<TCurve>* SkTSect<TCurve>::tail() {
template<typename TCurve>
void SkTSect<TCurve>::trim(SkTSpan<TCurve>* span, SkTSect* opp) {
span->initBounds(fCurve);
- for (int index = 0; index < span->fBounded.count(); ) {
+ int count = span->fBounded.count();
+ for (int index = 0; index < count; ) {
SkTSpan<TCurve>* test = span->fBounded[index];
- int oppSects, sects = this->intersects(span, opp, test, &oppSects);
- if (sects >= 1) {
- if (sects == 2) {
- span->initBounds(fCurve);
- this->removeAllBut(test, span, opp);
- }
- if (oppSects == 2) {
- test->initBounds(opp->fCurve);
- opp->removeAllBut(span, test, this);
- }
+ bool sects = intersects(span, opp, test);
+ if (sects) {
++index;
} else {
- if (span->removeBounded(test)) {
- this->removeSpan(span);
- }
- if (test->removeBounded(span)) {
- opp->removeSpan(test);
- }
+ removeOne(test, span);
+ opp->removeOne(span, test);
+ --count;
}
}
}
-template<typename TCurve>
-void SkTSect<TCurve>::unlinkSpan(SkTSpan<TCurve>* span) {
- SkTSpan<TCurve>* prev = span->fPrev;
- SkTSpan<TCurve>* next = span->fNext;
- if (prev) {
- prev->fNext = next;
- if (next) {
- next->fPrev = prev;
- }
- } else {
- fHead = next;
- if (next) {
- next->fPrev = NULL;
- }
- }
-}
-
-template<typename TCurve>
-bool SkTSect<TCurve>::updateBounded(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last,
- SkTSpan<TCurve>* oppFirst) {
- SkTSpan<TCurve>* test = first;
- const SkTSpan<TCurve>* final = last->next();
- bool deleteSpan = false;
- do {
- deleteSpan |= test->removeAllBounded();
- } while ((test = test->fNext) != final);
- first->fBounded.resize_back(1);
- first->fBounded[0] = oppFirst;
- // cannot call validate until remove span range is called
- return deleteSpan;
-}
-
-
+#if DEBUG_T_SECT
template<typename TCurve>
void SkTSect<TCurve>::validate() const {
-#if DEBUG_T_SECT
int count = 0;
if (fHead) {
const SkTSpan<TCurve>* span = fHead;
SkASSERT(!span->fPrev);
- SkDEBUGCODE(double last = 0);
+ double last = 0;
do {
span->validate();
SkASSERT(span->fStartT >= last);
- SkDEBUGCODE(last = span->fEndT);
+ last = span->fEndT;
++count;
} while ((span = span->fNext) != NULL);
}
@@ -1538,81 +906,54 @@ void SkTSect<TCurve>::validate() const {
++deletedCount;
deleted = deleted->fNext;
}
- const SkTSpan<TCurve>* coincident = fCoincident;
- while (coincident) {
- ++deletedCount;
- coincident = coincident->fNext;
- }
SkASSERT(fActiveCount + deletedCount == fDebugAllocatedCount);
-#endif
}
-
-template<typename TCurve>
-void SkTSect<TCurve>::validateBounded() const {
-#if DEBUG_T_SECT
- if (!fHead) {
- return;
- }
- const SkTSpan<TCurve>* span = fHead;
- do {
- span->validateBounded();
- } while ((span = span->fNext) != NULL);
#endif
-}
template<typename TCurve>
int SkTSect<TCurve>::EndsEqual(const SkTSect* sect1, const SkTSect* sect2,
SkIntersections* intersections) {
int zeroOneSet = 0;
- if (sect1->fCurve[0] == sect2->fCurve[0]) {
- zeroOneSet |= kZeroS1Set | kZeroS2Set;
- intersections->insert(0, 0, sect1->fCurve[0]);
- }
- if (sect1->fCurve[0] == sect2->fCurve[TCurve::kPointLast]) {
- zeroOneSet |= kZeroS1Set | kOneS2Set;
- intersections->insert(0, 1, sect1->fCurve[0]);
- }
- if (sect1->fCurve[TCurve::kPointLast] == sect2->fCurve[0]) {
- zeroOneSet |= kOneS1Set | kZeroS2Set;
- intersections->insert(1, 0, sect1->fCurve[TCurve::kPointLast]);
- }
- if (sect1->fCurve[TCurve::kPointLast] == sect2->fCurve[TCurve::kPointLast]) {
- zeroOneSet |= kOneS1Set | kOneS2Set;
- intersections->insert(1, 1, sect1->fCurve[TCurve::kPointLast]);
- }
// check for zero
- if (!(zeroOneSet & (kZeroS1Set | kZeroS2Set))
- && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
+ if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
zeroOneSet |= kZeroS1Set | kZeroS2Set;
- intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]);
- }
- if (!(zeroOneSet & (kZeroS1Set | kOneS2Set))
- && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) {
+ if (sect1->fCurve[0] != sect2->fCurve[0]) {
+ intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]);
+ } else {
+ intersections->insert(0, 0, sect1->fCurve[0]);
+ }
+ }
+ if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) {
zeroOneSet |= kZeroS1Set | kOneS2Set;
- intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[TCurve::kPointLast]);
- }
+ if (sect1->fCurve[0] != sect2->fCurve[TCurve::kPointLast]) {
+ intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[TCurve::kPointLast]);
+ } else {
+ intersections->insert(0, 1, sect1->fCurve[0]);
+ }
+ }
// check for one
- if (!(zeroOneSet & (kOneS1Set | kZeroS2Set))
- && sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
+ if (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
zeroOneSet |= kOneS1Set | kZeroS2Set;
- intersections->insertNear(1, 0, sect1->fCurve[TCurve::kPointLast], sect2->fCurve[0]);
- }
- if (!(zeroOneSet & (kOneS1Set | kOneS2Set))
- && sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[
- TCurve::kPointLast])) {
+ if (sect1->fCurve[TCurve::kPointLast] != sect2->fCurve[0]) {
+ intersections->insertNear(1, 0, sect1->fCurve[TCurve::kPointLast], sect2->fCurve[0]);
+ } else {
+ intersections->insert(1, 0, sect1->fCurve[TCurve::kPointLast]);
+ }
+ }
+ if (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) {
zeroOneSet |= kOneS1Set | kOneS2Set;
- intersections->insertNear(1, 1, sect1->fCurve[TCurve::kPointLast],
- sect2->fCurve[TCurve::kPointLast]);
+ if (sect1->fCurve[TCurve::kPointLast] != sect2->fCurve[TCurve::kPointLast]) {
+ intersections->insertNear(1, 1, sect1->fCurve[TCurve::kPointLast],
+ sect2->fCurve[TCurve::kPointLast]);
+ } else {
+ intersections->insert(1, 1, sect1->fCurve[TCurve::kPointLast]);
+ }
}
return zeroOneSet;
}
template<typename TCurve>
struct SkClosestRecord {
- bool operator<(const SkClosestRecord& rh) const {
- return fClosest < rh.fClosest;
- }
-
void addIntersection(SkIntersections* intersections) const {
double r1t = fC1Index ? fC1Span->endT() : fC1Span->startT();
double r2t = fC2Index ? fC2Span->endT() : fC2Span->startT();
@@ -1692,14 +1033,14 @@ struct SkClosestSect {
fClosest.push_back().reset();
}
- bool find(const SkTSpan<TCurve>* span1, const SkTSpan<TCurve>* span2) {
+ void find(const SkTSpan<TCurve>* span1, const SkTSpan<TCurve>* span2) {
SkClosestRecord<TCurve>* record = &fClosest[fUsed];
record->findEnd(span1, span2, 0, 0);
record->findEnd(span1, span2, 0, TCurve::kPointLast);
record->findEnd(span1, span2, TCurve::kPointLast, 0);
record->findEnd(span1, span2, TCurve::kPointLast, TCurve::kPointLast);
if (record->fClosest == FLT_MAX) {
- return false;
+ return;
}
for (int index = 0; index < fUsed; ++index) {
SkClosestRecord<TCurve>* test = &fClosest[index];
@@ -1709,46 +1050,37 @@ struct SkClosestSect {
}
test->update(*record);
record->reset();
- return false;
+ return;
}
}
++fUsed;
fClosest.push_back().reset();
- return true;
}
void finish(SkIntersections* intersections) const {
- SkSTArray<TCurve::kMaxIntersections * 2, const SkClosestRecord<TCurve>*, true> closestPtrs;
- for (int index = 0; index < fUsed; ++index) {
- closestPtrs.push_back(&fClosest[index]);
- }
- SkTQSort<const SkClosestRecord<TCurve> >(closestPtrs.begin(), closestPtrs.end() - 1);
for (int index = 0; index < fUsed; ++index) {
- const SkClosestRecord<TCurve>* test = closestPtrs[index];
- test->addIntersection(intersections);
+ const SkClosestRecord<TCurve>& test = fClosest[index];
+ test.addIntersection(intersections);
}
}
- // this is oversized so that an extra records can merge into final one
- SkSTArray<TCurve::kMaxIntersections * 2, SkClosestRecord<TCurve>, true> fClosest;
+ // this is oversized by one so that an extra record can merge into final one
+ SkSTArray<TCurve::kMaxIntersections + 1, SkClosestRecord<TCurve>, true> fClosest;
int fUsed;
};
// returns true if the rect is too small to consider
template<typename TCurve>
void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersections* intersections) {
- PATH_OPS_DEBUG_CODE(sect1->fOppSect = sect2);
- PATH_OPS_DEBUG_CODE(sect2->fOppSect = sect1);
intersections->reset();
- intersections->setMax(TCurve::kMaxIntersections * 2); // give extra for slop
+ intersections->setMax(TCurve::kMaxIntersections);
SkTSpan<TCurve>* span1 = sect1->fHead;
SkTSpan<TCurve>* span2 = sect2->fHead;
- int oppSect, sect = sect1->intersects(span1, sect2, span2, &oppSect);
-// SkASSERT(between(0, sect, 2));
- if (!sect) {
+ bool check;
+ if (!span1->intersects(span2, &check)) {
return;
}
- if (sect == 2 && oppSect == 2) {
+ if (check) {
(void) EndsEqual(sect1, sect2, intersections);
return;
}
@@ -1764,12 +1096,12 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio
bool split1 = !largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsMax
|| (!largest1->fCollapsed && largest2->fCollapsed)));
// split it
+ SkTSect* splitSect = split1 ? sect1 : sect2;
SkTSpan<TCurve>* half1 = split1 ? largest1 : largest2;
SkASSERT(half1);
if (half1->fCollapsed) {
break;
}
- SkTSect* splitSect = split1 ? sect1 : sect2;
// trim parts that don't intersect the opposite
SkTSpan<TCurve>* half2 = splitSect->addOne();
SkTSect* unsplitSect = split1 ? sect2 : sect1;
@@ -1778,52 +1110,50 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio
}
splitSect->trim(half1, unsplitSect);
splitSect->trim(half2, unsplitSect);
- sect1->validate();
- sect2->validate();
// if there are 9 or more continuous spans on both sects, suspect coincidence
if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
&& sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
sect1->coincidentCheck(sect2);
- sect1->validate();
- sect2->validate();
}
- if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
- && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
- sect1->computePerpendiculars(sect2, sect1->fHead, sect1->tail());
- sect2->computePerpendiculars(sect1, sect2->fHead, sect2->tail());
- sect1->removeByPerpendicular(sect2);
- sect1->validate();
- sect2->validate();
- }
-#if DEBUG_T_SECT_DUMP
- sect1->dumpBoth(sect2);
+#if DEBUG_T_SECT
+ sect1->validate();
+ sect2->validate();
+#endif
+#if DEBUG_T_SECT_DUMP > 1
+ sect1->dumpBoth(*sect2);
#endif
if (!sect1->fHead || !sect2->fHead) {
- break;
+ return;
}
} while (true);
- SkTSpan<TCurve>* coincident = sect1->fCoincident;
- if (coincident) {
- // if there is more than one coincident span, check loosely to see if they should be joined
- if (coincident->fNext) {
- sect1->mergeCoincidence(sect2);
- coincident = sect1->fCoincident;
- }
- SkASSERT(sect2->fCoincident); // courtesy check : coincidence only looks at sect 1
+ if (sect1->fActiveCount >= 2 && sect2->fActiveCount >= 2) {
+ // check for coincidence
+ SkTSpan<TCurve>* first = sect1->fHead;
do {
- SkASSERT(coincident->fCoinStart.isCoincident());
- SkASSERT(coincident->fCoinEnd.isCoincident());
- int index = intersections->insertCoincident(coincident->fStartT,
- coincident->fCoinStart.perpT(), coincident->fPart[0]);
- if ((intersections->insertCoincident(coincident->fEndT,
- coincident->fCoinEnd.perpT(),
- coincident->fPart[TCurve::kPointLast]) < 0) && index >= 0) {
+ if (!first->fCoinStart.isCoincident()) {
+ continue;
+ }
+ int spanCount = 1;
+ SkTSpan<TCurve>* last = first;
+ while (last->fCoinEnd.isCoincident()) {
+ SkTSpan<TCurve>* next = last->fNext;
+ if (!next || !next->fCoinEnd.isCoincident()) {
+ break;
+ }
+ last = next;
+ ++spanCount;
+ }
+ if (spanCount < 2) {
+ first = last;
+ continue;
+ }
+ int index = intersections->insertCoincident(first->fStartT, first->fCoinStart.perpT(),
+ first->fPart[0]);
+ if (intersections->insertCoincident(last->fEndT, last->fCoinEnd.perpT(),
+ last->fPart[TCurve::kPointLast]) < 0) {
intersections->clearCoincidence(index);
}
- } while ((coincident = coincident->fNext));
- }
- if (!sect1->fHead || !sect2->fHead) {
- return;
+ } while ((first = first->fNext));
}
int zeroOneSet = EndsEqual(sect1, sect2, intersections);
sect1->recoverCollapsed();
@@ -1833,41 +1163,33 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio
const SkTSpan<TCurve>* head1 = result1;
if (!(zeroOneSet & kZeroS1Set) && approximately_less_than_zero(head1->fStartT)) {
const SkDPoint& start1 = sect1->fCurve[0];
- if (head1->isBounded()) {
- double t = head1->closestBoundedT(start1);
- if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) {
- intersections->insert(0, t, start1);
- }
+ double t = head1->closestBoundedT(start1);
+ if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) {
+ intersections->insert(0, t, start1);
}
}
const SkTSpan<TCurve>* head2 = sect2->fHead;
if (!(zeroOneSet & kZeroS2Set) && approximately_less_than_zero(head2->fStartT)) {
const SkDPoint& start2 = sect2->fCurve[0];
- if (head2->isBounded()) {
- double t = head2->closestBoundedT(start2);
- if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) {
- intersections->insert(t, 0, start2);
- }
+ double t = head2->closestBoundedT(start2);
+ if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) {
+ intersections->insert(t, 0, start2);
}
}
const SkTSpan<TCurve>* tail1 = sect1->tail();
if (!(zeroOneSet & kOneS1Set) && approximately_greater_than_one(tail1->fEndT)) {
const SkDPoint& end1 = sect1->fCurve[TCurve::kPointLast];
- if (tail1->isBounded()) {
- double t = tail1->closestBoundedT(end1);
- if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) {
- intersections->insert(1, t, end1);
- }
+ double t = tail1->closestBoundedT(end1);
+ if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) {
+ intersections->insert(1, t, end1);
}
}
const SkTSpan<TCurve>* tail2 = sect2->tail();
if (!(zeroOneSet & kOneS2Set) && approximately_greater_than_one(tail2->fEndT)) {
const SkDPoint& end2 = sect2->fCurve[TCurve::kPointLast];
- if (tail2->isBounded()) {
- double t = tail2->closestBoundedT(end2);
- if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
- intersections->insert(t, 1, end2);
- }
+ double t = tail2->closestBoundedT(end2);
+ if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
+ intersections->insert(t, 1, end2);
}
}
SkClosestSect<TCurve> closest;
@@ -1879,39 +1201,11 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio
break;
}
SkTSpan<TCurve>* result2 = sect2->fHead;
- bool found = false;
while (result2) {
- found |= closest.find(result1, result2);
+ closest.find(result1, result2);
result2 = result2->fNext;
}
+
} while ((result1 = result1->fNext));
closest.finish(intersections);
- // if there is more than one intersection and it isn't already coincident, check
- int last = intersections->used() - 1;
- for (int index = 0; index < last; ) {
- if (intersections->isCoincident(index) && intersections->isCoincident(index + 1)) {
- ++index;
- continue;
- }
- double midT = ((*intersections)[0][index] + (*intersections)[0][index + 1]) / 2;
- SkDPoint midPt = sect1->fCurve.ptAtT(midT);
- // intersect perpendicular with opposite curve
- SkTCoincident<TCurve> perp;
- perp.setPerp(sect1->fCurve, midT, midPt, sect2->fCurve);
- if (!perp.isCoincident()) {
- ++index;
- continue;
- }
- if (intersections->isCoincident(index)) {
- intersections->removeOne(index);
- --last;
- } else if (intersections->isCoincident(index + 1)) {
- intersections->removeOne(index + 1);
- --last;
- } else {
- intersections->setCoincident(index++);
- }
- intersections->setCoincident(index);
- }
- SkASSERT(intersections->used() <= TCurve::kMaxIntersections);
}
diff --git a/src/pathops/SkPathOpsTightBounds.cpp b/src/pathops/SkPathOpsTightBounds.cpp
index d03efeb173..0f63f396e7 100644
--- a/src/pathops/SkPathOpsTightBounds.cpp
+++ b/src/pathops/SkPathOpsTightBounds.cpp
@@ -8,16 +8,14 @@
#include "SkPathOpsCommon.h"
bool TightBounds(const SkPath& path, SkRect* result) {
- SkOpContour contour;
- SkOpGlobalState globalState( NULL PATH_OPS_DEBUG_PARAMS(&contour));
// turn path into list of segments
- SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
- SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
- if (!builder.finish(&allocator)) {
+ SkTArray<SkOpContour> contours;
+ SkOpEdgeBuilder builder(path, contours);
+ if (!builder.finish()) {
return false;
}
- SkTDArray<SkOpContour* > contourList;
- MakeContourList(&contour, contourList, false, false);
+ SkTArray<SkOpContour*, true> contourList;
+ MakeContourList(contours, contourList, false, false);
SkOpContour** currentPtr = contourList.begin();
result->setEmpty();
if (!currentPtr) {
diff --git a/src/pathops/SkPathOpsTriangle.cpp b/src/pathops/SkPathOpsTriangle.cpp
new file mode 100644
index 0000000000..77845e0673
--- /dev/null
+++ b/src/pathops/SkPathOpsTriangle.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPathOpsTriangle.h"
+
+// http://www.blackpawn.com/texts/pointinpoly/default.html
+// return true if pt is inside triangle; false if outside or on the line
+bool SkDTriangle::contains(const SkDPoint& pt) const {
+// Compute vectors
+ SkDVector v0 = fPts[2] - fPts[0];
+ SkDVector v1 = fPts[1] - fPts[0];
+ SkDVector v2 = pt - fPts[0];
+
+// Compute dot products
+ double dot00 = v0.dot(v0);
+ double dot01 = v0.dot(v1);
+ double dot02 = v0.dot(v2);
+ double dot11 = v1.dot(v1);
+ double dot12 = v1.dot(v2);
+
+// original code doesn't handle degenerate input; isn't symmetric with inclusion of corner pts;
+// introduces error with divide; doesn't short circuit on early answer
+#if 0
+// Compute barycentric coordinates
+ double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
+ double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ double v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+// Check if point is in triangle
+ return (u >= 0) && (v >= 0) && (u + v <= 1);
+#else
+ double w = dot00 * dot11 - dot01 * dot01;
+ if (w == 0) {
+ return false;
+ }
+ double wSign = w < 0 ? -1 : 1;
+ double u = (dot11 * dot02 - dot01 * dot12) * wSign;
+ if (u <= 0) {
+ return false;
+ }
+ double v = (dot00 * dot12 - dot01 * dot02) * wSign;
+ if (v <= 0) {
+ return false;
+ }
+ return u + v < w * wSign;
+#endif
+}
diff --git a/src/pathops/SkPathOpsTriangle.h b/src/pathops/SkPathOpsTriangle.h
new file mode 100644
index 0000000000..8cc8c6d3b5
--- /dev/null
+++ b/src/pathops/SkPathOpsTriangle.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPathOpsTriangle_DEFINED
+#define SkPathOpsTriangle_DEFINED
+
+#include "SkPathOpsPoint.h"
+
+struct SkDTriangle {
+ SkDPoint fPts[3];
+
+ bool contains(const SkDPoint& pt) const;
+
+};
+
+#endif
diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h
index 0248e7115a..01fec0d0b6 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -22,111 +22,6 @@ enum SkPathOpsMask {
kEvenOdd_PathOpsMask = 1
};
-class SkOpCoincidence;
-class SkOpContour;
-
-class SkOpGlobalState {
-public:
- SkOpGlobalState(SkOpCoincidence* coincidence PATH_OPS_DEBUG_PARAMS(SkOpContour* head))
- : fCoincidence(coincidence)
- , fWindingFailed(false)
- , fAngleCoincidence(false)
-#if DEBUG_VALIDATE
- , fPhase(kIntersecting)
-#endif
- PATH_OPS_DEBUG_PARAMS(fHead(head))
- PATH_OPS_DEBUG_PARAMS(fAngleID(0))
- PATH_OPS_DEBUG_PARAMS(fContourID(0))
- PATH_OPS_DEBUG_PARAMS(fPtTID(0))
- PATH_OPS_DEBUG_PARAMS(fSegmentID(0))
- PATH_OPS_DEBUG_PARAMS(fSpanID(0)) {
- }
-
-#if DEBUG_VALIDATE
- enum Phase {
- kIntersecting,
- kWalking
- };
-#endif
-
- bool angleCoincidence() {
- return fAngleCoincidence;
- }
-
- SkOpCoincidence* coincidence() {
- return fCoincidence;
- }
-
-#ifdef SK_DEBUG
- const struct SkOpAngle* debugAngle(int id) const;
- SkOpContour* debugContour(int id);
- const class SkOpPtT* debugPtT(int id) const;
- const class SkOpSegment* debugSegment(int id) const;
- const class SkOpSpanBase* debugSpan(int id) const;
-
- int nextAngleID() {
- return ++fAngleID;
- }
-
- int nextContourID() {
- return ++fContourID;
- }
- int nextPtTID() {
- return ++fPtTID;
- }
-
- int nextSegmentID() {
- return ++fSegmentID;
- }
-
- int nextSpanID() {
- return ++fSpanID;
- }
-#endif
-
-#if DEBUG_VALIDATE
- Phase phase() const {
- return fPhase;
- }
-#endif
-
- void setAngleCoincidence() {
- fAngleCoincidence = true;
- }
-
-#if DEBUG_VALIDATE
- void setPhase(Phase phase) {
- SkASSERT(fPhase != phase);
- fPhase = phase;
- }
-#endif
-
- // called in very rare cases where angles are sorted incorrectly -- signfies op will fail
- void setWindingFailed() {
- fWindingFailed = true;
- }
-
- bool windingFailed() const {
- return fWindingFailed;
- }
-
-private:
- SkOpCoincidence* fCoincidence;
- bool fWindingFailed;
- bool fAngleCoincidence;
-#if DEBUG_VALIDATE
- Phase fPhase;
-#endif
-#ifdef SK_DEBUG
- SkOpContour* fHead;
- int fAngleID;
- int fContourID;
- int fPtTID;
- int fSegmentID;
- int fSpanID;
-#endif
-};
-
// Use Almost Equal when comparing coordinates. Use epsilon to compare T values.
bool AlmostEqualUlps(float a, float b);
inline bool AlmostEqualUlps(double a, double b) {
@@ -197,7 +92,6 @@ const double DBL_EPSILON_SUBDIVIDE_ERR = DBL_EPSILON * 16;
const double ROUGH_EPSILON = FLT_EPSILON * 64;
const double MORE_ROUGH_EPSILON = FLT_EPSILON * 256;
const double WAY_ROUGH_EPSILON = FLT_EPSILON * 2048;
-const double BUMP_EPSILON = FLT_EPSILON * 4096;
inline bool zero_or_one(double x) {
return x == 0 || x == 1;
@@ -247,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;
}
@@ -256,10 +156,6 @@ inline bool approximately_zero_when_compared_to(double x, double y) {
return x == 0 || fabs(x) < fabs(y * FLT_EPSILON);
}
-inline bool precisely_zero_when_compared_to(double x, double y) {
- return x == 0 || fabs(x) < fabs(y * DBL_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) {
@@ -408,8 +304,7 @@ inline bool precisely_between(double a, double b, double c) {
// returns true if (a <= b <= c) || (a >= b >= c)
inline bool between(double a, double b, double c) {
- SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
- || (precisely_zero(a) && precisely_zero(b) && precisely_zero(c)));
+ SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0));
return (a - b) * (c - b) <= 0;
}
@@ -417,15 +312,6 @@ inline bool roughly_equal(double x, double y) {
return fabs(x - y) < ROUGH_EPSILON;
}
-inline bool roughly_negative(double x) {
- return x < ROUGH_EPSILON;
-}
-
-inline bool roughly_between(double a, double b, double c) {
- return a <= c ? roughly_negative(a - b) && roughly_negative(b - c)
- : roughly_negative(b - a) && roughly_negative(c - b);
-}
-
inline bool more_roughly_equal(double x, double y) {
return fabs(x - y) < MORE_ROUGH_EPSILON;
}
@@ -438,6 +324,7 @@ struct SkDPoint;
struct SkDVector;
struct SkDLine;
struct SkDQuad;
+struct SkDTriangle;
struct SkDCubic;
struct SkDRect;
diff --git a/src/pathops/SkQuarticRoot.cpp b/src/pathops/SkQuarticRoot.cpp
new file mode 100644
index 0000000000..f9a7bf5179
--- /dev/null
+++ b/src/pathops/SkQuarticRoot.cpp
@@ -0,0 +1,168 @@
+// from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
+/*
+ * Roots3And4.c
+ *
+ * Utility functions to find cubic and quartic roots,
+ * coefficients are passed like this:
+ *
+ * c[0] + c[1]*x + c[2]*x^2 + c[3]*x^3 + c[4]*x^4 = 0
+ *
+ * The functions return the number of non-complex roots and
+ * put the values into the s array.
+ *
+ * Author: Jochen Schwarze (schwarze@isa.de)
+ *
+ * Jan 26, 1990 Version for Graphics Gems
+ * Oct 11, 1990 Fixed sign problem for negative q's in SolveQuartic
+ * (reported by Mark Podlipec),
+ * Old-style function definitions,
+ * IsZero() as a macro
+ * Nov 23, 1990 Some systems do not declare acos() and cbrt() in
+ * <math.h>, though the functions exist in the library.
+ * If large coefficients are used, EQN_EPS should be
+ * reduced considerably (e.g. to 1E-30), results will be
+ * correct but multiple roots might be reported more
+ * than once.
+ */
+
+#include "SkPathOpsCubic.h"
+#include "SkPathOpsQuad.h"
+#include "SkQuarticRoot.h"
+
+int SkReducedQuarticRoots(const double t4, const double t3, const double t2, const double t1,
+ const double t0, const bool oneHint, double roots[4]) {
+#ifdef SK_DEBUG
+ // create a string mathematica understands
+ // GDB set print repe 15 # if repeated digits is a bother
+ // set print elements 400 # if line doesn't fit
+ char str[1024];
+ sk_bzero(str, sizeof(str));
+ SK_SNPRINTF(str, sizeof(str),
+ "Solve[%1.19g x^4 + %1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]",
+ t4, t3, t2, t1, t0);
+ SkPathOpsDebug::MathematicaIze(str, sizeof(str));
+#if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
+ SkDebugf("%s\n", str);
+#endif
+#endif
+ if (approximately_zero_when_compared_to(t4, t0) // 0 is one root
+ && approximately_zero_when_compared_to(t4, t1)
+ && approximately_zero_when_compared_to(t4, t2)) {
+ if (approximately_zero_when_compared_to(t3, t0)
+ && approximately_zero_when_compared_to(t3, t1)
+ && approximately_zero_when_compared_to(t3, t2)) {
+ return SkDQuad::RootsReal(t2, t1, t0, roots);
+ }
+ if (approximately_zero_when_compared_to(t4, t3)) {
+ return SkDCubic::RootsReal(t3, t2, t1, t0, roots);
+ }
+ }
+ if ((approximately_zero_when_compared_to(t0, t1) || approximately_zero(t1)) // 0 is one root
+ // && approximately_zero_when_compared_to(t0, t2)
+ && approximately_zero_when_compared_to(t0, t3)
+ && approximately_zero_when_compared_to(t0, t4)) {
+ int num = SkDCubic::RootsReal(t4, t3, t2, t1, roots);
+ for (int i = 0; i < num; ++i) {
+ if (approximately_zero(roots[i])) {
+ return num;
+ }
+ }
+ roots[num++] = 0;
+ return num;
+ }
+ if (oneHint) {
+ SkASSERT(approximately_zero_double(t4 + t3 + t2 + t1 + t0) ||
+ approximately_zero_when_compared_to(t4 + t3 + t2 + t1 + t0, // 1 is one root
+ SkTMax(fabs(t4), SkTMax(fabs(t3), SkTMax(fabs(t2), SkTMax(fabs(t1), fabs(t0)))))));
+ // note that -C == A + B + D + E
+ int num = SkDCubic::RootsReal(t4, t4 + t3, -(t1 + t0), -t0, roots);
+ for (int i = 0; i < num; ++i) {
+ if (approximately_equal(roots[i], 1)) {
+ return num;
+ }
+ }
+ roots[num++] = 1;
+ return num;
+ }
+ return -1;
+}
+
+int SkQuarticRootsReal(int firstCubicRoot, const double A, const double B, const double C,
+ const double D, const double E, double s[4]) {
+ double u, v;
+ /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
+ const double invA = 1 / A;
+ const double a = B * invA;
+ const double b = C * invA;
+ const double c = D * invA;
+ const double d = E * invA;
+ /* substitute x = y - a/4 to eliminate cubic term:
+ x^4 + px^2 + qx + r = 0 */
+ const double a2 = a * a;
+ const double p = -3 * a2 / 8 + b;
+ const double q = a2 * a / 8 - a * b / 2 + c;
+ const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d;
+ int num;
+ double largest = SkTMax(fabs(p), fabs(q));
+ if (approximately_zero_when_compared_to(r, largest)) {
+ /* no absolute term: y(y^3 + py + q) = 0 */
+ num = SkDCubic::RootsReal(1, 0, p, q, s);
+ s[num++] = 0;
+ } else {
+ /* solve the resolvent cubic ... */
+ double cubicRoots[3];
+ int roots = SkDCubic::RootsReal(1, -p / 2, -r, r * p / 2 - q * q / 8, cubicRoots);
+ int index;
+ /* ... and take one real solution ... */
+ double z;
+ num = 0;
+ int num2 = 0;
+ for (index = firstCubicRoot; index < roots; ++index) {
+ z = cubicRoots[index];
+ /* ... to build two quadric equations */
+ u = z * z - r;
+ v = 2 * z - p;
+ if (approximately_zero_squared(u)) {
+ u = 0;
+ } else if (u > 0) {
+ u = sqrt(u);
+ } else {
+ continue;
+ }
+ if (approximately_zero_squared(v)) {
+ v = 0;
+ } else if (v > 0) {
+ v = sqrt(v);
+ } else {
+ continue;
+ }
+ num = SkDQuad::RootsReal(1, q < 0 ? -v : v, z - u, s);
+ num2 = SkDQuad::RootsReal(1, q < 0 ? v : -v, z + u, s + num);
+ if (!((num | num2) & 1)) {
+ break; // prefer solutions without single quad roots
+ }
+ }
+ num += num2;
+ if (!num) {
+ return 0; // no valid cubic root
+ }
+ }
+ /* resubstitute */
+ const double sub = a / 4;
+ for (int i = 0; i < num; ++i) {
+ s[i] -= sub;
+ }
+ // eliminate duplicates
+ for (int i = 0; i < num - 1; ++i) {
+ for (int j = i + 1; j < num; ) {
+ if (AlmostDequalUlps(s[i], s[j])) {
+ if (j < --num) {
+ s[j] = s[num];
+ }
+ } else {
+ ++j;
+ }
+ }
+ }
+ return num;
+}
diff --git a/src/pathops/SkQuarticRoot.h b/src/pathops/SkQuarticRoot.h
new file mode 100644
index 0000000000..6ce08671e2
--- /dev/null
+++ b/src/pathops/SkQuarticRoot.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkDQuarticRoot_DEFINED
+#define SkDQuarticRoot_DEFINED
+
+int SkReducedQuarticRoots(const double t4, const double t3, const double t2, const double t1,
+ const double t0, const bool oneHint, double s[4]);
+
+int SkQuarticRootsReal(int firstCubicRoot, const double A, const double B, const double C,
+ const double D, const double E, double s[4]);
+
+#endif
diff --git a/src/pathops/SkReduceOrder.cpp b/src/pathops/SkReduceOrder.cpp
index c19cd3db4b..6f06447a47 100644
--- a/src/pathops/SkReduceOrder.cpp
+++ b/src/pathops/SkReduceOrder.cpp
@@ -272,11 +272,6 @@ SkPath::Verb SkReduceOrder::Quad(const SkPoint a[3], SkPoint* reducePts) {
}
SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkPoint* reducePts) {
- if (SkDPoint::ApproximatelyEqual(a[0], a[1]) && SkDPoint::ApproximatelyEqual(a[0], a[2])
- && SkDPoint::ApproximatelyEqual(a[0], a[3])) {
- reducePts[0] = a[0];
- return SkPath::kMove_Verb;
- }
SkDCubic cubic;
cubic.set(a);
SkReduceOrder reducer;
diff --git a/src/pathops/SkReduceOrder.h b/src/pathops/SkReduceOrder.h
index 397b58d927..4ff9a1d127 100644
--- a/src/pathops/SkReduceOrder.h
+++ b/src/pathops/SkReduceOrder.h
@@ -7,9 +7,11 @@
#ifndef SkReduceOrder_DEFINED
#define SkReduceOrder_DEFINED
+#include "SkPath.h"
#include "SkPathOpsCubic.h"
#include "SkPathOpsLine.h"
#include "SkPathOpsQuad.h"
+#include "SkTArray.h"
union SkReduceOrder {
enum Quadratics {