aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar caryclark <caryclark@google.com>2015-03-26 07:52:43 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-03-26 07:52:43 -0700
commit54359294a7c9dc54802d512a5d891a35c1663392 (patch)
tree7339bbad708bb43a4a96f7b76075c84ff7732189 /src
parentc08330f1601aeca7f10aeb2194118decbfbf83e1 (diff)
cumulative pathops patch
Replace the implicit curve intersection with a geometric curve intersection. The implicit intersection proved mathematically unstable and took a long time to zero in on an answer. Use pointers instead of indices to refer to parts of curves. Indices required awkward renumbering. Unify t and point values so that small intervals can be eliminated in one pass. Break cubics up front to eliminate loops and cusps. Make the Simplify and Op code more regular and eliminate arbitrary differences. Add a builder that takes an array of paths and operators. Delete unused code. BUG=skia:3588 R=reed@google.com Review URL: https://codereview.chromium.org/1037573004
Diffstat (limited to 'src')
-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.cpp158
-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
-rw-r--r--src/pathops/SkOpBuilder.cpp6
-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.h480
-rw-r--r--src/pathops/SkOpEdgeBuilder.cpp100
-rw-r--r--src/pathops/SkOpEdgeBuilder.h39
-rw-r--r--src/pathops/SkOpSegment.cpp4800
-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.cpp202
-rw-r--r--src/pathops/SkPathOpsPoint.h53
-rwxr-xr-x[-rw-r--r--]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.h1723
-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
56 files changed, 6169 insertions, 10123 deletions
diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp
index c27434f9f7..b507eb7560 100644
--- a/src/pathops/SkAddIntersections.cpp
+++ b/src/pathops/SkAddIntersections.cpp
@@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "SkAddIntersections.h"
+#include "SkOpCoincidence.h"
#include "SkPathOpsBounds.h"
#if DEBUG_ADD_INTERSECTING_TS
@@ -130,20 +131,6 @@ 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& ) {
@@ -168,13 +155,10 @@ 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) {
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
+ SkChunkAlloc* allocator) {
if (test != next) {
if (AlmostLessUlps(test->bounds().fBottom, next->bounds().fTop)) {
return false;
@@ -186,10 +170,11 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
}
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;
}
@@ -306,14 +291,22 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
break;
}
case SkIntersectionHelper::kQuad_Segment: {
- pts = ts.quadQuad(wt.pts(), wn.pts());
- ts.alignQuadPts(wt.pts(), wn.pts());
+ SkDQuad quad1;
+ quad1.set(wt.pts());
+ SkDQuad quad2;
+ quad2.set(wn.pts());
+ pts = ts.intersect(quad1, quad2);
debugShowQuadIntersection(pts, wt, wn, ts);
break;
}
case SkIntersectionHelper::kCubic_Segment: {
swap = true;
- pts = ts.cubicQuad(wn.pts(), wt.pts());
+ SkDQuad quad1;
+ quad1.set(wt.pts());
+ SkDCubic cubic1 = quad1.toCubic();
+ SkDCubic cubic2;
+ cubic2.set(wn.pts());
+ pts = ts.intersect(cubic2, cubic1);
debugShowCubicQuadIntersection(pts, wn, wt, ts);
break;
}
@@ -339,12 +332,21 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
break;
}
case SkIntersectionHelper::kQuad_Segment: {
- pts = ts.cubicQuad(wt.pts(), wn.pts());
+ SkDCubic cubic1;
+ cubic1.set(wt.pts());
+ SkDQuad quad2;
+ quad2.set(wn.pts());
+ SkDCubic cubic2 = quad2.toCubic();
+ pts = ts.intersect(cubic1, cubic2);
debugShowCubicQuadIntersection(pts, wt, wn, ts);
break;
}
case SkIntersectionHelper::kCubic_Segment: {
- pts = ts.cubicCubic(wt.pts(), wn.pts());
+ SkDCubic cubic1;
+ cubic1.set(wt.pts());
+ SkDCubic cubic2;
+ cubic2.set(wn.pts());
+ pts = ts.intersect(cubic1, cubic2);
debugShowCubicIntersection(pts, wt, wn, ts);
break;
}
@@ -355,102 +357,53 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
default:
SkASSERT(0);
}
- 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)
- }
- }
- }
- }
+ int coinIndex = -1;
+ SkOpPtT* coinPtT[2];
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);
- 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);
+ 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;
}
+ 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 4c1947b635..23654e5e91 100644
--- a/src/pathops/SkAddIntersections.h
+++ b/src/pathops/SkAddIntersections.h
@@ -9,10 +9,10 @@
#include "SkIntersectionHelper.h"
#include "SkIntersections.h"
-#include "SkTArray.h"
-bool AddIntersectTs(SkOpContour* test, SkOpContour* next);
-void AddSelfIntersectTs(SkOpContour* test);
-bool CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total);
+class SkOpCoincidence;
+
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
+ SkChunkAlloc* allocator);
#endif
diff --git a/src/pathops/SkDCubicIntersection.cpp b/src/pathops/SkDCubicIntersection.cpp
deleted file mode 100644
index 2fb35e1827..0000000000
--- a/src/pathops/SkDCubicIntersection.cpp
+++ /dev/null
@@ -1,704 +0,0 @@
-/*
- * 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 696c42e835..f5fe01503b 100644
--- a/src/pathops/SkDCubicLineIntersection.cpp
+++ b/src/pathops/SkDCubicLineIntersection.cpp
@@ -93,6 +93,29 @@ 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;
@@ -131,32 +154,11 @@ public:
double cubicT = rootVals[index];
double lineT = findLineT(cubicT);
SkDPoint 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;
- }
- }
+ if (pinTs(&cubicT, &lineT, &pt, kPointUninitialized) && uniqueAnswer(cubicT, pt)) {
fIntersections->insert(cubicT, lineT, pt);
- skipInsert:
- ;
}
}
+ checkCoincident();
return fIntersections->used();
}
@@ -186,20 +188,43 @@ public:
int count = HorizontalIntersect(fCubic, axisIntercept, roots);
for (int index = 0; index < count; ++index) {
double cubicT = roots[index];
- SkDPoint pt;
- pt.fX = fCubic.ptAtT(cubicT).fX;
- pt.fY = axisIntercept;
+ SkDPoint pt = { fCubic.ptAtT(cubicT).fX, axisIntercept };
double lineT = (pt.fX - left) / (right - left);
- if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) {
+ if (pinTs(&cubicT, &lineT, &pt, kPointInitialized) && uniqueAnswer(cubicT, pt)) {
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);
@@ -226,17 +251,16 @@ public:
int count = VerticalIntersect(fCubic, axisIntercept, roots);
for (int index = 0; index < count; ++index) {
double cubicT = roots[index];
- SkDPoint pt;
- pt.fX = axisIntercept;
- pt.fY = fCubic.ptAtT(cubicT).fY;
+ SkDPoint pt = { axisIntercept, fCubic.ptAtT(cubicT).fY };
double lineT = (pt.fY - top) / (bottom - top);
- if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) {
+ if (pinTs(&cubicT, &lineT, &pt, kPointInitialized) && uniqueAnswer(cubicT, pt)) {
fIntersections->insert(cubicT, lineT, pt);
}
}
if (flipped) {
fIntersections->flip();
}
+ checkCoincident();
return fIntersections->used();
}
@@ -342,7 +366,7 @@ public:
double lT = *lineT = SkPinT(*lineT);
SkDPoint lPt = fLine.ptAtT(lT);
SkDPoint cPt = fCubic.ptAtT(cT);
- if (!lPt.moreRoughlyEqual(cPt)) {
+ if (!lPt.roughlyEqual(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 a28564d4c2..272b997d6c 100644
--- a/src/pathops/SkDCubicToQuads.cpp
+++ b/src/pathops/SkDCubicToQuads.cpp
@@ -1,4 +1,11 @@
/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
http://stackoverflow.com/questions/2009160/how-do-i-convert-the-2-control-points-of-a-cubic-curve-to-the-single-control-poi
*/
@@ -19,63 +26,12 @@ 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;
-}
+// used for testing only
SkDQuad SkDCubic::toQuad() const {
SkDQuad quad;
quad[0] = fPts[0];
@@ -86,101 +42,3 @@ 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 8fc673f2fb..ed96b9c5d7 100644
--- a/src/pathops/SkDLineIntersection.cpp
+++ b/src/pathops/SkDLineIntersection.cpp
@@ -7,45 +7,6 @@
#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);
@@ -58,6 +19,9 @@ void SkIntersections::cleanUpParallelLines(bool parallel) {
removeOne(endMatch);
}
}
+ if (fUsed == 2) {
+ fIsCoincident[0] = fIsCoincident[1] = 0x03;
+ }
}
void SkIntersections::computePoints(const SkDLine& line, int used) {
@@ -81,12 +45,6 @@ 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;
@@ -190,7 +148,6 @@ 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;
@@ -235,18 +192,6 @@ 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
@@ -323,18 +268,6 @@ 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
@@ -393,14 +326,3 @@ 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
deleted file mode 100644
index f0f66d1a10..0000000000
--- a/src/pathops/SkDQuadImplicit.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 24f1aac2ef..0000000000
--- a/src/pathops/SkDQuadImplicit.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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
deleted file mode 100644
index fcb9171f32..0000000000
--- a/src/pathops/SkDQuadIntersection.cpp
+++ /dev/null
@@ -1,617 +0,0 @@
-// 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 ef8edb02cd..b8a9a641dd 100644
--- a/src/pathops/SkDQuadLineIntersection.cpp
+++ b/src/pathops/SkDQuadLineIntersection.cpp
@@ -105,6 +105,29 @@ 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
@@ -140,20 +163,17 @@ public:
if (fAllowNear) {
addNearEndPoints();
}
- 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);
- }
+ 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);
}
}
+ checkCoincident();
return fIntersections->used();
}
@@ -178,16 +198,41 @@ public:
double quadT = rootVals[index];
SkDPoint pt = fQuad.ptAtT(quadT);
double lineT = (pt.fX - left) / (right - left);
- if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) {
+ if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
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
@@ -209,13 +254,14 @@ public:
double quadT = rootVals[index];
SkDPoint pt = fQuad.ptAtT(quadT);
double lineT = (pt.fY - top) / (bottom - top);
- if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) {
+ if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
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 3569c934de..c633fd02df 100644
--- a/src/pathops/SkIntersectionHelper.h
+++ b/src/pathops/SkIntersectionHelper.h
@@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "SkOpContour.h"
+#include "SkOpSegment.h"
#include "SkPath.h"
#ifdef SK_DEBUG
@@ -21,42 +22,9 @@ 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() {
- 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);
+ fSegment = fSegment->next();
+ return fSegment != NULL;
}
SkScalar bottom() const {
@@ -64,30 +32,15 @@ public:
}
const SkPathOpsBounds& bounds() const {
- return fContour->segments()[fIndex].bounds();
+ return fSegment->bounds();
}
- void init(SkOpContour* contour) {
- fContour = contour;
- fIndex = 0;
- fLast = contour->segments().count();
- }
-
- bool isAdjacent(const SkIntersectionHelper& next) {
- return fContour == next.fContour && fIndex + 1 == next.fIndex;
+ SkOpContour* contour() const {
+ return fSegment->contour();
}
- 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);
+ void init(SkOpContour* contour) {
+ fSegment = contour->first();
}
SkScalar left() const {
@@ -95,41 +48,40 @@ public:
}
const SkPoint* pts() const {
- return fContour->segments()[fIndex].pts();
+ return fSegment->pts();
}
SkScalar right() const {
return bounds().fRight;
}
+ SkOpSegment* segment() const {
+ return fSegment;
+ }
+
SegmentType segmentType() const {
- const SkOpSegment& segment = fContour->segments()[fIndex];
- SegmentType type = (SegmentType) segment.verb();
+ SegmentType type = (SegmentType) fSegment->verb();
if (type != kLine_Segment) {
return type;
}
- if (segment.isHorizontal()) {
+ if (fSegment->isHorizontal()) {
return kHorizontalLine_Segment;
}
- if (segment.isVertical()) {
+ if (fSegment->isVertical()) {
return kVerticalLine_Segment;
}
return kLine_Segment;
}
bool startAfter(const SkIntersectionHelper& after) {
- fIndex = after.fIndex;
- return advance();
+ fSegment = after.fSegment->next();
+ return fSegment != NULL;
}
SkScalar top() const {
return bounds().fTop;
}
- SkPath::Verb verb() const {
- return fContour->segments()[fIndex].verb();
- }
-
SkScalar x() const {
return bounds().fLeft;
}
@@ -147,10 +99,5 @@ public:
}
private:
- // utility callable by the user from the debugger when the implementation code is linked in
- void dump() const;
-
- SkOpContour* fContour;
- int fIndex;
- int fLast;
+ SkOpSegment* fSegment;
};
diff --git a/src/pathops/SkIntersections.cpp b/src/pathops/SkIntersections.cpp
index e9875cf69d..007efa7ff1 100644
--- a/src/pathops/SkIntersections.cpp
+++ b/src/pathops/SkIntersections.cpp
@@ -7,26 +7,25 @@
#include "SkIntersections.h"
-void SkIntersections::append(const SkIntersections& i) {
- for (int index = 0; index < i.fUsed; ++index) {
- insert(i[0][index], i[1][index], i.pt(index));
+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;
+ }
}
+ return closest;
}
-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
-};
-
+// called only by test code
int SkIntersections::coincidentUsed() const {
if (!fIsCoincident[0]) {
SkASSERT(!fIsCoincident[1]);
@@ -48,12 +47,12 @@ int SkIntersections::coincidentUsed() const {
return count;
}
-int SkIntersections::cubicRay(const SkPoint pts[4], const SkDLine& line) {
- SkDCubic cubic;
- cubic.set(pts);
- fMax = 3;
- return intersectRay(cubic, line);
-}
+int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar, SkScalar, SkScalar, bool) = {
+ NULL,
+ &SkIntersections::verticalLine,
+ &SkIntersections::verticalQuad,
+ &SkIntersections::verticalCubic
+};
void SkIntersections::flip() {
for (int index = 0; index < fUsed; ++index) {
@@ -105,7 +104,6 @@ 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);
@@ -125,39 +123,53 @@ void SkIntersections::insertNear(double one, double two, const SkDPoint& pt1, co
SkASSERT(one == 0 || one == 1);
SkASSERT(two == 0 || two == 1);
SkASSERT(pt1 != pt2);
- SkASSERT(fNearlySame[(int) one]);
+ fNearlySame[one ? 1 : 0] = true;
(void) insert(one, two, pt1);
- fPt2[one ? fUsed - 1 : 0] = pt2;
+ fPt2[one ? 1 : 0] = pt2;
}
-void SkIntersections::insertCoincident(double one, double two, const SkDPoint& pt) {
+int 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;
}
-int SkIntersections::lineRay(const SkPoint pts[2], const SkDLine& line) {
- SkDLine l;
- l.set(pts);
- fMax = 2;
- return intersectRay(l, line);
+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;
}
-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;
+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;
+ }
}
-}
-
-int SkIntersections::quadRay(const SkPoint pts[3], const SkDLine& line) {
- SkDQuad quad;
- quad.set(pts);
- fMax = 2;
- return intersectRay(quad, line);
+ return result;
}
void SkIntersections::quickRemoveOne(int index, int replace) {
@@ -172,7 +184,6 @@ 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);
@@ -182,13 +193,6 @@ 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 a1bde512db..15bac19def 100644
--- a/src/pathops/SkIntersections.h
+++ b/src/pathops/SkIntersections.h
@@ -16,7 +16,6 @@ class SkIntersections {
public:
SkIntersections()
: fSwap(0)
- , fFlatMeasure(false)
#ifdef SK_DEBUG
, fDepth(0)
#endif
@@ -24,7 +23,6 @@ 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
@@ -32,7 +30,7 @@ public:
class TArray {
public:
- explicit TArray(const double ts[9]) : fTArray(ts) {}
+ explicit TArray(const double ts[10]) : fTArray(ts) {}
double operator[](int n) const {
return fTArray[n];
}
@@ -40,28 +38,15 @@ public:
};
TArray operator[](int n) const { return TArray(fT[n]); }
- void allowFlatMeasure(bool flatAllowed) {
- fFlatMeasure = flatAllowed;
- }
-
void allowNear(bool nearAllowed) {
fAllowNear = nearAllowed;
}
- 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);
+ void clearCoincidence(int index) {
+ SkASSERT(index >= 0);
+ int bit = 1 << index;
+ fIsCoincident[0] &= ~bit;
+ fIsCoincident[1] &= ~bit;
}
int cubicHorizontal(const SkPoint a[4], SkScalar left, SkScalar right, SkScalar y,
@@ -88,19 +73,6 @@ 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);
@@ -178,19 +150,11 @@ 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) {
@@ -205,8 +169,6 @@ public:
fSwap ^= true;
}
- void swapPts();
-
bool swapped() const {
return fSwap;
}
@@ -219,19 +181,27 @@ 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]);
@@ -242,25 +212,20 @@ 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
- void insertCoincident(double one, double two, const SkDPoint& pt);
+ int 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&);
- 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 merge(const SkIntersections& , int , const SkIntersections& , int );
+ int mostOutside(double rangeStart, double rangeEnd, const SkDPoint& origin) const;
void quickRemoveOne(int index, int replace);
- int quadRay(const SkPoint pts[3], const SkDLine& line);
void removeOne(int index);
- static bool Test(const SkDLine& , const SkDLine&);
- int vertical(const SkDLine&, double x);
+ void setCoincident(int index);
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);
@@ -276,6 +241,8 @@ 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);
@@ -283,22 +250,20 @@ private:
void cleanUpParallelLines(bool parallel);
void computePoints(const SkDLine& line, int used);
- 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];
+ 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];
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 b3a188c1e8..c13a51a8cc 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(SkString* bugOut, int append, bool compare) {
+ static bool CompareResult(const char* func, SkString* bugOut, SkString* bugPart, 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(&bugOut, append, compare)
+ #define COMPARE_RESULT(append, compare) CompareResult(__FUNCTION__, &bugOut, bugPart, append, \
+ compare)
#else
#define COMPARE_RESULT(append, compare) compare
#endif
@@ -58,51 +58,50 @@
*/
// return true if lh < this < rh
-bool SkOpAngle::after(const SkOpAngle* test) const {
- const SkOpAngle& lh = *test;
- const SkOpAngle& rh = *lh.fNext;
- SkASSERT(&lh != &rh);
+bool SkOpAngle::after(SkOpAngle* test) {
+ SkOpAngle* lh = test;
+ 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.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));
+ 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() };
#endif
- if (lh.fComputeSector && !const_cast<SkOpAngle&>(lh).computeSector()) {
+ if (lh->fComputeSector && !lh->computeSector()) {
return COMPARE_RESULT(1, true);
}
- if (fComputeSector && !const_cast<SkOpAngle*>(this)->computeSector()) {
+ if (fComputeSector && !this->computeSector()) {
return COMPARE_RESULT(2, true);
}
- if (rh.fComputeSector && !const_cast<SkOpAngle&>(rh).computeSector()) {
+ if (rh->fComputeSector && !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.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));
+ 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());
#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
@@ -115,24 +114,24 @@ bool SkOpAngle::after(const SkOpAngle* test) const {
*/
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) {
@@ -145,20 +144,20 @@ bool SkOpAngle::after(const SkOpAngle* test) const {
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);
}
@@ -173,77 +172,50 @@ bool SkOpAngle::after(const SkOpAngle* test) const {
// 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) const {
+int SkOpAngle::allOnOneSide(const SkOpAngle* test) {
SkASSERT(!fIsCurve);
- SkASSERT(test.fIsCurve);
- const SkDPoint& origin = test.fCurvePart[0];
+ SkASSERT(test->fIsCurve);
+ const SkDPoint& origin = test->fCurvePart[0];
SkVector line;
- if (fSegment->verb() == SkPath::kLine_Verb) {
- const SkPoint* linePts = fSegment->pts();
- int lineStart = fStart < fEnd ? 0 : 1;
+ if (segment()->verb() == SkPath::kLine_Verb) {
+ const SkPoint* linePts = segment()->pts();
+ int lineStart = fStart->t() < fEnd->t() ? 0 : 1;
line = linePts[lineStart ^ 1] - linePts[lineStart];
} else {
SkPoint shortPts[2] = { fCurvePart[0].asSkPoint(), fCurvePart[1].asSkPoint() };
line = shortPts[1] - shortPts[0];
}
float crosses[3];
- SkPath::Verb testVerb = test.fSegment->verb();
+ SkPath::Verb testVerb = test->segment()->verb();
int iMax = SkPathOpsVerbToPoints(testVerb);
// SkASSERT(origin == test.fCurveHalf[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) {
+ 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) {
return -1;
}
- 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;
- }
+ }
+ 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);
@@ -251,31 +223,94 @@ bool SkOpAngle::checkCrossesZero() const {
return crossesZero;
}
-bool SkOpAngle::checkParallel(const SkOpAngle& rh) const {
+// 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) {
SkDVector scratch[2];
const SkDVector* sweep, * tweep;
- if (!fUnorderedSweep) {
- sweep = fSweep;
+ if (!this->fUnorderedSweep) {
+ sweep = this->fSweep;
} else {
- scratch[0] = fCurvePart[1] - fCurvePart[0];
+ scratch[0] = this->fCurvePart[1] - this->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;
}
- SkDVector m0 = fSegment->dPtAtT(midT()) - fCurvePart[0];
- SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[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];
double m0xm1 = m0.crossCheck(m1);
if (m0xm1 == 0) {
- fUnorderable = true;
- rh.fUnorderable = true;
+ this->fUnorderable = true;
+ rh->fUnorderable = true;
return true;
}
return m0xm1 < 0;
@@ -288,48 +323,51 @@ bool SkOpAngle::computeSector() {
if (fComputedSector) {
return !fUnorderable;
}
-// SkASSERT(fSegment->verb() != SkPath::kLine_Verb && small());
fComputedSector = true;
- int step = fStart < fEnd ? 1 : -1;
- int limit = step > 0 ? fSegment->count() : -1;
- int checkEnd = fEnd;
+ bool stepUp = fStart->t() < fEnd->t();
+ const SkOpSpanBase* checkEnd = fEnd;
+ if (checkEnd->final() && stepUp) {
+ fUnorderable = true;
+ return false;
+ }
do {
// advance end
- 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) {
+ const SkOpSegment* other = checkEnd->segment();
+ const SkOpSpanBase* oSpan = other->head();
+ do {
+ if (oSpan->segment() != segment()) {
continue;
}
- if (oSpan.fOtherIndex == checkEnd) {
+ if (oSpan == checkEnd) {
continue;
}
- if (!approximately_equal(oSpan.fOtherT, span.fT)) {
+ if (!approximately_equal(oSpan->t(), checkEnd->t())) {
continue;
}
goto recomputeSector;
- }
- checkEnd += step;
- } while (checkEnd != limit);
+ } while (!oSpan->final() && (oSpan = oSpan->upCast()->next()));
+ checkEnd = stepUp ? !checkEnd->final()
+ ? checkEnd->upCast()->next() : NULL
+ : checkEnd->prev();
+ } while (checkEnd);
recomputeSector:
- if (checkEnd == fEnd || checkEnd - step == fEnd) {
+ SkOpSpanBase* computedEnd = stepUp ? checkEnd ? checkEnd->prev() : fEnd->segment()->head()
+ : checkEnd ? checkEnd->upCast()->next() : fEnd->segment()->tail();
+ if (checkEnd == fEnd || computedEnd == fEnd || computedEnd == fStart) {
fUnorderable = true;
return false;
}
- int saveEnd = fEnd;
- fComputedEnd = fEnd = checkEnd - step;
+ SkOpSpanBase* saveEnd = fEnd;
+ fComputedEnd = fEnd = computedEnd;
setSpans();
setSector();
fEnd = saveEnd;
return !fUnorderable;
}
-// 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;
+int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) const {
+ const SkDVector* sweep = this->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]);
@@ -359,8 +397,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 = fSegment->dPtAtT(midT()) - fCurvePart[0];
- SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[0];
+ SkDVector m0 = this->segment()->dPtAtT(this->midT()) - this->fCurvePart[0];
+ SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fCurvePart[0];
double m0xm1 = m0.crossCheck(m1);
if (s0xt0 > 0 && m0xm1 > 0) {
return 0;
@@ -394,34 +432,30 @@ double SkOpAngle::distEndRatio(double dist) const {
return sqrt(longest) / dist;
}
-bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const {
- SkPath::Verb lVerb = fSegment->verb();
- SkPath::Verb rVerb = rh.fSegment->verb();
+bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
+ SkPath::Verb lVerb = this->segment()->verb();
+ SkPath::Verb rVerb = rh->segment()->verb();
int lPts = SkPathOpsVerbToPoints(lVerb);
int rPts = SkPathOpsVerbToPoints(rVerb);
- SkDLine rays[] = {{{fCurvePart[0], rh.fCurvePart[rPts]}},
- {{fCurvePart[0], fCurvePart[lPts]}}};
+ SkDLine rays[] = {{{this->fCurvePart[0], rh->fCurvePart[rPts]}},
+ {{this->fCurvePart[0], this->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;
}
-// 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;
+ 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());
double t = testAscends ? 0 : 1;
for (int idx2 = 0; idx2 < i.used(); ++idx2) {
double testT = i[0][idx2];
@@ -435,29 +469,6 @@ bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const {
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;
@@ -467,7 +478,7 @@ bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const {
if (smallTs[index] < 0) {
continue;
}
- const SkOpSegment& segment = index ? *rh.fSegment : *fSegment;
+ const SkOpSegment& segment = index ? *rh->segment() : *this->segment();
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
@@ -498,7 +509,7 @@ bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const {
double minX, minY, maxX, maxY;
minX = minY = SK_ScalarInfinity;
maxX = maxY = -SK_ScalarInfinity;
- const SkDCubic& curve = index ? rh.fCurvePart : fCurvePart;
+ const SkDCubic& curve = index ? rh->fCurvePart : this->fCurvePart;
int ptCount = index ? rPts : lPts;
for (int idx2 = 0; idx2 <= ptCount; ++idx2) {
minX = SkTMin(minX, curve[idx2].fX);
@@ -508,7 +519,7 @@ bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const {
}
double maxWidth = SkTMax(maxX - minX, maxY - minY);
delta /= maxWidth;
- if (delta > 1e-4 && (useIntersect ^= true)) { // FIXME: move this magic number
+ if (delta > 1e-3 && (useIntersect ^= true)) { // FIXME: move this magic number
sRayLonger = rayLonger;
sCept = cept;
sCeptT = smallTs[index];
@@ -516,9 +527,9 @@ bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const {
}
}
if (useIntersect) {
- const SkDCubic& curve = sIndex ? rh.fCurvePart : fCurvePart;
- const SkOpSegment& segment = sIndex ? *rh.fSegment : *fSegment;
- double tStart = segment.t(sIndex ? rh.fStart : fStart);
+ const SkDCubic& curve = sIndex ? rh->fCurvePart : this->fCurvePart;
+ const SkOpSegment& segment = sIndex ? *rh->segment() : *this->segment();
+ double tStart = sIndex ? rh->fStart->t() : fStart->t();
SkDVector mid = segment.dPtAtT(tStart + (sCeptT - tStart) / 2) - curve[0];
double septDir = mid.crossCheck(sCept);
if (!septDir) {
@@ -530,12 +541,65 @@ bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const {
}
}
+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.
-const SkOpAngle* SkOpAngle::findFirst() const {
- const SkOpAngle* best = this;
+SkOpAngle* SkOpAngle::findFirst() {
+ SkOpAngle* best = this;
int bestStart = SkTMin(fSectorStart, fSectorEnd);
- const SkOpAngle* angle = this;
+ SkOpAngle* angle = this;
while ((angle = angle->fNext) != this) {
int angleEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd);
if (angleEnd < bestStart) {
@@ -548,7 +612,7 @@ const SkOpAngle* SkOpAngle::findFirst() const {
}
}
// back up to the first possible angle
- const SkOpAngle* firstBest = best;
+ SkOpAngle* firstBest = best;
angle = best;
int bestEnd = SkTMax(best->fSectorStart, best->fSectorEnd);
while ((angle = angle->previous()) != firstBest) {
@@ -572,7 +636,7 @@ const SkOpAngle* SkOpAngle::findFirst() const {
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;
}
@@ -639,6 +703,11 @@ 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) {
@@ -662,9 +731,6 @@ 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;
@@ -678,9 +744,6 @@ 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;
@@ -689,48 +752,49 @@ void SkOpAngle::insert(SkOpAngle* angle) {
}
last = next;
next = next->fNext;
- if (last == this && next->fUnorderable) {
- fUnorderable = true;
+ if (last == this) {
+ if (next->fUnorderable) {
+ fUnorderable = true;
+ } else {
+ globalState()->setAngleCoincidence();
+ this->fNext = angle;
+ angle->fNext = next;
+ angle->fCheckCoincidence = true;
+ }
return;
}
- SkASSERT(last != this);
} while (true);
}
-bool SkOpAngle::isHorizontal() const {
- return !fIsCurve && fSweep[0].fY == 0;
-}
-
-SkOpSpan* SkOpAngle::lastMarked() const {
+SkOpSpanBase* SkOpAngle::lastMarked() const {
if (fLastMarked) {
- if (fLastMarked->fChased) {
+ if (fLastMarked->chased()) {
return NULL;
}
- fLastMarked->fChased = true;
+ fLastMarked->setChased(true);
}
return fLastMarked;
}
-bool SkOpAngle::loopContains(const SkOpAngle& test) const {
+bool SkOpAngle::loopContains(const SkOpAngle* angle) const {
if (!fNext) {
return false;
}
const SkOpAngle* first = this;
const SkOpAngle* loop = this;
- const SkOpSegment* tSegment = test.fSegment;
- double tStart = tSegment->span(test.fStart).fT;
- double tEnd = tSegment->span(test.fEnd).fT;
+ const SkOpSegment* tSegment = angle->fStart->segment();
+ double tStart = angle->fStart->t();
+ double tEnd = angle->fEnd->t();
do {
- const SkOpSegment* lSegment = loop->fSegment;
- // FIXME : use precisely_equal ? or compare points exactly ?
+ const SkOpSegment* lSegment = loop->fStart->segment();
if (lSegment != tSegment) {
continue;
}
- double lStart = lSegment->span(loop->fStart).fT;
+ double lStart = loop->fStart->t();
if (lStart != tEnd) {
continue;
}
- double lEnd = lSegment->span(loop->fEnd).fT;
+ double lEnd = loop->fEnd->t();
if (lEnd == tStart) {
return true;
}
@@ -782,39 +846,65 @@ 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 (fSegment->t(fStart) + fSegment->t(fEnd)) / 2;
+ 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;
}
-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(const SkOpAngle& rh) const {
+bool SkOpAngle::orderable(SkOpAngle* rh) {
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) {
@@ -829,14 +919,14 @@ bool SkOpAngle::orderable(const SkOpAngle& rh) const {
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;
}
}
@@ -846,27 +936,10 @@ bool SkOpAngle::orderable(const SkOpAngle& rh) const {
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 {
@@ -880,26 +953,32 @@ SkOpAngle* SkOpAngle::previous() const {
} while (true);
}
-void SkOpAngle::set(const SkOpSegment* segment, int start, int end) {
- fSegment = segment;
+SkOpSegment* SkOpAngle::segment() const {
+ return fStart->segment();
+}
+
+void SkOpAngle::set(SkOpSpanBase* start, SkOpSpanBase* end) {
fStart = start;
fComputedEnd = fEnd = end;
+ SkASSERT(start != end);
fNext = NULL;
- fComputeSector = fComputedSector = false;
+ fComputeSector = fComputedSector = fCheckCoincidence = false;
fStop = false;
setSpans();
setSector();
+ PATH_OPS_DEBUG_CODE(fID = start->globalState()->nextAngleID());
}
void SkOpAngle::setCurveHullSweep() {
fUnorderedSweep = false;
fSweep[0] = fCurvePart[1] - fCurvePart[0];
- if (SkPath::kLine_Verb == fSegment->verb()) {
+ const SkOpSegment* segment = fStart->segment();
+ if (SkPath::kLine_Verb == segment->verb()) {
fSweep[1] = fSweep[0];
return;
}
fSweep[1] = fCurvePart[2] - fCurvePart[0];
- if (SkPath::kCubic_Verb != fSegment->verb()) {
+ if (SkPath::kCubic_Verb != segment->verb()) {
if (!fSweep[0].fX && !fSweep[0].fY) {
fSweep[0] = fSweep[1];
}
@@ -933,64 +1012,16 @@ 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 = fSegment->isTiny(this);
+ fUnorderable = false;
fLastMarked = NULL;
- const SkPoint* pts = fSegment->pts();
+ const SkOpSegment* segment = fStart->segment();
+ const SkPoint* pts = segment->pts();
SkDEBUGCODE(fCurvePart[2].fX = fCurvePart[2].fY = fCurvePart[3].fX = fCurvePart[3].fY
= SK_ScalarNaN);
- fSegment->subDivide(fStart, fEnd, &fCurvePart);
+ segment->subDivide(fStart, fEnd, &fCurvePart);
setCurveHullSweep();
- const SkPath::Verb verb = fSegment->verb();
+ const SkPath::Verb verb = segment->verb();
if (verb != SkPath::kLine_Verb
&& !(fIsCurve = fSweep[0].crossCheck(fSweep[1]) != 0)) {
SkDLine lineHalf;
@@ -1002,9 +1033,9 @@ void SkOpAngle::setSpans() {
switch (verb) {
case SkPath::kLine_Verb: {
SkASSERT(fStart != fEnd);
- const SkPoint& cP1 = pts[fStart < fEnd];
+ const SkPoint& cP1 = pts[fStart->t() < fEnd->t()];
SkDLine lineHalf;
- lineHalf[0].set(fSegment->span(fStart).fPt);
+ lineHalf[0].set(fStart->pt());
lineHalf[1].set(cP1);
fTangentHalf.lineEndPoints(lineHalf);
fSide = 0;
@@ -1023,8 +1054,8 @@ void SkOpAngle::setSpans() {
double testTs[4];
// OPTIMIZATION: keep inflections precomputed with cubic segment?
int testCount = SkDCubic::FindInflections(pts, testTs);
- double startT = fSegment->t(fStart);
- double endT = fSegment->t(fEnd);
+ double startT = fStart->t();
+ double endT = fEnd->t();
double limitT = endT;
int index;
for (index = 0; index < testCount; ++index) {
@@ -1064,19 +1095,63 @@ void SkOpAngle::setSpans() {
}
}
-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;
- }
+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;
}
- return true;
+ 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);
+ }
+}
+
+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;
}
@@ -1090,7 +1165,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;
@@ -1100,36 +1175,6 @@ 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 ? distEndRatio(sDist) : rh.distEndRatio(tDist));
+ double mFactor = fabs(useS ? this->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 1dc4250613..84b37010c9 100644
--- a/src/pathops/SkOpAngle.h
+++ b/src/pathops/SkOpAngle.h
@@ -7,17 +7,18 @@
#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;
-struct SkOpSpan;
+class SkOpSpanBase;
+class SkOpSpan;
-// 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
+struct SkOpAngle {
enum IncludeType {
kUnaryWinding,
kUnaryXor,
@@ -25,29 +26,66 @@ public:
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 end() const {
- return fEnd;
+ int debugID() const {
+ return PATH_OPS_DEBUG_RELEASE(fID, -1);
}
- const SkOpAngle* findFirst() const;
+#if DEBUG_SORT
+ void debugLoop() const;
+#endif
- bool inLoop() const {
- return !!fNext;
+#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 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* );
- bool isHorizontal() const;
- SkOpSpan* lastMarked() const;
- bool loopContains(const SkOpAngle& ) const;
+ SkOpSpanBase* 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 {
@@ -58,120 +96,57 @@ public:
return fSectorStart;
}
- void set(const SkOpSegment* segment, int start, int end);
+ SkOpSegment* segment() const;
- void setLastMarked(SkOpSpan* marked) {
- fLastMarked = marked;
- }
+ void set(SkOpSpanBase* start, SkOpSpanBase* end);
+ void setCurveHullSweep();
- SkOpSegment* segment() const {
- return const_cast<SkOpSegment*>(fSegment);
+ void setID(int id) {
+ PATH_OPS_DEBUG_CODE(fID = id);
}
- int sign() const {
- return SkSign32(fStart - fEnd);
+ void setLastMarked(SkOpSpanBase* marked) {
+ fLastMarked = marked;
}
- bool small() const;
+ void setSector();
+ void setSpans();
+ int sign() const;
- int start() const {
+ SkOpSpanBase* start() const {
return fStart;
}
+ SkOpSpan* starter();
+ bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
+
bool unorderable() const {
return fUnorderable;
}
- // 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
+ 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;
- SkOpSpan* fLastMarked;
+ SkOpSpanBase* fLastMarked;
SkDVector fSweep[2];
- int fStart;
- int fEnd;
- int fComputedEnd;
+ SkOpSpanBase* fStart;
+ SkOpSpanBase* fEnd;
+ SkOpSpanBase* 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
- mutable bool fUnorderable; // this is editable by orderable()
+ bool fStop; // set if ordered angle is greater than the previous
+ bool fUnorderable;
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/SkOpBuilder.cpp b/src/pathops/SkOpBuilder.cpp
index 489ad639c1..1c21a5984c 100644
--- a/src/pathops/SkOpBuilder.cpp
+++ b/src/pathops/SkOpBuilder.cpp
@@ -10,9 +10,9 @@
#include "SkPathOps.h"
void SkOpBuilder::add(const SkPath& path, SkPathOp op) {
- if (0 == fOps.count() && op != kUnion_PathOp) {
+ if (0 == fOps.count() && op != kUnion_SkPathOp) {
fPathRefs.push_back() = SkPath();
- *fOps.append() = kUnion_PathOp;
+ *fOps.append() = kUnion_SkPathOp;
}
fPathRefs.push_back() = path;
*fOps.append() = op;
@@ -32,7 +32,7 @@ bool SkOpBuilder::resolve(SkPath* result) {
SkPath::Direction firstDir;
for (int index = 0; index < count; ++index) {
SkPath* test = &fPathRefs[index];
- if (kUnion_PathOp != fOps[index] || test->isInverseFillType()) {
+ if (kUnion_SkPathOp != fOps[index] || test->isInverseFillType()) {
allUnion = false;
break;
}
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
new file mode 100755
index 0000000000..45eee0a38e
--- /dev/null
+++ b/src/pathops/SkOpCoincidence.cpp
@@ -0,0 +1,388 @@
+/*
+ * 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 287bfd12d4..b79b88be88 100644
--- a/src/pathops/SkOpCoincidence.h
+++ b/src/pathops/SkOpCoincidence.h
@@ -19,6 +19,8 @@ struct SkCoincidentSpans {
SkOpPtT* fOppPtTStart;
SkOpPtT* fOppPtTEnd;
bool fFlipped;
+
+ void dump() const;
};
class SkOpCoincidence {
@@ -28,13 +30,27 @@ public:
}
void add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
- SkOpPtT* oppPtTEnd, bool flipped, SkChunkAlloc* allocator);
- void apply();
+ SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator);
+ bool addMissing(SkChunkAlloc* allocator);
+ bool 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 28c072a3c1..d17b18905b 100644
--- a/src/pathops/SkOpContour.cpp
+++ b/src/pathops/SkOpContour.cpp
@@ -4,42 +4,35 @@
* 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"
-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) {
+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) {
int segmentCount = fSortedSegments.count();
SkASSERT(segmentCount > 0);
for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
@@ -47,627 +40,27 @@ SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
if (testSegment->done()) {
continue;
}
- *start = *end = 0;
- while (testSegment->nextCandidate(start, end)) {
- if (!testSegment->isVertical(*start, *end)) {
+ SkOpSpanBase* span = testSegment->head();
+ SkOpSpanBase* testS, * testE;
+ while (SkOpSegment::NextCandidate(span, &testS, &testE)) {
+ if (!testSegment->isVertical(testS, testE)) {
+ *start = testS;
+ *end = testE;
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 {
- int segmentCount = fSegments.count();
- const SkPoint& pt = fSegments.front().pts()[0];
+ const SkPoint& pt = fHead.pts()[0];
path->deferredMove(pt);
- for (int test = 0; test < segmentCount; ++test) {
- fSegments[test].addCurveTo(0, 1, path, true);
- }
+ const SkOpSegment* segment = &fHead;
+ do {
+ segment->addCurveTo(segment->head(), segment->tail(), path, true);
+ } while ((segment = segment->next()));
path->close();
}
@@ -706,57 +99,14 @@ void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
}
}
-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()) {
+SkOpSegment* SkOpContour::undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr) {
+ SkOpSegment* segment = &fHead;
+ do {
+ if (segment->done()) {
continue;
}
- testSegment->undoneSpan(start, end);
- return testSegment;
- }
+ segment->undoneSpan(startPtr, endPtr);
+ return segment;
+ } while ((segment = segment->next()));
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 7a1cc09247..495b643fff 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -8,31 +8,22 @@
#define SkOpContour_DEFINED
#include "SkOpSegment.h"
-#include "SkTArray.h"
+#include "SkTDArray.h"
+#include "SkTSort.h"
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-#include "SkThread.h"
-#endif
-
-class SkIntersections;
-class SkOpContour;
+class SkChunkAlloc;
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
+ }
+
+ ~SkOpContour() {
+ if (fNext) {
+ fNext->~SkOpContour();
+ }
}
bool operator<(const SkOpContour& rh) const {
@@ -41,211 +32,255 @@ public:
: fBounds.fTop < rh.fBounds.fTop;
}
- bool addCoincident(int index, SkOpContour* other, int otherIndex,
- const SkIntersections& ts, bool swap);
- void addCoincidentPoints();
+ void addCubic(SkPoint pts[4], SkChunkAlloc* allocator) {
+ appendSegment(allocator).addCubic(pts, this);
+ }
- void addCross(const SkOpContour* crosser) {
-#ifdef DEBUG_CROSS
- for (int index = 0; index < fCrosses.count(); ++index) {
- SkASSERT(fCrosses[index] != crosser);
- }
-#endif
- fCrosses.push_back(crosser);
+ void addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator);
+
+ void addLine(SkPoint pts[2], SkChunkAlloc* allocator) {
+ appendSegment(allocator).addLine(pts, this);
}
- void addCubic(const SkPoint pts[4]) {
- fSegments.push_back().addCubic(pts, fOperand, fXor);
- fContainsCurves = fContainsCubics = true;
+ void addQuad(SkPoint pts[3], SkChunkAlloc* allocator) {
+ appendSegment(allocator).addQuad(pts, this);
}
- int addLine(const SkPoint pts[2]) {
- fSegments.push_back().addLine(pts, fOperand, fXor);
- return fSegments.count();
+ void align() {
+ SkASSERT(fCount > 0);
+ SkOpSegment* segment = &fHead;
+ do {
+ segment->align();
+ } while ((segment = segment->next()));
}
- void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
- fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
+ 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 addPartialCoincident(int index, SkOpContour* other, int otherIndex,
- const SkIntersections& ts, int ptIndex, bool swap);
+ SkOpContour* appendContour(SkChunkAlloc* allocator) {
+ SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(allocator);
+ contour->setNext(NULL);
+ SkOpContour* prev = this;
+ SkOpContour* next;
+ while ((next = prev->next())) {
+ prev = next;
+ }
+ prev->setNext(contour);
+ return contour;
+ }
+
+ const SkPathOpsBounds& bounds() const {
+ return fBounds;
+ }
- int addQuad(const SkPoint pts[3]) {
- fSegments.push_back().addQuad(pts, fOperand, fXor);
- fContainsCurves = true;
- return fSegments.count();
+ void calcAngles(SkChunkAlloc* allocator) {
+ SkASSERT(fCount > 0);
+ SkOpSegment* segment = &fHead;
+ do {
+ segment->calcAngles(allocator);
+ } 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);
+ void complete() {
+ setBounds();
}
- int addSelfT(int segIndex, const SkPoint& pt, double newT) {
- setContainsIntercepts();
- return fSegments[segIndex].addSelfT(pt, newT);
+ int count() const {
+ return fCount;
}
- void align(const SkOpSegment::AlignedSpan& aligned, bool swap, SkCoincidence* coincidence);
- void alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
- SkTArray<SkCoincidence, true>* coincidences);
+ int debugID() const {
+ return PATH_OPS_DEBUG_RELEASE(fID, -1);
+ }
- void alignCoincidence(const SkOpSegment::AlignedSpan& aligned) {
- alignCoincidence(aligned, &fCoincidences);
- alignCoincidence(aligned, &fPartialCoincidences);
+ int debugIndent() const {
+ return PATH_OPS_DEBUG_RELEASE(fIndent, 0);
}
- 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);
- }
- }
+#if DEBUG_ACTIVE_SPANS
+ void debugShowActiveSpans() {
+ SkOpSegment* segment = &fHead;
+ do {
+ segment->debugShowActiveSpans();
+ } while ((segment = segment->next()));
}
+#endif
- void alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
- bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const;
+ const SkOpAngle* debugAngle(int id) const {
+ return PATH_OPS_DEBUG_RELEASE(globalState()->debugAngle(id), NULL);
+ }
- const SkPathOpsBounds& bounds() const {
- return fBounds;
+ SkOpContour* debugContour(int id) {
+ return PATH_OPS_DEBUG_RELEASE(globalState()->debugContour(id), NULL);
}
- bool calcAngles();
- bool calcCoincidentWinding();
- void calcPartialCoincidentWinding();
+ const SkOpPtT* debugPtT(int id) const {
+ return PATH_OPS_DEBUG_RELEASE(globalState()->debugPtT(id), NULL);
+ }
- void checkDuplicates() {
- int segmentCount = fSegments.count();
- for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
- SkOpSegment& segment = fSegments[sIndex];
- if (segment.count() > 2) {
- segment.checkDuplicates();
- }
- }
+ const SkOpSegment* debugSegment(int id) const {
+ return PATH_OPS_DEBUG_RELEASE(globalState()->debugSegment(id), NULL);
}
- 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;
+ const SkOpSpanBase* debugSpan(int id) const {
+ return PATH_OPS_DEBUG_RELEASE(globalState()->debugSpan(id), NULL);
}
- 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();
- }
- }
+ SkOpGlobalState* globalState() const {
+ return fState;
}
- 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 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
}
- // 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();
- }
- }
+ bool done() const {
+ return fDone;
}
- void complete() {
- setBounds();
- fContainsIntercepts = false;
+ 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 containsCubics() const {
- return fContainsCubics;
+ SkOpSegment* first() {
+ SkASSERT(fCount > 0);
+ return &fHead;
}
- bool crosses(const SkOpContour* crosser) const {
- for (int index = 0; index < fCrosses.count(); ++index) {
- if (fCrosses[index] == crosser) {
- return true;
- }
- }
- return false;
+ const SkOpSegment* first() const {
+ SkASSERT(fCount > 0);
+ return &fHead;
}
- bool done() const {
- return fDone;
+ void indentDump() {
+ PATH_OPS_DEBUG_CODE(fIndent += 2);
}
- const SkPoint& end() const {
- const SkOpSegment& segment = fSegments.back();
- return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
+ void init(SkOpGlobalState* globalState, bool operand, bool isXor) {
+ fState = globalState;
+ fOperand = operand;
+ fXor = isXor;
}
- void fixOtherTIndex() {
- int segmentCount = fSegments.count();
- for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
- fSegments[sIndex].fixOtherTIndex();
- }
+ bool isXor() const {
+ return fXor;
+ }
+
+ 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);
+ }
+ } while ((segment = segment->next()));
+ }
+
+ bool moveNearby() {
+ SkASSERT(fCount > 0);
+ SkOpSegment* segment = &fHead;
+ do {
+ if (!segment->moveNearby()) {
+ return false;
+ }
+ } while ((segment = segment->next()));
+ return true;
}
- bool hasMultiples() const {
- return fMultiples;
+ SkOpContour* next() {
+ return fNext;
}
- void joinCoincidence() {
- joinCoincidence(fCoincidences, false);
- joinCoincidence(fPartialCoincidences, true);
+ const SkOpContour* next() const {
+ return fNext;
}
- SkOpSegment* nonVerticalSegment(int* start, int* end);
+ SkOpSegment* nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end);
bool operand() const {
return fOperand;
}
- void reset() {
- fSegments.reset();
- fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
- fContainsCurves = fContainsCubics = fContainsIntercepts = fDone = fMultiples = false;
+ bool oppXor() const {
+ return fOppXor;
+ }
+
+ void outdentDump() {
+ PATH_OPS_DEBUG_CODE(fIndent -= 2);
+ }
+
+ 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);
}
- void resolveNearCoincidence();
+ 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());
+ }
+ }
- SkTArray<SkOpSegment>& segments() {
- return fSegments;
+ void setGlobalState(SkOpGlobalState* state) {
+ fState = state;
}
- void setContainsIntercepts() {
- fContainsIntercepts = true;
+ void setNext(SkOpContour* contour) {
+// SkASSERT(!fNext == !!contour);
+ fNext = contour;
}
void setOperand(bool isOp) {
@@ -254,107 +289,68 @@ 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;
}
- void sortAngles();
- void sortSegments();
+ SkPath::Verb simplifyCubic(SkPoint pts[4]);
- const SkPoint& start() const {
- return fSegments.front().pts()[0];
+ void sortAngles() {
+ SkASSERT(fCount > 0);
+ SkOpSegment* segment = &fHead;
+ do {
+ segment->sortAngles();
+ } while ((segment = segment->next()));
}
- void toPath(SkPathWriter* path) const;
-
- void toPartialBackward(SkPathWriter* path) const {
- int segmentCount = fSegments.count();
- for (int test = segmentCount - 1; test >= 0; --test) {
- fSegments[test].addCurveTo(1, 0, path, true);
- }
- }
-
- void toPartialForward(SkPathWriter* path) const {
- int segmentCount = fSegments.count();
- for (int test = 0; test < segmentCount; ++test) {
- fSegments[test].addCurveTo(0, 1, path, true);
- }
+ void sortSegments() {
+ SkOpSegment* segment = &fHead;
+ do {
+ *fSortedSegments.append() = segment;
+ } while ((segment = segment->next()));
+ SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
+ fFirstSorted = 0;
}
- void topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, SkOpSegment** topStart);
- 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;
+ const SkPoint& start() const {
+ return fHead.pts()[0];
}
-#if DEBUG_TEST
- SkTArray<SkOpSegment>& debugSegments() {
- return fSegments;
+ void toPartialBackward(SkPathWriter* path) const {
+ const SkOpSegment* segment = fTail;
+ do {
+ segment->addCurveTo(segment->tail(), segment->head(), path, true);
+ } while ((segment = segment->prev()));
}
-#endif
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
- void debugShowActiveSpans() {
- for (int index = 0; index < fSegments.count(); ++index) {
- fSegments[index].debugShowActiveSpans();
- }
+ void toPartialForward(SkPathWriter* path) const {
+ const SkOpSegment* segment = &fHead;
+ do {
+ segment->addCurveTo(segment->head(), segment->tail(), path, true);
+ } while ((segment = segment->next()));
}
-#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;
+ void toPath(SkPathWriter* path) const;
+ void topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, SkOpSegment** topStart);
+ SkOpSegment* undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr);
private:
- 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;
- SkTArray<SkCoincidence, true> fCoincidences;
- SkTArray<SkCoincidence, true> fPartialCoincidences;
- SkTArray<const SkOpContour*, true> fCrosses;
+ SkOpGlobalState* fState;
+ SkOpSegment fHead;
+ SkOpSegment* fTail;
+ SkOpContour* fNext;
+ SkTDArray<SkOpSegment*> fSortedSegments; // set by find top segment
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
+ int fCount;
+ int fFirstSorted;
+ bool fDone; // set by find top segment
bool fOperand; // true for the second argument to a binary operator
- bool fXor;
- bool fOppXor;
-#if defined(SK_DEBUG) || !FORCE_RELEASE
- int debugID() const { return fID; }
- int fID;
-#else
- int debugID() const { return -1; }
-#endif
+ 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);
};
#endif
diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp
index 803a5f4739..e3dc139865 100644
--- a/src/pathops/SkOpEdgeBuilder.cpp
+++ b/src/pathops/SkOpEdgeBuilder.cpp
@@ -9,7 +9,7 @@
#include "SkReduceOrder.h"
void SkOpEdgeBuilder::init() {
- fCurrentContour = NULL;
+ fCurrentContour = fContoursHead;
fOperand = false;
fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
: kWinding_PathOpsMask;
@@ -19,32 +19,43 @@ void SkOpEdgeBuilder::init() {
void SkOpEdgeBuilder::addOperand(const SkPath& path) {
SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
- fPathVerbs.pop_back();
+ fPathVerbs.pop();
fPath = &path;
fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
: kWinding_PathOpsMask;
preFetch();
}
-bool SkOpEdgeBuilder::finish() {
- if (fUnparseable || !walk()) {
+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)) {
return false;
}
complete();
- if (fCurrentContour && !fCurrentContour->segments().count()) {
- fContours.pop_back();
+ if (fCurrentContour && !fCurrentContour->count()) {
+ fContoursHead->remove(fCurrentContour);
}
return true;
}
void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) {
if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) {
- fPathVerbs.push_back(SkPath::kLine_Verb);
- fPathPts.push_back_n(1, &curveStart);
+ *fPathVerbs.append() = SkPath::kLine_Verb;
+ *fPathPts.append() = curveStart;
} else {
fPathPts[fPathPts.count() - 1] = curveStart;
}
- fPathVerbs.push_back(SkPath::kClose_Verb);
+ *fPathVerbs.append() = SkPath::kClose_Verb;
}
// very tiny points cause numerical instability : don't allow them
@@ -57,7 +68,6 @@ static void force_small_to_zero(SkPoint* pt) {
}
}
-
int SkOpEdgeBuilder::preFetch() {
if (!fPath->isFinite()) {
fUnparseable = true;
@@ -78,18 +88,18 @@ int SkOpEdgeBuilder::preFetch() {
if (!fAllowOpenContours && lastCurve) {
closeContour(curve[0], curveStart);
}
- fPathVerbs.push_back(verb);
+ *fPathVerbs.append() = verb;
force_small_to_zero(&pts[0]);
- fPathPts.push_back(pts[0]);
+ *fPathPts.append() = 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.back();
+ uint8_t lastVerb = fPathVerbs.top();
if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) {
- fPathPts.back() = pts[1];
+ fPathPts.top() = pts[1];
}
continue; // skip degenerate points
}
@@ -109,9 +119,9 @@ int SkOpEdgeBuilder::preFetch() {
quadderTol);
const int nQuads = quadder.countQuads();
for (int i = 0; i < nQuads; ++i) {
- fPathVerbs.push_back(SkPath::kQuad_Verb);
+ *fPathVerbs.append() = SkPath::kQuad_Verb;
}
- fPathPts.push_back_n(nQuads * 2, &quadPts[1]);
+ fPathPts.append(nQuads * 2, &quadPts[1]);
curve[0] = pts[2];
lastCurve = true;
}
@@ -135,16 +145,16 @@ int SkOpEdgeBuilder::preFetch() {
case SkPath::kDone_Verb:
continue;
}
- fPathVerbs.push_back(verb);
+ *fPathVerbs.append() = verb;
int ptCount = SkPathOpsVerbToPoints(verb);
- fPathPts.push_back_n(ptCount, &pts[1]);
+ fPathPts.append(ptCount, &pts[1]);
curve[0] = pts[ptCount];
lastCurve = true;
} while (verb != SkPath::kDone_Verb);
if (!fAllowOpenContours && lastCurve) {
closeContour(curve[0], curveStart);
}
- fPathVerbs.push_back(SkPath::kDone_Verb);
+ *fPathVerbs.append() = SkPath::kDone_Verb;
return fPathVerbs.count() - 1;
}
@@ -153,10 +163,10 @@ bool SkOpEdgeBuilder::close() {
return true;
}
-bool SkOpEdgeBuilder::walk() {
+bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
uint8_t* verbPtr = fPathVerbs.begin();
uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
- const SkPoint* pointsPtr = fPathPts.begin() - 1;
+ SkPoint* pointsPtr = fPathPts.begin() - 1;
SkPath::Verb verb;
while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
if (verbPtr == endOfFirstHalf) {
@@ -165,7 +175,7 @@ bool SkOpEdgeBuilder::walk() {
verbPtr++;
switch (verb) {
case SkPath::kMove_Verb:
- if (fCurrentContour) {
+ if (fCurrentContour && fCurrentContour->count()) {
if (fAllowOpenContours) {
complete();
} else if (!close()) {
@@ -173,21 +183,46 @@ bool SkOpEdgeBuilder::walk() {
}
}
if (!fCurrentContour) {
- fCurrentContour = fContours.push_back_n(1);
- fCurrentContour->setOperand(fOperand);
- fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_PathOpsMask);
+ fCurrentContour = fContoursHead->appendContour(allocator);
}
+ fCurrentContour->init(fGlobalState, fOperand,
+ fXorMask[fOperand] == kEvenOdd_PathOpsMask);
pointsPtr += 1;
continue;
case SkPath::kLine_Verb:
- fCurrentContour->addLine(pointsPtr);
+ fCurrentContour->addLine(pointsPtr, fAllocator);
break;
case SkPath::kQuad_Verb:
- fCurrentContour->addQuad(pointsPtr);
- break;
- case SkPath::kCubic_Verb:
- fCurrentContour->addCubic(pointsPtr);
+ fCurrentContour->addQuad(pointsPtr, fAllocator);
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 (int index = 0; index < SkPathOpsVerbToPoints(v1); ++index) {
+ force_small_to_zero(&curve1[index]);
+ }
+ for (int index = 0; index < SkPathOpsVerbToPoints(v2); ++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()) {
@@ -198,10 +233,11 @@ bool SkOpEdgeBuilder::walk() {
SkDEBUGFAIL("bad verb");
return false;
}
- pointsPtr += SkPathOpsVerbToPoints(verb);
SkASSERT(fCurrentContour);
+ fCurrentContour->debugValidate();
+ pointsPtr += SkPathOpsVerbToPoints(verb);
}
- if (fCurrentContour && !fAllowOpenContours && !close()) {
+ if (fCurrentContour && fCurrentContour->count() &&!fAllowOpenContours && !close()) {
return false;
}
return true;
diff --git a/src/pathops/SkOpEdgeBuilder.h b/src/pathops/SkOpEdgeBuilder.h
index fd0744572d..3ecc915833 100644
--- a/src/pathops/SkOpEdgeBuilder.h
+++ b/src/pathops/SkOpEdgeBuilder.h
@@ -9,20 +9,25 @@
#include "SkOpContour.h"
#include "SkPathWriter.h"
-#include "SkTArray.h"
class SkOpEdgeBuilder {
public:
- SkOpEdgeBuilder(const SkPathWriter& path, SkTArray<SkOpContour>& contours)
- : fPath(path.nativePath())
- , fContours(contours)
+ 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)
, fAllowOpenContours(true) {
init();
}
- SkOpEdgeBuilder(const SkPath& path, SkTArray<SkOpContour>& contours)
- : fPath(&path)
- , fContours(contours)
+ SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkChunkAlloc* allocator,
+ SkOpGlobalState* globalState)
+ : fAllocator(allocator)
+ , fGlobalState(globalState)
+ , fPath(&path)
+ , fContoursHead(contours2)
, fAllowOpenContours(false) {
init();
}
@@ -30,13 +35,19 @@ public:
void addOperand(const SkPath& path);
void complete() {
- if (fCurrentContour && fCurrentContour->segments().count()) {
+ if (fCurrentContour && fCurrentContour->count()) {
fCurrentContour->complete();
fCurrentContour = NULL;
}
}
- bool finish();
+ int count() const;
+ bool finish(SkChunkAlloc* );
+
+ const SkOpContour* head() const {
+ return fContoursHead;
+ }
+
void init();
bool unparseable() const { return fUnparseable; }
SkPathOpsMask xorMask() const { return fXorMask[fOperand]; }
@@ -45,13 +56,15 @@ private:
void closeContour(const SkPoint& curveEnd, const SkPoint& curveStart);
bool close();
int preFetch();
- bool walk();
+ bool walk(SkChunkAlloc* );
+ SkChunkAlloc* fAllocator;
+ SkOpGlobalState* fGlobalState;
const SkPath* fPath;
- SkTArray<SkPoint, true> fPathPts;
- SkTArray<uint8_t, true> fPathVerbs;
+ SkTDArray<SkPoint> fPathPts;
+ SkTDArray<uint8_t> fPathVerbs;
SkOpContour* fCurrentContour;
- SkTArray<SkOpContour>& fContours;
+ SkOpContour* fContoursHead;
SkPathOpsMask fXorMask[2];
int fSecondHalf;
bool fOperand;
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 1fb5afa028..c6ccf93003 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -4,11 +4,20 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-#include "SkIntersections.h"
+#include "SkOpCoincidence.h"
#include "SkOpContour.h"
#include "SkOpSegment.h"
#include "SkPathWriter.h"
-#include "SkTSort.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.
+ */
#define F (false) // discard the edge
#define T (true) // keep the edge
@@ -19,7 +28,7 @@ static const bool gUnaryActiveEdge[2][2] = {
{F, T}, {T, F},
};
-static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
+static const bool gActiveEdge[kXOR_SkPathOp + 1][2][2][2][2] = {
// miFrom=0 miFrom=1
// miTo=0 miTo=1 miTo=0 miTo=1
// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
@@ -33,147 +42,125 @@ static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
#undef F
#undef T
-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)) {
+SkOpAngle* SkOpSegment::activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr,
+ SkOpSpanBase** endPtr, bool* done, bool* sortable) {
+ if (SkOpAngle* result = activeAngleInner(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;
- }
+ if (SkOpAngle* result = activeAngleOther(start, startPtr, endPtr, 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;
}
-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;
+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;
}
- if (!upSpan.fDone) {
- if (upSpan.fWindSum != SK_MinS32) {
- return spanToAngle(index, next);
+ if (!upSpan->done()) {
+ if (upSpan->windSum() != SK_MinS32) {
+ return spanToAngle(start, next);
}
*done = false;
}
} else {
- SkASSERT(upSpan.fDone);
+ SkASSERT(upSpan->done());
}
}
- int prev = nextExactSpan(index, -1);
+ SkOpSpan* downSpan = start->prev();
// edge leading into junction
- 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);
+ 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);
}
*done = false;
}
} else {
- SkASSERT(downSpan.fDone);
+ SkASSERT(downSpan->done());
}
}
return NULL;
}
-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);
+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);
}
-SkPoint SkOpSegment::activeLeftTop(int* firstT) const {
+SkPoint SkOpSegment::activeLeftTop(SkOpSpanBase** firstSpan) {
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;
- for (int index = 0; index < count; ++index) {
- const SkOpSpan& span = fTs[index];
- if (span.fDone && lastDone) {
- goto next;
- }
- if (approximately_negative(span.fT - lastT)) {
+ SkOpSpanBase* span = &fHead;
+ do {
+ if (lastDone && (span->final() || span->upCast()->done())) {
goto next;
}
{
- const SkPoint& xy = xyAtT(&span);
+ const SkPoint& xy = span->pt();
if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
topPt = xy;
- if (firstT) {
- *firstT = index;
+ if (firstSpan) {
+ *firstSpan = span;
}
}
if (fVerb != SkPath::kLine_Verb && !lastDone) {
- SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
+ SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT,
+ span->t());
if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
&& topPt.fX > curveTop.fX)) {
topPt = curveTop;
- if (firstT) {
- *firstT = index;
+ if (firstSpan) {
+ *firstSpan = span;
}
}
}
- lastT = span.fT;
+ lastT = span->t();
}
next:
- lastDone = span.fDone;
- }
+ if (span->final()) {
+ break;
+ }
+ lastDone = span->upCast()->done();
+ } while ((span = span->upCast()->next()));
return topPt;
}
-bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
- int sumMiWinding = updateWinding(endIndex, index);
- int sumSuWinding = updateOppWinding(endIndex, index);
+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);
#if DEBUG_LIMIT_WIND_SUM
SkASSERT(abs(sumMiWinding) <= DEBUG_LIMIT_WIND_SUM);
SkASSERT(abs(sumSuWinding) <= DEBUG_LIMIT_WIND_SUM);
#endif
- if (fOperand) {
+ if (this->operand()) {
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
- return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding);
+ return this->activeOp(xorMiMask, xorSuMask, start, end, op, &sumMiWinding, &sumSuWinding);
}
-bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
- int* sumMiWinding, int* sumSuWinding) {
+bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end,
+ SkPathOp op, int* sumMiWinding, int* sumSuWinding) {
int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
- setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
+ this->setUpWindings(start, end, sumMiWinding, sumSuWinding,
&maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
bool miFrom;
bool miTo;
@@ -193,178 +180,31 @@ bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex
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(), span(index).fT, span(endIndex).fT,
+ __FUNCTION__, debugID(), start->t(), end->t(),
SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
#endif
return result;
}
-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 = updateWinding(end, start);
+ return activeWinding(start, end, &sumWinding);
}
-bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) {
+bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding) {
int maxWinding;
- setUpWinding(index, endIndex, &maxWinding, sumWinding);
+ setUpWinding(start, end, &maxWinding, sumWinding);
bool from = maxWinding != 0;
bool to = *sumWinding != 0;
bool result = gUnaryActiveEdge[from][to];
return result;
}
-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 {
+void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
+ SkPathWriter* path, bool active) const {
SkPoint edge[4];
const SkPoint* ePtr;
- int lastT = fTs.count() - 1;
- if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
+ if ((start == &fHead && end == &fTail) || (start == &fTail && end == &fHead)) {
ePtr = fPts;
} else {
// OPTIMIZE? if not active, skip remainder and return xyAtT(end)
@@ -372,7 +212,7 @@ void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active
ePtr = edge;
}
if (active) {
- bool reverse = ePtr == fPts && start != 0;
+ bool reverse = ePtr == fPts && start != &fHead;
if (reverse) {
path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
switch (fVerb) {
@@ -388,7 +228,6 @@ void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active
default:
SkASSERT(0);
}
- // return ePtr[0];
} else {
path->deferredMoveLine(ePtr[0]);
switch (fVerb) {
@@ -406,1473 +245,350 @@ void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active
}
}
}
- // 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);
}
-void SkOpSegment::setFromAngle(int endIndex, SkOpAngle* angle) {
- int spanCount = fTs.count();
+SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) {
+ SkOpSpanBase* existing = NULL;
+ SkOpSpanBase* test = &fHead;
+ double testT;
do {
- 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;
+ 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);
}
- span.fOtherT = otherT;
- span.fOtherIndex = otherIndex;
-}
-
-void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
- init(pts, SkPath::kQuad_Verb, operand, evenOdd);
- fBounds.setQuadBounds(pts);
+ SkASSERT(result);
+ return result;
}
-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) {
+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();
break;
}
- 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) {
+ oEndSpan = ptT->span();
+ oStartSpan = oEndSpan->prev();
+ if (oStartSpan && oStartSpan->windValue()) {
break;
}
- 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);
+ }
+ SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ oAngle->set(oStartSpan, oEndSpan);
+ oStartSpan->setToAngle(oAngle);
*otherPtr = other;
- return &oAngle;
+ return oAngle;
}
-SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
+SkOpAngle* SkOpSegment::addSingletonAngles(int step, SkChunkAlloc* allocator) {
SkOpSegment* other;
SkOpAngle* angle, * otherAngle;
if (step > 0) {
- otherAngle = addSingletonAngleUp(&other, &angle);
+ otherAngle = addSingletonAngleUp(&other, &angle, allocator);
} else {
- otherAngle = addSingletonAngleDown(&other, &angle);
+ otherAngle = addSingletonAngleDown(&other, &angle, allocator);
}
angle->insert(otherAngle);
return angle;
}
-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;
+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()) {
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;
- }
- }
- }
- 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;
-}
-
-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;
- }
+ oStartSpan = oEndSpan->upCastable();
+ if (oStartSpan && oStartSpan->windValue()) {
+ oEndSpan = oStartSpan->next();
+ 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 {
- 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;
- }
- ++start; // range initial + 1
- if (terminus) {
- continue;
- }
- const SkPoint& pt = startSpan->fPt;
- int end = start; // range initial + 1
- while (end <= last) {
- const SkOpSpan& endSpan = this->span(end);
- if (!AlmostEqualUlps(endSpan.fPt, pt)) {
- break;
- }
- if (fVerb == SkPath::kCubic_Verb) {
- double tMid = (startSpan->fT + endSpan.fT) / 2;
- SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
- if (!midEndPt.approximatelyEqual(xyAtT(startSpan))) {
- break;
- }
- }
- ++end;
- }
- if (start == end) { // end == range final + 1
- continue;
- }
- while (--end >= start) { // end == range final
- const SkOpSpan& endSpan = this->span(end);
- const SkOpSpan& priorSpan = this->span(end - 1);
- if (endSpan.fPt != priorSpan.fPt || endSpan.fT != priorSpan.fT) {
- break; // end == range final + 1
- }
- }
- if (end < start) { // end == range final + 1
- continue;
- }
- int index = start - 1; // index == range initial
- start = end; // start = range final + 1
- const SkOpSpan& nextSpan = this->span(end);
- if (precisely_negative(nextSpan.fT - startSpan->fT)) {
- while (++index < end) {
- startSpan = &this->fTs[index];
- startSpan->fSmall = startSpan->fTiny = false; // sets range initial + 1
- if ((startSpan->fDone = !startSpan->fWindValue)) {
- ++fDoneSpans;
- }
- }
- continue;
- }
- if (!startSpan->fWindValue) {
- --fDoneSpans; // added back below
- }
- bool tiny = nextSpan.fPt == startSpan->fPt;
- do {
- fSmall = startSpan->fSmall = true; // sets range initial
- fTiny |= startSpan->fTiny = tiny;
- startSpan->fDone = true;
- ++fDoneSpans;
- startSpan = &this->fTs[++index];
- } while (index < end); // loop through tiny small range end (last)
- } while (start <= last);
+ SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ oAngle->set(oEndSpan, oStartSpan);
+ oEndSpan->setFromAngle(oAngle);
+ *otherPtr = other;
+ return oAngle;
}
-// 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;
+SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
+ debugValidate();
+ SkPoint pt = this->ptAtT(t);
+ SkOpSpanBase* span = &fHead;
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;
- }
+ SkOpPtT* result = span->ptT();
+ if (t == result->fT) {
+ return result;
}
- oFoundEnd = endPt == oTest->fPt;
- do {
- SkASSERT(originalWindValue == oTest->fWindValue);
- if (decrement) {
- if (binary && !bigger) {
- oTest->fOppValue--;
- } else {
- other->decrementSpan(oTest);
+ 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;
}
- } 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);
+ 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();
}
- }
- 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;
+ SkASSERT(span != &fTail);
+ } while ((span = span->upCast()->next()));
+ SkASSERT(0);
+ return NULL;
}
-// 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();
+// 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]);
}
- 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) {
+ while ((span = span->upCast()->next())) {
+ if (span == &fTail) {
break;
}
- 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;
+ span->align();
}
- 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;
- }
+ if (!span->aligned()) {
+ span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
}
debugValidate();
}
-void SkOpSegment::alignRange(int lower, int upper,
- const SkOpSegment* other, int oLower, int oUpper) {
- for (int oIndex = oLower; oIndex <= oUpper; ++oIndex) {
- const SkOpSpan& oSpan = other->span(oIndex);
- const SkOpSegment* oOther = oSpan.fOther;
- if (oOther == this) {
- continue;
- }
- SkOpSpan* matchSpan;
- int matchIndex;
- const SkOpSpan* refSpan;
- for (int iIndex = lower; iIndex <= upper; ++iIndex) {
- const SkOpSpan& iSpan = this->span(iIndex);
- const SkOpSegment* iOther = iSpan.fOther;
- if (iOther == other) {
- continue;
- }
- if (iOther == oOther) {
- goto nextI;
- }
- }
- {
- // oSpan does not have a match in this
- int iCount = this->count();
- const SkOpSpan* iMatch = NULL;
- double iMatchTDiff;
- matchIndex = -1;
- for (int iIndex = 0; iIndex < iCount; ++iIndex) {
- const SkOpSpan& iSpan = this->span(iIndex);
- const SkOpSegment* iOther = iSpan.fOther;
- if (iOther != oOther) {
- continue;
- }
- double testTDiff = fabs(iSpan.fOtherT - oSpan.fOtherT);
- if (!iMatch || testTDiff < iMatchTDiff) {
- matchIndex = iIndex;
- iMatch = &iSpan;
- iMatchTDiff = testTDiff;
- }
- }
- if (matchIndex < 0) {
- continue; // the entry is missing, & will be picked up later (FIXME: fix it here?)
- }
- matchSpan = &this->fTs[matchIndex];
- refSpan = &this->span(lower);
- if (!SkDPoint::ApproximatelyEqual(matchSpan->fPt, refSpan->fPt)) {
- goto nextI;
- }
- if (matchIndex != lower - 1 && matchIndex != upper + 1) {
- // the consecutive spans need to be rearranged to get the missing one close
- continue; // FIXME: more work to do
- }
- }
- {
- this->fixOtherTIndex();
- SkScalar newT;
- if (matchSpan->fT != 0 && matchSpan->fT != 1) {
- newT = matchSpan->fT = refSpan->fT;
- matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = refSpan->fT;
- } else { // leave span at the start or end there and adjust the neighbors
- newT = matchSpan->fT;
- for (int iIndex = lower; iIndex <= upper; ++iIndex) {
- matchSpan = &this->fTs[iIndex];
- matchSpan->fT = newT;
- matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = newT;
- }
- }
- this->resetSpanFlags(); // fix up small / tiny / done
- // align ts of other ranges with adjacent spans that match the aligned points
- lower = SkTMin(lower, matchIndex);
- while (lower > 0) {
- const SkOpSpan& span = this->span(lower - 1);
- if (span.fT != newT) {
- break;
- }
- --lower;
- }
- upper = SkTMax(upper, matchIndex);
- int last = this->count() - 1;
- while (upper < last) {
- const SkOpSpan& span = this->span(upper + 1);
- if (span.fT != newT) {
- break;
- }
- ++upper;
- }
- for (int iIndex = lower; iIndex <= upper; ++iIndex) {
- const SkOpSpan& span = this->span(iIndex);
- SkOpSegment* aOther = span.fOther;
- int aLower = span.fOtherIndex;
- SkScalar aT = span.fOtherT;
- bool aResetFlags = false;
- while (aLower > 0) {
- SkOpSpan* aSpan = &aOther->fTs[aLower - 1];
- for (int iIndex = lower; iIndex <= upper; ++iIndex) {
- if (aSpan->fPt == this->fTs[iIndex].fPt) {
- goto matchFound;
- }
- }
- break;
- matchFound:
- --aLower;
- }
- int aUpper = span.fOtherIndex;
- int aLast = aOther->count() - 1;
- while (aUpper < aLast) {
- SkOpSpan* aSpan = &aOther->fTs[aUpper + 1];
- for (int iIndex = lower; iIndex <= upper; ++iIndex) {
- if (aSpan->fPt == this->fTs[iIndex].fPt) {
- goto matchFound2;
- }
- }
- break;
- matchFound2:
- ++aUpper;
- }
- if (aOther->fTs[aLower].fT == 0) {
- aT = 0;
- } else if (aOther->fTs[aUpper].fT == 1) {
- aT = 1;
- }
- bool aFixed = false;
- for (int aIndex = aLower; aIndex <= aUpper; ++aIndex) {
- SkOpSpan* aSpan = &aOther->fTs[aIndex];
- if (aSpan->fT == aT) {
- continue;
- }
- SkASSERT(way_roughly_equal(aSpan->fT, aT));
- if (!aFixed) {
- aOther->fixOtherTIndex();
- aFixed = true;
- }
- aSpan->fT = aT;
- aSpan->fOther->fTs[aSpan->fOtherIndex].fOtherT = aT;
- aResetFlags = true;
- }
- if (aResetFlags) {
- aOther->resetSpanFlags();
- }
- }
- }
-nextI: ;
+bool SkOpSegment::BetweenTs(const SkOpSpanBase* lesser, double testT,
+ const SkOpSpanBase* greater) {
+ if (lesser->t() > greater->t()) {
+ SkTSwap<const SkOpSpanBase*>(lesser, greater);
}
+ return approximately_between(lesser->t(), testT, greater->t());
}
-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;
+void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
+ bool activePrior = !fHead.isCanceled();
+ if (activePrior && !fHead.simple()) {
+ addStartSpan(allocator);
}
- 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* prior = &fHead;
+ SkOpSpanBase* spanBase = fHead.next();
+ while (spanBase != &fTail) {
+ if (activePrior) {
+ SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ priorAngle->set(spanBase, prior);
+ spanBase->setFromAngle(priorAngle);
}
- 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);
+ 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);
}
+ activePrior = active;
+ prior = span;
+ spanBase = next;
}
- if (oAligned) {
- other->alignSpanState(oStart, oEnd);
- }
- return aligned;
-}
-
-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;
+ if (activePrior && !fTail.simple()) {
+ addEndSpan(allocator);
}
}
-void SkOpSegment::blindCancel(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 = other->count();
- do {
- SkOpSpan& oSpan = other->fTs[--oIndex];
- 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;
- while (++endIndex <= last) {
- SkOpSpan* 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 oStartIndex = oIndex;
- while (--oStartIndex >= 0) {
- SkOpSpan* oStartSpan = &other->fTs[oStartIndex];
- if (oStartSpan->fWindValue != oBaseWind || oStartSpan->fOppValue != oBaseOpp) {
- break;
- }
- oStartSpan->fCoincident = true;
- }
- 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);
-}
-
-void SkOpSegment::blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other) {
- bool binary = fOperand != other->fOperand;
- int index = 0;
- int last = this->count();
+void SkOpSegment::checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
+ SkOpSpanBase* base = &fHead;
+ SkOpSpan* span;
do {
- SkOpSpan& span = this->fTs[--last];
- if (span.fT != 1 && !span.fSmall) {
- break;
+ SkOpAngle* angle = base->fromAngle();
+ if (angle && angle->fCheckCoincidence) {
+ angle->checkNearCoincidence();
}
- 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;
+ 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;
- SkOpSpan* endSpan;
- while (++endIndex <= last) {
- 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 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);
+ } while ((base = span->next()));
}
-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);
- }
- }
- test = &fTs[index];
- testPt = &test->fPt;
- testT = test->fT;
- oTest = &other->fTs[oIndex];
- oTestPt = &oTest->fPt;
- if (endPt == *testPt || precisely_equal(endT, testT)) {
- break;
- }
- if (0 && foundEnd) { // FIXME: this is likely needed but wait until a test case triggers it
- break;
- }
-// SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
- } while (endPt != *oTestPt);
- // in rare cases, one may have ended before the other
- 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);
- }
- 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;
+// 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 (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
- return false;
+ if (inflectionT != startT) {
+ endT = inflectionT;
}
}
- oTest = &other->fTs[oIndex];
- oTestPt = &oTest->fPt;
- } while (endPt != *oTestPt);
- }
- }
- int outCount = outsidePts.count();
- if (!done() && outCount) {
- addCoinOutsides(outsidePts[0], endPt, other);
- }
- if (!other->done() && oOutsidePts.count()) {
- other->addCoinOutsides(oOutsidePts[0], endPt, this);
- }
- setCoincidentRange(startPt, endPt, other);
- other->setCoincidentRange(startPt, endPt, this);
- return true;
-}
-
-// 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;
}
}
- }
- 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;
- }
+ SkDCubic part = cubic.subDivide(startT, endT);
+ for (int index = 0; index < 4; ++index) {
+ edge[index] = part[index].asSkPoint();
}
+ } else {
+ subDivide(start, end, edge);
}
-#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;
+ 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 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;
-}
-
-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);
+ 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();
}
- return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
+ return sum <= 0;
}
-// 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);
+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);
}
}
- 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);
+ 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 {
- addEnd = fTs[endIndex].fOtherT != 0 || fTs[endIndex].fOther->multipleStarts();
- }
- 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);
+ nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
+ &maxWinding, &sumWinding);
+ last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
}
- return true;
+ nextAngle->setLastMarked(last);
}
-int SkOpSegment::checkSetAngle(int tIndex) const {
- const SkOpSpan* span = &fTs[tIndex];
- while (span->fTiny /* || span->fSmall */) {
- span = &fTs[++tIndex];
+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;
+ 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);
+ } else {
+ nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
+ &maxWinding, &sumWinding);
+ last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
}
- return isCanceled(tIndex) ? -1 : tIndex;
+ nextAngle->setLastMarked(last);
}
// at this point, the span is already ordered, or unorderable
-int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
+int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
+ SkOpAngle::IncludeType includeType) {
SkASSERT(includeType != SkOpAngle::kUnaryXor);
- SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
+ SkOpAngle* firstAngle = this->spanToAngle(end, start);
if (NULL == firstAngle || NULL == firstAngle->next()) {
return SK_NaN32;
}
@@ -1898,7 +614,7 @@ int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType
baseAngle = NULL;
continue;
}
- int testWinding = angle->segment()->windSum(angle);
+ int testWinding = angle->starter()->windSum();
if (SK_MinS32 != testWinding) {
baseAngle = angle;
tryReverse = true;
@@ -1906,10 +622,10 @@ int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType
}
if (baseAngle) {
ComputeOneSum(baseAngle, angle, includeType);
- baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
+ baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
}
} while (next != firstAngle);
- if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
+ if (baseAngle && SK_MinS32 == firstAngle->starter()->windSum()) {
firstAngle = baseAngle;
tryReverse = true;
}
@@ -1925,137 +641,41 @@ int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType
baseAngle = NULL;
continue;
}
- int testWinding = angle->segment()->windSum(angle);
+ int testWinding = angle->starter()->windSum();
if (SK_MinS32 != testWinding) {
baseAngle = angle;
continue;
}
if (baseAngle) {
ComputeOneSumReverse(baseAngle, angle, includeType);
- baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
+ baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
}
} while (prior != firstAngle);
}
- 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;
+ return start->starter(end)->windSum();
}
-int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
- bool* hitSomething, double mid, bool opp, bool current) const {
+SkOpSpan* SkOpSegment::crossedSpanY(const SkPoint& basePt, double mid, bool opp, bool current,
+ SkScalar* bestY, double* hitT, bool* hitSomething, bool* vertical) {
SkScalar bottom = fBounds.fBottom;
- int bestTIndex = -1;
+ *vertical = false;
if (bottom <= *bestY) {
- return bestTIndex;
+ return NULL;
}
SkScalar top = fBounds.fTop;
if (top >= basePt.fY) {
- return bestTIndex;
+ return NULL;
}
if (fBounds.fLeft > basePt.fX) {
- return bestTIndex;
+ return NULL;
}
if (fBounds.fRight < basePt.fX) {
- return bestTIndex;
+ return NULL;
}
if (fBounds.fLeft == fBounds.fRight) {
// if vertical, and directly above test point, wait for another one
- return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
+ *vertical = AlmostEqualUlps(basePt.fX, fBounds.fLeft);
+ return NULL;
}
// intersect ray starting at basePt with edge
SkIntersections intersections;
@@ -2065,7 +685,7 @@ int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hi
int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
(fPts, top, bottom, basePt.fX, false);
if (pts == 0 || (current && pts == 1)) {
- return bestTIndex;
+ return NULL;
}
if (current) {
SkASSERT(pts > 1);
@@ -2093,933 +713,73 @@ int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hi
continue;
}
if (pts > 1 && fVerb == SkPath::kLine_Verb) {
- return SK_MinS32; // if the intersection is edge on, wait for another one
+ *vertical = true;
+ return NULL; // 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)) {
- return SK_MinS32; // hit vertical, wait for another one
+ *vertical = true;
+ return NULL; // hit vertical, wait for another one
}
}
*bestY = testY;
bestT = foundT;
}
if (bestT < 0) {
- return bestTIndex;
+ return NULL;
}
SkASSERT(bestT >= 0);
- SkASSERT(bestT <= 1);
- int start;
- int end = 0;
- do {
- 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;
- bestTIndex = start;
- *hitSomething = true;
- }
- 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;
+ SkASSERT(bestT < 1);
+ SkOpSpanBase* testTSpanBase = &this->fHead;
+ SkOpSpanBase* nextTSpan;
+ double endT = 0;
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;
- }
+ nextTSpan = testTSpanBase->upCast()->next();
+ endT = nextTSpan->t();
+ if (endT >= bestT) {
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;
- }
-
}
+ testTSpanBase = nextTSpan;
+ } while (testTSpanBase);
+ SkOpSpan* bestTSpan = NULL;
+ SkOpSpan* testTSpan = testTSpanBase->upCast();
+ if (!testTSpan->isCanceled()) {
+ *hitT = bestT;
+ bestTSpan = testTSpan;
+ *hitSomething = true;
}
+ return bestTSpan;
}
-// 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();
+void SkOpSegment::detach(const SkOpSpan* span) {
+ if (span->done()) {
+ --this->fDoneCount;
}
+ --this->fCount;
}
-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)) {
+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())) {
continue;
}
- if (fVerb != SkPath::kCubic_Verb) {
- return true;
+ double testDistSq = testPt.distanceSquared(i.pt(index));
+ if (closestDistSq > testDistSq) {
+ closestDistSq = testDistSq;
}
- double tInterval = t - span.fT;
- double tMid = t - tInterval / 2;
- SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
- return midPt.approximatelyEqual(xyAtT(t));
}
- 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;
+ return closestDistSq;
}
/*
@@ -3029,71 +789,57 @@ int SkOpSegment::findEndSpan(int endIndex) const {
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<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)
- {
+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) {
// 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
- 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;
+ SkOpSpan* startSpan = start->starter(end);
+ if (startSpan->done()) {
return NULL;
}
+ markDone(startSpan);
+ *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
return other;
}
- const int end = nextExactSpan(startIndex, step);
- SkASSERT(end >= 0);
- SkASSERT(startIndex - endIndex != 0);
- SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+ 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));
// more than one viable candidate -- measure angles to find best
-
- int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp);
+ int calcWinding = computeSum(start, endNear, SkOpAngle::kBinaryOpp);
bool sortable = calcWinding != SK_NaN32;
if (!sortable) {
*unsortable = true;
- markDoneBinary(SkMin32(startIndex, endIndex));
+ markDone(start->starter(end));
return NULL;
}
- SkOpAngle* angle = spanToAngle(end, startIndex);
+ SkOpAngle* angle = this->spanToAngle(end, start);
if (angle->unorderable()) {
*unsortable = true;
- markDoneBinary(SkMin32(startIndex, endIndex));
+ markDone(start->starter(end));
return NULL;
}
#if DEBUG_SORT
SkDebugf("%s\n", __FUNCTION__);
angle->debugLoop();
#endif
- int sumMiWinding = updateWinding(endIndex, startIndex);
+ int sumMiWinding = updateWinding(end, start);
if (sumMiWinding == SK_MinS32) {
*unsortable = true;
- markDoneBinary(SkMin32(startIndex, endIndex));
+ markDone(start->starter(end));
return NULL;
}
- int sumSuWinding = updateOppWinding(endIndex, startIndex);
+ int sumSuWinding = updateOppWinding(end, start);
if (operand()) {
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
@@ -3110,11 +856,6 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
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);
}
@@ -3122,30 +863,24 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
if (nextSegment->done()) {
continue;
}
- if (nextSegment->isTiny(nextAngle)) {
- continue;
- }
if (!activeAngle) {
- (void) nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
+ (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
}
- SkOpSpan* last = nextAngle->lastMarked();
+ SkOpSpanBase* last = nextAngle->lastMarked();
if (last) {
SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
*chase->append() = last;
#if DEBUG_WINDING
- SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
- last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
- last->fSmall);
+ 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");
#endif
}
} while ((nextAngle = nextAngle->next()) != angle);
-#if DEBUG_ANGLE
- if (foundAngle) {
- foundAngle->debugSameAs(foundAngle);
- }
-#endif
-
- markDoneBinary(SkMin32(startIndex, endIndex));
+ start->segment()->markDone(start->starter(end));
if (!foundAngle) {
return NULL;
}
@@ -3159,62 +894,55 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
return nextSegment;
}
-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)
- {
+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) {
// 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
- 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;
+ SkOpSpan* startSpan = start->starter(end);
+ if (startSpan->done()) {
return NULL;
}
+ markDone(startSpan);
+ *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
return other;
}
- const int end = nextExactSpan(startIndex, step);
- SkASSERT(end >= 0);
- SkASSERT(startIndex - endIndex != 0);
- SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+ 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));
// more than one viable candidate -- measure angles to find best
-
- int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding);
+ int calcWinding = computeSum(start, endNear, SkOpAngle::kUnaryWinding);
bool sortable = calcWinding != SK_NaN32;
if (!sortable) {
*unsortable = true;
- markDoneUnary(SkMin32(startIndex, endIndex));
+ markDone(start->starter(end));
+ return NULL;
+ }
+ SkOpAngle* angle = this->spanToAngle(end, start);
+ if (angle->unorderable()) {
+ *unsortable = true;
+ markDone(start->starter(end));
return NULL;
}
- SkOpAngle* angle = spanToAngle(end, startIndex);
#if DEBUG_SORT
SkDebugf("%s\n", __FUNCTION__);
angle->debugLoop();
#endif
- int sumWinding = updateWinding(endIndex, startIndex);
+ int sumWinding = updateWinding(end, start);
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 {
@@ -3224,11 +952,6 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
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);
}
@@ -3236,24 +959,24 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
if (nextSegment->done()) {
continue;
}
- if (nextSegment->isTiny(nextAngle)) {
- continue;
- }
if (!activeAngle) {
- nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
+ (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
}
- SkOpSpan* last = nextAngle->lastMarked();
+ SkOpSpanBase* last = nextAngle->lastMarked();
if (last) {
SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
*chase->append() = last;
#if DEBUG_WINDING
- SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
- last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
- last->fSmall);
+ 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");
#endif
}
} while ((nextAngle = nextAngle->next()) != angle);
- markDoneUnary(SkMin32(startIndex, endIndex));
+ start->segment()->markDone(start->starter(end));
if (!foundAngle) {
return NULL;
}
@@ -3267,57 +990,39 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
return nextSegment;
}
-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)
- {
+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)
#if DEBUG_WINDING
SkDebugf("%s simple\n", __FUNCTION__);
#endif
- int min = SkMin32(startIndex, endIndex);
- if (fTs[min].fDone) {
+ SkOpSpan* startSpan = start->starter(end);
+ if (startSpan->done()) {
return NULL;
}
- 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());
+ markDone(startSpan);
+ *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
return other;
}
- 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);
+ 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;
+ }
#if DEBUG_SORT
SkDebugf("%s\n", __FUNCTION__);
angle->debugLoop();
@@ -3325,16 +1030,13 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort
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;
@@ -3342,7 +1044,7 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort
}
nextAngle = nextAngle->next();
} while (nextAngle != angle);
- markDone(SkMin32(startIndex, endIndex), 1);
+ start->segment()->markDone(start->starter(end));
if (!foundAngle) {
return NULL;
}
@@ -3356,105 +1058,39 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort
return nextSegment;
}
-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) {
+SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
+ bool* unsortable, SkChunkAlloc* allocator) {
// iterate through T intersections and return topmost
// topmost tangent from y-min to first pt is closer to horizontal
SkASSERT(!done());
- int firstT = -1;
- /* SkPoint topPt = */ activeLeftTop(&firstT);
- if (firstT < 0) {
+ SkOpSpanBase* firstT = NULL;
+ (void) this->activeLeftTop(&firstT);
+ if (!firstT) {
*unsortable = !firstPass;
- firstT = 0;
- while (fTs[firstT].fDone) {
- SkASSERT(firstT < fTs.count());
- ++firstT;
+ firstT = &fHead;
+ while (firstT->upCast()->done()) {
+ firstT = firstT->upCast()->next();
}
- *tIndexPtr = firstT;
- *endIndexPtr = nextExactSpan(firstT, 1);
+ *startPtr = firstT;
+ *endPtr = firstT->upCast()->next();
return this;
}
// sort the edges to find the leftmost
int step = 1;
- int end;
- if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) {
+ SkOpSpanBase* end;
+ if (firstT->final() || firstT->upCast()->done()) {
step = -1;
- end = nextSpan(firstT, step);
- SkASSERT(end != -1);
+ end = firstT->prev();
+ SkASSERT(end);
+ } else {
+ end = firstT->upCast()->next();
}
// 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 != 0);
+ SkASSERT(firstT != end);
SkOpAngle* markAngle = spanToAngle(firstT, end);
if (!markAngle) {
- markAngle = addSingletonAngles(step);
+ markAngle = addSingletonAngles(step, allocator);
}
markAngle->markStops();
const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
@@ -3467,7 +1103,7 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort
const SkOpAngle* angle = baseAngle;
do {
if (!angle->unorderable()) {
- SkOpSegment* next = angle->segment();
+ const SkOpSegment* next = angle->segment();
SkPathOpsBounds bounds;
next->subDivideBounds(angle->end(), angle->start(), &bounds);
bool nearSame = AlmostEqualUlps(top, bounds.top());
@@ -3495,9 +1131,10 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort
*unsortable = angle->unorderable();
if (firstPass || !*unsortable) {
leftSegment = angle->segment();
- *tIndexPtr = angle->end();
- *endIndexPtr = angle->start();
- if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
+ *startPtr = angle->end();
+ *endPtr = angle->start();
+ const SkOpSpan* firstSpan = (*startPtr)->starter(*endPtr);
+ if (!firstSpan->done()) {
break;
}
}
@@ -3508,157 +1145,52 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort
return NULL;
}
if (leftSegment->verb() >= SkPath::kQuad_Verb) {
- const int tIndex = *tIndexPtr;
- const int endIndex = *endIndexPtr;
+ SkOpSpanBase* start = *startPtr;
+ SkOpSpanBase* end = *endPtr;
bool swap;
- if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
+ if (!leftSegment->clockwise(start, end, &swap)) {
#if DEBUG_SWAP_TOP
- SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
+ SkDebugf("%s swap=%d inflections=%d monotonic=%d\n",
__FUNCTION__,
- swap, leftSegment->debugInflections(tIndex, endIndex),
- leftSegment->serpentine(tIndex, endIndex),
- leftSegment->controlsContainedByEnds(tIndex, endIndex),
- leftSegment->monotonicInY(tIndex, endIndex));
+ swap, leftSegment->debugInflections(start, end),
+ leftSegment->monotonicInY(start, end));
#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(*tIndexPtr, *endIndexPtr);
+ SkTSwap(*startPtr, *endPtr);
}
}
}
- SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
return leftSegment;
}
-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);
+SkOpGlobalState* SkOpSegment::globalState() const {
+ return contour()->globalState();
}
-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;
+void SkOpSegment::init(SkPoint pts[], SkOpContour* contour, SkPath::Verb verb) {
+ fContour = contour;
+ fNext = NULL;
fPts = pts;
fVerb = verb;
- fLoop = fMultiples = fSmall = fTiny = false;
-}
-
-void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
- int local = spanSign(start, end);
+ 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);
SkDEBUGCODE(bool success);
if (angleIncludeType == SkOpAngle::kBinarySingle) {
- int oppLocal = oppSign(start, end);
+ int oppLocal = SkOpSegment::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);
@@ -3679,12 +1211,13 @@ 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(int start, int end, double tHit, int winding, SkScalar hitDx,
- int oppWind, SkScalar hitOppDx) {
+bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHit,
+ int winding, SkScalar hitDx, int oppWind, SkScalar hitOppDx) {
+ SkASSERT(this == start->segment());
SkASSERT(hitDx || !winding);
SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
- SkASSERT(dx);
- int windVal = windValue(SkMin32(start, end));
+// SkASSERT(dx);
+ int windVal = start->starter(end)->windValue();
#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);
@@ -3693,9 +1226,9 @@ bool SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc
if (abs(winding) < abs(sideWind)) {
winding = sideWind;
}
- SkDEBUGCODE(int oppLocal = oppSign(start, end));
+ SkDEBUGCODE(int oppLocal = SkOpSegment::OppSign(start, end));
SkASSERT(hitOppDx || !oppWind || !oppLocal);
- int oppWindVal = oppValue(SkMin32(start, end));
+ int oppWindVal = start->starter(end)->oppValue();
if (!oppWind) {
oppWind = dx < 0 ? oppWindVal : -oppWindVal;
} else if (hitOppDx * dx >= 0) {
@@ -3714,149 +1247,57 @@ bool SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc
return success;
}
-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;
- }
- }
+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;
}
- } while (fTs[++tIndex].fT == 0);
+ }
return false;
}
-// 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;
+bool SkOpSegment::isXor() const {
+ return fContour->isXor();
}
-SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
- int step = SkSign32(endIndex - index);
- int min = SkMin32(index, endIndex);
- markDoneUnary(min);
- SkOpSpan* last = NULL;
+SkOpSpanBase* SkOpSegment::markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end) {
+ int step = start->step(end);
+ SkOpSpan* minSpan = start->starter(end);
+ markDone(minSpan);
+ SkOpSpanBase* last = NULL;
SkOpSegment* other = this;
- while ((other = other->nextChase(&index, &step, &min, &last))) {
+ while ((other = other->nextChase(&start, &step, &minSpan, &last))) {
if (other->done()) {
SkASSERT(!last);
break;
}
- other->markDoneUnary(min);
+ other->markDone(minSpan);
}
return last;
}
-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;
+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;
SkOpSegment* other = this;
- 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);
+ while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
+ if (spanStart->windSum() != SK_MinS32) {
+ SkASSERT(spanStart->windSum() == winding);
SkASSERT(!last);
break;
}
- (void) other->markWinding(min, winding);
+ (void) other->markWinding(spanStart, winding);
}
if (lastPtr) {
*lastPtr = last;
@@ -3864,37 +1305,32 @@ bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, SkOp
return success;
}
-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;
+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;
SkOpSegment* other = this;
- 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);
+ 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;
}
+ } else {
+ SkASSERT(spanStart->windSum() == oppWinding);
+ SkASSERT(spanStart->oppSum() == winding);
}
SkASSERT(!last);
-#endif
break;
}
- if (fOperand == other->fOperand) {
- (void) other->markWinding(min, winding, oppWinding);
+ if (this->operand() == other->operand()) {
+ (void) other->markWinding(spanStart, winding, oppWinding);
} else {
- (void) other->markWinding(min, oppWinding, winding);
+ (void) other->markWinding(spanStart, oppWinding, winding);
}
}
if (lastPtr) {
@@ -3903,33 +1339,29 @@ bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int
return success;
}
-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) {
+SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
SkASSERT(angle->segment() == this);
if (UseInnerWinding(maxWinding, sumWinding)) {
maxWinding = sumWinding;
}
- SkOpSpan* last;
- SkAssertResult(markAndChaseWinding(angle, maxWinding, &last));
+ SkOpSpanBase* last;
+ (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, &last);
#if DEBUG_WINDING
if (last) {
- SkDebugf("%s last id=%d windSum=", __FUNCTION__,
- last->fOther->fTs[last->fOtherIndex].fOther->debugID());
- SkPathOpsDebug::WindingPrintf(last->fWindSum);
- SkDebugf(" small=%d\n", last->fSmall);
+ 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");
}
#endif
return last;
}
-SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
- int oppSumWinding, const SkOpAngle* angle) {
+SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
+ int oppSumWinding, const SkOpAngle* angle) {
SkASSERT(angle->segment() == this);
if (UseInnerWinding(maxWinding, sumWinding)) {
maxWinding = sumWinding;
@@ -3937,440 +1369,161 @@ SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWindi
if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
oppMaxWinding = oppSumWinding;
}
- SkOpSpan* last;
+ SkOpSpanBase* last = NULL;
// caller doesn't require that this marks anything
- (void) markAndChaseWinding(angle, maxWinding, oppMaxWinding, &last);
+ (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, oppMaxWinding, &last);
#if DEBUG_WINDING
if (last) {
- SkDebugf("%s last id=%d windSum=", __FUNCTION__,
- last->fOther->fTs[last->fOtherIndex].fOther->debugID());
- SkPathOpsDebug::WindingPrintf(last->fWindSum);
- SkDebugf(" small=%d\n", last->fSmall);
+ 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");
}
#endif
return last;
}
-// 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);
- }
- do {
- markOneDone(__FUNCTION__, index, winding);
- } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
- debugValidate();
-}
-
-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) {
+void SkOpSegment::markDone(SkOpSpan* span) {
+ SkASSERT(this == span->segment());
+ if (span->done()) {
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;
+#if DEBUG_MARK_DONE
+ debugShowNewWinding(__FUNCTION__, span, span->windSum(), span->oppSum());
+#endif
+ span->setDone(true);
+ ++fDoneCount;
+ debugValidate();
}
-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) {
+bool SkOpSegment::markWinding(SkOpSpan* span, int winding) {
+ SkASSERT(this == span->segment());
+ SkASSERT(winding);
+ if (span->done()) {
return false;
}
#if DEBUG_MARK_DONE
- debugShowNewWinding(funName, *span, winding);
-#endif
- SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
-#if DEBUG_LIMIT_WIND_SUM
- SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
+ debugShowNewWinding(__FUNCTION__, span, winding);
#endif
- span->fWindSum = winding;
+ span->setWindSum(winding);
+ debugValidate();
return true;
}
-bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
- int oppWinding, SkOpSpan** lastPtr) {
- SkOpSpan* span = &fTs[tIndex];
- if (span->fDone && !span->fSmall) {
+bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
+ SkASSERT(this == span->segment());
+ SkASSERT(winding || oppWinding);
+ if (span->done()) {
return false;
}
#if DEBUG_MARK_DONE
- debugShowNewWinding(funName, *span, winding, oppWinding);
+ debugShowNewWinding(__FUNCTION__, span, winding, oppWinding);
#endif
- 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;
+ span->setWindSum(winding);
+ span->setOppSum(oppWinding);
debugValidate();
- if (lastPtr) {
- *lastPtr = span;
- }
return true;
}
-// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
-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;
- }
- }
+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;
}
- 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 (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
+ return false;
}
- 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 !ptsDisjoint(base->fT, base->fPt, testT, testPt);
+}
+
+static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
+ if (last) {
+ *last = endSpan;
}
- return sum <= 0;
+ return NULL;
}
-bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
+bool SkOpSegment::monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
SkASSERT(fVerb != SkPath::kLine_Verb);
if (fVerb == SkPath::kQuad_Verb) {
- SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
+ SkDQuad dst = SkDQuad::SubDivide(fPts, start->t(), end->t());
return dst.monotonicInY();
}
SkASSERT(fVerb == SkPath::kCubic_Verb);
- SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
+ SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
return dst.monotonicInY();
}
-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) {
+bool SkOpSegment::NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start,
+ SkOpSpanBase** end) {
+ while (span->final() || span->upCast()->done()) {
+ if (span->final()) {
return false;
}
- ++(*end);
+ span = span->upCast()->next();
}
- *start = *end;
- *end = nextExactSpan(*start, 1);
+ *start = span;
+ *end = span->upCast()->next();
return true;
}
-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;
+SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpSpan** minPtr,
+ SkOpSpanBase** last) const {
+ SkOpSpanBase* origStart = *startPtr;
int step = *stepPtr;
- 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;
+ SkOpSpanBase* endSpan = step > 0 ? origStart->upCast()->next() : origStart->prev();
+ SkASSERT(endSpan);
+ SkOpAngle* angle = step > 0 ? endSpan->fromAngle() : endSpan->upCast()->toAngle();
+ SkOpSpanBase* foundSpan;
+ SkOpSpanBase* otherEnd;
SkOpSegment* other;
if (angle == NULL) {
- if (endSpan.fT != 0 && endSpan.fT != 1) {
+ if (endSpan->t() != 0 && endSpan->t() != 1) {
return NULL;
}
- other = endSpan.fOther;
- foundIndex = endSpan.fOtherIndex;
- otherEnd = other->nextExactSpan(foundIndex, step);
+ SkOpPtT* otherPtT = endSpan->ptT()->next();
+ other = otherPtT->segment();
+ foundSpan = otherPtT->span();
+ otherEnd = step > 0 ? foundSpan->upCast()->next() : foundSpan->prev();
} 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);
}
+#endif
other = next->segment();
- foundIndex = end = next->start();
+ foundSpan = endSpan = next->start();
otherEnd = next->end();
}
- int foundStep = foundIndex < otherEnd ? 1 : -1;
+ int foundStep = foundSpan->step(otherEnd);
if (*stepPtr != foundStep) {
- return set_last(last, &endSpan);
+ return set_last(last, endSpan);
}
- SkASSERT(*indexPtr >= 0);
- if (otherEnd < 0) {
+ SkASSERT(*startPtr);
+ if (!otherEnd) {
return NULL;
}
// SkASSERT(otherEnd >= 0);
-#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);
+ 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);
}
-#endif
- *indexPtr = foundIndex;
+ *startPtr = foundSpan;
*stepPtr = foundStep;
if (minPtr) {
*minPtr = foundMin;
@@ -4378,101 +1531,217 @@ SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr,
return other;
}
-// 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;
+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();
}
- return to;
- }
- return -1;
+ } while ((span = span->next()->upCastable()));
}
-// 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) {
+// 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;
}
- return to;
- }
- } else {
- while (fTs[from].fTiny) {
- from++;
+ 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.
+ 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);
+ }
}
- const SkOpSpan& fromSpan = fTs[from];
- int count = fTs.count();
- while (++to < count) {
- const SkOpSpan& span = fTs[to];
- if (precisely_negative(span.fT - fromSpan.fT)) {
+ } 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;
continue;
}
- return to;
}
- }
- return -1;
+ 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;
}
-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::operand() const {
+ return fContour->operand();
}
-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;
- }
- }
- return p1IndexMin > p2IndexMax;
+bool SkOpSegment::oppXor() const {
+ return fContour->oppXor();
}
-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;
- }
+bool SkOpSegment::ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const {
+ if (fVerb == SkPath::kLine_Verb) {
+ return false;
}
+ // 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;
}
-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);
+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::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);
if (operand()) {
*maxWinding = *sumSuWinding;
*sumWinding = *sumSuWinding -= deltaSum;
@@ -4484,130 +1753,94 @@ void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int*
*oppMaxWinding = *sumSuWinding;
*oppSumWinding = *sumSuWinding -= oppDeltaSum;
}
-#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
+ SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+ SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
}
void SkOpSegment::sortAngles() {
- int spanCount = fTs.count();
- if (spanCount <= 2) {
- return;
- }
- int index = 0;
+ SkOpSpanBase* span = &this->fHead;
do {
- SkOpAngle* fromAngle = fTs[index].fFromAngle;
- SkOpAngle* toAngle = fTs[index].fToAngle;
+ SkOpAngle* fromAngle = span->fromAngle();
+ SkOpAngle* toAngle = span->final() ? NULL : span->upCast()->toAngle();
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
- if (toAngle) {
- if (!baseAngle) {
- baseAngle = toAngle;
- if (inLoop(baseAngle, spanCount, &index)) {
- continue;
- }
- } else {
- SkDEBUGCODE(int newIndex = index);
- SkASSERT(!inLoop(baseAngle, spanCount, &newIndex) && newIndex == index);
+ SkOpAngle* baseAngle = fromAngle;
+ if (fromAngle && toAngle) {
#if DEBUG_ANGLE
- SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
- index);
- wroteAfterHeader = true;
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), span->t(),
+ span->debugID());
+ wroteAfterHeader = true;
#endif
- baseAngle->insert(toAngle);
- }
+ fromAngle->insert(toAngle);
+ } else if (!fromAngle) {
+ baseAngle = toAngle;
}
- SkOpAngle* nextFrom, * nextTo;
- int firstIndex = index;
+ SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
do {
- SkOpSpan& span = fTs[index];
- SkOpSegment* other = span.fOther;
- SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
- SkOpAngle* oAngle = oSpan.fFromAngle;
+ SkOpSpanBase* oSpan = ptT->span();
+ if (oSpan == span) {
+ continue;
+ }
+ SkOpAngle* oAngle = oSpan->fromAngle();
if (oAngle) {
#if DEBUG_ANGLE
if (!wroteAfterHeader) {
- SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
- index);
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
+ span->t(), span->debugID());
wroteAfterHeader = true;
}
#endif
- if (!oAngle->loopContains(*baseAngle)) {
+ if (!oAngle->loopContains(baseAngle)) {
baseAngle->insert(oAngle);
}
}
- oAngle = oSpan.fToAngle;
- if (oAngle) {
+ if (!oSpan->final()) {
+ oAngle = oSpan->upCast()->toAngle();
+ if (oAngle) {
#if DEBUG_ANGLE
- if (!wroteAfterHeader) {
- SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
- index);
- wroteAfterHeader = true;
- }
+ if (!wroteAfterHeader) {
+ SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
+ span->t(), span->debugID());
+ wroteAfterHeader = true;
+ }
#endif
- if (!oAngle->loopContains(*baseAngle)) {
- baseAngle->insert(oAngle);
+ if (!oAngle->loopContains(baseAngle)) {
+ baseAngle->insert(oAngle);
+ }
}
}
- if (++index == spanCount) {
- break;
+ } while ((ptT = ptT->next()) != stopPtT);
+ if (baseAngle->loopCount() == 1) {
+ span->setFromAngle(NULL);
+ if (toAngle) {
+ span->upCast()->setToAngle(NULL);
}
- 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 (index < spanCount);
+ } while (!span->final() && (span = span->upCast()->next()));
}
// return true if midpoints were computed
-bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
+bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
+ SkPoint edge[4]) const {
SkASSERT(start != end);
- edge[0] = fTs[start].fPt;
+ const SkOpPtT& startPtT = *start->ptT();
+ const SkOpPtT& endPtT = *end->ptT();
+ edge[0] = startPtT.fPt;
int points = SkPathOpsVerbToPoints(fVerb);
- edge[points] = fTs[end].fPt;
+ edge[points] = endPtT.fPt;
if (fVerb == SkPath::kLine_Verb) {
return false;
}
- double startT = fTs[start].fT;
- double endT = fTs[end].fT;
+ double startT = startPtT.fT;
+ double endT = endPtT.fT;
if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
// don't compute midpoints if we already have them
if (fVerb == SkPath::kQuad_Verb) {
@@ -4637,17 +1870,19 @@ bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
return true;
}
-// return true if midpoints were computed
-bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
+bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
+ SkDCubic* result) const {
SkASSERT(start != end);
- (*result)[0].set(fTs[start].fPt);
+ const SkOpPtT& startPtT = *start->ptT();
+ const SkOpPtT& endPtT = *end->ptT();
+ (*result)[0].set(startPtT.fPt);
int points = SkPathOpsVerbToPoints(fVerb);
- (*result)[points].set(fTs[end].fPt);
+ (*result)[points].set(endPtT.fPt);
if (fVerb == SkPath::kLine_Verb) {
return false;
}
- double startT = fTs[start].fT;
- double endT = fTs[end].fT;
+ double startT = startPtT.fT;
+ double endT = endPtT.fT;
if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
// don't compute midpoints if we already have them
if (fVerb == SkPath::kQuad_Verb) {
@@ -4655,7 +1890,7 @@ bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
return false;
}
SkASSERT(fVerb == SkPath::kCubic_Verb);
- if (start < end) {
+ if (startT == 0) {
(*result)[1].set(fPts[1]);
(*result)[2].set(fPts[2]);
return false;
@@ -4673,49 +1908,29 @@ bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
return true;
}
-void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
+void SkOpSegment::subDivideBounds(const SkOpSpanBase* start, const SkOpSpanBase* end,
+ SkPathOpsBounds* bounds) const {
SkPoint edge[4];
subDivide(start, end, edge);
(bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
}
-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) {
+void SkOpSegment::undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end) {
+ SkOpSpan* span = this->head();
+ do {
+ if (!span->done()) {
break;
}
- }
- 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;
+ } while ((span = span->next()->upCastable()));
+ SkASSERT(span);
+ *start = span;
+ *end = span->next();
}
-int SkOpSegment::updateOppWinding(int index, int endIndex) const {
- int lesser = SkMin32(index, endIndex);
- int oppWinding = oppSum(lesser);
- int oppSpanWinding = oppSign(index, endIndex);
+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);
if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
&& oppWinding != SK_MaxS32) {
oppWinding -= oppSpanWinding;
@@ -4724,24 +1939,24 @@ int SkOpSegment::updateOppWinding(int index, int endIndex) const {
}
int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
- int startIndex = angle->start();
- int endIndex = angle->end();
- return updateOppWinding(endIndex, startIndex);
+ const SkOpSpanBase* startSpan = angle->start();
+ const SkOpSpanBase* endSpan = angle->end();
+ return updateOppWinding(endSpan, startSpan);
}
int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
- int startIndex = angle->start();
- int endIndex = angle->end();
- return updateOppWinding(startIndex, endIndex);
+ const SkOpSpanBase* startSpan = angle->start();
+ const SkOpSpanBase* endSpan = angle->end();
+ return updateOppWinding(startSpan, endSpan);
}
-int SkOpSegment::updateWinding(int index, int endIndex) const {
- int lesser = SkMin32(index, endIndex);
- int winding = windSum(lesser);
+int SkOpSegment::updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
+ const SkOpSpan* lesser = start->starter(end);
+ int winding = lesser->windSum();
if (winding == SK_MinS32) {
return winding;
}
- int spanWinding = spanSign(index, endIndex);
+ int spanWinding = SkOpSegment::SpanSign(start, end);
if (winding && UseInnerWinding(winding - spanWinding, winding)
&& winding != SK_MaxS32) {
winding -= spanWinding;
@@ -4750,26 +1965,15 @@ int SkOpSegment::updateWinding(int index, int endIndex) const {
}
int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
- 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;
+ const SkOpSpanBase* startSpan = angle->start();
+ const SkOpSpanBase* endSpan = angle->end();
+ return updateWinding(endSpan, startSpan);
}
int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
- int startIndex = angle->start();
- int endIndex = angle->end();
- return updateWindingReverse(endIndex, startIndex);
+ const SkOpSpanBase* startSpan = angle->start();
+ const SkOpSpanBase* endSpan = angle->end();
+ return updateWinding(startSpan, endSpan);
}
// OPTIMIZATION: does the following also work, and is it any faster?
@@ -4784,25 +1988,17 @@ bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
return result;
}
-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
+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
return SK_MinS32;
}
- int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
+ int winding = crossOpp ? span->oppSum() : span->windSum();
SkASSERT(winding != SK_MinS32);
- int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
+ int windVal = crossOpp ? span->oppValue() : span->windValue();
#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, t(tIndex), winding, windVal);
+ debugID(), crossOpp, tHit, span->t(), 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;
@@ -4828,20 +2024,6 @@ int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx
}
int SkOpSegment::windSum(const SkOpAngle* angle) const {
- 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;
+ const SkOpSpan* minSpan = angle->start()->starter(angle->end());
+ return minSpan->windSum();
}
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index b4da929d99..c1c6e696fe 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -9,159 +9,261 @@
#include "SkOpAngle.h"
#include "SkOpSpan.h"
+#include "SkOpTAllocator.h"
#include "SkPathOpsBounds.h"
#include "SkPathOpsCurve.h"
-#include "SkTArray.h"
-#include "SkTDArray.h"
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-#include "SkThread.h"
-#endif
-
-struct SkCoincidence;
+class SkOpCoincidence;
+class SkOpContour;
class SkPathWriter;
class SkOpSegment {
public:
- SkOpSegment() {
-#if defined(SK_DEBUG) || !FORCE_RELEASE
- fID = sk_atomic_inc(&SkPathOpsDebug::gSegmentID);
-#endif
- }
+ enum AllowAlias {
+ kAllowAlias,
+ kNoAlias
+ };
bool operator<(const SkOpSegment& rh) const {
return fBounds.fTop < rh.fBounds.fTop;
}
- struct AlignedSpan {
- double fOldT;
- double fT;
- SkPoint fOldPt;
- SkPoint fPt;
- const SkOpSegment* fSegment;
- const SkOpSegment* fOther1;
- const SkOpSegment* fOther2;
- };
+ 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);
- const SkPathOpsBounds& bounds() const {
- return fBounds;
+ SkPoint activeLeftTop(SkOpSpanBase** firstT);
+
+ bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end);
+ bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding);
+
+ void addCubic(SkPoint pts[4], SkOpContour* parent) {
+ init(pts, parent, SkPath::kCubic_Verb);
+ fBounds.setCubicBounds(pts);
}
- // 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 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;
}
- int count() const {
- return fTs.count();
+ void addLine(SkPoint pts[2], SkOpContour* parent) {
+ init(pts, parent, SkPath::kLine_Verb);
+ fBounds.set(pts, 2);
}
- bool done() const {
- SkASSERT(fDoneSpans <= fTs.count());
- return fDoneSpans == 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(int min) const {
- return fTs[min].fDone;
+ void addQuad(SkPoint pts[3], SkOpContour* parent) {
+ init(pts, parent, SkPath::kQuad_Verb);
+ fBounds.setQuadBounds(pts);
}
- bool done(const SkOpAngle* angle) const {
- return done(SkMin32(angle->start(), angle->end()));
+ 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;
}
- SkDPoint dPtAtT(double mid) const {
- return (*CurveDPointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
+ void bumpCount() {
+ ++fCount;
}
- SkVector dxdy(int index) const {
- return (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[index].fT);
+ 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;
}
- SkScalar dy(int index) const {
- return dxdy(index).fY;
+ int count() const {
+ return fCount;
}
- bool hasMultiples() const {
- return fMultiples;
+ 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 hasSmall() const {
- return fSmall;
+#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 hasTiny() const {
- return fTiny;
+ bool done(const SkOpAngle* angle) const {
+ return angle->start()->starter(angle->end())->done();
}
- bool intersected() const {
- return fTs.count() > 0;
+ SkDPoint dPtAtT(double mid) const {
+ return (*CurveDPointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
}
- bool isCanceled(int tIndex) const {
- return fTs[tIndex].fWindValue == 0 && fTs[tIndex].fOppValue == 0;
+ SkDVector dSlopeAtT(double mid) const {
+ return (*CurveDSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
}
- bool isConnected(int startIndex, int endIndex) const {
- return fTs[startIndex].fWindSum != SK_MinS32 || fTs[endIndex].fWindSum != SK_MinS32;
+ 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 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(int start, int end) const {
- return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start, end);
+ bool isVertical(SkOpSpanBase* start, SkOpSpanBase* end) const {
+ return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start->t(), end->t());
}
- bool operand() const {
- return fOperand;
- }
+ bool isXor() const;
- int oppSign(const SkOpAngle* angle) const {
- SkASSERT(angle->segment() == this);
- return oppSign(angle->start(), angle->end());
+ const SkPoint& lastPt() const {
+ return fPts[SkPathOpsVerbToPoints(fVerb)];
}
- 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;
- }
+ 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 oppSum(int tIndex) const {
- return fTs[tIndex].fOppSum;
+ SkOpSegment* next() const {
+ return fNext;
}
- int oppSum(const SkOpAngle* angle) const {
- int lesser = SkMin32(angle->start(), angle->end());
- return fTs[lesser].fOppSum;
- }
+ static bool NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start, SkOpSpanBase** end);
+ SkOpSegment* nextChase(SkOpSpanBase** , int* step, SkOpSpan** , SkOpSpanBase** last) const;
+ bool operand() const;
- int oppValue(int tIndex) const {
- return fTs[tIndex].fOppValue;
+ 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(const SkOpAngle* angle) const {
- int lesser = SkMin32(angle->start(), angle->end());
- return fTs[lesser].fOppValue;
- }
+ bool oppXor() const;
-#if DEBUG_VALIDATE
- bool oppXor() const {
- return fOppXor;
+ const SkOpSegment* prev() const {
+ return fPrev;
}
-#endif
SkPoint ptAtT(double mid) const {
return (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
@@ -171,399 +273,113 @@ public:
return fPts;
}
- 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, const SkOpPtT& test) const {
+ return ptsDisjoint(span.fT, span.fPt, test.fT, test.fPt);
}
- bool reversePoints(const SkPoint& p1, const SkPoint& p2) const;
-
- void setOppXor(bool isOppXor) {
- fOppXor = isOppXor;
+ bool ptsDisjoint(const SkOpPtT& span, double t, const SkPoint& pt) const {
+ return ptsDisjoint(span.fT, span.fPt, t, pt);
}
- void setUpWinding(int index, int endIndex, int* maxWinding, int* sumWinding) {
- int deltaSum = spanSign(index, endIndex);
- *maxWinding = *sumWinding;
- *sumWinding -= deltaSum;
- }
+ bool ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const;
- const SkOpSpan& span(int tIndex) const {
- return fTs[tIndex];
+ void resetVisited() {
+ fVisited = false;
}
- const SkOpAngle* spanToAngle(int tStart, int tEnd) const {
- SkASSERT(tStart != tEnd);
- const SkOpSpan& span = fTs[tStart];
- return tStart < tEnd ? span.fToAngle : span.fFromAngle;
+ void setContour(SkOpContour* contour) {
+ fContour = contour;
}
- // 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 setNext(SkOpSegment* next) {
+ fNext = next;
}
- int spanSign(const SkOpAngle* angle) const {
- SkASSERT(angle->segment() == this);
- return spanSign(angle->start(), angle->end());
+ void setPrev(SkOpSegment* prev) {
+ fPrev = prev;
}
- 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;
- }
-
- double t(int tIndex) const {
- return fTs[tIndex].fT;
- }
-
- double tAtMid(int start, int end, double mid) const {
- return fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
+ bool setVisited() {
+ if (fVisited) {
+ return false;
+ }
+ return (fVisited = true);
}
- void updatePts(const SkPoint pts[]) {
- fPts = pts;
+ void setUpWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* maxWinding, int* sumWinding) {
+ int deltaSum = SpanSign(start, end);
+ *maxWinding = *sumWinding;
+ *sumWinding -= deltaSum;
}
- SkPath::Verb verb() const {
- return fVerb;
- }
+ 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();
- int windSum(int tIndex) const {
- return fTs[tIndex].fWindSum;
+ static int SpanSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
+ int result = start->t() < end->t() ? -start->upCast()->windValue()
+ : end->upCast()->windValue();
+ return result;
}
- int windValue(int tIndex) const {
- return fTs[tIndex].fWindValue;
+ SkOpAngle* spanToAngle(SkOpSpanBase* start, SkOpSpanBase* end) {
+ SkASSERT(start != end);
+ return start->t() < end->t() ? start->upCast()->toAngle() : start->fromAngle();
}
-#if defined(SK_DEBUG) || DEBUG_WINDING
- SkScalar xAtT(int index) const {
- return xAtT(&fTs[index]);
- }
-#endif
+ 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;
-#if DEBUG_VALIDATE
- bool _xor() const { // FIXME: used only by SkOpAngle::debugValidateLoop()
- return fXor;
+ const SkOpSpanBase* tail() const {
+ return &fTail;
}
-#endif
- const SkPoint& xyAtT(const SkOpSpan* span) const {
- return span->fPt;
+ SkOpSpanBase* tail() {
+ return &fTail;
}
- const SkPoint& xyAtT(int index) const {
- return xyAtT(&fTs[index]);
+ static double TAtMid(const SkOpSpanBase* start, const SkOpSpanBase* end, double mid) {
+ return start->t() * (1 - mid) + end->t() * mid;
}
-#if defined(SK_DEBUG) || DEBUG_WINDING
- SkScalar yAtT(int index) const {
- return yAtT(&fTs[index]);
- }
-#endif
-
- 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;
-// 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:
- 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;
+ void undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end);
+ int updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
int updateOppWinding(const SkOpAngle* angle) const;
- int updateWinding(int index, int endIndex) const;
+ int updateOppWindingReverse(const SkOpAngle* angle) const;
+ int updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) 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);
+ int updateWindingReverse(const SkOpAngle* angle) const;
- SkScalar xAtT(const SkOpSpan* span) const {
- return xyAtT(span).fX;
- }
+ static bool UseInnerWinding(int outerWinding, int innerWinding);
- SkScalar yAtT(const SkOpSpan* span) const {
- return xyAtT(span).fY;
+ SkPath::Verb verb() const {
+ return fVerb;
}
- void zeroSpan(SkOpSpan* span);
+ int windingAtT(double tHit, const SkOpSpan* span, bool crossOpp, SkScalar* dx) const;
+ int windSum(const SkOpAngle* angle) const;
-#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 : '+';
+ SkPoint* writablePt(bool end) {
+ return &fPts[end ? SkPathOpsVerbToPoints(fVerb) : 0];
}
-#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 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;
+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)
+ SkPath::Verb fVerb;
+ bool fVisited; // used by missing coincidence check
+ PATH_OPS_DEBUG_CODE(int fID);
};
#endif
diff --git a/src/pathops/SkOpSpan.cpp b/src/pathops/SkOpSpan.cpp
new file mode 100755
index 0000000000..37d5120019
--- /dev/null
+++ b/src/pathops/SkOpSpan.cpp
@@ -0,0 +1,355 @@
+/*
+ * 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 d9ce44eb77..9e5939a5e1 100644
--- a/src/pathops/SkOpSpan.h
+++ b/src/pathops/SkOpSpan.h
@@ -7,36 +7,460 @@
#ifndef SkOpSpan_DEFINED
#define SkOpSpan_DEFINED
+#include "SkPathOpsDebug.h"
#include "SkPoint.h"
-class SkOpAngle;
+class SkChunkAlloc;
+struct SkOpAngle;
+class SkOpContour;
+class SkOpGlobalState;
class SkOpSegment;
+class SkOpSpanBase;
+class SkOpSpan;
-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
+// 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
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 c80c12f63b..e8835f02e6 100644
--- a/src/pathops/SkOpTAllocator.h
+++ b/src/pathops/SkOpTAllocator.h
@@ -19,6 +19,12 @@ 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 1a5bfc1889..1dc171caf3 100644
--- a/src/pathops/SkPathOpsCommon.cpp
+++ b/src/pathops/SkPathOpsCommon.cpp
@@ -5,47 +5,25 @@
* found in the LICENSE file.
*/
#include "SkAddIntersections.h"
+#include "SkOpCoincidence.h"
#include "SkOpEdgeBuilder.h"
#include "SkPathOpsCommon.h"
#include "SkPathWriter.h"
#include "SkTSort.h"
-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;
+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;
const double mid = *midPtr;
const SkOpSegment* current = *currentPtr;
- double tAtMid = current->tAtMid(index, endIndex, mid);
+ double tAtMid = SkOpSegment::TAtMid(start, end, mid);
SkPoint basePt = current->ptAtT(tAtMid);
int contourCount = contourList.count();
SkScalar bestY = SK_ScalarMin;
SkOpSegment* bestSeg = NULL;
- int bestTIndex = 0;
+ SkOpSpan* bestTSpan = NULL;
bool bestOpp;
bool hitSomething = false;
for (int cTest = 0; cTest < contourCount; ++cTest) {
@@ -57,37 +35,38 @@ static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, S
if (bestY > contour->bounds().fBottom) {
continue;
}
- int segmentCount = contour->segments().count();
- for (int test = 0; test < segmentCount; ++test) {
- SkOpSegment* testSeg = &contour->segments()[test];
+ SkOpSegment* testSeg = contour->first();
+ SkASSERT(testSeg);
+ do {
SkScalar testY = bestY;
double testHit;
- int testTIndex = testSeg->crossedSpanY(basePt, &testY, &testHit, &hitSomething, tAtMid,
- testOpp, testSeg == current);
- if (testTIndex < 0) {
- if (testTIndex == SK_MinS32) {
+ bool vertical;
+ SkOpSpan* testTSpan = testSeg->crossedSpanY(basePt, tAtMid, testOpp,
+ testSeg == current, &testY, &testHit, &hitSomething, &vertical);
+ if (!testTSpan) {
+ if (vertical) {
hitSomething = true;
bestSeg = NULL;
goto abortContours; // vertical encountered, return and try different point
}
continue;
}
- if (testSeg == current && current->betweenTs(index, testHit, endIndex)) {
- double baseT = current->t(index);
- double endT = current->t(endIndex);
+ if (testSeg == current && SkOpSegment::BetweenTs(start, testHit, end)) {
+ double baseT = start->t();
+ double endT = end->t();
double newMid = (testHit - baseT) / (endT - baseT);
#if DEBUG_WINDING
- double midT = current->tAtMid(index, endIndex, mid);
- SkPoint midXY = current->xyAtT(midT);
- double newMidT = current->tAtMid(index, endIndex, newMid);
- SkPoint newXY = current->xyAtT(newMidT);
+ double midT = SkOpSegment::TAtMid(start, end, mid);
+ SkPoint midXY = current->ptAtT(midT);
+ double newMidT = SkOpSegment::TAtMid(start, end, newMid);
+ SkPoint newXY = current->ptAtT(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, current->xAtT(index), current->yAtT(index),
+ baseT, start->pt().fX, start->pt().fY,
baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
- endT, current->xAtT(endIndex), current->yAtT(endIndex));
+ endT, end->pt().fX, end->pt().fY);
#endif
*midPtr = newMid * 2; // calling loop with divide by 2 before continuing
return SK_MinS32;
@@ -95,38 +74,39 @@ static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, S
bestSeg = testSeg;
*bestHit = testHit;
bestOpp = testOpp;
- bestTIndex = testTIndex;
+ bestTSpan = testTSpan;
bestY = testY;
- }
+ } while ((testSeg = testSeg->next()));
}
abortContours:
int result;
if (!bestSeg) {
result = hitSomething ? SK_MinS32 : 0;
} else {
- if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
+ if (bestTSpan->windSum() == SK_MinS32) {
*currentPtr = bestSeg;
- *indexPtr = bestTIndex;
- *endIndexPtr = bestSeg->nextSpan(bestTIndex, 1);
- SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
+ *startPtr = bestTSpan;
+ *endPtr = bestTSpan->next();
+ SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
*tryAgain = true;
return 0;
}
- result = bestSeg->windingAtT(*bestHit, bestTIndex, bestOpp, bestDx);
+ result = bestSeg->windingAtT(*bestHit, bestTSpan, bestOpp, bestDx);
SkASSERT(result == SK_MinS32 || *bestDx);
}
- double baseT = current->t(index);
- double endT = current->t(endIndex);
+ double baseT = (*startPtr)->t();
+ double endT = (*endPtr)->t();
*bestHit = baseT + mid * (endT - baseT);
return result;
}
-SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end) {
+SkOpSegment* FindUndone(SkTDArray<SkOpContour* >& contourList, SkOpSpanBase** startPtr,
+ SkOpSpanBase** endPtr) {
int contourCount = contourList.count();
SkOpSegment* result;
for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
SkOpContour* contour = contourList[cIndex];
- result = contour->undoneSegment(start, end);
+ result = contour->undoneSegment(startPtr, endPtr);
if (result) {
return result;
}
@@ -134,20 +114,23 @@ SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, i
return NULL;
}
-SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex) {
+SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
+ SkOpSpanBase** endPtr) {
while (chase->count()) {
- SkOpSpan* span;
+ SkOpSpanBase* span;
chase->pop(&span);
- const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
- SkOpSegment* segment = backPtr.fOther;
- *tIndex = backPtr.fOtherIndex;
+ SkOpSegment* segment = span->segment();
+ *startPtr = span->ptT()->next()->span();
bool sortable = true;
bool done = true;
- *endIndex = -1;
- if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
+ *endPtr = NULL;
+ if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done,
&sortable)) {
- *tIndex = last->start();
- *endIndex = last->end();
+ if (last->unorderable()) {
+ continue;
+ }
+ *startPtr = last->start();
+ *endPtr = last->end();
#if TRY_ROTATE
*chase->insert(0) = span;
#else
@@ -162,65 +145,58 @@ SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex)
continue;
}
// find first angle, initialize winding to computed wind sum
- const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
- const SkOpAngle* firstAngle;
- SkDEBUGCODE(firstAngle = angle);
- SkDEBUGCODE(bool loop = false);
- int winding;
+ const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr);
+ if (!angle) {
+ continue;
+ }
+ const SkOpAngle* firstAngle = angle;
+ bool loop = false;
+ int winding = SK_MinS32;
do {
angle = angle->next();
- SkASSERT(angle != firstAngle || !loop);
- SkDEBUGCODE(loop |= angle == firstAngle);
+ if (angle == firstAngle && loop) {
+ break; // if we get here, there's no winding, loop is unorderable
+ }
+ loop |= angle == firstAngle;
segment = angle->segment();
winding = segment->windSum(angle);
} while (winding == SK_MinS32);
- 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)
+ if (winding == SK_MinS32) {
+ continue;
+ }
+ int sumWinding = segment->updateWindingReverse(angle);
+ SkOpSegment* first = NULL;
firstAngle = angle;
- winding -= firstAngle->segment()->spanSign(firstAngle);
while ((angle = angle->next()) != firstAngle) {
segment = angle->segment();
- 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;
+ 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;
}
- // allowed to do nothing
- (void) segment->markAndChaseWinding(angle, maxWinding, 0, NULL);
- break;
+ // OPTIMIZATION: should this also add to the chase?
+ (void) segment->markAngle(maxWinding, sumWinding, angle);
}
}
- *chase->insert(0) = span;
- return segment;
+ if (first) {
+ #if TRY_ROTATE
+ *chase->insert(0) = span;
+ #else
+ *chase->append() = span;
+ #endif
+ return first;
+ }
}
return NULL;
}
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
-void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList) {
+#if DEBUG_ACTIVE_SPANS
+void DebugShowActiveSpans(SkTDArray<SkOpContour* >& contourList) {
int index;
for (index = 0; index < contourList.count(); ++ index) {
contourList[index]->debugShowActiveSpans();
@@ -228,11 +204,12 @@ void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList) {
}
#endif
-static SkOpSegment* findTopSegment(const SkTArray<SkOpContour*, true>& contourList, int* index,
- int* endIndex, SkPoint* topLeft, bool* unsortable, bool* done, bool firstPass) {
+static SkOpSegment* findTopSegment(const SkTDArray<SkOpContour* >& contourList,
+ bool firstPass, SkOpSpanBase** start, SkOpSpanBase** end, SkPoint* topLeft,
+ bool* unsortable, bool* done, SkChunkAlloc* allocator) {
SkOpSegment* result;
const SkOpSegment* lastTopStart = NULL;
- int lastIndex = -1, lastEndIndex = -1;
+ SkOpSpanBase* lastStart = NULL, * lastEnd = NULL;
do {
SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
int contourCount = contourList.count();
@@ -261,27 +238,27 @@ static SkOpSegment* findTopSegment(const SkTArray<SkOpContour*, true>& contourLi
return NULL;
}
*topLeft = bestXY;
- result = topStart->findTop(index, endIndex, unsortable, firstPass);
+ result = topStart->findTop(firstPass, start, end, unsortable, allocator);
if (!result) {
- if (lastTopStart == topStart && lastIndex == *index && lastEndIndex == *endIndex) {
+ if (lastTopStart == topStart && lastStart == *start && lastEnd == *end) {
*done = true;
return NULL;
}
lastTopStart = topStart;
- lastIndex = *index;
- lastEndIndex = *endIndex;
+ lastStart = *start;
+ lastEnd = *end;
}
} while (!result);
return result;
}
-static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList,
- SkOpSegment** currentPtr, int* indexPtr, int* endIndexPtr, double* tHit,
+static int rightAngleWinding(const SkTDArray<SkOpContour* >& contourList,
+ SkOpSegment** currentPtr, SkOpSpanBase** start, SkOpSpanBase** end, double* tHit,
SkScalar* hitDx, bool* tryAgain, bool* onlyVertical, bool opp) {
double test = 0.9;
int contourWinding;
do {
- contourWinding = contourRangeCheckY(contourList, currentPtr, indexPtr, endIndexPtr,
+ contourWinding = contourRangeCheckY(contourList, currentPtr, start, end,
tHit, hitDx, tryAgain, &test, opp);
if (contourWinding != SK_MinS32 || *tryAgain) {
return contourWinding;
@@ -296,9 +273,9 @@ static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList,
return contourWinding;
}
-static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
- SkOpSegment** current, int* index, int* endIndex) {
- if (!(*current)->isVertical(*index, *endIndex)) {
+static void skipVertical(const SkTDArray<SkOpContour* >& contourList,
+ SkOpSegment** current, SkOpSpanBase** start, SkOpSpanBase** end) {
+ if (!(*current)->isVertical(*start, *end)) {
return;
}
int contourCount = contourList.count();
@@ -307,7 +284,7 @@ static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
if (contour->done()) {
continue;
}
- SkOpSegment* nonVertical = contour->nonVerticalSegment(index, endIndex);
+ SkOpSegment* nonVertical = contour->nonVerticalSegment(start, end);
if (nonVertical) {
*current = nonVertical;
return;
@@ -316,41 +293,41 @@ static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
return;
}
-struct SortableTop { // error if local in pre-C++11
- SkOpSegment* fSegment;
- int fIndex;
- int fEndIndex;
+struct SortableTop2 { // error if local in pre-C++11
+ SkOpSpanBase* fStart;
+ SkOpSpanBase* fEnd;
};
-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);
+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);
if (!current) {
return NULL;
}
- const int startIndex = *indexPtr;
- const int endIndex = *endIndexPtr;
+ SkOpSpanBase* start = *startPtr;
+ SkOpSpanBase* end = *endPtr;
+ SkASSERT(current == start->segment());
if (*firstContour) {
- current->initWinding(startIndex, endIndex, angleIncludeType);
+ current->initWinding(start, end, angleIncludeType);
*firstContour = false;
return current;
}
- int minIndex = SkMin32(startIndex, endIndex);
- int sumWinding = current->windSum(minIndex);
+ SkOpSpan* minSpan = start->starter(end);
+ int sumWinding = minSpan->windSum();
if (sumWinding == SK_MinS32) {
- int index = endIndex;
- int oIndex = startIndex;
- do {
- const SkOpSpan& span = current->span(index);
- if ((oIndex < index ? span.fFromAngle : span.fToAngle) == NULL) {
- current->addSimpleAngle(index);
+ 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);
}
- sumWinding = current->computeSum(oIndex, index, angleIncludeType);
- SkTSwap(index, oIndex);
- } while (sumWinding == SK_MinS32 && index == startIndex);
+ sumWinding = current->computeSum(oSpan, iSpan, angleIncludeType);
+ SkTSwap(iSpan, oSpan);
+ } while (sumWinding == SK_MinS32 && iSpan == start);
}
if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) {
return current;
@@ -364,26 +341,28 @@ SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
SkScalar hitDx = 0;
SkScalar hitOppDx = 0;
// keep track of subsequent returns to detect infinite loops
- SkTDArray<SortableTop> sortableTops;
+ SkTDArray<SortableTop2> 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(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
- skipVertical(contourList, &current, indexPtr, endIndexPtr);
+ SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
+ SkASSERT(current == (*startPtr)->segment());
+ skipVertical(contourList, &current, startPtr, endPtr);
SkASSERT(current); // FIXME: if null, all remaining are vertical
- SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
+ SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
+ SkASSERT(current == (*startPtr)->segment());
tryAgain = false;
- contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
+ contourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &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 SortableTop& prev = sortableTops[index];
+ const SortableTop2& prev = sortableTops[index];
if (giveUp) {
- prev.fSegment->markDoneFinal(prev.fIndex);
- } else if (prev.fSegment == current
- && (prev.fIndex == *indexPtr || prev.fEndIndex == *endIndexPtr)) {
+ prev.fStart->segment()->markDone(prev.fStart->starter(prev.fEnd));
+ } else if (prev.fStart == *startPtr || prev.fEnd == *endPtr) {
// 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;
@@ -395,14 +374,13 @@ SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
return NULL;
}
}
- SortableTop* sortableTop = sortableTops.append();
- sortableTop->fSegment = current;
- sortableTop->fIndex = *indexPtr;
- sortableTop->fEndIndex = *endIndexPtr;
+ SortableTop2* sortableTop = sortableTops.append();
+ sortableTop->fStart = *startPtr;
+ sortableTop->fEnd = *endPtr;
#if DEBUG_SORT
SkDebugf("%s current=%d index=%d endIndex=%d tHit=%1.9g hitDx=%1.9g try=%d vert=%d\n",
- __FUNCTION__, current->debugID(), *indexPtr, *endIndexPtr, tHit, hitDx, tryAgain,
- *onlyVertical);
+ __FUNCTION__, current->debugID(), (*startPtr)->debugID(), (*endPtr)->debugID(),
+ tHit, hitDx, tryAgain, *onlyVertical);
#endif
if (*onlyVertical) {
return current;
@@ -413,127 +391,35 @@ SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
if (angleIncludeType < SkOpAngle::kBinarySingle) {
break;
}
- oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
+ oppContourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &tHit,
&hitOppDx, &tryAgain, NULL, true);
+ SkASSERT(current == (*startPtr)->segment());
} while (tryAgain);
- bool success = current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx,
+ bool success = current->initWinding(*startPtr, *endPtr, tHit, contourWinding, hitDx,
oppContourWinding, hitOppDx);
if (current->done()) {
return NULL;
} else if (!success) { // check if the span has a valid winding
- int min = SkTMin(*indexPtr, *endIndexPtr);
- const SkOpSpan& span = current->span(min);
- if (span.fWindSum == SK_MinS32) {
+ SkOpSpan* minSpan = (*startPtr)->t() < (*endPtr)->t() ? (*startPtr)->upCast()
+ : (*endPtr)->upCast();
+ if (minSpan->windSum() == SK_MinS32) {
return NULL;
}
}
return current;
}
-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;
- }
- }
- 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,
+void MakeContourList(SkOpContour* contour, SkTDArray<SkOpContour* >& list,
bool evenOdd, bool oppEvenOdd) {
- int count = contours.count();
- if (count == 0) {
+ do {
+ if (contour->count()) {
+ contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
+ *list.append() = contour;
+ }
+ } while ((contour = contour->next()));
+ if (list.count() < 2) {
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);
}
@@ -554,19 +440,22 @@ public:
reassemble contour pieces into new path
*/
void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
+ SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
+ SkOpContour contour;
+ SkOpGlobalState globalState(NULL PATH_OPS_DEBUG_PARAMS(&contour));
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("%s\n", __FUNCTION__);
#endif
- 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();
+ 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();
#if DEBUG_ASSEMBLE
SkDebugf("%s contour", __FUNCTION__);
if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
@@ -578,44 +467,42 @@ 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.push_back(outer);
- }
- count = runs.count();
+ *runs.append() = eContour;
+ } while ((eContour = eContour->next()));
+ int count = runs.count();
if (count == 0) {
return;
}
- SkTArray<int, true> sLink, eLink;
- sLink.push_back_n(count);
- eLink.push_back_n(count);
+ SkTDArray<int> sLink, eLink;
+ sLink.append(count);
+ eLink.append(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
- SkTArray<double, true> distances;
- distances.push_back_n(entries);
+ SkTDArray<double> distances;
+ distances.append(entries);
for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
- outer = runs[rIndex >> 1];
- const SkOpContour& oContour = contours[outer];
- const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
+ const SkOpContour* oContour = runs[rIndex >> 1];
+ 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) {
- int inner = runs[iIndex >> 1];
- const SkOpContour& iContour = contours[inner];
- const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
+ const SkOpContour* iContour = runs[iIndex >> 1];
+ 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
}
}
- SkTArray<int, true> sortedDist;
- sortedDist.push_back_n(entries);
+ SkTDArray<int> sortedDist;
+ sortedDist.append(entries);
for (rIndex = 0; rIndex < entries; ++rIndex) {
sortedDist[rIndex] = rIndex;
}
@@ -678,17 +565,16 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
eIndex < 0 ? ~eIndex : eIndex);
#endif
do {
- outer = runs[rIndex];
- const SkOpContour& contour = contours[outer];
+ const SkOpContour* contour = runs[rIndex];
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,
@@ -742,36 +628,88 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
#endif
}
-bool HandleCoincidence(SkTArray<SkOpContour*, true>* contourList, int total) {
-#if DEBUG_SHOW_WINDING
- SkOpContour::debugShowWindingValues(contourList);
-#endif
- if (!CoincidenceCheck(contourList, total)) {
+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)) {
return false;
}
-#if DEBUG_SHOW_WINDING
- SkOpContour::debugShowWindingValues(contourList);
+ 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);
#endif
- fixOtherTIndex(contourList);
- if (!checkEnds(contourList)) { // check if connecting curve intersected at the same end
+ 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
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);
- if (!calcAngles(contourList)) {
- return false;
- }
+ calcAngles(contourList, allocator);
sortAngles(contourList);
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
+ if (globalState->angleCoincidence()) {
+ missingCoincidence(contourList, coincidence, allocator);
+ if (!coincidence->apply()) {
+ return false;
+ }
+ }
+#if DEBUG_ACTIVE_SPANS
DebugShowActiveSpans(*contourList);
#endif
return true;
diff --git a/src/pathops/SkPathOpsCommon.h b/src/pathops/SkPathOpsCommon.h
index 0d8cfc42f9..1bf17919ad 100644
--- a/src/pathops/SkPathOpsCommon.h
+++ b/src/pathops/SkPathOpsCommon.h
@@ -8,24 +8,28 @@
#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);
-// 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,
+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,
bool evenOdd, bool oppEvenOdd);
-bool HandleCoincidence(SkTArray<SkOpContour*, true>* , int );
+bool HandleCoincidence(SkTDArray<SkOpContour*>* , SkOpCoincidence* , SkChunkAlloc* ,
+ SkOpGlobalState* );
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
-void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList);
+#if DEBUG_ACTIVE_SPANS
+void DebugShowActiveSpans(SkTDArray<SkOpContour*>& contourList);
#endif
#endif
diff --git a/src/pathops/SkPathOpsCubic.cpp b/src/pathops/SkPathOpsCubic.cpp
index 9d70d58ec1..d4a5898a1d 100644
--- a/src/pathops/SkPathOpsCubic.cpp
+++ b/src/pathops/SkPathOpsCubic.cpp
@@ -4,6 +4,7 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#include "SkGeometry.h"
#include "SkLineParameters.h"
#include "SkPathOpsCubic.h"
#include "SkPathOpsLine.h"
@@ -26,8 +27,8 @@ double SkDCubic::binarySearch(double min, double max, double axisIntercept,
double priorT = t - step;
SkASSERT(priorT >= min);
SkDPoint lessPt = ptAtT(priorT);
- if (approximately_equal(lessPt.fX, cubicAtT.fX)
- && approximately_equal(lessPt.fY, cubicAtT.fY)) {
+ if (approximately_equal_half(lessPt.fX, cubicAtT.fX)
+ && approximately_equal_half(lessPt.fY, cubicAtT.fY)) {
return -1; // binary search found no point at this axis intercept
}
double lessDist = (&lessPt.fX)[xAxis] - axisIntercept;
@@ -41,10 +42,12 @@ double SkDCubic::binarySearch(double min, double max, double axisIntercept,
t = priorT;
} else {
double nextT = t + lastStep;
- SkASSERT(nextT <= max);
+ if (nextT > max) {
+ return -1;
+ }
SkDPoint morePt = ptAtT(nextT);
- if (approximately_equal(morePt.fX, cubicAtT.fX)
- && approximately_equal(morePt.fY, cubicAtT.fY)) {
+ if (approximately_equal_half(morePt.fX, cubicAtT.fX)
+ && approximately_equal_half(morePt.fY, cubicAtT.fY)) {
return -1; // binary search found no point at this axis intercept
}
double moreDist = (&morePt.fX)[xAxis] - axisIntercept;
@@ -88,35 +91,6 @@ 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))
@@ -124,17 +98,120 @@ 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(distance)) {
+ if (!approximately_zero_when_compared_to(distance, largest)) {
return false;
}
distance = lineParameters.controlPtDistance(*this, 2);
- return approximately_zero(distance);
+ 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;
}
bool SkDCubic::monotonicInY() const {
@@ -142,6 +219,13 @@ 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]);
@@ -163,26 +247,6 @@ 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;
@@ -505,25 +569,10 @@ 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 1037cae4f7..9932e1d1bc 100644
--- a/src/pathops/SkPathOpsCubic.h
+++ b/src/pathops/SkPathOpsCubic.h
@@ -10,7 +10,6 @@
#include "SkPath.h"
#include "SkPathOpsPoint.h"
-#include "SkTArray.h"
struct SkDCubicPair {
const SkDCubic& first() const { return (const SkDCubic&) pts[0]; }
@@ -19,13 +18,33 @@ struct SkDCubicPair {
};
struct SkDCubic {
+ static const int kPointCount = 4;
+ static const int kPointLast = kPointCount - 1;
+ static const int kMaxIntersections = 9;
+
enum SearchAxis {
kXAxis,
kYAxis
};
- 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]; }
+ 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]; }
void align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const;
double binarySearch(double min, double max, double axisIntercept, SearchAxis xAxis) const;
@@ -33,30 +52,35 @@ struct SkDCubic {
SkDCubicPair chopAt(double t) const;
bool clockwise() const;
static void Coefficients(const double* cubic, double* A, double* B, double* C, double* D);
- bool controlsContainedByEnds() const;
+ 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;
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[4], double tValues[2]) {
+ static int FindInflections(const SkPoint a[kPointCount], 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[4]) {
+ void set(const SkPoint pts[kPointCount]) {
fPts[0] = pts[0];
fPts[1] = pts[1];
fPts[2] = pts[2];
@@ -65,7 +89,7 @@ struct SkDCubic {
SkDCubic subDivide(double t1, double t2) const;
- static SkDCubic SubDivide(const SkPoint a[4], double t1, double t2) {
+ static SkDCubic SubDivide(const SkPoint a[kPointCount], double t1, double t2) {
SkDCubic cubic;
cubic.set(a);
return cubic.subDivide(t1, t2);
@@ -73,7 +97,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[4], const SkDPoint& a, const SkDPoint& d, double t1,
+ static void SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& d, double t1,
double t2, SkDPoint p[2]) {
SkDCubic cubic;
cubic.set(pts);
@@ -81,16 +105,29 @@ 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[4];
+ SkDPoint fPts[kPointCount];
};
+/* 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
deleted file mode 100644
index d7634449b6..0000000000
--- a/src/pathops/SkPathOpsCubicSect.h
+++ /dev/null
@@ -1,175 +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.
- */
-#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 7db93f5e96..0331f34e8a 100644
--- a/src/pathops/SkPathOpsDebug.cpp
+++ b/src/pathops/SkPathOpsDebug.cpp
@@ -7,6 +7,13 @@
#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
@@ -26,10 +33,10 @@ int SkPathOpsDebug::gSortCount;
const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
#endif
-bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpan *>& chaseArray,
- const SkOpSpan* span) {
+bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
+ const SkOpSpanBase* span) {
for (int index = 0; index < chaseArray.count(); ++index) {
- const SkOpSpan* entry = chaseArray[index];
+ const SkOpSpanBase* entry = chaseArray[index];
if (entry == span) {
return true;
}
@@ -65,6 +72,8 @@ void SkPathOpsDebug::WindingPrintf(int wind) {
SkDebugf("%d", wind);
}
}
+#endif // defined SK_DEBUG || !FORCE_RELEASE
+
#if DEBUG_SHOW_TEST_NAME
void* SkPathOpsDebug::CreateNameStr() {
@@ -97,470 +106,368 @@ 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
-bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const {
+int SkOpSegment::debugInflections(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
if (fVerb != SkPath::kCubic_Verb) {
return false;
}
- 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);
+ SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
double inflections[2];
return dst.findInflections(inflections);
}
#endif
-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) {
+SkOpAngle* SkOpSegment::debugLastAngle() {
+ SkOpAngle* result = NULL;
+ SkOpSpan* span = this->head();
+ do {
+ if (span->toAngle()) {
SkASSERT(!result);
- result = span.fToAngle;
+ result = span->toAngle();
}
- }
+ } while ((span = span->next()->upCastable()));
SkASSERT(result);
return result;
}
void SkOpSegment::debugReset() {
- 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");
+ this->init(this->fPts, this->contour(), this->verb());
}
-#endif
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
+#if DEBUG_ACTIVE_SPANS
void SkOpSegment::debugShowActiveSpans() const {
debugValidate();
if (done()) {
return;
}
-#if DEBUG_ACTIVE_SPANS_SHORT_FORM
int lastId = -1;
double lastT = -1;
-#endif
- for (int i = 0; i < fTs.count(); ++i) {
- if (fTs[i].fDone) {
+ const SkOpSpan* span = &fHead;
+ do {
+ if (span->done()) {
continue;
}
- SK_ALWAYSBREAK(i < fTs.count() - 1);
-#if DEBUG_ACTIVE_SPANS_SHORT_FORM
- if (lastId == fID && lastT == fTs[i].fT) {
+ if (lastId == fID && lastT == span->t()) {
continue;
}
lastId = fID;
- lastT = fTs[i].fT;
-#endif
+ lastT = span->t();
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 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) {
+ 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) {
SkDebugf("?");
} else {
- SkDebugf("%d", fTs[i].fWindSum);
+ SkDebugf("%d", span->windSum());
}
- SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
- }
+ SkDebugf(" windValue=%d oppValue=%d", span->windValue(), span->oppValue());
+ SkDebugf("\n");
+ } while ((span = span->next()->upCastable()));
}
#endif
-#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
-void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) {
- const SkPoint& pt = xyAtT(&span);
+#if DEBUG_MARK_DONE
+void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
+ const SkPoint& pt = span->ptT()->fPt;
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);
}
- 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(") 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) {
SkDebugf("?");
} else {
- SkDebugf("%d", span.fWindSum);
+ SkDebugf("%d", winding);
}
- SkDebugf(" windValue=%d\n", span.fWindValue);
+ SkDebugf(" windSum=");
+ if (span->windSum() == SK_MinS32) {
+ SkDebugf("?");
+ } else {
+ SkDebugf("%d", span->windSum());
+ }
+ SkDebugf(" windValue=%d\n", span->windValue());
}
-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 = xyAtT(&span);
+ const SkPoint& pt = span->ptT()->fPt;
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);
}
- 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(") 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) {
SkDebugf("?");
} else {
- SkDebugf("%d", span.fOppSum);
+ SkDebugf("%d", span->oppSum());
}
SkDebugf(" windSum=");
- if (span.fWindSum == SK_MinS32) {
+ if (span->windSum() == SK_MinS32) {
SkDebugf("?");
} else {
- SkDebugf("%d", span.fWindSum);
+ SkDebugf("%d", span->windSum());
}
- SkDebugf(" windValue=%d oppValue=%d\n", span.fWindValue, span.fOppValue);
+ SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
}
+
#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;
+#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
+}
+
+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
- int count = fTs.count();
- SK_ALWAYSBREAK(count >= 2);
- SK_ALWAYSBREAK(fTs[0].fT == 0);
- SK_ALWAYSBREAK(fTs[count - 1].fT == 1);
+ const SkOpSpanBase* span = &fHead;
+ double lastT = -1;
+ const SkOpSpanBase* prev = NULL;
+ int count = 0;
int done = 0;
- 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);
+ 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();
+ }
#endif
-// 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;
+}
+
+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;
+ }
}
}
- last = &span;
- hasLoop |= last->fLoop;
+ ++loop;
+ } while ((next = next->fNext) && next != this);
+ return 0;
+}
+
+void SkOpPtT::debugValidate() const {
+#if DEBUG_VALIDATE
+ if (contour()->globalState()->phase() == SkOpGlobalState::kIntersecting) {
+ return;
}
- SK_ALWAYSBREAK(done == fDoneSpans);
-// if (fAngles.count() ) {
-// fAngles.begin()->debugValidateLoop();
-// }
+ SkASSERT(fNext);
+ SkASSERT(fNext != this);
+ SkASSERT(fNext->fNext);
+ SkASSERT(debugLoopLimit(false) == 0);
#endif
}
diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h
index 5770aefec5..72a9ea528f 100644
--- a/src/pathops/SkPathOpsDebug.h
+++ b/src/pathops/SkPathOpsDebug.h
@@ -39,35 +39,22 @@
#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_PAIR 0
+#define DEBUG_ADD_T 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_UNSORTABLE 0
+#define DEBUG_T_SECT 0
+#define DEBUG_T_SECT_DUMP 0
#define DEBUG_VALIDATE 0
-#define DEBUG_WIND_BUMP 0
#define DEBUG_WINDING 0
#define DEBUG_WINDING_AT_T 0
@@ -75,51 +62,56 @@
#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_PAIR 1
+#define DEBUG_ADD_T 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 4
+#define DEBUG_LIMIT_WIND_SUM 5
#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_UNSORTABLE 1
-#define DEBUG_VALIDATE 0
-#define DEBUG_WIND_BUMP 0
+#define DEBUG_T_SECT 1
+#define DEBUG_T_SECT_DUMP 02
+#define DEBUG_VALIDATE 1
#define DEBUG_WINDING 1
#define DEBUG_WINDING_AT_T 1
#endif
-#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}}"
+#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(...)
#else
-#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 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__
#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
@@ -135,7 +127,6 @@
#include "SkTLS.h"
#endif
-#include "SkTArray.h"
#include "SkTDArray.h"
class SkPathOpsDebug {
@@ -156,7 +147,6 @@ 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);
@@ -171,66 +161,96 @@ 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 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 *>* );
+
+ 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);
};
// shorthand for calling from debugger
-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);
+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>* );
// generates tools/path_sorter.htm and path_visualizer.htm compatible data
-void DumpQ(const struct SkDQuad& quad1, const struct SkDQuad& quad2, int testNo);
+void DumpQ(const SkDQuad& quad1, const SkDQuad& quad2, int testNo);
+void DumpT(const SkDQuad& quad, double t);
-void DumpT(const struct SkDQuad& quad, double t);
+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 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 e4fc97bc61..70f2e12472 100644
--- a/src/pathops/SkPathOpsLine.cpp
+++ b/src/pathops/SkPathOpsLine.cpp
@@ -6,14 +6,6 @@
*/
#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
@@ -28,6 +20,7 @@ SkDLine SkDLine::subDivide(double t1, double t2) const {
// 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
@@ -110,19 +103,6 @@ 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 74eb615348..bb25162860 100644
--- a/src/pathops/SkPathOpsLine.h
+++ b/src/pathops/SkPathOpsLine.h
@@ -20,27 +20,20 @@ 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 f2b25c04ec..25ddb7dec6 100644
--- a/src/pathops/SkPathOpsOp.cpp
+++ b/src/pathops/SkPathOpsOp.cpp
@@ -5,27 +5,29 @@
* found in the LICENSE file.
*/
#include "SkAddIntersections.h"
+#include "SkOpCoincidence.h"
#include "SkOpEdgeBuilder.h"
#include "SkPathOpsCommon.h"
#include "SkPathWriter.h"
-static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* endIndex) {
+static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr,
+ SkOpSpanBase** endPtr) {
while (chase.count()) {
- SkOpSpan* span;
+ SkOpSpanBase* span;
chase.pop(&span);
- const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
- SkOpSegment* segment = backPtr.fOther;
- *tIndex = backPtr.fOtherIndex;
+ // OPTIMIZE: prev makes this compatible with old code -- but is it necessary?
+ *startPtr = span->ptT()->prev()->span();
+ SkOpSegment* segment = (*startPtr)->segment();
bool sortable = true;
bool done = true;
- *endIndex = -1;
- if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
+ *endPtr = NULL;
+ if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done,
&sortable)) {
if (last->unorderable()) {
continue;
}
- *tIndex = last->start();
- *endIndex = last->end();
+ *startPtr = last->start();
+ *endPtr = last->end();
#if TRY_ROTATE
*chase.insert(0) = span;
#else
@@ -40,7 +42,7 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* e
continue;
}
// find first angle, initialize winding to computed fWindSum
- const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
+ const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr);
if (!angle) {
continue;
}
@@ -65,33 +67,25 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* e
SkTSwap<int>(sumMiWinding, sumSuWinding);
}
SkOpSegment* first = NULL;
- bool badData = false;
- while ((angle = angle->next()) != firstAngle && !badData) {
+ firstAngle = angle;
+ while ((angle = angle->next()) != firstAngle) {
segment = angle->segment();
- int start = angle->start();
- int end = angle->end();
+ SkOpSpanBase* start = angle->start();
+ SkOpSpanBase* 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;
- *tIndex = start;
- *endIndex = end;
- }
- if (segment->inconsistentAngle(maxWinding, sumWinding, oppMaxWinding,
- oppSumWinding, angle)) {
- badData = true;
- break;
+ *startPtr = start;
+ *endPtr = end;
}
// 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;
@@ -104,36 +98,8 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* e
return NULL;
}
-/*
-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) {
+static bool bridgeOp(SkTDArray<SkOpContour* >& contourList, const SkPathOp op,
+ const int xorMask, const int xorOpMask, SkPathWriter* simple, SkChunkAlloc* allocator) {
bool firstContour = true;
bool unsortable = false;
bool topUnsortable = false;
@@ -141,12 +107,14 @@ static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp o
SkPoint lastTopLeft;
SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
do {
- int index, endIndex;
+ SkOpSpanBase* start;
+ SkOpSpanBase* end;
bool topDone;
bool onlyVertical = false;
lastTopLeft = topLeft;
- SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kBinarySingle, &firstContour,
- &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
+ SkOpSegment* current = FindSortableTop(contourList, firstPass, SkOpAngle::kBinarySingle,
+ &firstContour, &start, &end, &topLeft, &topUnsortable, &topDone, &onlyVertical,
+ allocator);
if (!current) {
if ((!topUnsortable || firstPass) && !topDone) {
SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
@@ -165,69 +133,65 @@ static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp o
break;
}
firstPass = !topUnsortable || lastTopLeft != topLeft;
- SkTDArray<SkOpSpan*> chase;
+ SkTDArray<SkOpSpanBase*> chase;
do {
- if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
+ if (current->activeOp(start, end, xorMask, xorOpMask, op)) {
do {
if (!unsortable && current->done()) {
break;
}
SkASSERT(unsortable || !current->done());
- int nextStart = index;
- int nextEnd = endIndex;
+ SkOpSpanBase* nextStart = start;
+ SkOpSpanBase* nextEnd = end;
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(index, endIndex, simple, true);
+ 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(), current->xyAtT(index).fX, current->xyAtT(index).fY,
- current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
+ 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);
#endif
- current->addCurveTo(index, endIndex, simple, true);
+ current->addCurveTo(start, end, simple, true);
current = next;
- 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);
+ 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);
}
}
simple->close();
} else {
- SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
- if (last && !last->fChased && !last->fLoop) {
- last->fChased = true;
+ SkOpSpanBase* last = current->markAndChaseDone(start, end);
+ if (last && !last->chased()) {
+ last->setChased(true);
SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
*chase.append() = last;
#if DEBUG_WINDING
- SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
- last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
- last->fSmall);
+ SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
+ if (!last->final()) {
+ SkDebugf(" windSum=%d", last->upCast()->windSum());
+ }
+ SkDebugf("\n");
#endif
}
}
- current = findChaseOp(chase, &index, &endIndex);
+ current = findChaseOp(chase, &start, &end);
#if DEBUG_ACTIVE_SPANS
DebugShowActiveSpans(contourList);
#endif
@@ -241,17 +205,17 @@ static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp o
// pretty picture:
// https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b-7vD9JP2V-kn9Ss/edit?usp=sharing
-static const SkPathOp gOpInverse[kReverseDifference_PathOp + 1][2][2] = {
+static const SkPathOp gOpInverse[kReverseDifference_SkPathOp + 1][2][2] = {
// inside minuend outside minuend
// inside subtrahend outside subtrahend inside subtrahend outside subtrahend
- {{ kDifference_PathOp, kIntersect_PathOp }, { kUnion_PathOp, kReverseDifference_PathOp }},
- {{ kIntersect_PathOp, kDifference_PathOp }, { kReverseDifference_PathOp, kUnion_PathOp }},
- {{ kUnion_PathOp, kReverseDifference_PathOp }, { kDifference_PathOp, kIntersect_PathOp }},
- {{ kXOR_PathOp, kXOR_PathOp }, { kXOR_PathOp, kXOR_PathOp }},
- {{ kReverseDifference_PathOp, kUnion_PathOp }, { kIntersect_PathOp, kDifference_PathOp }},
+{{ kDifference_SkPathOp, kIntersect_SkPathOp }, { kUnion_SkPathOp, kReverseDifference_SkPathOp }},
+{{ kIntersect_SkPathOp, kDifference_SkPathOp }, { kReverseDifference_SkPathOp, kUnion_SkPathOp }},
+{{ kUnion_SkPathOp, kReverseDifference_SkPathOp }, { kDifference_SkPathOp, kIntersect_SkPathOp }},
+{{ kXOR_SkPathOp, kXOR_SkPathOp }, { kXOR_SkPathOp, kXOR_SkPathOp }},
+{{ kReverseDifference_SkPathOp, kUnion_SkPathOp }, { kIntersect_SkPathOp, kDifference_SkPathOp }},
};
-static const bool gOutInverse[kReverseDifference_PathOp + 1][2][2] = {
+static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = {
{{ false, false }, { true, false }}, // diff
{{ false, false }, { false, true }}, // sect
{{ false, true }, { true, true }}, // union
@@ -291,16 +255,20 @@ 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) {
+ SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tune
+ SkOpContour contour;
+ SkOpCoincidence coincidence;
+ SkOpGlobalState globalState(&coincidence PATH_OPS_DEBUG_PARAMS(&contour));
#if DEBUGGING_PATHOPS_FROM_HOST
dump_op(one, two, op);
-#endif
-#if DEBUG_SHOW_TEST_NAME
+#endif
+#if 0 && DEBUG_SHOW_TEST_NAME
char* debugName = DEBUG_FILENAME_STRING;
if (debugName && debugName[0]) {
SkPathOpsDebug::BumpTestName(debugName);
@@ -312,62 +280,62 @@ bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
const SkPath* minuend = &one;
const SkPath* subtrahend = &two;
- if (op == kReverseDifference_PathOp) {
+ if (op == kReverseDifference_SkPathOp) {
minuend = &two;
subtrahend = &one;
- op = kDifference_PathOp;
+ op = kDifference_SkPathOp;
}
#if DEBUG_SORT || DEBUG_SWAP_TOP
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
// turn path into list of segments
- SkTArray<SkOpContour> contours;
- // FIXME: add self-intersecting cubics' T values to segment
- SkOpEdgeBuilder builder(*minuend, contours);
+ SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState);
if (builder.unparseable()) {
return false;
}
const int xorMask = builder.xorMask();
builder.addOperand(*subtrahend);
- if (!builder.finish()) {
+ if (!builder.finish(&allocator)) {
return false;
}
+#if !FORCE_RELEASE
+ contour.dumpSegments(op);
+#endif
+
result->reset();
result->setFillType(fillType);
const int xorOpMask = builder.xorMask();
- SkTArray<SkOpContour*, true> contourList;
- MakeContourList(contours, contourList, xorMask == kEvenOdd_PathOpsMask,
+ SkTDArray<SkOpContour* > contourList;
+ MakeContourList(&contour, 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) && nextPtr != listEnd);
+ } while (AddIntersectTs(current, next, &coincidence, &allocator) && nextPtr != listEnd);
} while (currentPtr != listEnd);
+#if DEBUG_VALIDATE
+ globalState.setPhase(SkOpGlobalState::kWalking);
+#endif
// eat through coincident edges
-
- int total = 0;
- int index;
- for (index = 0; index < contourList.count(); ++index) {
- total += contourList[index]->segments().count();
- }
- if (!HandleCoincidence(&contourList, total)) {
+ if (!HandleCoincidence(&contourList, &coincidence, &allocator, &globalState)) {
return false;
}
// construct closed contours
SkPathWriter wrapper(*result);
- bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
+ bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator);
{ // 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 7ddfbfb5d1..2d07427783 100644
--- a/src/pathops/SkPathOpsPoint.h
+++ b/src/pathops/SkPathOpsPoint.h
@@ -25,21 +25,25 @@ 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;
@@ -50,6 +54,7 @@ struct SkDVector {
return v;
}
+ // only used by testing
double cross(const SkDVector& a) const {
return fX * a.fY - fY * a.fX;
}
@@ -98,11 +103,13 @@ 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;
@@ -122,7 +129,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 AlmostBequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
+ return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
}
bool approximatelyEqual(const SkPoint& a) const {
@@ -145,44 +152,10 @@ 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 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?
+ return AlmostPequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
}
+ // only used by testing
bool approximatelyZero() const {
return approximately_zero(fX) && approximately_zero(fY);
}
@@ -209,7 +182,7 @@ struct SkDPoint {
return result;
}
- bool moreRoughlyEqual(const SkDPoint& a) const {
+ bool roughlyEqual(const SkDPoint& a) const {
if (roughly_equal(fX, a.fX) && roughly_equal(fY, a.fY)) {
return true;
}
@@ -220,10 +193,6 @@ 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 15a1900ce3..eb2d1ab3ab 100644..100755
--- a/src/pathops/SkPathOpsPostSect.cpp
+++ b/src/pathops/SkPathOpsPostSect.cpp
@@ -17,8 +17,8 @@ SkOpContour* SkOpPtT::contour() const {
return segment()->contour();
}
-SkOpDebugState* SkOpPtT::debugState() const {
- return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL);
+SkOpGlobalState* SkOpPtT::globalState() const {
+ return PATH_OPS_DEBUG_RELEASE(contour()->globalState(), 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->debugState()->fPtTID);
+ PATH_OPS_DEBUG_CODE(fID = ++span->globalState()->fPtTID);
}
bool SkOpPtT::onEnd() const {
@@ -45,7 +45,7 @@ SkOpPtT* SkOpPtT::remove() {
do {
SkOpPtT* next = prev->fNext;
if (next == this) {
- prev->removeNext();
+ prev->removeNext(this);
fDeleted = true;
return prev;
}
@@ -55,14 +55,14 @@ SkOpPtT* SkOpPtT::remove() {
return NULL;
}
-void SkOpPtT::removeNext() {
+void SkOpPtT::removeNext(SkOpPtT* kept) {
SkASSERT(this->fNext);
SkOpPtT* next = this->fNext;
this->fNext = next->fNext;
SkOpSpanBase* span = next->span();
next->setDeleted();
if (span->ptT() == next) {
- span->upCast()->detach();
+ span->upCast()->detach(kept);
}
}
@@ -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();
+ prev->removeNext(ptT);
test = prev;
} else {
SkASSERT(ptT->alias());
@@ -239,8 +239,8 @@ SkOpContour* SkOpSpanBase::contour() const {
return segment()->contour();
}
-SkOpDebugState* SkOpSpanBase::debugState() const {
- return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL);
+SkOpGlobalState* SkOpSpanBase::globalState() const {
+ return PATH_OPS_DEBUG_RELEASE(contour()->globalState(), 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 = ++debugState()->fSpanID);
+ PATH_OPS_DEBUG_CODE(fID = ++globalState()->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();
+ span->detach(this->ptT());
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() {
+void SkOpSpan::detach(SkOpPtT* kept) {
SkASSERT(!final());
SkOpSpan* prev = this->prev();
SkASSERT(prev);
@@ -313,6 +313,9 @@ void SkOpSpan::detach() {
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 c1d068af34..4913c9f9f3 100644
--- a/src/pathops/SkPathOpsQuad.cpp
+++ b/src/pathops/SkPathOpsQuad.cpp
@@ -8,7 +8,61 @@
#include "SkLineParameters.h"
#include "SkPathOpsCubic.h"
#include "SkPathOpsQuad.h"
-#include "SkPathOpsTriangle.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];
+ }
+}
// from http://blog.gludion.com/2009/08/distance-to-quadratic-bezier-curve.html
// (currently only used by testing)
@@ -43,10 +97,6 @@ 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];
@@ -140,7 +190,12 @@ 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);
- return approximately_zero(distance);
+ 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);
}
SkDCubic SkDQuad::toCubic() const {
@@ -240,13 +295,6 @@ 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])}};
@@ -258,7 +306,6 @@ 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 932c5fbe75..81638cf0bc 100644
--- a/src/pathops/SkPathOpsQuad.h
+++ b/src/pathops/SkPathOpsQuad.h
@@ -17,43 +17,61 @@ struct SkDQuadPair {
};
struct SkDQuad {
- SkDPoint fPts[3];
+ 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;
+ }
SkDQuad flip() const {
SkDQuad result = {{fPts[2], fPts[1], fPts[0]}};
return result;
}
- void set(const SkPoint pts[3]) {
+ static bool IsCubic() { return false; }
+
+ void set(const SkPoint pts[kPointCount]) {
fPts[0] = pts[0];
fPts[1] = pts[1];
fPts[2] = pts[2];
}
- 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]; }
+ 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]; }
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;
- bool pointInHull(const SkDPoint&) const;
+ void otherPts(int oddMan, const SkDPoint* endPt[2]) 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[3], double t1, double t2) {
+ static SkDQuad SubDivide(const SkPoint a[kPointCount], 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[3], const SkDPoint& a, const SkDPoint& c,
+ static SkDPoint SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& c,
double t1, double t2) {
SkDQuad quad;
quad.set(pts);
@@ -64,7 +82,8 @@ struct SkDQuad {
// utilities callable by the user from the debugger when the implementation code is linked in
void dump() const;
- void dumpComma(const char*) const;
+ void dumpID(int id) const;
+ void dumpInner() const;
private:
// static double Tangent(const double* quadratic, double t); // uncalled
diff --git a/src/pathops/SkPathOpsQuadSect.h b/src/pathops/SkPathOpsQuadSect.h
deleted file mode 100644
index 57f1aa08d8..0000000000
--- a/src/pathops/SkPathOpsQuadSect.h
+++ /dev/null
@@ -1,175 +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.
- */
-#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 2ceed32900..5dd3d8def5 100644
--- a/src/pathops/SkPathOpsRect.cpp
+++ b/src/pathops/SkPathOpsRect.cpp
@@ -9,11 +9,6 @@
#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]);
@@ -30,13 +25,6 @@ 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);
}
@@ -56,10 +44,3 @@ 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 2c47f43b88..2b37a5f098 100644
--- a/src/pathops/SkPathOpsRect.h
+++ b/src/pathops/SkPathOpsRect.h
@@ -13,18 +13,10 @@ struct SkDRect {
double fLeft, fTop, fRight, fBottom;
void add(const SkDPoint& pt) {
- 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;
- }
+ fLeft = SkTMin(fLeft, pt.fX);
+ fTop = SkTMin(fTop, pt.fY);
+ fRight = SkTMax(fRight, pt.fX);
+ fBottom = SkTMax(fBottom, pt.fY);
}
bool contains(const SkDPoint& pt) const {
@@ -32,12 +24,15 @@ struct SkDRect {
&& approximately_between(fTop, pt.fY, fBottom);
}
- bool intersects(SkDRect* r) const {
+ bool intersects(const SkDRect& r) const {
+ if (fLeft > fRight) {
+ SkDebugf("!");
+ }
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) {
@@ -53,11 +48,8 @@ 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 57090ac423..5c8a7fd840 100644
--- a/src/pathops/SkPathOpsSimplify.cpp
+++ b/src/pathops/SkPathOpsSimplify.cpp
@@ -5,11 +5,13 @@
* found in the LICENSE file.
*/
#include "SkAddIntersections.h"
+#include "SkOpCoincidence.h"
#include "SkOpEdgeBuilder.h"
#include "SkPathOpsCommon.h"
#include "SkPathWriter.h"
-static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
+static bool bridgeWinding(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simple,
+ SkChunkAlloc* allocator) {
bool firstContour = true;
bool unsortable = false;
bool topUnsortable = false;
@@ -17,15 +19,24 @@ static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWrite
SkPoint lastTopLeft;
SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
do {
- int index, endIndex;
+ SkOpSpanBase* start;
+ SkOpSpanBase* end;
bool topDone;
bool onlyVertical = false;
lastTopLeft = topLeft;
- SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour,
- &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
+ SkOpSegment* current = FindSortableTop(contourList, firstPass, SkOpAngle::kUnaryWinding,
+ &firstContour, &start, &end, &topLeft, &topUnsortable, &topDone, &onlyVertical,
+ allocator);
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;
}
@@ -34,62 +45,66 @@ static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWrite
break;
}
firstPass = !topUnsortable || lastTopLeft != topLeft;
- SkTDArray<SkOpSpan*> chase;
+ SkTDArray<SkOpSpanBase*> chase;
do {
- if (current->activeWinding(index, endIndex)) {
+ if (current->activeWinding(start, end)) {
do {
if (!unsortable && current->done()) {
break;
}
SkASSERT(unsortable || !current->done());
- int nextStart = index;
- int nextEnd = endIndex;
+ SkOpSpanBase* nextStart = start;
+ SkOpSpanBase* nextEnd = end;
SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
&unsortable);
if (!next) {
if (!unsortable && simple->hasMove()
&& current->verb() != SkPath::kLine_Verb
&& !simple->isClosed()) {
- current->addCurveTo(index, endIndex, simple, true);
- SkASSERT(simple->isClosed());
+ current->addCurveTo(start, end, simple, true);
+ #if DEBUG_ACTIVE_SPANS
+ if (!simple->isClosed()) {
+ DebugShowActiveSpans(contourList);
+ }
+ #endif
}
break;
}
#if DEBUG_FLOW
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);
+ current->debugID(), start->pt().fX, start->pt().fY,
+ end->pt().fX, end->pt().fY);
#endif
- current->addCurveTo(index, endIndex, simple, true);
+ current->addCurveTo(start, end, simple, true);
current = next;
- 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);
+ 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);
}
}
simple->close();
} else {
- SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex);
- if (last && !last->fChased && !last->fLoop) {
- last->fChased = true;
+ SkOpSpanBase* last = current->markAndChaseDone(start, end);
+ if (last && !last->chased()) {
+ last->setChased(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 windSum=%d small=%d\n", __FUNCTION__,
- last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
- last->fSmall);
+ SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
+ if (!last->final()) {
+ SkDebugf(" windSum=%d", last->upCast()->windSum());
+ }
+ SkDebugf("\n");
#endif
}
}
- current = FindChase(&chase, &index, &endIndex);
+ current = FindChase(&chase, &start, &end);
#if DEBUG_ACTIVE_SPANS
DebugShowActiveSpans(contourList);
#endif
@@ -102,9 +117,11 @@ static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWrite
}
// returns true if all edges were processed
-static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
+static bool bridgeXor(SkTDArray<SkOpContour* >& contourList, SkPathWriter* simple,
+ SkChunkAlloc* allocator) {
SkOpSegment* current;
- int start, end;
+ SkOpSpanBase* start;
+ SkOpSpanBase* end;
bool unsortable = false;
bool closable = true;
while ((current = FindUndone(contourList, &start, &end))) {
@@ -115,34 +132,38 @@ static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* s
}
#endif
SkASSERT(unsortable || !current->done());
- int nextStart = start;
- int nextEnd = end;
+ SkOpSpanBase* nextStart = start;
+ SkOpSpanBase* 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);
- SkASSERT(simple->isClosed());
+ #if DEBUG_ACTIVE_SPANS
+ if (!simple->isClosed()) {
+ DebugShowActiveSpans(contourList);
+ }
+ #endif
}
break;
}
#if DEBUG_FLOW
SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
- current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
- current->xyAtT(end).fX, current->xyAtT(end).fY);
+ current->debugID(), start->pt().fX, start->pt().fY,
+ end->pt().fX, end->pt().fY);
#endif
current->addCurveTo(start, end, simple, true);
current = next;
start = nextStart;
end = nextEnd;
- } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
+ } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
if (!simple->isClosed()) {
SkASSERT(unsortable);
- int min = SkMin32(start, end);
- if (!current->done(min)) {
+ SkOpSpan* spanStart = start->starter(end);
+ if (!spanStart->done()) {
current->addCurveTo(start, end, simple, true);
- current->markDone(min, 1);
+ current->markDone(spanStart);
}
closable = false;
}
@@ -156,52 +177,68 @@ static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* s
// 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
+ SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
// 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
- SkTArray<SkOpContour> contours;
- SkOpEdgeBuilder builder(path, contours);
- if (!builder.finish()) {
+ SkOpCoincidence coincidence;
+ SkOpContour contour;
+ SkOpGlobalState globalState(&coincidence PATH_OPS_DEBUG_PARAMS(&contour));
+#if DEBUG_SORT || DEBUG_SWAP_TOP
+ SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
+#endif
+ SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
+ if (!builder.finish(&allocator)) {
return false;
}
- SkTArray<SkOpContour*, true> contourList;
- MakeContourList(contours, contourList, false, false);
- SkOpContour** currentPtr = contourList.begin();
+#if !FORCE_RELEASE
+ contour.dumpSegments((SkPathOp) -1);
+#endif
result->reset();
result->setFillType(fillType);
+ SkTDArray<SkOpContour* > contourList;
+ MakeContourList(&contour, contourList, false, false);
+ SkOpContour** currentPtr = contourList.begin();
if (!currentPtr) {
return true;
}
- SkOpContour** listEnd = contourList.end();
+ if ((*currentPtr)->count() == 0) {
+ SkASSERT((*currentPtr)->next() == NULL);
+ return true;
+ }
+ SkOpContour** listEnd2 = 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) && nextPtr != listEnd);
- } while (currentPtr != listEnd);
- if (!HandleCoincidence(&contourList, 0)) {
+ } 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)) {
return false;
}
// construct closed contours
- SkPathWriter simple(*result);
- if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
- : !bridgeXor(contourList, &simple))
+ SkPathWriter wrapper(*result);
+ if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &wrapper, &allocator)
+ : !bridgeXor(contourList, &wrapper, &allocator))
{ // if some edges could not be resolved, assemble remaining fragments
SkPath temp;
temp.setFillType(fillType);
SkPathWriter assembled(temp);
- Assemble(simple, &assembled);
+ Assemble(wrapper, &assembled);
*result = *assembled.nativePath();
result->setFillType(fillType);
}
diff --git a/src/pathops/SkPathOpsTCubicSect.cpp b/src/pathops/SkPathOpsTCubicSect.cpp
index 0b3ddd7d0d..10a84a3843 100644
--- a/src/pathops/SkPathOpsTCubicSect.cpp
+++ b/src/pathops/SkPathOpsTCubicSect.cpp
@@ -7,9 +7,9 @@
#include "SkPathOpsTSect.h"
-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));
+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));
SkTSect<SkDCubic>::BinarySearch(&sect1, &sect2, this);
return used();
}
diff --git a/src/pathops/SkPathOpsTQuadSect.cpp b/src/pathops/SkPathOpsTQuadSect.cpp
index 46ce5cfdbb..06b5f2f8e9 100644
--- a/src/pathops/SkPathOpsTQuadSect.cpp
+++ b/src/pathops/SkPathOpsTQuadSect.cpp
@@ -7,9 +7,9 @@
#include "SkPathOpsTSect.h"
-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));
+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));
SkTSect<SkDQuad>::BinarySearch(&sect1, &sect2, this);
return used();
}
diff --git a/src/pathops/SkPathOpsTSect.h b/src/pathops/SkPathOpsTSect.h
index 4e7d3b1795..5f2730a832 100644
--- a/src/pathops/SkPathOpsTSect.h
+++ b/src/pathops/SkPathOpsTSect.h
@@ -6,15 +6,25 @@
*/
#include "SkChunkAlloc.h"
+#include "SkPathOpsBounds.h"
#include "SkPathOpsRect.h"
#include "SkPathOpsQuad.h"
#include "SkIntersections.h"
-#include "SkTArray.h"
+#include "SkTSort.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;
}
@@ -49,84 +59,117 @@ private:
};
template<typename TCurve> class SkTSect;
+template<typename TCurve> class SkTSpan;
+
+template<typename TCurve>
+struct SkTSpanBounded {
+ SkTSpan<TCurve>* fBounded;
+ SkTSpanBounded* fNext;
+};
/* Curve is either TCurve or SkDCubic */
template<typename TCurve>
class SkTSpan {
public:
- void init(const TCurve& );
- void initBounds(const TCurve& );
-
+ void addBounded(SkTSpan* , SkChunkAlloc* );
double closestBoundedT(const SkDPoint& pt) const;
+ bool contains(double t) const;
- bool contains(double t) const {
- return !! const_cast<SkTSpan*>(this)->innerFind(t);
- }
-
- bool contains(const SkTSpan* span) 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;
double endT() const {
return fEndT;
}
- SkTSpan* find(double t) {
- SkTSpan* result = innerFind(t);
+ SkTSpan* findOppSpan(const SkTSpan* opp) const;
+
+ SkTSpan* findOppT(double t) const {
+ SkTSpan* result = oppT(t);
SkASSERT(result);
return result;
}
- bool intersects(const SkTSpan* span, bool* check);
+ 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 != NULL;
+ }
+
+ bool linearsIntersect(SkTSpan* span);
+ double linearT(const SkDPoint& ) const;
+
+ void markCoincident() {
+ fCoinStart.markCoincident();
+ fCoinEnd.markCoincident();
+ }
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();
+ fBounded = NULL;
}
- bool split(SkTSpan* work) {
- return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
+ void resetBounds(const TCurve& curve) {
+ fIsLinear = fIsLine = false;
+ initBounds(curve);
}
- bool splitAt(SkTSpan* work, double t);
+ bool split(SkTSpan* work, SkChunkAlloc* heap) {
+ return splitAt(work, (work->fStartT + work->fEndT) * 0.5, heap);
+ }
+
+ bool splitAt(SkTSpan* work, double t, SkChunkAlloc* heap);
double startT() const {
return fStartT;
}
- bool tightBoundsIntersects(const SkTSpan* span) const;
+private:
// implementation is for testing only
- void dump() const {
- dump(NULL);
+ int debugID() const {
+ return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1);
}
-private:
- SkTSpan* innerFind(double t);
- bool linearIntersects(const TCurve& ) const;
+ void dumpID() 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;
+ int hullCheck(const SkTSpan* opp, bool* start, bool* oppStart);
+ int linearIntersects(const TCurve& ) const;
+ SkTSpan* oppT(double t) const;
-#if DEBUG_T_SECT
void validate() const;
-#endif
+ void validateBounded() const;
+ void validatePerpT(double oppT) const;
+ void validatePerpPt(double t, const SkDPoint& ) const;
TCurve fPart;
SkTCoincident<TCurve> fCoinStart;
SkTCoincident<TCurve> fCoinEnd;
- SkSTArray<4, SkTSpan*, true> fBounded;
+ SkTSpanBounded<TCurve>* fBounded;
SkTSpan* fPrev;
SkTSpan* fNext;
SkDRect fBounds;
@@ -136,23 +179,33 @@ private:
bool fCollapsed;
bool fHasPerp;
bool fIsLinear;
-#if DEBUG_T_SECT
- int fDebugID;
- bool fDebugDeleted;
-#endif
+ bool fIsLine;
+ bool fDeleted;
+ PATH_OPS_DEBUG_CODE(SkTSect<TCurve>* fDebugSect);
+ PATH_OPS_DEBUG_T_SECT_CODE(int fID);
friend class SkTSect<TCurve>;
};
template<typename TCurve>
class SkTSect {
public:
- SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id));
+ SkTSect(const TCurve& c PATH_OPS_DEBUG_T_SECT_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(const SkTSect& opp) const;
- void dumpBoth(const SkTSect* opp) const;
+ void dumpBoth(SkTSect* ) const;
+ void dumpBounds(int id) const;
+ void dumpCoin() const;
+ void dumpCoinCurves() const;
void dumpCurves() const;
private:
@@ -163,36 +216,72 @@ private:
kOneS2Set = 8
};
+ SkTSpan<TCurve>* addFollowing(SkTSpan<TCurve>* prior);
+ void addForPerp(SkTSpan<TCurve>* span, double t);
SkTSpan<TCurve>* addOne();
- bool binarySearchCoin(const SkTSect& , double tStart, double tStep, double* t, double* oppT);
+
+ SkTSpan<TCurve>* addSplitAt(SkTSpan<TCurve>* span, double t) {
+ SkTSpan<TCurve>* result = this->addOne();
+ result->splitAt(span, t, &fHeap);
+ result->initBounds(fCurve);
+ span->initBounds(fCurve);
+ return result;
+ }
+
+ bool binarySearchCoin(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* );
- bool intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
- const SkTSpan<TCurve>* oppSpan) const;
- void onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
+ 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);
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 removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span);
+ void removeSpanRange(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
void removeSpans(SkTSpan<TCurve>* span, SkTSect* opp);
- void setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
- const SkTSpan<TCurve>* tail() const;
+ SkTSpan<TCurve>* spanAtT(double t, SkTSpan<TCurve>** priorSpan);
+ SkTSpan<TCurve>* tail();
void trim(SkTSpan<TCurve>* span, SkTSect* opp);
-
-#if DEBUG_T_SECT
- int debugID() const { return fDebugID; }
+ void unlinkSpan(SkTSpan<TCurve>* span);
+ bool updateBounded(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last, SkTSpan<TCurve>* oppFirst);
void validate() const;
-#else
- int debugID() const { return 0; }
-#endif
+ void validateBounded() const;
+
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
@@ -208,8 +297,8 @@ void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t,
SkIntersections i;
int used = i.intersectRay(c2, perp);
// only keep closest
- if (used == 0) {
- fPerpT = -1;
+ if (used == 0 || used == 3) {
+ this->clear();
return;
}
fPerpT = i[0][0];
@@ -223,6 +312,10 @@ 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) {
@@ -232,38 +325,65 @@ void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t,
}
template<typename TCurve>
-void SkTSpan<TCurve>::init(const TCurve& c) {
- fPrev = fNext = NULL;
- fIsLinear = false;
- fStartT = 0;
- fEndT = 1;
- initBounds(c);
+void SkTSpan<TCurve>::addBounded(SkTSpan* span, SkChunkAlloc* heap) {
+ SkTSpanBounded<TCurve>* bounded = SkNEW_PLACEMENT(heap->allocThrow(
+ sizeof(SkTSpanBounded<TCurve>)), SkTSpanBounded<TCurve>);
+ bounded->fBounded = span;
+ bounded->fNext = fBounded;
+ fBounded = bounded;
}
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;
-#if DEBUG_T_SECT
- fDebugDeleted = false;
- if (fCollapsed) {
- SkDebugf(""); // for convenient breakpoints
+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;
}
+ 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 addBounded span=%d opp=%d\n", __FUNCTION__, priorSpan->debugID(),
+ opp->debugID());
+#endif
+ opp->addBounded(span, &fHeap);
+ span->addBounded(opp, &fHeap);
+ }
+ this->validate();
+ span->validatePerpT(t);
}
template<typename TCurve>
double SkTSpan<TCurve>::closestBoundedT(const SkDPoint& pt) const {
- int count = fBounded.count();
double result = -1;
double closest = FLT_MAX;
- for (int index = 0; index < count; ++index) {
- const SkTSpan* test = fBounded[index];
+ const SkTSpanBounded<TCurve>* testBounded = fBounded;
+ while (testBounded) {
+ const SkTSpan* test = testBounded->fBounded;
double startDist = test->fPart[0].distanceSquared(pt);
if (closest > startDist) {
closest = startDist;
@@ -274,60 +394,151 @@ double SkTSpan<TCurve>::closestBoundedT(const SkDPoint& pt) const {
closest = endDist;
result = test->fEndT;
}
+ testBounded = testBounded->fNext;
}
SkASSERT(between(0, result, 1));
return result;
}
+#ifdef SK_DEBUG
template<typename TCurve>
-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) {
+bool SkTSpan<TCurve>::debugIsBefore(const SkTSpan* span) const {
+ const SkTSpan* work = this;
+ do {
+ if (span == work) {
return true;
}
- }
+ } while ((work = work->fNext));
return false;
}
+#endif
template<typename TCurve>
-SkTSpan<TCurve>* SkTSpan<TCurve>::innerFind(double t) {
- SkTSpan* work = this;
+bool SkTSpan<TCurve>::contains(double t) const {
+ const SkTSpan* work = this;
do {
if (between(work->fStartT, t, work->fEndT)) {
- return work;
+ return true;
}
} 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 {
+ SkTSpanBounded<TCurve>* bounded = fBounded;
+ while (bounded) {
+ SkTSpan* test = bounded->fBounded;
+ if (opp == test) {
+ return test;
+ }
+ bounded = bounded->fNext;
+ }
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>
-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 SkTSpan<TCurve>::hullsIntersect(SkTSpan* opp, bool* start, bool* oppStart) {
+ if (!fBounds.intersects(opp->fBounds)) {
+ return 0;
}
- if (!fIsLinear && fPart.hullIntersects(span->fPart, check)) {
- if (!*check) {
- return true;
- }
- fIsLinear = true;
+ int hullSect = this->hullCheck(opp, start, oppStart);
+ if (hullSect >= 0) {
+ return hullSect;
}
- if (fIsLinear) {
- *check = false;
- return linearIntersects(span->fPart);
+ hullSect = opp->hullCheck(this, oppStart, start);
+ if (hullSect >= 0) {
+ return hullSect;
+ }
+ return -1;
+}
+
+template<typename TCurve>
+void SkTSpan<TCurve>::init(const TCurve& c) {
+ fPrev = fNext = NULL;
+ fStartT = 0;
+ fEndT = 1;
+ fBounded = NULL;
+ 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
}
- return *check;
+#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>
-bool SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const {
+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 {
// looks like q1 is near-linear
- int start = 0, end = TCurve::kPointCount - 1; // the outside points are usually the extremes
+ int start = 0, end = TCurve::kPointLast; // 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) {
@@ -347,25 +558,132 @@ bool 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 sign;
+ double maxPart = SkTMax(fabs(adj), fabs(opp));
+ double sign = 0; // initialization to shut up warning in release build
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(test)) {
- return true;
+ if (precisely_zero_when_compared_to(test, maxVal)) {
+ return 1;
+ }
+ if (approximately_zero_when_compared_to(test, maxVal)) {
+ return 3;
}
if (n == 0) {
sign = test;
continue;
}
if (test * sign < 0) {
- return true;
+ 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 {
+ SkTSpanBounded<TCurve>* bounded = fBounded;
+ while (bounded) {
+ SkTSpan* test = bounded->fBounded;
+ if (between(test->fStartT, t, test->fEndT)) {
+ return test;
+ }
+ bounded = bounded->fNext;
+ }
+ return NULL;
+}
+
+template<typename TCurve>
+bool SkTSpan<TCurve>::removeAllBounded() {
+ bool deleteSpan = false;
+ SkTSpanBounded<TCurve>* bounded = fBounded;
+ while (bounded) {
+ SkTSpan* opp = bounded->fBounded;
+ deleteSpan |= opp->removeBounded(this);
+ bounded = bounded->fNext;
+ }
+ return deleteSpan;
+}
+
+template<typename TCurve>
+bool SkTSpan<TCurve>::removeBounded(const SkTSpan* opp) {
+ if (fHasPerp) {
+ bool foundStart = false;
+ bool foundEnd = false;
+ SkTSpanBounded<TCurve>* bounded = fBounded;
+ while (bounded) {
+ SkTSpan* test = bounded->fBounded;
+ if (opp != test) {
+ foundStart |= between(test->fStartT, fCoinStart.perpT(), test->fEndT);
+ foundEnd |= between(test->fStartT, fCoinEnd.perpT(), test->fEndT);
+ }
+ bounded = bounded->fNext;
+ }
+ if (!foundStart || !foundEnd) {
+ fHasPerp = false;
+ fCoinStart.init();
+ fCoinEnd.init();
+ }
+ }
+ SkTSpanBounded<TCurve>* bounded = fBounded;
+ SkTSpanBounded<TCurve>* prev = NULL;
+ while (bounded) {
+ SkTSpanBounded<TCurve>* boundedNext = bounded->fNext;
+ if (opp == bounded->fBounded) {
+ if (prev) {
+ prev->fNext = boundedNext;
+ return false;
+ } else {
+ fBounded = boundedNext;
+ return fBounded == NULL;
+ }
+ }
+ prev = bounded;
+ bounded = boundedNext;
+ }
+ SkASSERT(0);
return false;
}
template<typename TCurve>
-bool SkTSpan<TCurve>::splitAt(SkTSpan* work, double t) {
+bool SkTSpan<TCurve>::splitAt(SkTSpan* work, double t, SkChunkAlloc* heap) {
fStartT = t;
fEndT = work->fEndT;
if (fStartT == fEndT) {
@@ -380,115 +698,99 @@ 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;
}
- fBounded = work->fBounded;
- int count = fBounded.count();
- for (int index = 0; index < count; ++index) {
- fBounded[index]->fBounded.push_back() = this;
+ SkTSpanBounded<TCurve>* bounded = work->fBounded;
+ fBounded = NULL;
+ while (bounded) {
+ this->addBounded(bounded->fBounded, heap);
+ bounded = bounded->fNext;
+ }
+ bounded = fBounded;
+ while (bounded) {
+ bounded->fBounded->addBounded(this, heap);
+ bounded = bounded->fNext;
}
return true;
}
template<typename TCurve>
-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;
- }
- } 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;
- }
- }
- }
- // 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;
+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);
+ this->validateBounded();
+ if (fHasPerp) {
+ if (fCoinStart.isCoincident()) {
+ validatePerpT(fCoinStart.perpT());
+ validatePerpPt(fCoinStart.perpT(), fCoinStart.perpPt());
}
- } else {
- r2[1].fX -= (r2[1].fY - r1[0].fY) * ratio;
- if (TCurve::IsCubic()) {
- r2[2].fX -= (r2[2].fY - r1[0].fY) * ratio;
+ if (fCoinEnd.isCoincident()) {
+ validatePerpT(fCoinEnd.perpT());
+ validatePerpPt(fCoinEnd.perpT(), fCoinEnd.perpPt());
}
}
- 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
+}
+
+template<typename TCurve>
+void SkTSpan<TCurve>::validateBounded() const {
+#if DEBUG_VALIDATE
+ const SkTSpanBounded<TCurve>* testBounded = fBounded;
+ while (testBounded) {
+ const SkTSpan* overlap = testBounded->fBounded;
+ SkASSERT(!overlap->fDeleted);
+ SkASSERT(((this->debugID() ^ overlap->debugID()) & 1) == 1);
+ SkASSERT(overlap->findOppSpan(this));
+ testBounded = testBounded->fNext;
}
+#endif
}
-#if DEBUG_T_SECT
template<typename TCurve>
-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];
- SkASSERT(((fDebugID ^ overlap->fDebugID) & 1) == 1);
- SkASSERT(overlap->contains(this));
+void SkTSpan<TCurve>::validatePerpT(double oppT) const {
+#if DEBUG_VALIDATE
+ const SkTSpanBounded<TCurve>* testBounded = fBounded;
+ while (testBounded) {
+ const SkTSpan* overlap = testBounded->fBounded;
+ if (between(overlap->fStartT, oppT, overlap->fEndT)) {
+ return;
+ }
+ testBounded = testBounded->fNext;
}
+ 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_PARAMS(int id))
+SkTSect<TCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_T_SECT_PARAMS(int id))
: fCurve(c)
, fHeap(sizeof(SkTSpan<TCurve>) * 4)
+ , fCoincident(NULL)
, fDeleted(NULL)
, fActiveCount(0)
- PATH_OPS_DEBUG_PARAMS(fDebugID(id))
- PATH_OPS_DEBUG_PARAMS(fDebugCount(0))
- PATH_OPS_DEBUG_PARAMS(fDebugAllocatedCount(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))
{
fHead = addOne();
fHead->init(c);
@@ -503,27 +805,28 @@ SkTSpan<TCurve>* SkTSect<TCurve>::addOne() {
fDeleted = result->fNext;
} else {
result = SkNEW_PLACEMENT(fHeap.allocThrow(sizeof(SkTSpan<TCurve>)), SkTSpan<TCurve>);
+ result->fBounded = NULL;
#if DEBUG_T_SECT
++fDebugAllocatedCount;
#endif
}
++fActiveCount;
-#if DEBUG_T_SECT
- result->fDebugID = fDebugCount++ * 2 + fDebugID;
-#endif
+ PATH_OPS_DEBUG_T_SECT_CODE(result->fID = fDebugCount++ * 2 + fID);
+ PATH_OPS_DEBUG_CODE(result->fDebugSect = this);
return result;
}
template<typename TCurve>
-bool SkTSect<TCurve>::binarySearchCoin(const SkTSect& sect2, double tStart, double tStep,
+bool SkTSect<TCurve>::binarySearchCoin(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;
@@ -541,8 +844,11 @@ bool SkTSect<TCurve>::binarySearchCoin(const SkTSect& sect2, double tStart, doub
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);
@@ -574,194 +880,472 @@ template<typename TCurve>
SkTSpan<TCurve>* SkTSect<TCurve>::boundsMax() const {
SkTSpan<TCurve>* test = fHead;
SkTSpan<TCurve>* largest = fHead;
- bool largestCoin = largest->fCoinStart.isCoincident() && largest->fCoinEnd.isCoincident();
+ bool lCollapsed = largest->fCollapsed;
while ((test = test->fNext)) {
- bool testCoin = test->fCoinStart.isCoincident() || test->fCoinEnd.isCoincident();
- if ((largestCoin && !testCoin) || (largestCoin == testCoin
- && (largest->fBoundsMax < test->fBoundsMax
- || (largest->fCollapsed && !test->fCollapsed)))) {
+ bool tCollapsed = test->fCollapsed;
+ if ((lCollapsed && !tCollapsed) || (lCollapsed == tCollapsed &&
+ largest->fBoundsMax < test->fBoundsMax)) {
largest = test;
- largestCoin = testCoin;
}
}
- return largestCoin ? NULL : largest;
+ return largest;
}
template<typename TCurve>
void SkTSect<TCurve>::coincidentCheck(SkTSect* sect2) {
SkTSpan<TCurve>* first = fHead;
- SkTSpan<TCurve>* next;
+ SkTSpan<TCurve>* last, * next;
do {
- 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);
+ int consecutive = this->countConsecutiveSpans(first, &last);
+ next = last->fNext;
if (consecutive < COINCIDENT_SPAN_COUNT) {
continue;
}
- setPerp(sect2->fCurve, first, last);
+ this->validate();
+ sect2->validate();
+ this->computePerpendiculars(sect2, first, last);
+ this->validate();
+ sect2->validate();
// check to see if a range of points are on the curve
- 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;
- }
- if (removalCandidate) {
- removeSpans(removalCandidate, sect2);
- }
- if (!last->fCoinStart.isCoincident()) {
- continue;
+ 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;
}
- if (!last->fCoinEnd.isCoincident()) {
- if (--consecutive < COINCIDENT_SPAN_COUNT) {
- 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);
+ }
}
- last = last->fPrev;
- SkASSERT(last->fCoinStart.isCoincident());
- SkASSERT(last->fCoinEnd.isCoincident());
+ work->fHasPerp = true;
}
- 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);
+ if (work == last) {
+ 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);
+ 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;
}
- SkTSpan<TCurve>* removeMe = first->fNext;
- while (removeMe != last) {
- SkTSpan<TCurve>* removeNext = removeMe->fNext;
- removeSpans(removeMe, sect2);
- removeMe = removeNext;
+ if (next->fStartT > last->fEndT) {
+ break;
}
- } while ((first = next));
+ ++consecutive;
+ last = next;
+ } while (true);
+ *lastPtr = last;
+ return consecutive;
}
template<typename TCurve>
-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)) {
+bool SkTSect<TCurve>::debugHasBounded(const SkTSpan<TCurve>* span) const {
+ const SkTSpan<TCurve>* test = fHead;
+ if (!test) {
return false;
}
- if (fActiveCount < COINCIDENT_SPAN_COUNT || opp->fActiveCount < COINCIDENT_SPAN_COUNT) {
- return true;
+ 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) {
+ this->removeSpan(test);
+ }
}
- return span->tightBoundsIntersects(oppSpan);
}
template<typename TCurve>
-void SkTSect<TCurve>::onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
+SkTSpan<TCurve>* SkTSect<TCurve>::extractCoincident(SkTSect* sect2, SkTSpan<TCurve>* first,
+ SkTSpan<TCurve>* last) {
+ first = findCoincidentRun(first, &last, sect2);
+ if (!first) {
+ return NULL;
+ }
+ // 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;
+}
+
+template<typename TCurve>
+SkTSpan<TCurve>* SkTSect<TCurve>::findCoincidentRun(SkTSpan<TCurve>* first,
+ SkTSpan<TCurve>** lastPtr, const SkTSect* sect2) {
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 if (first) {
- break;
+ } else {
+ lastCandidate = NULL;
+ SkASSERT(!first);
}
- if (work == last) {
- break;
+ if (work == *lastPtr) {
+ return first;
}
work = work->fNext;
SkASSERT(work);
} while (true);
- if (!first) {
- return;
+ if (lastCandidate) {
+ *lastPtr = lastCandidate;
}
- // 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);
+ 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 || !span->fBounded->fNext) {
+ SkASSERT(!span->fBounded || span->fBounded->fBounded == oppSpan);
+ if (spanStart) {
+ span->fEndT = span->fStartT;
+ } else {
+ span->fStartT = span->fEndT;
+ }
+ } else {
+ hullResult = 1;
+ }
+ if (!oppSpan->fBounded || !oppSpan->fBounded->fNext) {
+ SkASSERT(!oppSpan->fBounded || oppSpan->fBounded->fBounded == span);
+ if (oppStart) {
+ oppSpan->fEndT = oppSpan->fStartT;
} else {
- 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();
+ oppSpan->fStartT = oppSpan->fEndT;
}
+ *oppResult = 2;
+ } else {
+ *oppResult = 1;
}
+ } else {
+ *oppResult = 1;
}
+ return hullResult;
}
- 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 (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 (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>
@@ -782,80 +1366,118 @@ void SkTSect<TCurve>::recoverCollapsed() {
}
template<typename TCurve>
-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;
- }
- } else {
- fHead = next;
- if (next) {
- next->fPrev = NULL;
+void SkTSect<TCurve>::removeAllBut(const SkTSpan<TCurve>* keep, SkTSpan<TCurve>* span,
+ SkTSect* opp) {
+ const SkTSpanBounded<TCurve>* testBounded = span->fBounded;
+ while (testBounded) {
+ SkTSpan<TCurve>* bounded = testBounded->fBounded;
+ const SkTSpanBounded<TCurve>* next = testBounded->fNext;
+ // may have been deleted when opp did 'remove all but'
+ if (bounded != keep && !bounded->fDeleted) {
+ SkAssertResult(SkDEBUGCODE(!) span->removeBounded(bounded));
+ if (bounded->removeBounded(span)) {
+ opp->removeSpan(bounded);
+ }
}
+ testBounded = next;
}
- --fActiveCount;
- span->fNext = fDeleted;
- fDeleted = span;
+ 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];
#if DEBUG_T_SECT
- SkASSERT(!span->fDebugDeleted);
- span->fDebugDeleted = true;
+ 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));
#endif
+ if (startV.dot(endV) <= 0) {
+ continue;
+ }
+ this->removeSpans(test, opp);
+ } while ((test = next));
}
template<typename TCurve>
-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;
- }
+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>::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];
- removeOne(bounded, span); // shuffles last into position 0
- opp->removeOne(span, bounded);
- }
+void SkTSect<TCurve>::removeSpan(SkTSpan<TCurve>* span) {
+ this->unlinkSpan(span);
+ this->markSpanGone(span);
}
template<typename TCurve>
-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);
+void SkTSect<TCurve>::removeSpanRange(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
+ if (first == last) {
+ return;
}
- do {
- if (!work->fHasPerp) {
- work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp);
- work->fHasPerp = true;
- }
- if (work == last) {
- break;
+ 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;
+ }
+ first->fNext = final;
+}
+
+template<typename TCurve>
+void SkTSect<TCurve>::removeSpans(SkTSpan<TCurve>* span, SkTSect<TCurve>* opp) {
+ SkTSpanBounded<TCurve>* bounded = span->fBounded;
+ while (bounded) {
+ SkTSpan<TCurve>* spanBounded = bounded->fBounded;
+ SkTSpanBounded<TCurve>* next = bounded->fNext;
+ if (span->removeBounded(spanBounded)) { // shuffles last into position 0
+ this->removeSpan(span);
}
- SkTSpan<TCurve>* last = work;
- work = work->fNext;
- SkASSERT(work);
- if (!work->fHasPerp) {
- work->fCoinStart = last->fCoinEnd;
+ if (spanBounded->removeBounded(span)) {
+ opp->removeSpan(spanBounded);
}
- } while (true);
+ SkASSERT(!span->fDeleted || !opp->debugHasBounded(span));
+ bounded = next;
+ }
+}
+
+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;
+ }
+ *priorSpan = prev;
+ return test && test->fStartT <= t ? test : NULL;
}
template<typename TCurve>
-const SkTSpan<TCurve>* SkTSect<TCurve>::tail() const {
- const SkTSpan<TCurve>* result = fHead;
- const SkTSpan<TCurve>* next = fHead;
+SkTSpan<TCurve>* SkTSect<TCurve>::tail() {
+ SkTSpan<TCurve>* result = fHead;
+ SkTSpan<TCurve>* next = fHead;
while ((next = next->fNext)) {
if (next->fEndT > result->fEndT) {
result = next;
@@ -869,32 +1491,78 @@ const SkTSpan<TCurve>* SkTSect<TCurve>::tail() const {
template<typename TCurve>
void SkTSect<TCurve>::trim(SkTSpan<TCurve>* span, SkTSect* opp) {
span->initBounds(fCurve);
- int count = span->fBounded.count();
- for (int index = 0; index < count; ) {
- SkTSpan<TCurve>* test = span->fBounded[index];
- bool sects = intersects(span, opp, test);
- if (sects) {
- ++index;
+ const SkTSpanBounded<TCurve>* testBounded = span->fBounded;
+ while (testBounded) {
+ SkTSpan<TCurve>* test = testBounded->fBounded;
+ const SkTSpanBounded<TCurve>* next = testBounded->fNext;
+ int oppSects, sects = this->intersects(span, opp, test, &oppSects);
+ if (sects >= 1) {
+ if (oppSects == 2) {
+ test->initBounds(opp->fCurve);
+ opp->removeAllBut(span, test, this);
+ }
+ if (sects == 2) {
+ span->initBounds(fCurve);
+ this->removeAllBut(test, span, opp);
+ return;
+ }
} else {
- removeOne(test, span);
- opp->removeOne(span, test);
- --count;
+ if (span->removeBounded(test)) {
+ this->removeSpan(span);
+ }
+ if (test->removeBounded(span)) {
+ opp->removeSpan(test);
+ }
}
+ testBounded = next;
}
}
-#if DEBUG_T_SECT
+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 = NULL;
+ first->addBounded(oppFirst, &fHeap);
+ // cannot call validate until remove span range is called
+ return deleteSpan;
+}
+
+
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);
- double last = 0;
+ SkDEBUGCODE(double last = 0);
do {
span->validate();
SkASSERT(span->fStartT >= last);
- last = span->fEndT;
+ SkDEBUGCODE(last = span->fEndT);
++count;
} while ((span = span->fNext) != NULL);
}
@@ -906,54 +1574,81 @@ 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 (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
+ if (!(zeroOneSet & (kZeroS1Set | kZeroS2Set))
+ && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
zeroOneSet |= kZeroS1Set | kZeroS2Set;
- 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])) {
+ intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]);
+ }
+ if (!(zeroOneSet & (kZeroS1Set | kOneS2Set))
+ && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) {
zeroOneSet |= kZeroS1Set | kOneS2Set;
- 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]);
- }
- }
+ intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[TCurve::kPointLast]);
+ }
// check for one
- if (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
+ if (!(zeroOneSet & (kOneS1Set | kZeroS2Set))
+ && sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
zeroOneSet |= kOneS1Set | kZeroS2Set;
- 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])) {
+ intersections->insertNear(1, 0, sect1->fCurve[TCurve::kPointLast], sect2->fCurve[0]);
+ }
+ if (!(zeroOneSet & (kOneS1Set | kOneS2Set))
+ && sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[
+ TCurve::kPointLast])) {
zeroOneSet |= kOneS1Set | kOneS2Set;
- 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]);
- }
+ intersections->insertNear(1, 1, sect1->fCurve[TCurve::kPointLast],
+ sect2->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();
@@ -1033,14 +1728,14 @@ struct SkClosestSect {
fClosest.push_back().reset();
}
- void find(const SkTSpan<TCurve>* span1, const SkTSpan<TCurve>* span2) {
+ bool 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;
+ return false;
}
for (int index = 0; index < fUsed; ++index) {
SkClosestRecord<TCurve>* test = &fClosest[index];
@@ -1050,42 +1745,54 @@ struct SkClosestSect {
}
test->update(*record);
record->reset();
- return;
+ return false;
}
}
++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 = fClosest[index];
- test.addIntersection(intersections);
+ const SkClosestRecord<TCurve>* test = closestPtrs[index];
+ test->addIntersection(intersections);
}
}
- // this is oversized by one so that an extra record can merge into final one
- SkSTArray<TCurve::kMaxIntersections + 1, SkClosestRecord<TCurve>, true> fClosest;
+ // this is oversized so that an extra records can merge into final one
+ SkSTArray<TCurve::kMaxIntersections * 2, 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) {
+#if DEBUG_T_SECT_DUMP > 1
+ gDumpTSectNum = 0;
+#endif
+ PATH_OPS_DEBUG_CODE(sect1->fOppSect = sect2);
+ PATH_OPS_DEBUG_CODE(sect2->fOppSect = sect1);
intersections->reset();
- intersections->setMax(TCurve::kMaxIntersections);
+ intersections->setMax(TCurve::kMaxIntersections * 2); // give extra for slop
SkTSpan<TCurve>* span1 = sect1->fHead;
SkTSpan<TCurve>* span2 = sect2->fHead;
- bool check;
- if (!span1->intersects(span2, &check)) {
+ int oppSect, sect = sect1->intersects(span1, sect2, span2, &oppSect);
+// SkASSERT(between(0, sect, 2));
+ if (!sect) {
return;
}
- if (check) {
+ if (sect == 2 && oppSect == 2) {
(void) EndsEqual(sect1, sect2, intersections);
return;
}
- span1->fBounded.push_back() = span2;
- span2->fBounded.push_back() = span1;
+ span1->addBounded(span2, &sect1->fHeap);
+ span2->addBounded(span1, &sect2->fHeap);
do {
// find the largest bounds
SkTSpan<TCurve>* largest1 = sect1->boundsMax();
@@ -1096,64 +1803,66 @@ 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;
- if (!half2->split(half1)) {
+ if (!half2->split(half1, &splitSect->fHeap)) {
break;
}
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 DEBUG_T_SECT
- sect1->validate();
- sect2->validate();
-#endif
-#if DEBUG_T_SECT_DUMP > 1
- sect1->dumpBoth(*sect2);
+ 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);
#endif
if (!sect1->fHead || !sect2->fHead) {
- return;
+ break;
}
} while (true);
- if (sect1->fActiveCount >= 2 && sect2->fActiveCount >= 2) {
- // check for coincidence
- SkTSpan<TCurve>* first = sect1->fHead;
+ 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
do {
- 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) {
+ 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) {
intersections->clearCoincidence(index);
}
- } while ((first = first->fNext));
+ } while ((coincident = coincident->fNext));
+ }
+ if (!sect1->fHead || !sect2->fHead) {
+ return;
}
int zeroOneSet = EndsEqual(sect1, sect2, intersections);
sect1->recoverCollapsed();
@@ -1163,33 +1872,41 @@ 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];
- double t = head1->closestBoundedT(start1);
- if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) {
- intersections->insert(0, t, start1);
+ if (head1->isBounded()) {
+ 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];
- double t = head2->closestBoundedT(start2);
- if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) {
- intersections->insert(t, 0, start2);
+ if (head2->isBounded()) {
+ 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];
- double t = tail1->closestBoundedT(end1);
- if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) {
- intersections->insert(1, t, end1);
+ if (tail1->isBounded()) {
+ 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];
- double t = tail2->closestBoundedT(end2);
- if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
- intersections->insert(t, 1, end2);
+ if (tail2->isBounded()) {
+ double t = tail2->closestBoundedT(end2);
+ if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
+ intersections->insert(t, 1, end2);
+ }
}
}
SkClosestSect<TCurve> closest;
@@ -1201,11 +1918,39 @@ void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersectio
break;
}
SkTSpan<TCurve>* result2 = sect2->fHead;
+ bool found = false;
while (result2) {
- closest.find(result1, result2);
+ found |= 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 0f63f396e7..2f09f412f2 100644
--- a/src/pathops/SkPathOpsTightBounds.cpp
+++ b/src/pathops/SkPathOpsTightBounds.cpp
@@ -8,14 +8,16 @@
#include "SkPathOpsCommon.h"
bool TightBounds(const SkPath& path, SkRect* result) {
+ SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
+ SkOpContour contour;
+ SkOpGlobalState globalState( NULL PATH_OPS_DEBUG_PARAMS(&contour));
// turn path into list of segments
- SkTArray<SkOpContour> contours;
- SkOpEdgeBuilder builder(path, contours);
- if (!builder.finish()) {
+ SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
+ if (!builder.finish(&allocator)) {
return false;
}
- SkTArray<SkOpContour*, true> contourList;
- MakeContourList(contours, contourList, false, false);
+ SkTDArray<SkOpContour* > contourList;
+ MakeContourList(&contour, contourList, false, false);
SkOpContour** currentPtr = contourList.begin();
result->setEmpty();
if (!currentPtr) {
diff --git a/src/pathops/SkPathOpsTriangle.cpp b/src/pathops/SkPathOpsTriangle.cpp
deleted file mode 100644
index 77845e0673..0000000000
--- a/src/pathops/SkPathOpsTriangle.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 8cc8c6d3b5..0000000000
--- a/src/pathops/SkPathOpsTriangle.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * 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 01fec0d0b6..0248e7115a 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -22,6 +22,111 @@ 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) {
@@ -92,6 +197,7 @@ 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;
@@ -141,12 +247,6 @@ 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;
}
@@ -156,6 +256,10 @@ 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) {
@@ -304,7 +408,8 @@ 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));
+ SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
+ || (precisely_zero(a) && precisely_zero(b) && precisely_zero(c)));
return (a - b) * (c - b) <= 0;
}
@@ -312,6 +417,15 @@ 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;
}
@@ -324,7 +438,6 @@ 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
deleted file mode 100644
index f9a7bf5179..0000000000
--- a/src/pathops/SkQuarticRoot.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-// 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
deleted file mode 100644
index 6ce08671e2..0000000000
--- a/src/pathops/SkQuarticRoot.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * 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 6f06447a47..c19cd3db4b 100644
--- a/src/pathops/SkReduceOrder.cpp
+++ b/src/pathops/SkReduceOrder.cpp
@@ -272,6 +272,11 @@ 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 4ff9a1d127..397b58d927 100644
--- a/src/pathops/SkReduceOrder.h
+++ b/src/pathops/SkReduceOrder.h
@@ -7,11 +7,9 @@
#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 {