pathops version two
R=reed@google.com
marked 'no commit' to attempt to get trybots to run
TBR=reed@google.com
Review URL: https://codereview.chromium.org/1002693002
diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp
index c27434f..b507eb7 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 @@
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 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 @@
}
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 @@
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 @@
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 @@
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 4c1947b..23654e5 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 2fb35e1..0000000
--- 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 696c42e..f5fe015 100644
--- a/src/pathops/SkDCubicLineIntersection.cpp
+++ b/src/pathops/SkDCubicLineIntersection.cpp
@@ -93,6 +93,29 @@
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 @@
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 @@
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 @@
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 @@
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 a28564d..2d034b6 100644
--- a/src/pathops/SkDCubicToQuads.cpp
+++ b/src/pathops/SkDCubicToQuads.cpp
@@ -19,62 +19,10 @@
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 = ⊂
- }
- const SkDCubic& c = *cPtr;
- double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX;
- double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY;
- double dist = sqrt(dx * dx + dy * dy);
- double tDiv3 = precision / (adjust * dist);
- double t = SkDCubeRoot(tDiv3);
- if (start > 0) {
- t = start + (1 - start) * t;
- }
- return t;
-}
SkDQuad SkDCubic::toQuad() const {
SkDQuad quad;
@@ -86,101 +34,3 @@
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 8fc673f..ed96b9c 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 @@
removeOne(endMatch);
}
}
+ if (fUsed == 2) {
+ fIsCoincident[0] = fIsCoincident[1] = 0x03;
+ }
}
void SkIntersections::computePoints(const SkDLine& line, int used) {
@@ -81,12 +45,6 @@
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 @@
}
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 @@
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 @@
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 @@
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 f0f66d1..0000000
--- 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 24f1aac..0000000
--- 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 fcb9171..0000000
--- 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, ©I);
- lookNearEnd(q1, q2, 1, *this, false, ©I);
- lookNearEnd(q2, q1, 0, *this, true, ©I);
- lookNearEnd(q2, q1, 1, *this, true, ©I);
- 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 ef8edb0..b8a9a64 100644
--- a/src/pathops/SkDQuadLineIntersection.cpp
+++ b/src/pathops/SkDQuadLineIntersection.cpp
@@ -105,6 +105,29 @@
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 @@
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 @@
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 @@
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 3569c93..c633fd0 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 @@
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 @@
}
const SkPathOpsBounds& bounds() const {
- return fContour->segments()[fIndex].bounds();
+ return fSegment->bounds();
+ }
+
+ SkOpContour* contour() const {
+ return fSegment->contour();
}
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;
- }
-
- 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);
+ fSegment = contour->first();
}
SkScalar left() const {
@@ -95,41 +48,40 @@
}
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 @@
}
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 e9875cf..007efa7 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 @@
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 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 @@
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 @@
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 @@
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 a1bde51..15bac19 100644
--- a/src/pathops/SkIntersections.h
+++ b/src/pathops/SkIntersections.h
@@ -16,7 +16,6 @@
public:
SkIntersections()
: fSwap(0)
- , fFlatMeasure(false)
#ifdef SK_DEBUG
, fDepth(0)
#endif
@@ -24,7 +23,6 @@
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 @@
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 @@
};
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 @@
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 @@
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 @@
fSwap ^= true;
}
- void swapPts();
-
bool swapped() const {
return fSwap;
}
@@ -219,19 +181,27 @@
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 @@
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 @@
#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 @@
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 b3a188c..c13a51a 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 @@
*/
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 @@
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 @@
// 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 @@
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 @@
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;
- 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) {
- continue;
- }
- if (oSpan.fOtherIndex == checkEnd) {
- continue;
- }
- if (!approximately_equal(oSpan.fOtherT, span.fT)) {
- continue;
- }
- goto recomputeSector;
- }
- checkEnd += step;
- } while (checkEnd != limit);
-recomputeSector:
- if (checkEnd == fEnd || checkEnd - step == fEnd) {
+ bool stepUp = fStart->t() < fEnd->t();
+ const SkOpSpanBase* checkEnd = fEnd;
+ if (checkEnd->final() && stepUp) {
fUnorderable = true;
return false;
}
- int saveEnd = fEnd;
- fComputedEnd = fEnd = checkEnd - step;
+ do {
+// advance end
+ const SkOpSegment* other = checkEnd->segment();
+ const SkOpSpanBase* oSpan = other->head();
+ do {
+ if (oSpan->segment() != segment()) {
+ continue;
+ }
+ if (oSpan == checkEnd) {
+ continue;
+ }
+ if (!approximately_equal(oSpan->t(), checkEnd->t())) {
+ continue;
+ }
+ goto recomputeSector;
+ } while (!oSpan->final() && (oSpan = oSpan->upCast()->next()));
+ checkEnd = stepUp ? !checkEnd->final()
+ ? checkEnd->upCast()->next() : NULL
+ : checkEnd->prev();
+ } while (checkEnd);
+recomputeSector:
+ 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;
+ }
+ 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
}
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::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 @@
}
}
// 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 @@
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 @@
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 @@
}
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 @@
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 @@
}
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 @@
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::oppositePlanes(const SkOpAngle& rh) const {
- int startSpan = abs(rh.fSectorStart - fSectorStart);
+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);
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 @@
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 @@
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 @@
} 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 @@
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 @@
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 @@
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 @@
}
}
-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);
+ }
}
-bool SkOpAngle::tangentsDiverge(const SkOpAngle& rh, double s0xt0) const {
+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 {
if (s0xt0 == 0) {
return false;
}
@@ -1090,7 +1165,7 @@
// 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 @@
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 1dc4250..84b3701 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 @@
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;
- int end() const {
+ const SkOpAngle* debugAngle(int id) const;
+ SkOpContour* debugContour(int id);
+
+ int debugID() const {
+ return PATH_OPS_DEBUG_RELEASE(fID, -1);
+ }
+
+#if DEBUG_SORT
+ void debugLoop() const;
+#endif
+
+#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;
}
- const SkOpAngle* findFirst() const;
-
- bool inLoop() const {
- return !!fNext;
- }
-
+ bool endsIntersect(SkOpAngle* );
+ bool endToSide(const SkOpAngle* rh, bool* inside) const;
+ SkOpAngle* findFirst();
+ int findSector(SkPath::Verb verb, double x, double y) const;
+ SkOpGlobalState* globalState() const;
void insert(SkOpAngle* );
- 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 @@
return fSectorStart;
}
- void set(const SkOpSegment* segment, int start, int end);
+ SkOpSegment* segment() const;
- void setLastMarked(SkOpSpan* marked) {
+ void set(SkOpSpanBase* start, SkOpSpanBase* end);
+ void setCurveHullSweep();
+
+ void setID(int id) {
+ PATH_OPS_DEBUG_CODE(fID = id);
+ }
+
+ void setLastMarked(SkOpSpanBase* marked) {
fLastMarked = marked;
}
- SkOpSegment* segment() const {
- return const_cast<SkOpSegment*>(fSegment);
- }
+ void setSector();
+ void setSpans();
+ int sign() const;
- int sign() const {
- return SkSign32(fStart - fEnd);
- }
-
- bool small() 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/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
new file mode 100755
index 0000000..45eee0a
--- /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 287bfd1..b79b88b 100644
--- a/src/pathops/SkOpCoincidence.h
+++ b/src/pathops/SkOpCoincidence.h
@@ -19,6 +19,8 @@
SkOpPtT* fOppPtTStart;
SkOpPtT* fOppPtTEnd;
bool fFlipped;
+
+ void dump() const;
};
class SkOpCoincidence {
@@ -28,13 +30,27 @@
}
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 28c072a..d17b189 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;
+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);
}
- 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) {
+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 @@
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 @@
}
}
-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 7a1cc09..be1f59f 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -8,31 +8,16 @@
#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
}
bool operator<(const SkOpContour& rh) const {
@@ -41,211 +26,255 @@
: 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);
+ void addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator);
+
+ void addLine(SkPoint pts[2], SkChunkAlloc* allocator) {
+ appendSegment(allocator).addLine(pts, this);
+ }
+
+ void addQuad(SkPoint pts[3], SkChunkAlloc* allocator) {
+ appendSegment(allocator).addQuad(pts, this);
+ }
+
+ void align() {
+ SkASSERT(fCount > 0);
+ SkOpSegment* segment = &fHead;
+ do {
+ segment->align();
+ } while ((segment = segment->next()));
+ }
+
+ SkOpSegment& appendSegment(SkChunkAlloc* allocator) {
+ SkOpSegment* result = fCount++
+ ? SkOpTAllocator<SkOpSegment>::Allocate(allocator) : &fHead;
+ result->setPrev(fTail);
+ if (fTail) {
+ fTail->setNext(result);
}
-#endif
- fCrosses.push_back(crosser);
+ fTail = result;
+ return *result;
}
- void addCubic(const SkPoint pts[4]) {
- fSegments.push_back().addCubic(pts, fOperand, fXor);
- fContainsCurves = fContainsCubics = true;
- }
-
- int addLine(const SkPoint pts[2]) {
- fSegments.push_back().addLine(pts, fOperand, fXor);
- return fSegments.count();
- }
-
- void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
- fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
- }
-
- bool addPartialCoincident(int index, SkOpContour* other, int otherIndex,
- const SkIntersections& ts, int ptIndex, bool swap);
-
- int addQuad(const SkPoint pts[3]) {
- fSegments.push_back().addQuad(pts, fOperand, fXor);
- fContainsCurves = true;
- return fSegments.count();
- }
-
- int addT(int segIndex, SkOpContour* other, int otherIndex, const SkPoint& pt, double newT) {
- setContainsIntercepts();
- return fSegments[segIndex].addT(&other->fSegments[otherIndex], pt, newT);
- }
-
- int addSelfT(int segIndex, const SkPoint& pt, double newT) {
- setContainsIntercepts();
- return fSegments[segIndex].addSelfT(pt, newT);
- }
-
- void align(const SkOpSegment::AlignedSpan& aligned, bool swap, SkCoincidence* coincidence);
- void alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
- SkTArray<SkCoincidence, true>* coincidences);
-
- void alignCoincidence(const SkOpSegment::AlignedSpan& aligned) {
- alignCoincidence(aligned, &fCoincidences);
- alignCoincidence(aligned, &fPartialCoincidences);
- }
-
- 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);
- }
+ SkOpContour* appendContour(SkChunkAlloc* allocator) {
+ SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(allocator);
+
+ SkOpContour* prev = this;
+ SkOpContour* next;
+ while ((next = prev->next())) {
+ prev = next;
}
+ prev->setNext(contour);
+ return contour;
}
-
- void alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
- bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const;
-
+
const SkPathOpsBounds& bounds() const {
return fBounds;
}
- bool calcAngles();
- bool calcCoincidentWinding();
- void calcPartialCoincidentWinding();
-
- void checkDuplicates() {
- int segmentCount = fSegments.count();
- for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
- SkOpSegment& segment = fSegments[sIndex];
- if (segment.count() > 2) {
- segment.checkDuplicates();
- }
- }
- }
-
- 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;
- }
-
- 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();
- }
- }
- }
-
- 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();
- }
- }
- }
-
- // if same point has different T values, choose a common T
- void checkTiny() {
- int segmentCount = fSegments.count();
- if (segmentCount <= 2) {
- return;
- }
- for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
- SkOpSegment& segment = fSegments[sIndex];
- if (segment.hasTiny()) {
- segment.checkTiny();
- }
- }
+ void calcAngles(SkChunkAlloc* allocator) {
+ SkASSERT(fCount > 0);
+ SkOpSegment* segment = &fHead;
+ do {
+ segment->calcAngles(allocator);
+ } while ((segment = segment->next()));
}
void complete() {
setBounds();
- fContainsIntercepts = false;
}
- bool containsCubics() const {
- return fContainsCubics;
+ int count() const {
+ return fCount;
}
- bool crosses(const SkOpContour* crosser) const {
- for (int index = 0; index < fCrosses.count(); ++index) {
- if (fCrosses[index] == crosser) {
- return true;
- }
- }
- return false;
+ int debugID() const {
+ return PATH_OPS_DEBUG_RELEASE(fID, -1);
+ }
+
+ int debugIndent() const {
+ return PATH_OPS_DEBUG_RELEASE(fIndent, 0);
+ }
+
+#if DEBUG_ACTIVE_SPANS
+ void debugShowActiveSpans() {
+ SkOpSegment* segment = &fHead;
+ do {
+ segment->debugShowActiveSpans();
+ } while ((segment = segment->next()));
+ }
+#endif
+
+ const SkOpAngle* debugAngle(int id) const {
+ return PATH_OPS_DEBUG_RELEASE(globalState()->debugAngle(id), NULL);
+ }
+
+ SkOpContour* debugContour(int id) {
+ return PATH_OPS_DEBUG_RELEASE(globalState()->debugContour(id), NULL);
+ }
+
+ const SkOpPtT* debugPtT(int id) const {
+ return PATH_OPS_DEBUG_RELEASE(globalState()->debugPtT(id), NULL);
+ }
+
+ const SkOpSegment* debugSegment(int id) const {
+ return PATH_OPS_DEBUG_RELEASE(globalState()->debugSegment(id), NULL);
+ }
+
+ const SkOpSpanBase* debugSpan(int id) const {
+ return PATH_OPS_DEBUG_RELEASE(globalState()->debugSpan(id), NULL);
+ }
+
+ SkOpGlobalState* globalState() const {
+ return fState;
+ }
+
+ void debugValidate() const {
+#if DEBUG_VALIDATE
+ const SkOpSegment* segment = &fHead;
+ const SkOpSegment* prior = NULL;
+ do {
+ segment->debugValidate();
+ SkASSERT(segment->prev() == prior);
+ prior = segment;
+ } while ((segment = segment->next()));
+ SkASSERT(prior == fTail);
+#endif
}
bool done() const {
return fDone;
}
+ 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 {
- const SkOpSegment& segment = fSegments.back();
- return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
+ return fTail->pts()[SkPathOpsVerbToPoints(fTail->verb())];
}
- void fixOtherTIndex() {
- int segmentCount = fSegments.count();
- for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
- fSegments[sIndex].fixOtherTIndex();
- }
+ SkOpSegment* first() {
+ SkASSERT(fCount > 0);
+ return &fHead;
}
- bool hasMultiples() const {
- return fMultiples;
+ const SkOpSegment* first() const {
+ SkASSERT(fCount > 0);
+ return &fHead;
}
- void joinCoincidence() {
- joinCoincidence(fCoincidences, false);
- joinCoincidence(fPartialCoincidences, true);
+ void indentDump() {
+ PATH_OPS_DEBUG_CODE(fIndent += 2);
}
- SkOpSegment* nonVerticalSegment(int* start, int* end);
+ void init(SkOpGlobalState* globalState, bool operand, bool isXor) {
+ fState = globalState;
+ fOperand = operand;
+ fXor = isXor;
+ }
+
+ 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;
+ }
+
+ SkOpContour* next() {
+ return fNext;
+ }
+
+ const SkOpContour* next() const {
+ return fNext;
+ }
+
+ SkOpSegment* nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end);
bool operand() const {
return fOperand;
}
+ 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 reset() {
- fSegments.reset();
- fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
- fContainsCurves = fContainsCubics = fContainsIntercepts = fDone = fMultiples = false;
+ 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 resolveNearCoincidence();
-
- SkTArray<SkOpSegment>& segments() {
- return fSegments;
+ void setBounds() {
+ SkASSERT(fCount > 0);
+ const SkOpSegment* segment = &fHead;
+ fBounds = segment->bounds();
+ while ((segment = segment->next())) {
+ fBounds.add(segment->bounds());
+ }
}
- void setContainsIntercepts() {
- fContainsIntercepts = true;
+ void setGlobalState(SkOpGlobalState* state) {
+ fState = state;
+ }
+
+ void setNext(SkOpContour* contour) {
+ SkASSERT(!fNext == !!contour);
+ fNext = contour;
}
void setOperand(bool isOp) {
@@ -254,107 +283,68 @@
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 sortSegments() {
+ SkOpSegment* segment = &fHead;
+ do {
+ *fSortedSegments.append() = segment;
+ } while ((segment = segment->next()));
+ SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
+ fFirstSorted = 0;
+ }
+
+ const SkPoint& start() const {
+ return fHead.pts()[0];
+ }
void toPartialBackward(SkPathWriter* path) const {
- int segmentCount = fSegments.count();
- for (int test = segmentCount - 1; test >= 0; --test) {
- fSegments[test].addCurveTo(1, 0, path, true);
- }
+ const SkOpSegment* segment = fTail;
+ do {
+ segment->addCurveTo(segment->tail(), segment->head(), path, true);
+ } while ((segment = segment->prev()));
}
void toPartialForward(SkPathWriter* path) const {
- int segmentCount = fSegments.count();
- 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()));
}
+ void toPath(SkPathWriter* path) const;
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;
- }
-
-#if DEBUG_TEST
- SkTArray<SkOpSegment>& debugSegments() {
- return fSegments;
- }
-#endif
-
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
- void debugShowActiveSpans() {
- for (int index = 0; index < fSegments.count(); ++index) {
- fSegments[index].debugShowActiveSpans();
- }
- }
-#endif
-
-#if DEBUG_SHOW_WINDING
- int debugShowWindingValues(int totalSegments, int ofInterest);
- static void debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList);
-#endif
-
- // available to test routines only
- void dump() const;
- void dumpAngles() const;
- void dumpCoincidence(const SkCoincidence& ) const;
- void dumpCoincidences() const;
- void dumpPt(int ) const;
- void dumpPts() const;
- void dumpSpan(int ) const;
- void dumpSpans() const;
+ 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 803a5f4..bd21d72 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::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 @@
}
}
-
int SkOpEdgeBuilder::preFetch() {
if (!fPath->isFinite()) {
fUnparseable = true;
@@ -78,18 +88,18 @@
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 @@
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 @@
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 @@
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 @@
verbPtr++;
switch (verb) {
case SkPath::kMove_Verb:
- if (fCurrentContour) {
+ if (fCurrentContour && fCurrentContour->count()) {
if (fAllowOpenContours) {
complete();
} else if (!close()) {
@@ -173,21 +183,44 @@
}
}
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);
+ fCurrentContour->addQuad(pointsPtr, fAllocator);
break;
- case SkPath::kCubic_Verb:
- fCurrentContour->addCubic(pointsPtr);
- break;
+ case SkPath::kCubic_Verb: {
+ // split self-intersecting cubics in two before proceeding
+ // if the cubic is convex, it doesn't self intersect.
+ SkScalar loopT;
+ if (SkDCubic::ComplexBreak(pointsPtr, &loopT)) {
+ SkPoint cubicPair[7];
+ SkChopCubicAt(pointsPtr, cubicPair, loopT);
+ SkPoint cStorage[2][4];
+ SkPath::Verb v1 = SkReduceOrder::Cubic(&cubicPair[0], cStorage[0]);
+ SkPath::Verb v2 = SkReduceOrder::Cubic(&cubicPair[3], cStorage[1]);
+ if (v1 != SkPath::kMove_Verb && v2 != SkPath::kMove_Verb) {
+ SkPoint* curve1 = v1 == SkPath::kCubic_Verb ? &cubicPair[0] : cStorage[0];
+ SkPoint* curve2 = v2 == SkPath::kCubic_Verb ? &cubicPair[3] : cStorage[1];
+ for (size_t index = 0; index < SK_ARRAY_COUNT(curve1); ++index) {
+ force_small_to_zero(&curve1[index]);
+ force_small_to_zero(&curve2[index]);
+ }
+ fCurrentContour->addCurve(v1, curve1, fAllocator);
+ fCurrentContour->addCurve(v2, curve2, fAllocator);
+ } else {
+ fCurrentContour->addCubic(pointsPtr, fAllocator);
+ }
+ } else {
+ fCurrentContour->addCubic(pointsPtr, fAllocator);
+ }
+ } break;
case SkPath::kClose_Verb:
SkASSERT(fCurrentContour);
if (!close()) {
@@ -198,10 +231,11 @@
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 fd07445..3ecc915 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 @@
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 @@
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 1fb5afa..902f273 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
@@ -33,147 +42,125 @@
#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) {
+ if (downSpan->windValue() || downSpan->oppValue()) {
+ if (!*endPtr) {
+ *startPtr = start;
+ *endPtr = downSpan;
}
- if (!downSpan.fDone) {
- if (downSpan.fWindSum != SK_MinS32) {
- return spanToAngle(index, prev);
+ 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 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 @@
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 @@
default:
SkASSERT(0);
}
- // return ePtr[0];
} else {
path->deferredMoveLine(ePtr[0]);
switch (fVerb) {
@@ -406,1538 +245,285 @@
}
}
}
- // 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;
- }
- 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);
-}
-
-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.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) {
+ if ((testT = test->ptT()->fT) >= t) {
+ if (testT == t) {
+ existing = test;
+ }
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;
+ } while ((test = test->upCast()->next()));
+ SkOpPtT* result;
+ if (existing && existing->contains(opp)) {
+ result = existing->ptT();
+ } else {
+ result = this->addT(t, SkOpSegment::kNoAlias, allocator);
+ }
+ SkASSERT(result);
+ return result;
}
-SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
- int endIndex = nextExactSpan(0, 1);
- SkASSERT(endIndex > 0);
- SkOpAngle& angle = fAngles.push_back();
- *anglePtr = ∠
- 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) {
+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;
}
- 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);
+ oEndSpan = ptT->span();
+ oStartSpan = oEndSpan->prev();
+ if (oStartSpan && oStartSpan->windValue()) {
+ break;
+ }
+ }
+ 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;
- }
- }
- ++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);
-}
-
-// set spans from start to end to decrement by one
-// note this walks other backwards
-// FIXME: there's probably an edge case that can be constructed where
-// two span in one segment are separated by float epsilon on one span but
-// not the other, if one segment is very small. For this
-// case the counts asserted below may or may not be enough to separate the
-// spans. Even if the counts work out, what if the spans aren't correctly
-// sorted? It feels better in such a case to match the span's other span
-// pointer since both coincident segments must contain the same spans.
-// FIXME? It seems that decrementing by one will fail for complex paths that
-// have three or more coincident edges. Shouldn't this subtract the difference
-// between the winding values?
-/* |--> |-->
-this 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4
-other 2<<<<1<<<<0 2<<<<1<<<<0 2<<<<1<<<<0
- ^ ^ <--| <--|
- startPt endPt test/oTest first pos test/oTest final pos
-*/
-void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) {
- bool binary = fOperand != other->fOperand;
- int index = 0;
- while (startPt != fTs[index].fPt) {
- SkASSERT(index < fTs.count());
- ++index;
- }
- while (index > 0 && precisely_equal(fTs[index].fT, fTs[index - 1].fT)) {
- --index;
- }
- bool oFoundEnd = false;
- int oIndex = other->fTs.count();
- while (startPt != other->fTs[--oIndex].fPt) { // look for startPt match
- SkASSERT(oIndex > 0);
- }
- double oStartT = other->fTs[oIndex].fT;
- // look for first point beyond match
- while (startPt == other->fTs[--oIndex].fPt || precisely_equal(oStartT, other->fTs[oIndex].fT)) {
- if (!oIndex) {
- return; // tiny spans may move in the wrong direction
- }
- }
- SkOpSpan* test = &fTs[index];
- SkOpSpan* oTest = &other->fTs[oIndex];
- SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
- SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
- bool decrement, track, bigger;
- int originalWindValue;
- const SkPoint* testPt;
- const SkPoint* oTestPt;
- do {
- SkASSERT(test->fT < 1);
- SkASSERT(oTest->fT < 1);
- decrement = test->fWindValue && oTest->fWindValue;
- track = test->fWindValue || oTest->fWindValue;
- bigger = test->fWindValue >= oTest->fWindValue;
- testPt = &test->fPt;
- double testT = test->fT;
- oTestPt = &oTest->fPt;
- double oTestT = oTest->fT;
- do {
- if (decrement) {
- if (binary && bigger) {
- test->fOppValue--;
- } else {
- decrementSpan(test);
- }
- } else if (track) {
- TrackOutsidePair(&outsidePts, *testPt, *oTestPt);
- }
- SkASSERT(index < fTs.count() - 1);
- test = &fTs[++index];
- } while (*testPt == test->fPt || precisely_equal(testT, test->fT));
- originalWindValue = oTest->fWindValue;
- do {
- SkASSERT(oTest->fT < 1);
- SkASSERT(originalWindValue == oTest->fWindValue);
- if (decrement) {
- if (binary && !bigger) {
- oTest->fOppValue--;
- } else {
- other->decrementSpan(oTest);
- }
- } else if (track) {
- TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
- }
- if (!oIndex) {
- break;
- }
- oFoundEnd |= endPt == oTest->fPt;
- oTest = &other->fTs[--oIndex];
- } while (*oTestPt == oTest->fPt || precisely_equal(oTestT, oTest->fT));
- } while (endPt != test->fPt && test->fT < 1);
- // FIXME: determine if canceled edges need outside ts added
- if (!oFoundEnd) {
- for (int oIdx2 = oIndex; oIdx2 >= 0; --oIdx2) {
- SkOpSpan* oTst2 = &other->fTs[oIdx2];
- if (originalWindValue != oTst2->fWindValue) {
- goto skipAdvanceOtherCancel;
- }
- if (!oTst2->fWindValue) {
- goto skipAdvanceOtherCancel;
- }
- if (endPt == other->fTs[oIdx2].fPt) {
- break;
- }
- }
- oFoundEnd = endPt == oTest->fPt;
- do {
- SkASSERT(originalWindValue == oTest->fWindValue);
- if (decrement) {
- if (binary && !bigger) {
- oTest->fOppValue--;
- } else {
- other->decrementSpan(oTest);
- }
- } else if (track) {
- TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
- }
- if (!oIndex) {
- break;
- }
- oTest = &other->fTs[--oIndex];
- oFoundEnd |= endPt == oTest->fPt;
- } while (!oFoundEnd || endPt == oTest->fPt);
- }
-skipAdvanceOtherCancel:
- int outCount = outsidePts.count();
- if (!done() && outCount) {
- addCancelOutsides(outsidePts[0], outsidePts[1], other);
- if (outCount > 2) {
- addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other);
- }
- }
- if (!other->done() && oOutsidePts.count()) {
- other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
- }
- setCoincidentRange(startPt, endPt, other);
- other->setCoincidentRange(startPt, endPt, this);
-}
-
-int SkOpSegment::addSelfT(const SkPoint& pt, double newT) {
- // if the tail nearly intersects itself but not quite, the caller records this separately
- int result = addT(this, pt, newT);
- SkOpSpan* span = &fTs[result];
- fLoop = span->fLoop = true;
- return result;
-}
-
-// find the starting or ending span with an existing loop of angles
-// FIXME? replicate for all identical starting/ending spans?
-// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
-// FIXME? assert that only one other span has a valid windValue or oppValue
-void SkOpSegment::addSimpleAngle(int index) {
- SkOpSpan* span = &fTs[index];
- int idx;
- int start, end;
- if (span->fT == 0) {
- idx = 0;
- span = &fTs[0];
- do {
- if (span->fToAngle) {
- SkASSERT(span->fToAngle->loopCount() == 2);
- SkASSERT(!span->fFromAngle);
- span->fFromAngle = span->fToAngle->next();
- return;
- }
- span = &fTs[++idx];
- } while (span->fT == 0);
- SkASSERT(!fTs[0].fTiny && fTs[idx].fT > 0);
- addStartSpan(idx);
- start = 0;
- end = idx;
- } else {
- idx = count() - 1;
- span = &fTs[idx];
- do {
- if (span->fFromAngle) {
- SkASSERT(span->fFromAngle->loopCount() == 2);
- SkASSERT(!span->fToAngle);
- span->fToAngle = span->fFromAngle->next();
- return;
- }
- span = &fTs[--idx];
- } while (span->fT == 1);
- SkASSERT(!fTs[idx].fTiny && fTs[idx].fT < 1);
- addEndSpan(++idx);
- start = idx;
- end = count();
- }
- 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) {
+ oStartSpan = oEndSpan->upCastable();
+ if (oStartSpan && oStartSpan->windValue()) {
+ oEndSpan = oStartSpan->next();
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;
}
- angle->insert(oAngle);
+ SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+ oAngle->set(oEndSpan, oStartSpan);
+ oEndSpan->setFromAngle(oAngle);
+ *otherPtr = other;
+ return oAngle;
}
-void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
+SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
debugValidate();
- int count = this->count();
- for (int index = 0; index < count; ++index) {
- SkOpSpan& span = fTs[index];
- if (!span.fMultiple) {
- continue;
+ SkPoint pt = this->ptAtT(t);
+ SkOpSpanBase* span = &fHead;
+ do {
+ SkOpPtT* result = span->ptT();
+ if (t == result->fT) {
+ return result;
}
- 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;
- }
+ 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;
}
- 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:
- ;
+ duplicatePt |= ptMatch;
+ loop = loop->next();
}
- ++index;
+ 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();
+ }
+ SkASSERT(span != &fTail);
+ } while ((span = span->upCast()->next()));
+ SkASSERT(0);
+ return NULL;
+}
+
+// choose a solitary t and pt value; remove aliases; align the opposite ends
+void SkOpSegment::align() {
+ debugValidate();
+ SkOpSpanBase* span = &fHead;
+ if (!span->aligned()) {
+ span->alignEnd(0, fPts[0]);
+ }
+ while ((span = span->upCast()->next())) {
+ if (span == &fTail) {
+ break;
+ }
+ span->align();
+ }
+ if (!span->aligned()) {
+ span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
}
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;
+void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
+ bool activePrior = !fHead.isCanceled();
+ if (activePrior && !fHead.simple()) {
+ addStartSpan(allocator);
}
- if (span->fPt != thisPt) {
- span->fPt = thisPt;
- oSpan->fPt = thisPt;
- aligned = true;
- }
- double oT = oSpan->fT;
- if (oT == 0) {
- return aligned;
- }
- 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);
}
- }
- 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;
- }
-}
-
-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();
- do {
- SkOpSpan& span = this->fTs[--last];
- if (span.fT != 1 && !span.fSmall) {
- break;
- }
- span.fCoincident = true;
- } while (true);
- int oIndex = 0;
- int oLast = other->count();
- do {
- SkOpSpan& oSpan = other->fTs[--oLast];
- if (oSpan.fT != 1 && !oSpan.fSmall) {
- break;
- }
- oSpan.fCoincident = true;
- } while (true);
- do {
- SkOpSpan* test = &this->fTs[index];
- int baseWind = test->fWindValue;
- int baseOpp = test->fOppValue;
- int endIndex = index;
- SkOpSpan* endSpan;
- while (++endIndex <= last) {
- endSpan = &this->fTs[endIndex];
- SkASSERT(endSpan->fT < 1);
- if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
- break;
- }
- endSpan->fCoincident = true;
- }
- SkOpSpan* oTest = &other->fTs[oIndex];
- int oBaseWind = oTest->fWindValue;
- int oBaseOpp = oTest->fOppValue;
- int oEndIndex = oIndex;
- SkOpSpan* oEndSpan;
- while (++oEndIndex <= oLast) {
- oEndSpan = &this->fTs[oEndIndex];
- SkASSERT(oEndSpan->fT < 1);
- if (oEndSpan->fWindValue != oBaseWind || oEndSpan->fOppValue != oBaseOpp) {
- break;
- }
- oEndSpan->fCoincident = true;
- }
- // consolidate the winding count even if done
- if ((test->fWindValue || test->fOppValue) && (oTest->fWindValue || oTest->fOppValue)) {
- if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
- bumpCoincidentBlind(binary, index, endIndex);
- other->bumpCoincidentOBlind(oIndex, oEndIndex);
- } else {
- other->bumpCoincidentBlind(binary, oIndex, oEndIndex);
- bumpCoincidentOBlind(index, endIndex);
- }
- }
- index = endIndex;
- oIndex = oEndIndex;
- } while (index <= last && oIndex <= oLast);
- SkASSERT(index > last);
- SkASSERT(oIndex > oLast);
-}
-
-void SkOpSegment::bumpCoincidentBlind(bool binary, int index, int endIndex) {
- const SkOpSpan& oTest = fTs[index];
- int oWindValue = oTest.fWindValue;
- int oOppValue = oTest.fOppValue;
- if (binary) {
- SkTSwap<int>(oWindValue, oOppValue);
- }
- do {
- (void) bumpSpan(&fTs[index], oWindValue, oOppValue);
- } while (++index < endIndex);
-}
-
-bool SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
- SkTArray<SkPoint, true>* outsideTs) {
- int index = *indexPtr;
- int oWindValue = oTest.fWindValue;
- int oOppValue = oTest.fOppValue;
- if (binary) {
- SkTSwap<int>(oWindValue, oOppValue);
- }
- SkOpSpan* const test = &fTs[index];
- SkOpSpan* end = test;
- const SkPoint& oStartPt = oTest.fPt;
- do {
- if (end->fDone && !end->fTiny && !end->fSmall) { // extremely large paths trigger this
- return false;
- }
- if (bumpSpan(end, oWindValue, oOppValue)) {
- TrackOutside(outsideTs, oStartPt);
- }
- end = &fTs[++index];
- } while ((end->fPt == test->fPt || precisely_equal(end->fT, test->fT)) && end->fT < 1);
- *indexPtr = index;
- return true;
-}
-
-void SkOpSegment::bumpCoincidentOBlind(int index, int endIndex) {
- do {
- zeroSpan(&fTs[index]);
- } while (++index < endIndex);
-}
-
-// because of the order in which coincidences are resolved, this and other
-// may not have the same intermediate points. Compute the corresponding
-// intermediate T values (using this as the master, other as the follower)
-// and walk other conditionally -- hoping that it catches up in the end
-bool SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
- SkTArray<SkPoint, true>* oOutsidePts, const SkPoint& oEndPt) {
- int oIndex = *oIndexPtr;
- SkOpSpan* const oTest = &fTs[oIndex];
- SkOpSpan* oEnd = oTest;
- const SkPoint& oStartPt = oTest->fPt;
- double oStartT = oTest->fT;
-#if 0 // FIXME : figure out what disabling this breaks
- const SkPoint& startPt = test.fPt;
- // this is always true since oEnd == oTest && oStartPt == oTest->fPt -- find proper condition
- if (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
- TrackOutside(oOutsidePts, startPt);
- }
-#endif
- bool foundEnd = false;
- while (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
- foundEnd |= oEndPt == oEnd->fPt;
- zeroSpan(oEnd);
- oEnd = &fTs[++oIndex];
- }
- *oIndexPtr = oIndex;
- return foundEnd;
-}
-
-// FIXME: need to test this case:
-// contourA has two segments that are coincident
-// contourB has two segments that are coincident in the same place
-// each ends up with +2/0 pairs for winding count
-// since logic below doesn't transfer count (only increments/decrements) can this be
-// resolved to +4/0 ?
-
-// set spans from start to end to increment the greater by one and decrement
-// the lesser
-bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
- SkOpSegment* other) {
- bool binary = fOperand != other->fOperand;
- int index = 0;
- while (startPt != fTs[index].fPt) {
- SkASSERT(index < fTs.count());
- ++index;
- }
- double startT = fTs[index].fT;
- while (index > 0 && precisely_equal(fTs[index - 1].fT, startT)) {
- --index;
- }
- int oIndex = 0;
- while (startPt != other->fTs[oIndex].fPt) {
- SkASSERT(oIndex < other->fTs.count());
- ++oIndex;
- }
- double oStartT = other->fTs[oIndex].fT;
- while (oIndex > 0 && precisely_equal(other->fTs[oIndex - 1].fT, oStartT)) {
- --oIndex;
- }
- SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
- SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
- SkOpSpan* test = &fTs[index];
- const SkPoint* testPt = &test->fPt;
- double testT = test->fT;
- SkOpSpan* oTest = &other->fTs[oIndex];
- const SkPoint* oTestPt = &oTest->fPt;
- // paths with extreme data will fail this test and eject out of pathops altogether later on
- // SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
- do {
- SkASSERT(test->fT < 1);
- if (oTest->fT == 1) {
- // paths with extreme data may be so mismatched that we fail here
- return false;
- }
-
- // consolidate the winding count even if done
- bool foundEnd = false;
- if ((test->fWindValue == 0 && test->fOppValue == 0)
- || (oTest->fWindValue == 0 && oTest->fOppValue == 0)) {
- SkDEBUGCODE(int firstWind = test->fWindValue);
- SkDEBUGCODE(int firstOpp = test->fOppValue);
- do {
- SkASSERT(firstWind == fTs[index].fWindValue);
- SkASSERT(firstOpp == fTs[index].fOppValue);
- ++index;
- SkASSERT(index < fTs.count());
- } while (*testPt == fTs[index].fPt);
- SkDEBUGCODE(firstWind = oTest->fWindValue);
- SkDEBUGCODE(firstOpp = oTest->fOppValue);
- do {
- SkASSERT(firstWind == other->fTs[oIndex].fWindValue);
- SkASSERT(firstOpp == other->fTs[oIndex].fOppValue);
- ++oIndex;
- SkASSERT(oIndex < other->fTs.count());
- } while (*oTestPt == other->fTs[oIndex].fPt);
- } else {
- if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
- if (!bumpCoincidentThis(*oTest, binary, &index, &outsidePts)) {
- return false;
- }
- foundEnd = other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt);
- } else {
- if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
- return false;
- }
- foundEnd = bumpCoincidentOther(*oTest, &index, &outsidePts, endPt);
- }
- }
- 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;
- }
- } else {
- if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
- return false;
- }
- }
- 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;
- }
- }
- }
-#if DEBUG_ADD_T_PAIR
- SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
- __FUNCTION__, fID, t, other->fID, otherT);
-#endif
- SkASSERT(other != this);
- int insertedAt = addT(other, pt, t);
- int otherInsertedAt = other->addT(this, pt2, otherT);
- this->addOtherT(insertedAt, otherT, otherInsertedAt);
- other->addOtherT(otherInsertedAt, t, insertedAt);
- this->matchWindingValue(insertedAt, t, borrowWind);
- other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
- SkOpSpan& span = this->fTs[insertedAt];
- if (pt != pt2) {
- span.fNear = true;
- SkOpSpan& oSpan = other->fTs[otherInsertedAt];
- oSpan.fNear = true;
- }
- // if the newly inserted spans match a neighbor on one but not the other, make them agree
- int lower = this->nextExactSpan(insertedAt, -1) + 1;
- int upper = this->nextExactSpan(insertedAt, 1) - 1;
- if (upper < 0) {
- upper = this->count() - 1;
- }
- int oLower = other->nextExactSpan(otherInsertedAt, -1) + 1;
- int oUpper = other->nextExactSpan(otherInsertedAt, 1) - 1;
- if (oUpper < 0) {
- oUpper = other->count() - 1;
- }
- if (lower == upper && oLower == oUpper) {
- return &span;
- }
-#if DEBUG_CONCIDENT
- SkDebugf("%s id=%d lower=%d upper=%d other=%d oLower=%d oUpper=%d\n", __FUNCTION__,
- debugID(), lower, upper, other->debugID(), oLower, oUpper);
-#endif
- // find the nearby spans in one range missing in the other
- this->alignRange(lower, upper, other, oLower, oUpper);
- other->alignRange(oLower, oUpper, this, lower, upper);
- return &span;
-}
-
-const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
- const SkPoint& pt) {
- return addTPair(t, other, otherT, borrowWind, pt, pt);
-}
-
-bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
- const SkPoint midPt = ptAtT(midT);
- SkPathOpsBounds bounds;
- bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
- bounds.sort();
- return bounds.almostContains(midPt);
-}
-
-bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const {
- if (lesser > greater) {
- SkTSwap<int>(lesser, greater);
- }
- return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
-}
-
-// in extreme cases (like the buffer overflow test) return false to abort
-// for now, if one t value represents two different points, then the values are too extreme
-// to generate meaningful results
-bool SkOpSegment::calcAngles() {
- int spanCount = fTs.count();
- if (spanCount <= 2) {
- return spanCount == 2;
- }
- int index = 1;
- const SkOpSpan* firstSpan = &fTs[index];
- int activePrior = checkSetAngle(0);
- const SkOpSpan* span = &fTs[0];
- if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) {
- index = findStartSpan(0); // curve start intersects
- if (fTs[index].fT == 0) {
- return false;
- }
- SkASSERT(index > 0);
- if (activePrior >= 0) {
- addStartSpan(index);
- }
- }
- bool addEnd;
- int endIndex = spanCount - 1;
- span = &fTs[endIndex - 1];
- if ((addEnd = span->fT == 1 || span->fTiny)) { // if curve end intersects
- endIndex = findEndSpan(endIndex);
- SkASSERT(endIndex > 0);
- } else {
- 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;
+ prior = span;
+ spanBase = next;
}
- if (addEnd && activePrior >= 0) {
- addEndSpan(endIndex);
+ if (activePrior && !fTail.simple()) {
+ addEndSpan(allocator);
}
- return true;
}
-int SkOpSegment::checkSetAngle(int tIndex) const {
- const SkOpSpan* span = &fTs[tIndex];
- while (span->fTiny /* || span->fSmall */) {
- span = &fTs[++tIndex];
- }
- return isCanceled(tIndex) ? -1 : tIndex;
-}
-
-// at this point, the span is already ordered, or unorderable
-int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
- SkASSERT(includeType != SkOpAngle::kUnaryXor);
- SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
- if (NULL == firstAngle || NULL == firstAngle->next()) {
- return SK_NaN32;
- }
- // if all angles have a computed winding,
- // or if no adjacent angles are orderable,
- // or if adjacent orderable angles have no computed winding,
- // there's nothing to do
- // if two orderable angles are adjacent, and both are next to orderable angles,
- // and one has winding computed, transfer to the other
- SkOpAngle* baseAngle = NULL;
- bool tryReverse = false;
- // look for counterclockwise transfers
- SkOpAngle* angle = firstAngle->previous();
- SkOpAngle* next = angle->next();
- firstAngle = next;
+void SkOpSegment::checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
+ SkOpSpanBase* base = &fHead;
+ SkOpSpan* span;
do {
- SkOpAngle* prior = angle;
- angle = next;
- next = angle->next();
- SkASSERT(prior->next() == angle);
- SkASSERT(angle->next() == next);
- if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
- baseAngle = NULL;
- continue;
+ SkOpAngle* angle = base->fromAngle();
+ if (angle && angle->fCheckCoincidence) {
+ angle->checkNearCoincidence();
}
- int testWinding = angle->segment()->windSum(angle);
- if (SK_MinS32 != testWinding) {
- baseAngle = angle;
- tryReverse = true;
- continue;
+ if (base->final()) {
+ break;
}
- if (baseAngle) {
- ComputeOneSum(baseAngle, angle, includeType);
- baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
+ span = base->upCast();
+ angle = span->toAngle();
+ if (angle && angle->fCheckCoincidence) {
+ angle->checkNearCoincidence();
}
- } while (next != firstAngle);
- if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
- firstAngle = baseAngle;
- tryReverse = true;
+ } while ((base = span->next()));
+}
+
+// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
+bool SkOpSegment::clockwise(const SkOpSpanBase* start, const SkOpSpanBase* end, bool* swap) const {
+ SkASSERT(fVerb != SkPath::kLine_Verb);
+ SkPoint edge[4];
+ if (fVerb == SkPath::kCubic_Verb) {
+ double startT = start->t();
+ double endT = end->t();
+ bool flip = startT > endT;
+ SkDCubic cubic;
+ cubic.set(fPts);
+ double inflectionTs[2];
+ int inflections = cubic.findInflections(inflectionTs);
+ for (int index = 0; index < inflections; ++index) {
+ double inflectionT = inflectionTs[index];
+ if (between(startT, inflectionT, endT)) {
+ if (flip) {
+ if (inflectionT != endT) {
+ startT = inflectionT;
+ }
+ } else {
+ if (inflectionT != startT) {
+ endT = inflectionT;
+ }
+ }
+ }
+ }
+ SkDCubic part = cubic.subDivide(startT, endT);
+ for (int index = 0; index < 4; ++index) {
+ edge[index] = part[index].asSkPoint();
+ }
+ } else {
+ subDivide(start, end, edge);
}
- if (tryReverse) {
- baseAngle = NULL;
- SkOpAngle* prior = firstAngle;
- do {
- angle = prior;
- prior = angle->previous();
- SkASSERT(prior->next() == angle);
- next = angle->next();
- if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
- baseAngle = NULL;
- continue;
- }
- int testWinding = angle->segment()->windSum(angle);
- if (SK_MinS32 != testWinding) {
- baseAngle = angle;
- continue;
- }
- if (baseAngle) {
- ComputeOneSumReverse(baseAngle, angle, includeType);
- baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
- }
- } while (prior != firstAngle);
+ 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);
+ }
}
- int minIndex = SkMin32(startIndex, endIndex);
- return windSum(minIndex);
+ 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 sum <= 0;
}
void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
@@ -1954,7 +540,7 @@
}
SkOpSegment* nextSegment = nextAngle->segment();
int maxWinding, sumWinding;
- SkOpSpan* last;
+ SkOpSpanBase* last;
if (binary) {
int oppMaxWinding, oppSumWinding;
nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
@@ -1983,7 +569,7 @@
}
SkOpSegment* nextSegment = nextAngle->segment();
int maxWinding, sumWinding;
- SkOpSpan* last;
+ SkOpSpanBase* last;
if (binary) {
int oppMaxWinding, oppSumWinding;
nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
@@ -1998,64 +584,98 @@
nextAngle->setLastMarked(last);
}
-bool SkOpSegment::containsPt(const SkPoint& pt, int index, int endIndex) const {
- int step = index < endIndex ? 1 : -1;
+// at this point, the span is already ordered, or unorderable
+int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
+ SkOpAngle::IncludeType includeType) {
+ SkASSERT(includeType != SkOpAngle::kUnaryXor);
+ SkOpAngle* firstAngle = this->spanToAngle(end, start);
+ if (NULL == firstAngle || NULL == firstAngle->next()) {
+ return SK_NaN32;
+ }
+ // if all angles have a computed winding,
+ // or if no adjacent angles are orderable,
+ // or if adjacent orderable angles have no computed winding,
+ // there's nothing to do
+ // if two orderable angles are adjacent, and both are next to orderable angles,
+ // and one has winding computed, transfer to the other
+ SkOpAngle* baseAngle = NULL;
+ bool tryReverse = false;
+ // look for counterclockwise transfers
+ SkOpAngle* angle = firstAngle->previous();
+ SkOpAngle* next = angle->next();
+ firstAngle = next;
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;
+ SkOpAngle* prior = angle;
+ angle = next;
+ next = angle->next();
+ SkASSERT(prior->next() == angle);
+ SkASSERT(angle->next() == next);
+ if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
+ baseAngle = NULL;
+ continue;
}
- 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;
+ int testWinding = angle->starter()->windSum();
+ if (SK_MinS32 != testWinding) {
+ baseAngle = angle;
+ tryReverse = true;
+ continue;
}
- if (t == span.fT) {
- if (other != span.fOther) {
+ if (baseAngle) {
+ ComputeOneSum(baseAngle, angle, includeType);
+ baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
+ }
+ } while (next != firstAngle);
+ if (baseAngle && SK_MinS32 == firstAngle->starter()->windSum()) {
+ firstAngle = baseAngle;
+ tryReverse = true;
+ }
+ if (tryReverse) {
+ baseAngle = NULL;
+ SkOpAngle* prior = firstAngle;
+ do {
+ angle = prior;
+ prior = angle->previous();
+ SkASSERT(prior->next() == angle);
+ next = angle->next();
+ if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
+ baseAngle = NULL;
continue;
}
- if (other->fVerb != SkPath::kCubic_Verb) {
- return true;
+ int testWinding = angle->starter()->windSum();
+ if (SK_MinS32 != testWinding) {
+ baseAngle = angle;
+ continue;
}
- if (!other->fLoop) {
- return true;
+ if (baseAngle) {
+ ComputeOneSumReverse(baseAngle, angle, includeType);
+ baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
}
- double otherMidT = (otherT + span.fOtherT) / 2;
- SkPoint otherPt = other->ptAtT(otherMidT);
- return SkDPoint::ApproximatelyEqual(span.fPt, otherPt);
- }
+ } while (prior != firstAngle);
}
- 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 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 @@
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;
+ SkASSERT(bestT < 1);
+ SkOpSpanBase* testTSpanBase = &this->fHead;
+ SkOpSpanBase* nextTSpan;
+ double endT = 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)) {
+ nextTSpan = testTSpanBase->upCast()->next();
+ endT = nextTSpan->t();
+ if (endT >= bestT) {
+ break;
+ }
+ testTSpanBase = nextTSpan;
+ } while (testTSpanBase);
+ SkOpSpan* bestTSpan = NULL;
+ SkOpSpan* testTSpan = testTSpanBase->upCast();
+ if (!testTSpan->isCanceled()) {
*hitT = bestT;
- bestTIndex = start;
+ bestTSpan = testTSpan;
*hitSomething = true;
}
- return bestTIndex;
+ return bestTSpan;
}
-bool SkOpSegment::decrementSpan(SkOpSpan* span) {
- SkASSERT(span->fWindValue > 0);
- if (--(span->fWindValue) == 0) {
- if (!span->fOppValue && !span->fDone) {
- span->fDone = true;
- ++fDoneSpans;
- return true;
- }
+void SkOpSegment::detach(const SkOpSpan* span) {
+ if (span->done()) {
+ --this->fDoneCount;
}
- return false;
+ --this->fCount;
}
-bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
- SkASSERT(!span->fDone || span->fTiny || span->fSmall);
- span->fWindValue += windDelta;
- SkASSERT(span->fWindValue >= 0);
- span->fOppValue += oppDelta;
- SkASSERT(span->fOppValue >= 0);
- if (fXor) {
- span->fWindValue &= 1;
- }
- if (fOppXor) {
- span->fOppValue &= 1;
- }
- if (!span->fWindValue && !span->fOppValue) {
- if (!span->fDone) {
- span->fDone = true;
- ++fDoneSpans;
- }
- return true;
- }
- return false;
-}
-
-const SkOpSpan& SkOpSegment::firstSpan(const SkOpSpan& thisSpan) const {
- const SkOpSpan* firstSpan = &thisSpan; // rewind to the start
- const SkOpSpan* beginSpan = fTs.begin();
- const SkPoint& testPt = thisSpan.fPt;
- while (firstSpan > beginSpan && firstSpan[-1].fPt == testPt) {
- --firstSpan;
- }
- return *firstSpan;
-}
-
-const SkOpSpan& SkOpSegment::lastSpan(const SkOpSpan& thisSpan) const {
- const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
- const SkOpSpan* lastSpan = &thisSpan; // find the end
- const SkPoint& testPt = thisSpan.fPt;
- while (lastSpan < endSpan && lastSpan[1].fPt == testPt) {
- ++lastSpan;
- }
- return *lastSpan;
-}
-
-// with a loop, the comparison is move involved
-// scan backwards and forwards to count all matching points
-// (verify that there are twp scans marked as loops)
-// compare that against 2 matching scans for loop plus other results
-bool SkOpSegment::calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts) {
- const SkOpSpan& firstSpan = this->firstSpan(thisSpan); // rewind to the start
- const SkOpSpan& lastSpan = this->lastSpan(thisSpan); // find the end
- double firstLoopT = -1, lastLoopT = -1;
- const SkOpSpan* testSpan = &firstSpan - 1;
- while (++testSpan <= &lastSpan) {
- if (testSpan->fLoop) {
- firstLoopT = testSpan->fT;
- break;
- }
- }
- testSpan = &lastSpan + 1;
- while (--testSpan >= &firstSpan) {
- if (testSpan->fLoop) {
- lastLoopT = testSpan->fT;
- break;
- }
- }
- SkASSERT((firstLoopT == -1) == (lastLoopT == -1));
- if (firstLoopT == -1) {
- return false;
- }
- SkASSERT(firstLoopT < lastLoopT);
- testSpan = &firstSpan - 1;
- smallCounts[0] = smallCounts[1] = 0;
- while (++testSpan <= &lastSpan) {
- SkASSERT(approximately_equal(testSpan->fT, firstLoopT) +
- approximately_equal(testSpan->fT, lastLoopT) == 1);
- smallCounts[approximately_equal(testSpan->fT, lastLoopT)]++;
- }
- return true;
-}
-
-double SkOpSegment::calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
- double hiEnd, const SkOpSegment* other, int thisStart) {
- if (max >= hiEnd) {
- return -1;
- }
- int end = findOtherT(hiEnd, ref);
- if (end < 0) {
- return -1;
- }
- double tHi = span(end).fT;
- double tLo, refLo;
- if (thisStart >= 0) {
- tLo = span(thisStart).fT;
- refLo = min;
- } else {
- int start1 = findOtherT(loEnd, ref);
- SkASSERT(start1 >= 0);
- tLo = span(start1).fT;
- refLo = loEnd;
- }
- double missingT = (max - refLo) / (hiEnd - refLo);
- missingT = tLo + missingT * (tHi - tLo);
- return missingT;
-}
-
-double SkOpSegment::calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
- double hiEnd, const SkOpSegment* other, int thisEnd) {
- if (min <= loEnd) {
- return -1;
- }
- int start = findOtherT(loEnd, ref);
- if (start < 0) {
- return -1;
- }
- double tLo = span(start).fT;
- double tHi, refHi;
- if (thisEnd >= 0) {
- tHi = span(thisEnd).fT;
- refHi = max;
- } else {
- int end1 = findOtherT(hiEnd, ref);
- if (end1 < 0) {
- return -1;
- }
- tHi = span(end1).fT;
- refHi = hiEnd;
- }
- double missingT = (min - loEnd) / (refHi - loEnd);
- missingT = tLo + missingT * (tHi - tLo);
- return missingT;
-}
-
-// see if spans with two or more intersections have the same number on the other end
-void SkOpSegment::checkDuplicates() {
- debugValidate();
- SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
- int index;
- int endIndex = 0;
- bool endFound;
- do {
- index = endIndex;
- endIndex = nextExactSpan(index, 1);
- if ((endFound = endIndex < 0)) {
- endIndex = count();
- }
- int dupCount = endIndex - index;
- if (dupCount < 2) {
+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;
}
- 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);
+ double testDistSq = testPt.distanceSquared(i.pt(index));
+ if (closestDistSq > testDistSq) {
+ closestDistSq = testDistSq;
}
}
- for (index = 0; index < missingCount; ++index) {
- MissingSpan& missing = missingSpans[index];
- missing.fSegment->fixOtherTIndex();
- missing.fOther->fixOtherTIndex();
- }
- for (index = 0; index < missingCoincidence.count(); ++index) {
- MissingSpan& missing = missingCoincidence[index];
- missing.fSegment->fixOtherTIndex();
- }
- debugValidate();
-}
-
-// look to see if the curve end intersects an intermediary that intersects the other
-bool SkOpSegment::checkEnds() {
- debugValidate();
- SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
- int count = fTs.count();
- for (int index = 0; index < count; ++index) {
- const SkOpSpan& span = fTs[index];
- double otherT = span.fOtherT;
- if (otherT != 0 && otherT != 1) { // only check ends
- continue;
- }
- const SkOpSegment* other = span.fOther;
- // peek start/last describe the range of spans that match the other t of this span
- int peekStart = span.fOtherIndex;
- while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT)
- ;
- int otherCount = other->fTs.count();
- int peekLast = span.fOtherIndex;
- while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT)
- ;
- if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do
- continue;
- }
- // t start/last describe the range of spans that match the t of this span
- double t = span.fT;
- double tBottom = -1;
- int tStart = -1;
- int tLast = count;
- bool lastSmall = false;
- double afterT = t;
- for (int inner = 0; inner < count; ++inner) {
- double innerT = fTs[inner].fT;
- if (innerT <= t && innerT > tBottom) {
- if (innerT < t || !lastSmall) {
- tStart = inner - 1;
- }
- tBottom = innerT;
- }
- if (innerT > afterT) {
- if (t == afterT && lastSmall) {
- afterT = innerT;
- } else {
- tLast = inner;
- break;
- }
- }
- lastSmall = innerT <= t ? fTs[inner].fSmall : false;
- }
- for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) {
- if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span
- continue;
- }
- const SkOpSpan& peekSpan = other->fTs[peekIndex];
- SkOpSegment* match = peekSpan.fOther;
- if (match->done()) {
- continue; // if the edge has already been eaten (likely coincidence), ignore it
- }
- const double matchT = peekSpan.fOtherT;
- // see if any of the spans match the other spans
- for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
- const SkOpSpan& tSpan = fTs[tIndex];
- if (tSpan.fOther == match) {
- if (tSpan.fOtherT == matchT) {
- goto nextPeekIndex;
- }
- double midT = (tSpan.fOtherT + matchT) / 2;
- if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
- goto nextPeekIndex;
- }
- }
- }
- if (missingSpans.count() > 0) {
- const MissingSpan& lastMissing = missingSpans.back();
- if (lastMissing.fT == t
- && lastMissing.fOther == match
- && lastMissing.fOtherT == matchT) {
- SkASSERT(SkDPoint::ApproximatelyEqual(lastMissing.fPt, peekSpan.fPt));
- continue;
- }
- }
- if (this == match) {
- return false; // extremely large paths can trigger this
- }
-#if DEBUG_CHECK_ALIGN
- SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n",
- __FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY);
-#endif
- // this segment is missing a entry that the other contains
- // remember so we can add the missing one and recompute the indices
- {
- MissingSpan& missing = missingSpans.push_back();
- SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
- missing.fT = t;
- SkASSERT(this != match);
- missing.fOther = match;
- missing.fOtherT = matchT;
- missing.fPt = peekSpan.fPt;
- }
- break;
-nextPeekIndex:
- ;
- }
- }
- if (missingSpans.count() == 0) {
- debugValidate();
- return true;
- }
- debugValidate();
- int missingCount = missingSpans.count();
- for (int index = 0; index < missingCount; ++index) {
- MissingSpan& missing = missingSpans[index];
- if (this != missing.fOther) {
- addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
- }
- }
- fixOtherTIndex();
- // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
- // avoid this
- for (int index = 0; index < missingCount; ++index) {
- missingSpans[index].fOther->fixOtherTIndex();
- }
- debugValidate();
- return true;
-}
-
-void SkOpSegment::checkLinks(const SkOpSpan* base,
- SkTArray<MissingSpan, true>* missingSpans) const {
- const SkOpSpan* first = fTs.begin();
- const SkOpSpan* last = fTs.end() - 1;
- SkASSERT(base >= first && last >= base);
- const SkOpSegment* other = base->fOther;
- const SkOpSpan* oFirst = other->fTs.begin();
- const SkOpSpan* oLast = other->fTs.end() - 1;
- const SkOpSpan* oSpan = &other->fTs[base->fOtherIndex];
- const SkOpSpan* test = base;
- const SkOpSpan* missing = NULL;
- while (test > first && (--test)->fPt == base->fPt) {
- if (this == test->fOther) {
- continue;
- }
- CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
- }
- test = base;
- while (test < last && (++test)->fPt == base->fPt) {
- SkASSERT(this != test->fOther || test->fLoop);
- CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
- }
-}
-
-// see if spans with two or more intersections all agree on common t and point values
-void SkOpSegment::checkMultiples() {
- debugValidate();
- int index;
- int end = 0;
- while (fTs[++end].fT == 0)
- ;
- while (fTs[end].fT < 1) {
- int start = index = end;
- end = nextExactSpan(index, 1);
- if (end <= index) {
- return; // buffer overflow example triggers this
- }
- if (index + 1 == end) {
- continue;
- }
- // force the duplicates to agree on t and pt if not on the end
- SkOpSpan& span = fTs[index];
- double thisT = span.fT;
- const SkPoint& thisPt = span.fPt;
- span.fMultiple = true;
- bool aligned = false;
- while (++index < end) {
- aligned |= alignSpan(index, thisT, thisPt);
- }
- if (aligned) {
- alignSpanState(start, end);
- }
- fMultiples = true;
- }
- debugValidate();
-}
-
-void SkOpSegment::CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
- const SkOpSpan* oFirst, const SkOpSpan* oLast, const SkOpSpan** missingPtr,
- SkTArray<MissingSpan, true>* missingSpans) {
- SkASSERT(oSpan->fPt == test->fPt);
- const SkOpSpan* oTest = oSpan;
- while (oTest > oFirst && (--oTest)->fPt == test->fPt) {
- if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
- return;
- }
- }
- oTest = oSpan;
- while (oTest < oLast && (++oTest)->fPt == test->fPt) {
- if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
- return;
- }
- }
- if (*missingPtr) {
- missingSpans->push_back();
- }
- MissingSpan& lastMissing = missingSpans->back();
- if (*missingPtr) {
- lastMissing = missingSpans->end()[-2];
- }
- *missingPtr = test;
- lastMissing.fOther = test->fOther;
- lastMissing.fOtherT = test->fOtherT;
-}
-
-bool SkOpSegment::checkSmall(int index) const {
- if (fTs[index].fSmall) {
- return true;
- }
- double tBase = fTs[index].fT;
- while (index > 0 && precisely_negative(tBase - fTs[--index].fT))
- ;
- return fTs[index].fSmall;
-}
-
-// a pair of curves may turn into coincident lines -- small may be a hint that that happened
-// if a cubic contains a loop, the counts must be adjusted
-void SkOpSegment::checkSmall() {
- SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
- const SkOpSpan* beginSpan = fTs.begin();
- const SkOpSpan* thisSpan = beginSpan - 1;
- const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
- while (++thisSpan < endSpan) {
- if (!thisSpan->fSmall) {
- continue;
- }
- if (!thisSpan->fWindValue) {
- continue;
- }
- const SkOpSpan& firstSpan = this->firstSpan(*thisSpan);
- const SkOpSpan& lastSpan = this->lastSpan(*thisSpan);
- const SkOpSpan* nextSpan = &firstSpan + 1;
- ptrdiff_t smallCount = &lastSpan - &firstSpan + 1;
- SkASSERT(1 <= smallCount && smallCount < count());
- if (smallCount <= 1 && !nextSpan->fSmall) {
- SkASSERT(1 == smallCount);
- checkSmallCoincidence(firstSpan, NULL);
- continue;
- }
- // at this point, check for missing computed intersections
- const SkPoint& testPt = firstSpan.fPt;
- thisSpan = &firstSpan - 1;
- SkOpSegment* other = NULL;
- while (++thisSpan <= &lastSpan) {
- other = thisSpan->fOther;
- if (other != this) {
- break;
- }
- }
- SkASSERT(other != this);
- int oIndex = thisSpan->fOtherIndex;
- const SkOpSpan& oSpan = other->span(oIndex);
- const SkOpSpan& oFirstSpan = other->firstSpan(oSpan);
- const SkOpSpan& oLastSpan = other->lastSpan(oSpan);
- ptrdiff_t oCount = &oLastSpan - &oFirstSpan + 1;
- if (fLoop) {
- int smallCounts[2];
- SkASSERT(!other->fLoop); // FIXME: we need more complicated logic for pair of loops
- if (calcLoopSpanCount(*thisSpan, smallCounts)) {
- if (smallCounts[0] && oCount != smallCounts[0]) {
- SkASSERT(0); // FIXME: need a working test case to properly code & debug
- }
- if (smallCounts[1] && oCount != smallCounts[1]) {
- SkASSERT(0); // FIXME: need a working test case to properly code & debug
- }
- goto nextSmallCheck;
- }
- }
- if (other->fLoop) {
- int otherCounts[2];
- if (other->calcLoopSpanCount(other->span(oIndex), otherCounts)) {
- if (otherCounts[0] && otherCounts[0] != smallCount) {
- SkASSERT(0); // FIXME: need a working test case to properly code & debug
- }
- if (otherCounts[1] && otherCounts[1] != smallCount) {
- SkASSERT(0); // FIXME: need a working test case to properly code & debug
- }
- goto nextSmallCheck;
- }
- }
- if (oCount != smallCount) { // check if number of pts in this match other
- MissingSpan& missing = missingSpans.push_back();
- missing.fOther = NULL;
- SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
- missing.fPt = testPt;
- const SkOpSpan& oSpan = other->span(oIndex);
- if (oCount > smallCount) {
- missing.fSegment = this;
- missing.fT = thisSpan->fT;
- other->checkLinks(&oSpan, &missingSpans);
- } else {
- missing.fSegment = other;
- missing.fT = oSpan.fT;
- checkLinks(thisSpan, &missingSpans);
- }
- if (!missingSpans.back().fOther || missing.fSegment->done()) {
- missingSpans.pop_back();
- }
- }
-nextSmallCheck:
- thisSpan = &lastSpan;
- }
- int missingCount = missingSpans.count();
- for (int index = 0; index < missingCount; ++index) {
- MissingSpan& missing = missingSpans[index];
- SkOpSegment* missingOther = missing.fOther;
- // note that add t pair may edit span arrays, so prior pointers to spans are no longer valid
- if (!missing.fSegment->addTPair(missing.fT, missingOther, missing.fOtherT, false,
- missing.fPt)) {
- continue;
- }
- int otherTIndex = missingOther->findT(missing.fOtherT, missing.fPt, missing.fSegment);
- const SkOpSpan& otherSpan = missingOther->span(otherTIndex);
- if (otherSpan.fSmall) {
- const SkOpSpan* nextSpan = &otherSpan;
- if (nextSpan->fPt == missing.fPt) {
- continue;
- }
- do {
- ++nextSpan;
- } while (nextSpan->fSmall);
- if (nextSpan->fT == 1) {
- continue;
- }
- SkAssertResult(missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt,
- nextSpan->fT, missingOther));
- } else if (otherSpan.fT > 0) {
- const SkOpSpan* priorSpan = &otherSpan;
- do {
- --priorSpan;
- } while (priorSpan->fT == otherSpan.fT);
- if (priorSpan->fSmall) {
- missing.fSegment->addTCancel(missing.fPt, priorSpan->fPt, missingOther);
- }
- }
- }
- // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
- // avoid this
- for (int index = 0; index < missingCount; ++index) {
- MissingSpan& missing = missingSpans[index];
- missing.fSegment->fixOtherTIndex();
- missing.fOther->fixOtherTIndex();
- }
- debugValidate();
-}
-
-void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span,
- SkTArray<MissingSpan, true>* checkMultiple) {
- SkASSERT(span.fSmall);
- if (0 && !span.fWindValue) {
- return;
- }
- SkASSERT(&span < fTs.end() - 1);
- const SkOpSpan* next = &span + 1;
- SkASSERT(!next->fSmall || checkMultiple);
- if (checkMultiple) {
- while (next->fSmall) {
- ++next;
- SkASSERT(next < fTs.end());
- }
- }
- SkOpSegment* other = span.fOther;
- while (other != next->fOther) {
- if (!checkMultiple) {
- return;
- }
- const SkOpSpan* test = next + 1;
- if (test == fTs.end()) {
- return;
- }
- if (test->fPt != next->fPt || !precisely_equal(test->fT, next->fT)) {
- return;
- }
- next = test;
- }
- SkASSERT(span.fT < next->fT);
- int oStartIndex = other->findExactT(span.fOtherT, this);
- int oEndIndex = other->findExactT(next->fOtherT, this);
- // FIXME: be overly conservative by limiting this to the caller that allows multiple smalls
- if (!checkMultiple || fVerb != SkPath::kLine_Verb || other->fVerb != SkPath::kLine_Verb) {
- SkPoint mid = ptAtT((span.fT + next->fT) / 2);
- const SkOpSpan& oSpanStart = other->fTs[oStartIndex];
- const SkOpSpan& oSpanEnd = other->fTs[oEndIndex];
- SkPoint oMid = other->ptAtT((oSpanStart.fT + oSpanEnd.fT) / 2);
- if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
- return;
- }
- }
- // FIXME: again, be overly conservative to avoid breaking existing tests
- const SkOpSpan& oSpan = oStartIndex < oEndIndex ? other->fTs[oStartIndex]
- : other->fTs[oEndIndex];
- if (checkMultiple && !oSpan.fSmall) {
- return;
- }
-// SkASSERT(oSpan.fSmall);
- if (oStartIndex < oEndIndex) {
- SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, other));
- } else {
- addTCancel(span.fPt, next->fPt, other);
- }
- if (!checkMultiple) {
- return;
- }
- // check to see if either segment is coincident with a third segment -- if it is, and if
- // the opposite segment is not already coincident with the third, make it so
- // OPTIMIZE: to make this check easier, add coincident and cancel could set a coincident bit
- if (span.fWindValue != 1 || span.fOppValue != 0) {
-// start here;
- // iterate through the spans, looking for the third coincident case
- // if we find one, we need to return state to the caller so that the indices can be fixed
- // this also suggests that all of this function is fragile since it relies on a valid index
- }
- // probably should make this a common function rather than copy/paste code
- if (oSpan.fWindValue != 1 || oSpan.fOppValue != 0) {
- const SkOpSpan* oTest = &oSpan;
- while (--oTest >= other->fTs.begin()) {
- if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
- break;
- }
- SkOpSegment* testOther = oTest->fOther;
- SkASSERT(testOther != this);
- // look in both directions to see if there is a coincident span
- const SkOpSpan* tTest = testOther->fTs.begin();
- for (int testIndex = 0; testIndex < testOther->count(); ++testIndex) {
- if (tTest->fPt != span.fPt) {
- ++tTest;
- continue;
- }
- if (testOther->verb() != SkPath::kLine_Verb
- || other->verb() != SkPath::kLine_Verb) {
- SkPoint mid = ptAtT((span.fT + next->fT) / 2);
- SkPoint oMid = other->ptAtT((oTest->fOtherT + tTest->fT) / 2);
- if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
- continue;
- }
- }
-#if DEBUG_CONCIDENT
- SkDebugf("%s coincident found=%d %1.9g %1.9g\n", __FUNCTION__, testOther->fID,
- oTest->fOtherT, tTest->fT);
-#endif
- if (tTest->fT < oTest->fOtherT) {
- SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, testOther));
- } else {
- addTCancel(span.fPt, next->fPt, testOther);
- }
- MissingSpan missing;
- missing.fSegment = testOther;
- checkMultiple->push_back(missing);
- break;
- }
- }
- oTest = &oSpan;
- while (++oTest < other->fTs.end()) {
- if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
- break;
- }
-
- }
- }
-}
-
-// 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();
- }
-}
-
-bool SkOpSegment::coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const {
- int count = this->count();
- for (int index = 0; index < count; ++index) {
- const SkOpSpan& span = this->span(index);
- if (span.fOther != other) {
- continue;
- }
- if (span.fPt == pt) {
- continue;
- }
- if (!AlmostEqualUlps(span.fPt, pt)) {
- continue;
- }
- if (fVerb != SkPath::kCubic_Verb) {
- return true;
- }
- double tInterval = t - span.fT;
- double tMid = t - tInterval / 2;
- SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
- return midPt.approximatelyEqual(xyAtT(t));
- }
- return 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 @@
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) {
+ SkOpSpan* startSpan = start->starter(end);
+ if (startSpan->done()) {
return NULL;
}
- markDoneBinary(min);
- double startT = other->fTs[*nextStart].fT;
- *nextEnd = *nextStart;
- do {
- *nextEnd += step;
- } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
- SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
- if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
- *unsortable = true;
- return NULL;
- }
+ markDone(startSpan);
+ *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
return other;
}
- 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 @@
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 @@
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 @@
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) {
+ SkOpSpan* startSpan = start->starter(end);
+ if (startSpan->done()) {
return NULL;
}
- markDoneUnary(min);
- double startT = other->fTs[*nextStart].fT;
- *nextEnd = *nextStart;
- do {
- *nextEnd += step;
- } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
- SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
- if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
- *unsortable = true;
- return NULL;
- }
+ markDone(startSpan);
+ *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
return other;
}
- 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 = spanToAngle(end, startIndex);
+ 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();
#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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
*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 @@
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;
+SkOpGlobalState* SkOpSegment::globalState() const {
+ return contour()->globalState();
}
-// 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);
-}
-
-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;
+ 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(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
- int local = spanSign(start, end);
+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 @@
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 @@
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 @@
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
+bool SkOpSegment::isXor() const {
+ return fContour->isXor();
+}
-SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
- int step = SkSign32(endIndex - index);
- int min = SkMin32(index, endIndex);
- markDoneBinary(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->markDoneBinary(min);
+ other->markDone(minSpan);
}
return last;
}
-SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
- int step = SkSign32(endIndex - index);
- int min = SkMin32(index, endIndex);
- markDoneUnary(min);
- 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->done()) {
+ while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
+ if (spanStart->windSum() != SK_MinS32) {
+ SkASSERT(spanStart->windSum() == winding);
SkASSERT(!last);
break;
}
- other->markDoneUnary(min);
- }
- 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;
- 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);
- SkASSERT(!last);
- break;
- }
- (void) other->markWinding(min, winding);
+ (void) other->markWinding(spanStart, winding);
}
if (lastPtr) {
*lastPtr = last;
@@ -3864,37 +1305,32 @@
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 @@
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 @@
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());
+void SkOpSegment::markDone(SkOpSpan* span) {
+ SkASSERT(this == span->segment());
+ if (span->done()) {
+ return;
+ }
+#if DEBUG_MARK_DONE
+ debugShowNewWinding(__FUNCTION__, span, span->windSum(), span->oppSum());
+#endif
+ span->setDone(true);
+ ++fDoneCount;
+ debugValidate();
+}
+
+bool SkOpSegment::markWinding(SkOpSpan* span, int winding) {
+ SkASSERT(this == span->segment());
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) {
- return;
- }
- SkASSERT(!span->fDone);
- span->fDone = true;
- ++fDoneSpans;
-}
-
-void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
- SkOpSpan* span = verifyOneWindingU(funName, tIndex);
- if (!span) {
- return;
- }
- if (span->fWindSum == SK_MinS32) {
- SkDebugf("%s uncomputed\n", __FUNCTION__);
- }
- SkASSERT(!span->fDone);
- span->fDone = true;
- ++fDoneSpans;
-}
-
-bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding, SkOpSpan** lastPtr) {
- SkOpSpan* span = &fTs[tIndex];
- if (lastPtr) {
- *lastPtr = span;
- }
- if (span->fDone && !span->fSmall) {
+ if (span->done()) {
return false;
}
#if DEBUG_MARK_DONE
- debugShowNewWinding(funName, *span, winding);
+ debugShowNewWinding(__FUNCTION__, span, winding);
#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;
+ 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) {
- return false;
- }
-#if DEBUG_MARK_DONE
- debugShowNewWinding(funName, *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;
- 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;
- }
- }
- }
- 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 (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 sum <= 0;
-}
-
-bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
- SkASSERT(fVerb != SkPath::kLine_Verb);
- if (fVerb == SkPath::kQuad_Verb) {
- SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
- return dst.monotonicInY();
- }
- SkASSERT(fVerb == SkPath::kCubic_Verb);
- SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
- 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());
+bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
+ SkASSERT(this == span->segment());
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);
+ if (span->done()) {
+ return false;
}
- do {
- success |= markOneWinding(__FUNCTION__, index, winding, oppWinding, NULL);
- } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+#if DEBUG_MARK_DONE
+ debugShowNewWinding(__FUNCTION__, span, winding, oppWinding);
+#endif
+ span->setWindSum(winding);
+ span->setOppSum(oppWinding);
debugValidate();
- return success;
-}
-
-void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
- int nextDoorWind = SK_MaxS32;
- int nextOppWind = SK_MaxS32;
- // prefer exact matches
- if (tIndex > 0) {
- const SkOpSpan& below = fTs[tIndex - 1];
- if (below.fT == t) {
- nextDoorWind = below.fWindValue;
- nextOppWind = below.fOppValue;
- }
- }
- if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
- const SkOpSpan& above = fTs[tIndex + 1];
- if (above.fT == t) {
- nextDoorWind = above.fWindValue;
- nextOppWind = above.fOppValue;
- }
- }
- if (nextDoorWind == SK_MaxS32 && tIndex > 0) {
- const SkOpSpan& below = fTs[tIndex - 1];
- if (approximately_negative(t - below.fT)) {
- nextDoorWind = below.fWindValue;
- nextOppWind = below.fOppValue;
- }
- }
- if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
- const SkOpSpan& above = fTs[tIndex + 1];
- if (approximately_negative(above.fT - t)) {
- nextDoorWind = above.fWindValue;
- nextOppWind = above.fOppValue;
- }
- }
- if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
- const SkOpSpan& below = fTs[tIndex - 1];
- nextDoorWind = below.fWindValue;
- nextOppWind = below.fOppValue;
- }
- if (nextDoorWind != SK_MaxS32) {
- SkOpSpan& newSpan = fTs[tIndex];
- newSpan.fWindValue = nextDoorWind;
- newSpan.fOppValue = nextOppWind;
- if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
- newSpan.fDone = true;
- ++fDoneSpans;
- }
- }
-}
-
-bool SkOpSegment::nextCandidate(int* start, int* end) const {
- while (fTs[*end].fDone) {
- if (fTs[*end].fT == 1) {
- return false;
- }
- ++(*end);
- }
- *start = *end;
- *end = nextExactSpan(*start, 1);
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
+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 (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
+ return false;
+ }
+ return !ptsDisjoint(base->fT, base->fPt, testT, testPt);
+}
+
+static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
+ if (last) {
+ *last = endSpan;
}
return NULL;
}
-SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr,
- SkOpSpan** last) const {
- int origIndex = *indexPtr;
+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, start->t(), end->t());
+ return dst.monotonicInY();
+ }
+ SkASSERT(fVerb == SkPath::kCubic_Verb);
+ SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
+ return dst.monotonicInY();
+}
+
+bool SkOpSegment::NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start,
+ SkOpSpanBase** end) {
+ while (span->final() || span->upCast()->done()) {
+ if (span->final()) {
+ return false;
+ }
+ span = span->upCast()->next();
+ }
+ *start = span;
+ *end = span->upCast()->next();
+ return true;
+}
+
+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 @@
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++;
- }
- const SkOpSpan& fromSpan = fTs[from];
- int count = fTs.count();
- while (++to < count) {
- const SkOpSpan& span = fTs[to];
- if (precisely_negative(span.fT - fromSpan.fT)) {
+ if (opp->verb() == SkPath::kLine_Verb) {
continue;
}
- return to;
- }
- }
- return -1;
-}
-
-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::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;
+ 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;
}
- } else if (span.fPt == p2) {
- p2IndexMax = index;
+ if (span->containsCoinEnd(opp)) {
+ continue;
+ }
+ // if already visited and visited again, check for coin
+ if (span == &fHead) {
+ continue;
+ }
+ SkOpPtT* priorPtT = NULL, * priorStopPtT;
+ // find prior span containing opp segment
+ SkOpSegment* priorOpp = NULL;
+ prior = span;
+ while (!priorOpp && (prior = prior->prev())) {
+ priorStopPtT = priorPtT = prior->ptT();
+ while ((priorPtT = priorPtT->next()) != priorStopPtT) {
+ SkOpSegment* segment = priorPtT->span()->segment();
+ if (segment == opp) {
+ priorOpp = opp;
+ break;
+ }
+ }
+ }
+ if (!priorOpp) {
+ continue;
+ }
+ SkOpPtT* oppStart = prior->ptT();
+ SkOpPtT* oppEnd = span->ptT();
+ bool swapped = priorPtT->fT > ptT->fT;
+ if (swapped) {
+ SkTSwap(priorPtT, ptT);
+ SkTSwap(oppStart, oppEnd);
+ }
+ bool flipped = oppStart->fT > oppEnd->fT;
+ bool coincident;
+ if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
+ goto swapBack;
+ }
+ {
+ // average t, find mid pt
+ double midT = (prior->t() + span->t()) / 2;
+ SkPoint midPt = this->ptAtT(midT);
+ coincident = true;
+ // if the mid pt is not near either end pt, project perpendicular through opp seg
+ if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt)
+ && !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) {
+ coincident = false;
+ SkIntersections i;
+ int ptCount = SkPathOpsVerbToPoints(this->verb());
+ SkVector dxdy = (*CurveSlopeAtT[ptCount])(pts(), midT);
+ SkDLine ray = {{{midPt.fX, midPt.fY},
+ {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}};
+ int oppPtCount = SkPathOpsVerbToPoints(opp->verb());
+ (*CurveIntersectRay[oppPtCount])(opp->pts(), ray, &i);
+ // measure distance and see if it's small enough to denote coincidence
+ for (int index = 0; index < i.used(); ++index) {
+ SkDPoint oppPt = i.pt(index);
+ if (oppPt.approximatelyEqual(midPt)) {
+ SkVector oppDxdy = (*CurveSlopeAtT[oppPtCount])(opp->pts(),
+ i[index][0]);
+ oppDxdy.normalize();
+ dxdy.normalize();
+ SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON);
+ coincident |= flatness < 5000; // FIXME: replace with tuned value
+ }
+ }
+ }
+ }
+ if (coincident) {
+ // mark coincidence
+ coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator);
+ clear_visited(&fHead);
+ missingCoincidence(coincidences, allocator);
+ return;
+ }
+ swapBack:
+ if (swapped) {
+ SkTSwap(priorPtT, ptT);
+ }
}
- }
- return p1IndexMin > p2IndexMax;
+ } while ((span = span->next()->upCastable()));
+ clear_visited(&fHead);
}
-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;
+// 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;
+ }
}
- }
+ 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::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);
+bool SkOpSegment::operand() const {
+ return fContour->operand();
+}
+
+bool SkOpSegment::oppXor() const {
+ return fContour->oppXor();
+}
+
+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(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 @@
*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 @@
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 @@
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 @@
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(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(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 @@
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::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 b4da929..c1c6e69 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -9,160 +9,262 @@
#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);
+
+ 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);
+ }
+
+ 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;
+ }
+
+ void addLine(SkPoint pts[2], SkOpContour* parent) {
+ init(pts, parent, SkPath::kLine_Verb);
+ fBounds.set(pts, 2);
+ }
+
+ 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;
+ }
+
+ void addQuad(SkPoint pts[3], SkOpContour* parent) {
+ init(pts, parent, SkPath::kQuad_Verb);
+ fBounds.setQuadBounds(pts);
+ }
+
+ 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;
}
- // 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 bumpCount() {
+ ++fCount;
+ }
+
+ 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;
}
int count() const {
- return fTs.count();
+ return fCount;
}
+ 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);
+ }
+
+#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(fDoneSpans <= fTs.count());
- return fDoneSpans == fTs.count();
- }
-
- bool done(int min) const {
- return fTs[min].fDone;
+ SkASSERT(fDoneCount <= fCount);
+ return fDoneCount == fCount;
}
bool done(const SkOpAngle* angle) const {
- return done(SkMin32(angle->start(), angle->end()));
+ return angle->start()->starter(angle->end())->done();
}
SkDPoint dPtAtT(double mid) const {
return (*CurveDPointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
}
- SkVector dxdy(int index) const {
- return (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[index].fT);
+ SkDVector dSlopeAtT(double mid) const {
+ return (*CurveDSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
}
- SkScalar dy(int index) const {
- return dxdy(index).fY;
+ 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;
}
- bool hasMultiples() const {
- return fMultiples;
+ SkOpSpan* head() {
+ return &fHead;
}
- bool hasSmall() const {
- return fSmall;
+ 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 hasTiny() const {
- return fTiny;
- }
-
- bool intersected() const {
- return fTs.count() > 0;
- }
-
- bool isCanceled(int tIndex) const {
- return fTs[tIndex].fWindValue == 0 && fTs[tIndex].fOppValue == 0;
- }
-
- bool isConnected(int startIndex, int endIndex) const {
- return fTs[startIndex].fWindSum != SK_MinS32 || fTs[endIndex].fWindSum != SK_MinS32;
- }
+ bool isClose(double t, const SkOpSegment* opp) const;
bool isHorizontal() const {
return fBounds.fTop == fBounds.fBottom;
}
+ SkOpSegment* isSimple(SkOpSpanBase** end, int* step) {
+ return nextChase(end, step, NULL, NULL);
+ }
+
bool isVertical() const {
return fBounds.fLeft == fBounds.fRight;
}
- bool isVertical(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;
+
+ const SkPoint& lastPt() const {
+ return fPts[SkPathOpsVerbToPoints(fVerb)];
}
- int oppSign(const SkOpAngle* angle) const {
- SkASSERT(angle->segment() == this);
- return oppSign(angle->start(), angle->end());
+ SkOpSpanBase* markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end);
+ bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
+ SkOpSpanBase** lastPtr);
+ bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
+ int oppWinding, SkOpSpanBase** lastPtr);
+ SkOpSpanBase* markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle);
+ SkOpSpanBase* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
+ const SkOpAngle* angle);
+ void markDone(SkOpSpan* );
+ bool markWinding(SkOpSpan* , int winding);
+ bool markWinding(SkOpSpan* , int winding, int oppWinding);
+ bool match(const SkOpPtT* span, const SkOpSegment* parent, double t, const SkPoint& pt) const;
+ void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
+ bool monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
+ bool moveNearby();
+
+ SkOpSegment* next() const {
+ return fNext;
}
- 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
+ static bool NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start, SkOpSpanBase** end);
+ SkOpSegment* nextChase(SkOpSpanBase** , int* step, SkOpSpan** , SkOpSpanBase** last) const;
+ bool operand() const;
+
+ static int OppSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
+ int result = start->t() < end->t() ? -start->upCast()->oppValue()
+ : end->upCast()->oppValue();
return result;
}
- int oppSum(int tIndex) const {
- return fTs[tIndex].fOppSum;
- }
+ bool oppXor() const;
- int oppSum(const SkOpAngle* angle) const {
- int lesser = SkMin32(angle->start(), angle->end());
- return fTs[lesser].fOppSum;
+ const SkOpSegment* prev() const {
+ return fPrev;
}
- int oppValue(int tIndex) const {
- return fTs[tIndex].fOppValue;
- }
-
- int oppValue(const SkOpAngle* angle) const {
- int lesser = SkMin32(angle->start(), angle->end());
- return fTs[lesser].fOppValue;
- }
-
-#if DEBUG_VALIDATE
- bool oppXor() const {
- return fOppXor;
- }
-#endif
-
SkPoint ptAtT(double mid) const {
return (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
}
@@ -171,399 +273,113 @@
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);
+ bool ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const;
+
+ void resetVisited() {
+ fVisited = false;
+ }
+
+ void setContour(SkOpContour* contour) {
+ fContour = contour;
+ }
+
+ void setNext(SkOpSegment* next) {
+ fNext = next;
+ }
+
+ void setPrev(SkOpSegment* prev) {
+ fPrev = prev;
+ }
+
+ bool setVisited() {
+ if (fVisited) {
+ return false;
+ }
+ return (fVisited = true);
+ }
+
+ void setUpWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* maxWinding, int* sumWinding) {
+ int deltaSum = SpanSign(start, end);
*maxWinding = *sumWinding;
*sumWinding -= deltaSum;
}
- const SkOpSpan& span(int tIndex) const {
- return fTs[tIndex];
- }
+ 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();
- const SkOpAngle* spanToAngle(int tStart, int tEnd) const {
- SkASSERT(tStart != tEnd);
- const SkOpSpan& span = fTs[tStart];
- return tStart < tEnd ? span.fToAngle : span.fFromAngle;
- }
-
- // 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);
- }
-
- int spanSign(const SkOpAngle* angle) const {
- SkASSERT(angle->segment() == this);
- return spanSign(angle->start(), angle->end());
- }
-
- 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
+ static int SpanSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
+ int result = start->t() < end->t() ? -start->upCast()->windValue()
+ : end->upCast()->windValue();
return result;
}
- double t(int tIndex) const {
- return fTs[tIndex].fT;
+ SkOpAngle* spanToAngle(SkOpSpanBase* start, SkOpSpanBase* end) {
+ SkASSERT(start != end);
+ return start->t() < end->t() ? start->upCast()->toAngle() : start->fromAngle();
}
- double tAtMid(int start, int end, double mid) const {
- return fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
+ 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;
+
+ const SkOpSpanBase* tail() const {
+ return &fTail;
}
- void updatePts(const SkPoint pts[]) {
- fPts = pts;
+ SkOpSpanBase* tail() {
+ return &fTail;
}
+ static double TAtMid(const SkOpSpanBase* start, const SkOpSpanBase* end, double mid) {
+ return start->t() * (1 - mid) + end->t() * mid;
+ }
+
+ void undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end);
+ int updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
+ int updateOppWinding(const SkOpAngle* angle) const;
+ int updateOppWindingReverse(const SkOpAngle* angle) const;
+ int updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
+ int updateWinding(const SkOpAngle* angle) const;
+ int updateWindingReverse(const SkOpAngle* angle) const;
+
+ static bool UseInnerWinding(int outerWinding, int innerWinding);
+
SkPath::Verb verb() const {
return fVerb;
}
- int windSum(int tIndex) const {
- return fTs[tIndex].fWindSum;
- }
-
- int windValue(int tIndex) const {
- return fTs[tIndex].fWindValue;
- }
-
-#if defined(SK_DEBUG) || DEBUG_WINDING
- SkScalar xAtT(int index) const {
- return xAtT(&fTs[index]);
- }
-#endif
-
-#if DEBUG_VALIDATE
- bool _xor() const { // FIXME: used only by SkOpAngle::debugValidateLoop()
- return fXor;
- }
-#endif
-
- const SkPoint& xyAtT(const SkOpSpan* span) const {
- return span->fPt;
- }
-
- const SkPoint& xyAtT(int index) const {
- return xyAtT(&fTs[index]);
- }
-
-#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 windingAtT(double tHit, const SkOpSpan* span, 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;
+
+ SkPoint* writablePt(bool end) {
+ return &fPts[end ? SkPathOpsVerbToPoints(fVerb) : 0];
}
-#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;
- int updateOppWinding(const SkOpAngle* angle) const;
- int updateWinding(int index, int endIndex) const;
- int updateWinding(const SkOpAngle* angle) const;
- int updateWindingReverse(int index, int endIndex) const;
- SkOpSpan* verifyOneWinding(const char* funName, int tIndex);
- SkOpSpan* verifyOneWindingU(const char* funName, int tIndex);
-
- SkScalar xAtT(const SkOpSpan* span) const {
- return xyAtT(span).fX;
- }
-
- SkScalar yAtT(const SkOpSpan* span) const {
- return xyAtT(span).fY;
- }
-
- void zeroSpan(SkOpSpan* span);
-
-#if DEBUG_SWAP_TOP
- bool controlsContainedByEnds(int tStart, int tEnd) const;
-#endif
- void debugAddAngle(int start, int end);
-#if DEBUG_CONCIDENT
- void debugAddTPair(double t, const SkOpSegment& other, double otherT) const;
-#endif
-#if DEBUG_ANGLE
- void debugCheckPointsEqualish(int tStart, int tEnd) const;
-#endif
-#if DEBUG_SWAP_TOP
- int debugInflections(int index, int endIndex) const;
-#endif
-#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
- void debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding);
- void debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding, int oppWinding);
-#endif
-#if DEBUG_WINDING
- static char as_digit(int value) {
- return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
- }
-#endif
- // available to testing only
- void debugConstruct();
- void debugConstructCubic(SkPoint shortQuad[4]);
- void debugConstructLine(SkPoint shortQuad[2]);
- void debugConstructQuad(SkPoint shortQuad[3]);
- void debugReset();
- void dumpDPts() const;
- void dumpHexPts() const;
- void dumpSpan(int index) const;
-
- const SkPoint* fPts;
- SkPathOpsBounds fBounds;
- // FIXME: can't convert to SkTArray because it uses insert
- SkTDArray<SkOpSpan> fTs; // 2+ (always includes t=0 t=1) -- at least (number of spans) + 1
- SkOpAngleSet fAngles; // empty or 2+ -- (number of non-zero spans) * 2
- // OPTIMIZATION: could pack donespans, verb, operand, xor into 1 int-sized value
- int fDoneSpans; // quick check that segment is finished
- // OPTIMIZATION: force the following to be byte-sized
+ 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 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;
+ 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 0000000..37d5120
--- /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 d9ce44e..9e5939a 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 c80c12f..e8835f0 100644
--- a/src/pathops/SkOpTAllocator.h
+++ b/src/pathops/SkOpTAllocator.h
@@ -19,6 +19,12 @@
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 1a5bfc1..b0fd822 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 @@
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 @@
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 @@
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 @@
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;
+ if (winding == SK_MinS32) {
+ continue;
}
- // 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)
+ 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 @@
}
#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 @@
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 @@
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 @@
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 @@
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 @@
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, ¤t, indexPtr, endIndexPtr);
+ SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
+ SkASSERT(current == (*startPtr)->segment());
+ skipVertical(contourList, ¤t, 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, ¤t, indexPtr, endIndexPtr, &tHit,
+ contourWinding = rightAngleWinding(contourList, ¤t, 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 @@
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 @@
if (angleIncludeType < SkOpAngle::kBinarySingle) {
break;
}
- oppContourWinding = rightAngleWinding(contourList, ¤t, indexPtr, endIndexPtr, &tHit,
+ oppContourWinding = rightAngleWinding(contourList, ¤t, 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 @@
reassemble contour pieces into new path
*/
void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
+ SkOpContour contour;
+ SkOpGlobalState globalState(NULL PATH_OPS_DEBUG_PARAMS(&contour));
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("%s\n", __FUNCTION__);
#endif
- 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();
+ SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
+ SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
+ builder.finish(&allocator);
+ SkTDArray<const SkOpContour* > runs; // indices of partial contours
+ const SkOpContour* eContour = builder.head();
+ do {
+ if (!eContour->count()) {
+ continue;
+ }
+ const SkPoint& eStart = eContour->start();
+ const SkPoint& eEnd = eContour->end();
#if DEBUG_ASSEMBLE
SkDebugf("%s contour", __FUNCTION__);
if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
@@ -578,44 +467,42 @@
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 @@
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 @@
#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
- fixOtherTIndex(contourList);
- if (!checkEnds(contourList)) { // check if connecting curve intersected at the same end
+ coincidence->addMissing(allocator);
+#if DEBUG_VALIDATE
+ globalState->setPhase(SkOpGlobalState::kWalking);
+#endif
+ coincidence->expand(); // check to see if, loosely, coincident ranges may be expanded
+ coincidence->mark(); // mark spans of coincident segments as coincident
+ missingCoincidence(contourList, coincidence, allocator); // look for coincidence missed earlier
+ if (!coincidence->apply()) { // adjust the winding value to account for coincident edges
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 0d8cfc4..1bf1791 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 9d70d58..d4a5898 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 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 @@
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 @@
*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 @@
&& 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 @@
&& 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 @@
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::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 1037cae..9932e1d 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 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 @@
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 @@
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 @@
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 @@
}
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 d763444..0000000
--- 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 7db93f5..0331f34 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 @@
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 @@
SkDebugf("%d", wind);
}
}
+#endif // defined SK_DEBUG || !FORCE_RELEASE
+
#if DEBUG_SHOW_TEST_NAME
void* SkPathOpsDebug::CreateNameStr() {
@@ -97,11 +106,160 @@
}
#endif
-#endif // defined SK_DEBUG || !FORCE_RELEASE
-
#include "SkOpAngle.h"
#include "SkOpSegment.h"
+#if DEBUG_SWAP_TOP
+int SkOpSegment::debugInflections(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
+ if (fVerb != SkPath::kCubic_Verb) {
+ return false;
+ }
+ SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
+ double inflections[2];
+ return dst.findInflections(inflections);
+}
+#endif
+
+SkOpAngle* SkOpSegment::debugLastAngle() {
+ SkOpAngle* result = NULL;
+ SkOpSpan* span = this->head();
+ do {
+ if (span->toAngle()) {
+ SkASSERT(!result);
+ result = span->toAngle();
+ }
+ } while ((span = span->next()->upCastable()));
+ SkASSERT(result);
+ return result;
+}
+
+void SkOpSegment::debugReset() {
+ this->init(this->fPts, this->contour(), this->verb());
+}
+
+#if DEBUG_ACTIVE_SPANS
+void SkOpSegment::debugShowActiveSpans() const {
+ debugValidate();
+ if (done()) {
+ return;
+ }
+ int lastId = -1;
+ double lastT = -1;
+ const SkOpSpan* span = &fHead;
+ do {
+ if (span->done()) {
+ continue;
+ }
+ if (lastId == fID && lastT == span->t()) {
+ continue;
+ }
+ lastId = fID;
+ 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 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", span->windSum());
+ }
+ SkDebugf(" windValue=%d oppValue=%d", span->windValue(), span->oppValue());
+ SkDebugf("\n");
+ } while ((span = span->next()->upCastable()));
+}
+#endif
+
+#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);
+ }
+ 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", winding);
+ }
+ 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,
+ int oppWinding) {
+ 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);
+ }
+ 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->oppSum());
+ }
+ SkDebugf(" windSum=");
+ if (span->windSum() == SK_MinS32) {
+ SkDebugf("?");
+ } else {
+ SkDebugf("%d", span->windSum());
+ }
+ SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
+}
+
+#endif
+
+#if DEBUG_ANGLE
+SkString SkOpAngle::debugPart() const {
+ SkString result;
+ switch (this->segment()->verb()) {
+ case SkPath::kLine_Verb:
+ result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart),
+ this->segment()->debugID());
+ break;
+ case SkPath::kQuad_Verb:
+ result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart),
+ this->segment()->debugID());
+ break;
+ case SkPath::kCubic_Verb:
+ result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fCurvePart),
+ this->segment()->debugID());
+ break;
+ default:
+ SkASSERT(0);
+ }
+ return result;
+}
+#endif
+
#if DEBUG_SORT
void SkOpAngle::debugLoop() const {
const SkOpAngle* first = this;
@@ -111,25 +269,59 @@
SkDebugf("\n");
next = next->fNext;
} while (next && next != first);
+ next = first;
+ do {
+ next->debugValidate();
+ 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
-
+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);
@@ -145,422 +337,137 @@
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 {
- 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);
- 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) {
- SkASSERT(!result);
- result = span.fToAngle;
- }
- }
- 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");
-}
-#endif
-
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
-void SkOpSegment::debugShowActiveSpans() const {
- debugValidate();
- if (done()) {
- return;
- }
-#if DEBUG_ACTIVE_SPANS_SHORT_FORM
- int lastId = -1;
- double lastT = -1;
-#endif
- for (int i = 0; i < fTs.count(); ++i) {
- if (fTs[i].fDone) {
- continue;
- }
- SK_ALWAYSBREAK(i < fTs.count() - 1);
-#if DEBUG_ACTIVE_SPANS_SHORT_FORM
- if (lastId == fID && lastT == fTs[i].fT) {
- continue;
- }
- lastId = fID;
- lastT = fTs[i].fT;
-#endif
- SkDebugf("%s id=%d", __FUNCTION__, fID);
- SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
- for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
- SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
- }
- const SkOpSpan* span = &fTs[i];
- SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span));
- int iEnd = i + 1;
- while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) {
- ++iEnd;
- }
- SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT);
- const SkOpSegment* other = fTs[i].fOther;
- SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
- other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
- if (fTs[i].fWindSum == SK_MinS32) {
- SkDebugf("?");
- } else {
- SkDebugf("%d", fTs[i].fWindSum);
- }
- SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
- }
-}
-#endif
-
-#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
-void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) {
- const SkPoint& pt = xyAtT(&span);
- SkDebugf("%s id=%d", fun, fID);
- SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
- for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
- SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
- }
- SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
- fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
- SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d windSum=",
- span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
- (&span)[1].fT, winding);
- if (span.fWindSum == SK_MinS32) {
- SkDebugf("?");
- } else {
- SkDebugf("%d", span.fWindSum);
- }
- SkDebugf(" windValue=%d\n", span.fWindValue);
-}
-
-void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding,
- int oppWinding) {
- const SkPoint& pt = xyAtT(&span);
- SkDebugf("%s id=%d", fun, fID);
- SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
- for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
- SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
- }
- SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
- fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
- SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=",
- span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
- (&span)[1].fT, winding, oppWinding);
- if (span.fOppSum == SK_MinS32) {
- SkDebugf("?");
- } else {
- SkDebugf("%d", span.fOppSum);
- }
- SkDebugf(" windSum=");
- if (span.fWindSum == SK_MinS32) {
- SkDebugf("?");
- } else {
- SkDebugf("%d", span.fWindSum);
- }
- SkDebugf(" windValue=%d oppValue=%d\n", span.fWindValue, span.fOppValue);
-}
-#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;
-}
-#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
-// 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 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;
+ }
}
}
- last = &span;
- hasLoop |= last->fLoop;
+ ++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());
}
- SK_ALWAYSBREAK(done == fDoneSpans);
-// if (fAngles.count() ) {
-// fAngles.begin()->debugValidateLoop();
-// }
+ if (fFromAngle) {
+ fFromAngle->debugValidate();
+ }
+ if (!this->final() && this->upCast()->toAngle()) {
+ this->upCast()->toAngle()->debugValidate();
+ }
+#endif
+}
+
+bool SkOpSpan::debugCoinLoopCheck() const {
+ int loop = 0;
+ const SkOpSpan* next = this;
+ SkOpSpan* nextCoin;
+ do {
+ nextCoin = next->fCoincident;
+ SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
+ for (int check = 1; check < loop - 1; ++check) {
+ const SkOpSpan* checkCoin = this->fCoincident;
+ const SkOpSpan* innerCoin = checkCoin;
+ for (int inner = check + 1; inner < loop; ++inner) {
+ innerCoin = innerCoin->fCoincident;
+ if (checkCoin == innerCoin) {
+ SkDebugf("*** bad coincident loop ***\n");
+ return false;
+ }
+ }
+ }
+ ++loop;
+ } while ((next = nextCoin) && next != this);
+ return true;
+}
+
+#include "SkOpContour.h"
+
+int SkOpPtT::debugLoopLimit(bool report) const {
+ int loop = 0;
+ const SkOpPtT* next = this;
+ do {
+ for (int check = 1; check < loop - 1; ++check) {
+ const SkOpPtT* checkPtT = this->fNext;
+ const SkOpPtT* innerPtT = checkPtT;
+ for (int inner = check + 1; inner < loop; ++inner) {
+ innerPtT = innerPtT->fNext;
+ if (checkPtT == innerPtT) {
+ if (report) {
+ SkDebugf("*** bad ptT loop ***\n");
+ }
+ return loop;
+ }
+ }
+ }
+ ++loop;
+ } while ((next = next->fNext) && next != this);
+ return 0;
+}
+
+void SkOpPtT::debugValidate() const {
+#if DEBUG_VALIDATE
+ if (contour()->globalState()->phase() == SkOpGlobalState::kIntersecting) {
+ return;
+ }
+ 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 5770aef..72a9ea5 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 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_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 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 @@
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 @@
#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);
+template<typename TCurve> class SkTSect;
+template<typename TCurve> class SkTSpan;
-void Dump(const SkTDArray<SkOpSpan* >& chase);
-void Dump(const SkTDArray<SkOpSpan* >* chase);
+struct SkDQuad;
+struct SkDCubic;
-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);
+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);
-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);
+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 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);
+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 e4fc97b..70f2e12 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 @@
// 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 @@
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 74eb615..bb25162 100644
--- a/src/pathops/SkPathOpsLine.h
+++ b/src/pathops/SkPathOpsLine.h
@@ -20,27 +20,20 @@
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 f2b25c0..77ae2de 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 @@
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 @@
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 @@
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 @@
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 @@
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
@@ -291,16 +255,19 @@
dump_path(file, two, false, true);
fprintf(file, " SkPath path2(path);\n");
fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
- fprintf(file, "}\n");
+ fprintf(file, "}\n");
fclose(file);
}
#endif
bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
+ SkOpContour contour;
+ SkOpCoincidence coincidence;
+ SkOpGlobalState globalState(&coincidence PATH_OPS_DEBUG_PARAMS(&contour));
#if DEBUGGING_PATHOPS_FROM_HOST
dump_op(one, two, op);
-#endif
-#if DEBUG_SHOW_TEST_NAME
+#endif
+#if 0 && DEBUG_SHOW_TEST_NAME
char* debugName = DEBUG_FILENAME_STRING;
if (debugName && debugName[0]) {
SkPathOpsDebug::BumpTestName(debugName);
@@ -321,53 +288,54 @@
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);
+ SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tune
+ 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 7ddfbfb..2d07427 100644
--- a/src/pathops/SkPathOpsPoint.h
+++ b/src/pathops/SkPathOpsPoint.h
@@ -25,21 +25,25 @@
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 @@
return v;
}
+ // only used by testing
double cross(const SkDVector& a) const {
return fX * a.fY - fY * a.fX;
}
@@ -98,11 +103,13 @@
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 @@
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 @@
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?
+ return AlmostPequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
}
- static bool RoughlyEqual(const SkPoint& a, const SkPoint& b) {
- if (approximately_equal(a.fX, b.fX) && approximately_equal(a.fY, b.fY)) {
- return true;
- }
- return RoughlyEqualUlps(a.fX, b.fX) && RoughlyEqualUlps(a.fY, b.fY);
- }
-
- bool approximatelyPEqual(const SkDPoint& a) const {
- if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
- return true;
- }
- if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
- return false;
- }
- double dist = distance(a); // OPTIMIZATION: can we compare against distSq instead ?
- double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
- double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
- largest = SkTMax(largest, -tiniest);
- return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
- }
-
- bool approximatelyDEqual(const SkDPoint& a) const {
- if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
- return true;
- }
- if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
- return false;
- }
- double dist = distance(a); // OPTIMIZATION: can we compare against distSq instead ?
- double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
- double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
- largest = SkTMax(largest, -tiniest);
- return AlmostDequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
- }
-
+ // only used by testing
bool approximatelyZero() const {
return approximately_zero(fX) && approximately_zero(fY);
}
@@ -209,7 +182,7 @@
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 @@
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
old mode 100644
new mode 100755
index 15a1900..eb2d1ab
--- a/src/pathops/SkPathOpsPostSect.cpp
+++ b/src/pathops/SkPathOpsPostSect.cpp
@@ -17,8 +17,8 @@
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 @@
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 @@
do {
SkOpPtT* next = prev->fNext;
if (next == this) {
- prev->removeNext();
+ prev->removeNext(this);
fDeleted = true;
return prev;
}
@@ -55,14 +55,14 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
return false;
}
-void SkOpSpan::detach() {
+void SkOpSpan::detach(SkOpPtT* kept) {
SkASSERT(!final());
SkOpSpan* prev = this->prev();
SkASSERT(prev);
@@ -313,6 +313,9 @@
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 c1d068a..4913c9f 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 @@
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 @@
// 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 @@
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 @@
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 932c5fb..81638cf 100644
--- a/src/pathops/SkPathOpsQuad.h
+++ b/src/pathops/SkPathOpsQuad.h
@@ -17,43 +17,61 @@
};
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 @@
// 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 57f1aa0..0000000
--- 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 2ceed32..5dd3d8d 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::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 @@
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 2c47f43..2b37a5f 100644
--- a/src/pathops/SkPathOpsRect.h
+++ b/src/pathops/SkPathOpsRect.h
@@ -13,18 +13,10 @@
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 @@
&& 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 @@
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 57090ac..7a234ec 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 @@
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 @@
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 @@
}
// 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 @@
}
#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 @@
// FIXME : add this as a member of SkPath
bool Simplify(const SkPath& path, SkPath* result) {
-#if DEBUG_SORT || DEBUG_SWAP_TOP
- SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
-#endif
// returns 1 for evenodd, -1 for winding, regardless of inverse-ness
SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
: SkPath::kEvenOdd_FillType;
-
+ if (path.isConvex()) {
+ if (result != &path) {
+ *result = path;
+ }
+ result->setFillType(fillType);
+ return true;
+ }
// turn path into list of segments
- 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
+ SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
+ 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 0b3ddd7..10a84a3 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(§1, §2, this);
return used();
}
diff --git a/src/pathops/SkPathOpsTQuadSect.cpp b/src/pathops/SkPathOpsTQuadSect.cpp
index 46ce5cf..06b5f2f 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(§1, §2, this);
return used();
}
diff --git a/src/pathops/SkPathOpsTSect.h b/src/pathops/SkPathOpsTSect.h
index 4e7d3b17..5c76da7 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;
}
@@ -54,41 +64,73 @@
template<typename TCurve>
class SkTSpan {
public:
- void init(const TCurve& );
- void initBounds(const TCurve& );
-
+ void addBounded(SkTSpan* );
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.count() > 0;
+ }
+
+ 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();
}
+ void resetBounds(const TCurve& curve) {
+ fIsLinear = fIsLine = false;
+ initBounds(curve);
+ }
+
bool split(SkTSpan* work) {
return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
}
@@ -99,29 +141,23 @@
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;
@@ -136,23 +172,33 @@
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 +209,72 @@
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);
+ 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);
- 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);
- void recoverCollapsed();
- void removeSpan(SkTSpan<TCurve>* span);
- void removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span);
- void removeSpans(SkTSpan<TCurve>* span, SkTSect* opp);
- void setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
- const SkTSpan<TCurve>* tail() const;
- void trim(SkTSpan<TCurve>* span, SkTSect* opp);
+ bool coincidentHasT(double t);
+ void computePerpendiculars(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
+ int countConsecutiveSpans(SkTSpan<TCurve>* first, SkTSpan<TCurve>** last) const;
-#if DEBUG_T_SECT
- int debugID() const { return fDebugID; }
+ int debugID() const {
+ return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1);
+ }
+
+ void deleteEmptySpans();
+ void dumpCommon(const SkTSpan<TCurve>* ) const;
+ void dumpCommonCurves(const SkTSpan<TCurve>* ) const;
+ static int EndsEqual(const SkTSect* sect1, const SkTSect* sect2, SkIntersections* );
+ SkTSpan<TCurve>* extractCoincident(SkTSect* sect2, SkTSpan<TCurve>* first,
+ SkTSpan<TCurve>* last);
+ SkTSpan<TCurve>* findCoincidentRun(SkTSpan<TCurve>* first, SkTSpan<TCurve>** lastPtr,
+ const SkTSect* sect2);
+ int intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
+ SkTSpan<TCurve>* oppSpan, int* oppResult) const;
+ int linesIntersect(const SkTSpan<TCurve>* span, const SkTSect* opp,
+ const SkTSpan<TCurve>* oppSpan, SkIntersections* ) const;
+ void markSpanGone(SkTSpan<TCurve>* span);
+ bool matchedDirection(double t, const SkTSect* sect2, double t2) const;
+ void matchedDirCheck(double t, const SkTSect* sect2, double t2,
+ bool* calcMatched, bool* oppMatched) const;
+ void mergeCoincidence(SkTSect* sect2);
+ SkTSpan<TCurve>* prev(SkTSpan<TCurve>* ) const;
+ void removeByPerpendicular(SkTSect* opp);
+ void recoverCollapsed();
+ void removeCoincident(SkTSpan<TCurve>* span, bool isBetween);
+ void removeAllBut(const SkTSpan<TCurve>* keep, SkTSpan<TCurve>* span, SkTSect* opp);
+ void removeSpan(SkTSpan<TCurve>* span);
+ void removeSpanRange(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
+ void removeSpans(SkTSpan<TCurve>* span, SkTSect* opp);
+ SkTSpan<TCurve>* spanAtT(double t, SkTSpan<TCurve>** priorSpan);
+ SkTSpan<TCurve>* tail();
+ void trim(SkTSpan<TCurve>* span, SkTSect* opp);
+ void unlinkSpan(SkTSpan<TCurve>* span);
+ bool updateBounded(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last, SkTSpan<TCurve>* oppFirst);
void validate() const;
-#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 +290,8 @@
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 +305,10 @@
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,29 +318,55 @@
}
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) {
+ if (this->findOppSpan(span)) {
+ return;
+ }
+ fBounded.push_back() = span;
}
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 fBounded.push_back span=%d opp=%d\n", __FUNCTION__, priorSpan->debugID(),
+ opp->debugID());
+#endif
+ opp->fBounded.push_back(span);
+ span->fBounded.push_back(opp);
+ }
+ this->validate();
+ span->validatePerpT(t);
}
template<typename TCurve>
@@ -279,55 +391,143 @@
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>
+bool SkTSpan<TCurve>::contains(double t) const {
+ const SkTSpan* work = this;
+ do {
+ if (between(work->fStartT, t, work->fEndT)) {
+ return true;
+ }
+ } while ((work = work->fNext));
return false;
}
template<typename TCurve>
-SkTSpan<TCurve>* SkTSpan<TCurve>::innerFind(double t) {
- SkTSpan* work = this;
- do {
- if (between(work->fStartT, t, work->fEndT)) {
- return work;
+const SkTSect<TCurve>* SkTSpan<TCurve>::debugOpp() const {
+ return PATH_OPS_DEBUG_RELEASE(fDebugSect->debugOpp(), NULL);
+}
+
+template<typename TCurve>
+SkTSpan<TCurve>* SkTSpan<TCurve>::findOppSpan(const SkTSpan* opp) const {
+ int count = fBounded.count();
+ for (int index = 0; index < count; ++index) {
+ SkTSpan* test = fBounded[index];
+ if (opp == test) {
+ return test;
}
- } while ((work = work->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 *check;
+ return -1;
}
template<typename TCurve>
-bool SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const {
+void SkTSpan<TCurve>::init(const TCurve& c) {
+ fPrev = fNext = NULL;
+ fStartT = 0;
+ fEndT = 1;
+ resetBounds(c);
+}
+
+template<typename TCurve>
+void SkTSpan<TCurve>::initBounds(const TCurve& c) {
+ fPart = c.subDivide(fStartT, fEndT);
+ fBounds.setBounds(fPart);
+ fCoinStart.init();
+ fCoinEnd.init();
+ fBoundsMax = SkTMax(fBounds.width(), fBounds.height());
+ fCollapsed = fPart.collapsed();
+ fHasPerp = false;
+ fDeleted = false;
+#if DEBUG_T_SECT
+ if (fCollapsed) {
+ SkDebugf(""); // for convenient breakpoints
+ }
+#endif
+}
+
+template<typename TCurve>
+bool SkTSpan<TCurve>::linearsIntersect(SkTSpan* span) {
+ int result = this->linearIntersects(span->fPart);
+ if (result <= 1) {
+ return SkToBool(result);
+ }
+ SkASSERT(span->fIsLinear);
+ result = span->linearIntersects(this->fPart);
+// SkASSERT(result <= 1);
+ return SkToBool(result);
+}
+
+template<typename TCurve>
+double SkTSpan<TCurve>::linearT(const SkDPoint& pt) const {
+ SkDVector len = fPart[TCurve::kPointLast] - fPart[0];
+ return fabs(len.fX) > fabs(len.fY)
+ ? (pt.fX - fPart[0].fX) / len.fX
+ : (pt.fY - fPart[0].fY) / len.fY;
+}
+
+template<typename TCurve>
+int SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const {
// 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,20 +547,116 @@
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 {
+ int count = fBounded.count();
+ for (int index = 0; index < count; ++index) {
+ SkTSpan* test = fBounded[index];
+ if (between(test->fStartT, t, test->fEndT)) {
+ return test;
+ }
+ }
+ return NULL;
+}
+
+template<typename TCurve>
+bool SkTSpan<TCurve>::removeAllBounded() {
+ bool deleteSpan = false;
+ int count = fBounded.count();
+ for (int index = 0; index < count; ++index) {
+ SkTSpan* opp = fBounded[index];
+ deleteSpan |= opp->removeBounded(this);
+ }
+ return deleteSpan;
+}
+
+template<typename TCurve>
+bool SkTSpan<TCurve>::removeBounded(const SkTSpan* opp) {
+ int count = fBounded.count();
+ if (fHasPerp) {
+ bool foundStart = false;
+ bool foundEnd = false;
+ for (int index = 0; index < count; ++index) {
+ const SkTSpan* test = fBounded[index];
+ if (opp == test) {
+ continue;
+ }
+ foundStart |= between(test->fStartT, fCoinStart.perpT(), test->fEndT);
+ foundEnd |= between(test->fStartT, fCoinEnd.perpT(), test->fEndT);
+ }
+ if (!foundStart || !foundEnd) {
+ fHasPerp = false;
+ fCoinStart.init();
+ fCoinEnd.init();
+ }
+ }
+ for (int index = 0; index < count; ++index) {
+ if (opp == fBounded[index]) {
+ fBounded.removeShuffle(index);
+ SkASSERT((count == 1) == (fBounded.count() == 0));
+ return count == 1;
+ }
+ }
+ SkASSERT(0);
return false;
}
@@ -380,6 +676,8 @@
fPrev = work;
fNext = work->fNext;
fIsLinear = work->fIsLinear;
+ fIsLine = work->fIsLine;
+
work->fNext = this;
if (fNext) {
fNext->fPrev = this;
@@ -393,102 +691,74 @@
}
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;
- }
- } else {
- r2[1].fX -= (r2[1].fY - r1[0].fY) * ratio;
- if (TCurve::IsCubic()) {
- r2[2].fX -= (r2[2].fY - r1[0].fY) * ratio;
- }
- }
- SkDRect sBounds;
- sBounds.setBounds(r2);
- // see if tight bounds overlap
- if (skewToXAxis) {
- return bounds.fTop <= sBounds.fBottom && sBounds.fTop <= bounds.fBottom;
- } else {
- return bounds.fLeft <= sBounds.fRight && sBounds.fLeft <= bounds.fRight;
- }
-}
-
-#if DEBUG_T_SECT
-template<typename TCurve>
void SkTSpan<TCurve>::validate() const {
+#if DEBUG_T_SECT
SkASSERT(fNext == NULL || fNext != fPrev);
SkASSERT(fNext == NULL || this == fNext->fPrev);
- SkASSERT(fBounds.width() || fBounds.height());
+ 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(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));
+ this->validateBounded();
+ if (fHasPerp) {
+ if (fCoinStart.isCoincident()) {
+ validatePerpT(fCoinStart.perpT());
+ validatePerpPt(fCoinStart.perpT(), fCoinStart.perpPt());
+ }
+ if (fCoinEnd.isCoincident()) {
+ validatePerpT(fCoinEnd.perpT());
+ validatePerpPt(fCoinEnd.perpT(), fCoinEnd.perpPt());
+ }
}
-}
#endif
+}
template<typename TCurve>
-SkTSect<TCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id))
+void SkTSpan<TCurve>::validateBounded() const {
+#if DEBUG_VALIDATE
+ for (int index = 0; index < fBounded.count(); ++index) {
+ const SkTSpan* overlap = fBounded[index];
+ SkASSERT(!overlap->fDeleted);
+ SkASSERT(((this->debugID() ^ overlap->debugID()) & 1) == 1);
+ SkASSERT(overlap->findOppSpan(this));
+ }
+#endif
+}
+
+template<typename TCurve>
+void SkTSpan<TCurve>::validatePerpT(double oppT) const {
+#if DEBUG_VALIDATE
+ for (int index = 0; index < fBounded.count(); ++index) {
+ const SkTSpan* overlap = fBounded[index];
+ if (between(overlap->fStartT, oppT, overlap->fEndT)) {
+ return;
+ }
+ }
+ SkASSERT(0);
+#endif
+}
+
+template<typename TCurve>
+void SkTSpan<TCurve>::validatePerpPt(double t, const SkDPoint& pt) const {
+#if DEBUG_T_SECT
+ PATH_OPS_DEBUG_CODE(SkASSERT(fDebugSect->fOppSect->fCurve.ptAtT(t) == pt));
+#endif
+}
+
+
+template<typename TCurve>
+SkTSect<TCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_T_SECT_PARAMS(int id))
: 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);
@@ -508,22 +778,22 @@
#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 +811,11 @@
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 +847,472 @@
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;
- }
- if (!last->fCoinEnd.isCoincident()) {
- if (--consecutive < COINCIDENT_SPAN_COUNT) {
- continue;
- }
- last = last->fPrev;
- SkASSERT(last->fCoinStart.isCoincident());
- SkASSERT(last->fCoinEnd.isCoincident());
- }
- 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);
- }
- 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);
- }
- SkTSpan<TCurve>* removeMe = first->fNext;
- while (removeMe != last) {
- SkTSpan<TCurve>* removeNext = removeMe->fNext;
- removeSpans(removeMe, sect2);
- removeMe = removeNext;
- }
+ SkTSpan<TCurve>* coinStart = first;
+ do {
+ coinStart = this->extractCoincident(sect2, coinStart, last);
+ } while (coinStart && !last->fDeleted);
} while ((first = next));
}
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)) {
- return false;
+bool SkTSect<TCurve>::coincidentHasT(double t) {
+ SkTSpan<TCurve>* test = fCoincident;
+ while (test) {
+ if (between(test->fStartT, t, test->fEndT)) {
+ return true;
+ }
+ test = test->fNext;
}
- if (fActiveCount < COINCIDENT_SPAN_COUNT || opp->fActiveCount < COINCIDENT_SPAN_COUNT) {
- return true;
- }
- return span->tightBoundsIntersects(oppSpan);
+ return false;
}
template<typename TCurve>
-void SkTSect<TCurve>::onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
- SkTSpan<TCurve>* work = first;
- first = NULL;
+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->fCoinStart.isCoincident()) {
- if (!first) {
- first = work;
+ if (!work->fHasPerp && !work->fCollapsed) {
+ if (prior) {
+ work->fCoinStart = prior->fCoinEnd;
+ } else {
+ work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp);
}
- } else if (first) {
- break;
+ 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);
+ }
+ }
+ work->fHasPerp = true;
}
if (work == last) {
break;
}
+ 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;
+ }
+ if (next->fStartT > last->fEndT) {
+ break;
+ }
+ ++consecutive;
+ last = next;
+ } while (true);
+ *lastPtr = last;
+ return consecutive;
+}
+
+template<typename TCurve>
+bool SkTSect<TCurve>::debugHasBounded(const SkTSpan<TCurve>* span) const {
+ const SkTSpan<TCurve>* test = fHead;
+ if (!test) {
+ return false;
+ }
+ do {
+ if (test->findOppSpan(span)) {
+ return true;
+ }
+ } while ((test = test->next()));
+ return false;
+}
+
+template<typename TCurve>
+void SkTSect<TCurve>::deleteEmptySpans() {
+ SkTSpan<TCurve>* test;
+ SkTSpan<TCurve>* next = fHead;
+ while ((test = next)) {
+ next = test->fNext;
+ if (test->fBounded.count() == 0) {
+ this->removeSpan(test);
+ }
+ }
+}
+
+template<typename TCurve>
+SkTSpan<TCurve>* SkTSect<TCurve>::extractCoincident(SkTSect* sect2, SkTSpan<TCurve>* first,
+ SkTSpan<TCurve>* last) {
+ first = findCoincidentRun(first, &last, sect2);
if (!first) {
- return;
+ return NULL;
}
// march outwards to find limit of coincidence from here to previous and next spans
double startT = first->fStartT;
- double oppT;
+ double oppStartT;
+ double oppEndT SK_INIT_TO_AVOID_WARNING;
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);
+ 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 {
+ lastCandidate = NULL;
+ SkASSERT(!first);
+ }
+ if (work == *lastPtr) {
+ return first;
+ }
+ work = work->fNext;
+ SkASSERT(work);
+ } while (true);
+ if (lastCandidate) {
+ *lastPtr = lastCandidate;
+ }
+ return first;
+}
+
+template<typename TCurve>
+int SkTSect<TCurve>::intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
+ SkTSpan<TCurve>* oppSpan, int* oppResult) const {
+ bool spanStart, oppStart;
+ int hullResult = span->hullsIntersect(oppSpan, &spanStart, &oppStart);
+ if (hullResult >= 0) {
+ if (hullResult == 2) { // hulls have one point in common
+ if (span->fBounded.count() <= 1) {
+ SkASSERT(span->fBounded.count() == 0 || span->fBounded[0] == oppSpan);
+ if (spanStart) {
+ span->fEndT = span->fStartT;
} else {
- 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();
+ span->fStartT = span->fEndT;
+ }
+ } else {
+ hullResult = 1;
+ }
+ if (oppSpan->fBounded.count() <= 1) {
+ SkASSERT(span->fBounded.count() == 0 || oppSpan->fBounded[0] == span);
+ if (oppStart) {
+ oppSpan->fEndT = oppSpan->fStartT;
+ } else {
+ oppSpan->fStartT = oppSpan->fEndT;
+ }
+ *oppResult = 2;
+ } else {
+ *oppResult = 1;
+ }
+ } else {
+ *oppResult = 1;
+ }
+ return hullResult;
+ }
+ if (span->fIsLine && oppSpan->fIsLine) {
+ SkIntersections i;
+ int sects = this->linesIntersect(span, opp, oppSpan, &i);
+ if (!sects) {
+ return -1;
+ }
+ span->fStartT = span->fEndT = i[0][0];
+ oppSpan->fStartT = oppSpan->fEndT = i[1][0];
+ return *oppResult = 2;
+ }
+ if (span->fIsLinear || oppSpan->fIsLinear) {
+ return *oppResult = (int) span->linearsIntersect(oppSpan);
+ }
+ return *oppResult = 1;
+}
+
+// while the intersection points are sufficiently far apart:
+// construct the tangent lines from the intersections
+// find the point where the tangent line intersects the opposite curve
+template<typename TCurve>
+int SkTSect<TCurve>::linesIntersect(const SkTSpan<TCurve>* span, const SkTSect* opp,
+ const SkTSpan<TCurve>* oppSpan, SkIntersections* i) const {
+ SkIntersections thisRayI, oppRayI;
+ SkDLine thisLine = {{ span->fPart[0], span->fPart[TCurve::kPointLast] }};
+ SkDLine oppLine = {{ oppSpan->fPart[0], oppSpan->fPart[TCurve::kPointLast] }};
+ int loopCount = 0;
+ double bestDistSq = DBL_MAX;
+ do {
+ if (!thisRayI.intersectRay(opp->fCurve, thisLine)) {
+ return 0;
+ }
+ if (!oppRayI.intersectRay(this->fCurve, oppLine)) {
+ return 0;
+ }
+ // pick the closest pair of points
+ double closest = DBL_MAX;
+ int closeIndex SK_INIT_TO_AVOID_WARNING;
+ int oppCloseIndex SK_INIT_TO_AVOID_WARNING;
+ for (int index = 0; index < oppRayI.used(); ++index) {
+ if (!roughly_between(span->fStartT, oppRayI[0][index], span->fEndT)) {
+ continue;
+ }
+ for (int oIndex = 0; oIndex < thisRayI.used(); ++oIndex) {
+ if (!roughly_between(oppSpan->fStartT, thisRayI[0][oIndex], oppSpan->fEndT)) {
+ continue;
+ }
+ double distSq = thisRayI.pt(index).distanceSquared(oppRayI.pt(oIndex));
+ if (closest > distSq) {
+ closest = distSq;
+ closeIndex = index;
+ oppCloseIndex = oIndex;
}
}
}
- }
- if (!work->fCoinEnd.isCoincident()) {
- if (work->fEndT == 1) {
- SkDebugf("!");
+ if (closest == DBL_MAX) {
+ return 0;
}
-// 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);
- }
- }
+ 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 +1333,118 @@
}
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;
+void SkTSect<TCurve>::removeAllBut(const SkTSpan<TCurve>* keep, SkTSpan<TCurve>* span,
+ SkTSect* opp) {
+ int count = span->fBounded.count();
+ for (int index = 0; index < count; ++index) {
+ SkTSpan<TCurve>* bounded = span->fBounded[0];
+ if (bounded == keep) {
+ continue;
}
- } else {
- fHead = next;
- if (next) {
- next->fPrev = NULL;
+ if (bounded->fDeleted) { // may have been deleted when opp did 'remove all but'
+ continue;
+ }
+ SkAssertResult(SkDEBUGCODE(!) span->removeBounded(bounded));
+ if (bounded->removeBounded(span)) {
+ opp->removeSpan(bounded);
}
}
- --fActiveCount;
- span->fNext = fDeleted;
- fDeleted = span;
-#if DEBUG_T_SECT
- SkASSERT(!span->fDebugDeleted);
- span->fDebugDeleted = true;
-#endif
+ SkASSERT(!span->fDeleted);
+ SkASSERT(span->findOppSpan(keep));
+ SkASSERT(keep->findOppSpan(span));
}
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>::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
+ 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>::removeCoincident(SkTSpan<TCurve>* span, bool isBetween) {
+ this->unlinkSpan(span);
+ if (isBetween || between(0, span->fCoinStart.perpT(), 1)) {
+ --fActiveCount;
+ span->fNext = fCoincident;
+ fCoincident = span;
+ } else {
+ this->markSpanGone(span);
}
}
template<typename TCurve>
+void SkTSect<TCurve>::removeSpan(SkTSpan<TCurve>* span) {
+ this->unlinkSpan(span);
+ this->markSpanGone(span);
+}
+
+template<typename TCurve>
+void SkTSect<TCurve>::removeSpanRange(SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
+ if (first == last) {
+ return;
+ }
+ SkTSpan<TCurve>* span = first;
+ SkASSERT(span);
+ SkTSpan<TCurve>* final = last->fNext;
+ SkTSpan<TCurve>* next = span->fNext;
+ while ((span = next) && span != final) {
+ next = span->fNext;
+ this->markSpanGone(span);
+ }
+ if (final) {
+ final->fPrev = first;
+ }
+ first->fNext = final;
+}
+
+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);
+ if (span->removeBounded(bounded)) { // shuffles last into position 0
+ this->removeSpan(span);
+ }
+ if (bounded->removeBounded(span)) {
+ opp->removeSpan(bounded);
+ }
+ SkASSERT(!span->fDeleted || !opp->debugHasBounded(span));
+
}
}
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);
+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;
}
- do {
- if (!work->fHasPerp) {
- work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp);
- work->fHasPerp = true;
- }
- if (work == last) {
- break;
- }
- SkTSpan<TCurve>* last = work;
- work = work->fNext;
- SkASSERT(work);
- if (!work->fHasPerp) {
- work->fCoinStart = last->fCoinEnd;
- }
- } while (true);
+ *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 +1458,75 @@
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; ) {
+ for (int index = 0; index < span->fBounded.count(); ) {
SkTSpan<TCurve>* test = span->fBounded[index];
- bool sects = intersects(span, opp, test);
- if (sects) {
+ int oppSects, sects = this->intersects(span, opp, test, &oppSects);
+ if (sects >= 1) {
+ if (sects == 2) {
+ span->initBounds(fCurve);
+ this->removeAllBut(test, span, opp);
+ }
+ if (oppSects == 2) {
+ test->initBounds(opp->fCurve);
+ opp->removeAllBut(span, test, this);
+ }
++index;
} else {
- removeOne(test, span);
- opp->removeOne(span, test);
- --count;
+ if (span->removeBounded(test)) {
+ this->removeSpan(span);
+ }
+ if (test->removeBounded(span)) {
+ opp->removeSpan(test);
+ }
}
}
}
-#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.resize_back(1);
+ first->fBounded[0] = oppFirst;
+ // 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 +1538,81 @@
++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;
- // check for zero
- if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
+ if (sect1->fCurve[0] == 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->insert(0, 0, sect1->fCurve[0]);
+ }
+ if (sect1->fCurve[0] == 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]);
- }
- }
- // check for one
- if (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
+ intersections->insert(0, 1, sect1->fCurve[0]);
+ }
+ if (sect1->fCurve[TCurve::kPointLast] == 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->insert(1, 0, sect1->fCurve[TCurve::kPointLast]);
+ }
+ if (sect1->fCurve[TCurve::kPointLast] == 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]);
- }
+ }
+ // check for zero
+ if (!(zeroOneSet & (kZeroS1Set | kZeroS2Set))
+ && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
+ zeroOneSet |= kZeroS1Set | kZeroS2Set;
+ intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]);
+ }
+ if (!(zeroOneSet & (kZeroS1Set | kOneS2Set))
+ && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) {
+ zeroOneSet |= kZeroS1Set | kOneS2Set;
+ intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[TCurve::kPointLast]);
+ }
+ // check for one
+ if (!(zeroOneSet & (kOneS1Set | kZeroS2Set))
+ && sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
+ zeroOneSet |= kOneS1Set | kZeroS2Set;
+ intersections->insertNear(1, 0, sect1->fCurve[TCurve::kPointLast], sect2->fCurve[0]);
+ }
+ if (!(zeroOneSet & (kOneS1Set | kOneS2Set))
+ && sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[
+ TCurve::kPointLast])) {
+ zeroOneSet |= kOneS1Set | kOneS2Set;
+ 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 +1692,14 @@
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,37 +1709,46 @@
}
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) {
- const SkClosestRecord<TCurve>& test = fClosest[index];
- test.addIntersection(intersections);
+ closestPtrs.push_back(&fClosest[index]);
+ }
+ SkTQSort<const SkClosestRecord<TCurve> >(closestPtrs.begin(), closestPtrs.end() - 1);
+ for (int index = 0; index < fUsed; ++index) {
+ const SkClosestRecord<TCurve>* test = closestPtrs[index];
+ test->addIntersection(intersections);
}
}
- // 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) {
+ 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;
}
@@ -1096,12 +1764,12 @@
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;
@@ -1110,50 +1778,52 @@
}
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 +1833,41 @@
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 +1879,39 @@
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 0f63f39..d03efeb 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) {
+ 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()) {
+ SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
+ 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 77845e0..0000000
--- 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 8cc8c6d..0000000
--- 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 01fec0d..0248e71 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -22,6 +22,111 @@
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 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 @@
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 @@
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 @@
// 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 @@
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 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 f9a7bf5..0000000
--- 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 6ce0867..0000000
--- 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 6f06447..c19cd3d 100644
--- a/src/pathops/SkReduceOrder.cpp
+++ b/src/pathops/SkReduceOrder.cpp
@@ -272,6 +272,11 @@
}
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 4ff9a1d..397b58d 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 {