aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/pathops/SkOpContour.cpp
diff options
context:
space:
mode:
authorGravatar reed <reed@google.com>2015-03-24 13:55:33 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-03-24 13:55:33 -0700
commit0dc4dd6dda9a7912f696b46d9c02155ec1d1ba5f (patch)
tree994c85a8e418986415175ddccc71adf924df3846 /src/pathops/SkOpContour.cpp
parent82dec0e16ae10026194ce45b67af931700510450 (diff)
Revert of pathops version two (patchset #16 id:150001 of https://codereview.chromium.org/1002693002/)
Reason for revert: ASAN investigation Original issue's description: > pathops version two > > R=reed@google.com > > marked 'no commit' to attempt to get trybots to run > > TBR=reed@google.com > > Committed: https://skia.googlesource.com/skia/+/ccec0f958ffc71a9986d236bc2eb335cb2111119 TBR=caryclark@google.com NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true Review URL: https://codereview.chromium.org/1029993002
Diffstat (limited to 'src/pathops/SkOpContour.cpp')
-rw-r--r--src/pathops/SkOpContour.cpp738
1 files changed, 694 insertions, 44 deletions
diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp
index d17b18905b..28c072a3c1 100644
--- a/src/pathops/SkOpContour.cpp
+++ b/src/pathops/SkOpContour.cpp
@@ -4,35 +4,42 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#include "SkIntersections.h"
#include "SkOpContour.h"
-#include "SkOpTAllocator.h"
#include "SkPathWriter.h"
-#include "SkReduceOrder.h"
#include "SkTSort.h"
-void SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator) {
- switch (verb) {
- case SkPath::kLine_Verb: {
- SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 2);
- memcpy(ptStorage, pts, sizeof(SkPoint) * 2);
- appendSegment(allocator).addLine(ptStorage, this);
- } break;
- case SkPath::kQuad_Verb: {
- SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 3);
- memcpy(ptStorage, pts, sizeof(SkPoint) * 3);
- appendSegment(allocator).addQuad(ptStorage, this);
- } break;
- case SkPath::kCubic_Verb: {
- SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 4);
- memcpy(ptStorage, pts, sizeof(SkPoint) * 4);
- appendSegment(allocator).addCubic(ptStorage, this);
- } break;
- default:
- SkASSERT(0);
- }
-}
-
-SkOpSegment* SkOpContour::nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end) {
+bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
+ const SkIntersections& ts, bool swap) {
+ SkPoint pt0 = ts.pt(0).asSkPoint();
+ SkPoint pt1 = ts.pt(1).asSkPoint();
+ if (pt0 == pt1 || ts[0][0] == ts[0][1] || ts[1][0] == ts[1][1]) {
+ // FIXME: one could imagine a case where it would be incorrect to ignore this
+ // suppose two self-intersecting cubics overlap to be coincident --
+ // this needs to check that by some measure the t values are far enough apart
+ // or needs to check to see if the self-intersection bit was set on the cubic segment
+ return false;
+ }
+ SkCoincidence& coincidence = fCoincidences.push_back();
+ coincidence.fOther = other;
+ coincidence.fSegments[0] = index;
+ coincidence.fSegments[1] = otherIndex;
+ coincidence.fTs[swap][0] = ts[0][0];
+ coincidence.fTs[swap][1] = ts[0][1];
+ coincidence.fTs[!swap][0] = ts[1][0];
+ coincidence.fTs[!swap][1] = ts[1][1];
+ coincidence.fPts[swap][0] = pt0;
+ coincidence.fPts[swap][1] = pt1;
+ bool nearStart = ts.nearlySame(0);
+ bool nearEnd = ts.nearlySame(1);
+ coincidence.fPts[!swap][0] = nearStart ? ts.pt2(0).asSkPoint() : pt0;
+ coincidence.fPts[!swap][1] = nearEnd ? ts.pt2(1).asSkPoint() : pt1;
+ coincidence.fNearly[0] = nearStart;
+ coincidence.fNearly[1] = nearEnd;
+ return true;
+}
+
+SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
int segmentCount = fSortedSegments.count();
SkASSERT(segmentCount > 0);
for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
@@ -40,27 +47,627 @@ SkOpSegment* SkOpContour::nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase*
if (testSegment->done()) {
continue;
}
- SkOpSpanBase* span = testSegment->head();
- SkOpSpanBase* testS, * testE;
- while (SkOpSegment::NextCandidate(span, &testS, &testE)) {
- if (!testSegment->isVertical(testS, testE)) {
- *start = testS;
- *end = testE;
+ *start = *end = 0;
+ while (testSegment->nextCandidate(start, end)) {
+ if (!testSegment->isVertical(*start, *end)) {
return testSegment;
}
- span = span->upCast()->next();
}
}
return NULL;
}
+// if one is very large the smaller may have collapsed to nothing
+static void bump_out_close_span(double* startTPtr, double* endTPtr) {
+ double startT = *startTPtr;
+ double endT = *endTPtr;
+ if (approximately_negative(endT - startT)) {
+ if (endT <= 1 - FLT_EPSILON) {
+ *endTPtr += FLT_EPSILON;
+ SkASSERT(*endTPtr <= 1);
+ } else {
+ *startTPtr -= FLT_EPSILON;
+ SkASSERT(*startTPtr >= 0);
+ }
+ }
+}
+
+// first pass, add missing T values
+// second pass, determine winding values of overlaps
+void SkOpContour::addCoincidentPoints() {
+ int count = fCoincidences.count();
+ for (int index = 0; index < count; ++index) {
+ SkCoincidence& coincidence = fCoincidences[index];
+ int thisIndex = coincidence.fSegments[0];
+ SkOpSegment& thisOne = fSegments[thisIndex];
+ SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ SkOpSegment& other = otherContour->fSegments[otherIndex];
+ if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
+ // OPTIMIZATION: remove from array
+ continue;
+ }
+ #if DEBUG_CONCIDENT
+ thisOne.debugShowTs("-");
+ other.debugShowTs("o");
+ #endif
+ double startT = coincidence.fTs[0][0];
+ double endT = coincidence.fTs[0][1];
+ bool startSwapped, oStartSwapped, cancelers;
+ if ((cancelers = startSwapped = startT > endT)) {
+ SkTSwap(startT, endT);
+ }
+ bump_out_close_span(&startT, &endT);
+ SkASSERT(!approximately_negative(endT - startT));
+ double oStartT = coincidence.fTs[1][0];
+ double oEndT = coincidence.fTs[1][1];
+ if ((oStartSwapped = oStartT > oEndT)) {
+ SkTSwap(oStartT, oEndT);
+ cancelers ^= true;
+ }
+ bump_out_close_span(&oStartT, &oEndT);
+ SkASSERT(!approximately_negative(oEndT - oStartT));
+ const SkPoint& startPt = coincidence.fPts[0][startSwapped];
+ if (cancelers) {
+ // make sure startT and endT have t entries
+ if (startT > 0 || oEndT < 1
+ || thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) {
+ thisOne.addTPair(startT, &other, oEndT, true, startPt,
+ coincidence.fPts[1][startSwapped]);
+ }
+ const SkPoint& oStartPt = coincidence.fPts[1][oStartSwapped];
+ if (oStartT > 0 || endT < 1
+ || thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) {
+ other.addTPair(oStartT, &thisOne, endT, true, oStartPt,
+ coincidence.fPts[0][oStartSwapped]);
+ }
+ } else {
+ if (startT > 0 || oStartT > 0
+ || thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) {
+ thisOne.addTPair(startT, &other, oStartT, true, startPt,
+ coincidence.fPts[1][startSwapped]);
+ }
+ const SkPoint& oEndPt = coincidence.fPts[1][!oStartSwapped];
+ if (endT < 1 || oEndT < 1
+ || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) {
+ other.addTPair(oEndT, &thisOne, endT, true, oEndPt,
+ coincidence.fPts[0][!oStartSwapped]);
+ }
+ }
+ #if DEBUG_CONCIDENT
+ thisOne.debugShowTs("+");
+ other.debugShowTs("o");
+ #endif
+ }
+ // if there are multiple pairs of coincidence that share an edge, see if the opposite
+ // are also coincident
+ for (int index = 0; index < count - 1; ++index) {
+ const SkCoincidence& coincidence = fCoincidences[index];
+ int thisIndex = coincidence.fSegments[0];
+ SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ for (int idx2 = 1; idx2 < count; ++idx2) {
+ const SkCoincidence& innerCoin = fCoincidences[idx2];
+ int innerThisIndex = innerCoin.fSegments[0];
+ if (thisIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 1, innerCoin, 1, false);
+ }
+ if (this == otherContour && otherIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 0, innerCoin, 1, false);
+ }
+ SkOpContour* innerOtherContour = innerCoin.fOther;
+ innerThisIndex = innerCoin.fSegments[1];
+ if (this == innerOtherContour && thisIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 1, innerCoin, 0, false);
+ }
+ if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 0, innerCoin, 0, false);
+ }
+ }
+ }
+}
+
+bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex,
+ const SkIntersections& ts, int ptIndex, bool swap) {
+ SkPoint pt0 = ts.pt(ptIndex).asSkPoint();
+ SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint();
+ if (SkDPoint::ApproximatelyEqual(pt0, pt1)) {
+ // FIXME: one could imagine a case where it would be incorrect to ignore this
+ // suppose two self-intersecting cubics overlap to form a partial coincidence --
+ // although it isn't clear why the regular coincidence could wouldn't pick this up
+ // this is exceptional enough to ignore for now
+ return false;
+ }
+ SkCoincidence& coincidence = fPartialCoincidences.push_back();
+ coincidence.fOther = other;
+ coincidence.fSegments[0] = index;
+ coincidence.fSegments[1] = otherIndex;
+ coincidence.fTs[swap][0] = ts[0][ptIndex];
+ coincidence.fTs[swap][1] = ts[0][ptIndex + 1];
+ coincidence.fTs[!swap][0] = ts[1][ptIndex];
+ coincidence.fTs[!swap][1] = ts[1][ptIndex + 1];
+ coincidence.fPts[0][0] = coincidence.fPts[1][0] = pt0;
+ coincidence.fPts[0][1] = coincidence.fPts[1][1] = pt1;
+ coincidence.fNearly[0] = 0;
+ coincidence.fNearly[1] = 0;
+ return true;
+}
+
+void SkOpContour::align(const SkOpSegment::AlignedSpan& aligned, bool swap,
+ SkCoincidence* coincidence) {
+ for (int idx2 = 0; idx2 < 2; ++idx2) {
+ if (coincidence->fPts[0][idx2] == aligned.fOldPt
+ && coincidence->fTs[swap][idx2] == aligned.fOldT) {
+ SkASSERT(SkDPoint::RoughlyEqual(coincidence->fPts[0][idx2], aligned.fPt));
+ coincidence->fPts[0][idx2] = aligned.fPt;
+ SkASSERT(way_roughly_equal(coincidence->fTs[swap][idx2], aligned.fT));
+ coincidence->fTs[swap][idx2] = aligned.fT;
+ }
+ }
+}
+
+void SkOpContour::alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
+ SkTArray<SkCoincidence, true>* coincidences) {
+ int count = coincidences->count();
+ for (int index = 0; index < count; ++index) {
+ SkCoincidence& coincidence = (*coincidences)[index];
+ int thisIndex = coincidence.fSegments[0];
+ const SkOpSegment* thisOne = &fSegments[thisIndex];
+ const SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ const SkOpSegment* other = &otherContour->fSegments[otherIndex];
+ if (thisOne == aligned.fOther1 && other == aligned.fOther2) {
+ align(aligned, false, &coincidence);
+ } else if (thisOne == aligned.fOther2 && other == aligned.fOther1) {
+ align(aligned, true, &coincidence);
+ }
+ }
+}
+
+void SkOpContour::alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
+ bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const {
+ int zeroPt;
+ if ((zeroPt = alignT(swap, tIndex, ts)) >= 0) {
+ alignPt(segmentIndex, point, zeroPt);
+ }
+ if ((zeroPt = other->alignT(!swap, tIndex, ts)) >= 0) {
+ other->alignPt(otherIndex, point, zeroPt);
+ }
+}
+
+void SkOpContour::alignPt(int index, SkPoint* point, int zeroPt) const {
+ const SkOpSegment& segment = fSegments[index];
+ if (0 == zeroPt) {
+ *point = segment.pts()[0];
+ } else {
+ *point = segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
+ }
+}
+
+int SkOpContour::alignT(bool swap, int tIndex, SkIntersections* ts) const {
+ double tVal = (*ts)[swap][tIndex];
+ if (tVal != 0 && precisely_zero(tVal)) {
+ ts->set(swap, tIndex, 0);
+ return 0;
+ }
+ if (tVal != 1 && precisely_equal(tVal, 1)) {
+ ts->set(swap, tIndex, 1);
+ return 1;
+ }
+ return -1;
+}
+
+bool SkOpContour::calcAngles() {
+ int segmentCount = fSegments.count();
+ for (int test = 0; test < segmentCount; ++test) {
+ if (!fSegments[test].calcAngles()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool SkOpContour::calcCoincidentWinding() {
+ int count = fCoincidences.count();
+#if DEBUG_CONCIDENT
+ if (count > 0) {
+ SkDebugf("%s count=%d\n", __FUNCTION__, count);
+ }
+#endif
+ for (int index = 0; index < count; ++index) {
+ SkCoincidence& coincidence = fCoincidences[index];
+ if (!calcCommonCoincidentWinding(coincidence)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void SkOpContour::calcPartialCoincidentWinding() {
+ int count = fPartialCoincidences.count();
+#if DEBUG_CONCIDENT
+ if (count > 0) {
+ SkDebugf("%s count=%d\n", __FUNCTION__, count);
+ }
+#endif
+ for (int index = 0; index < count; ++index) {
+ SkCoincidence& coincidence = fPartialCoincidences[index];
+ calcCommonCoincidentWinding(coincidence);
+ }
+ // if there are multiple pairs of partial coincidence that share an edge, see if the opposite
+ // are also coincident
+ for (int index = 0; index < count - 1; ++index) {
+ const SkCoincidence& coincidence = fPartialCoincidences[index];
+ int thisIndex = coincidence.fSegments[0];
+ SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ for (int idx2 = 1; idx2 < count; ++idx2) {
+ const SkCoincidence& innerCoin = fPartialCoincidences[idx2];
+ int innerThisIndex = innerCoin.fSegments[0];
+ if (thisIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 1, innerCoin, 1, true);
+ }
+ if (this == otherContour && otherIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 0, innerCoin, 1, true);
+ }
+ SkOpContour* innerOtherContour = innerCoin.fOther;
+ innerThisIndex = innerCoin.fSegments[1];
+ if (this == innerOtherContour && thisIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 1, innerCoin, 0, true);
+ }
+ if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
+ checkCoincidentPair(coincidence, 0, innerCoin, 0, true);
+ }
+ }
+ }
+}
+
+void SkOpContour::checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
+ const SkCoincidence& twoCoin, int twoIdx, bool partial) {
+ SkASSERT((oneIdx ? this : oneCoin.fOther) == (twoIdx ? this : twoCoin.fOther));
+ SkASSERT(oneCoin.fSegments[!oneIdx] == twoCoin.fSegments[!twoIdx]);
+ // look for common overlap
+ double min = SK_ScalarMax;
+ double max = SK_ScalarMin;
+ double min1 = oneCoin.fTs[!oneIdx][0];
+ double max1 = oneCoin.fTs[!oneIdx][1];
+ double min2 = twoCoin.fTs[!twoIdx][0];
+ double max2 = twoCoin.fTs[!twoIdx][1];
+ bool cancelers = (min1 < max1) != (min2 < max2);
+ if (min1 > max1) {
+ SkTSwap(min1, max1);
+ }
+ if (min2 > max2) {
+ SkTSwap(min2, max2);
+ }
+ if (between(min1, min2, max1)) {
+ min = min2;
+ }
+ if (between(min1, max2, max1)) {
+ max = max2;
+ }
+ if (between(min2, min1, max2)) {
+ min = SkTMin(min, min1);
+ }
+ if (between(min2, max1, max2)) {
+ max = SkTMax(max, max1);
+ }
+ if (min >= max) {
+ return; // no overlap
+ }
+ // look to see if opposite are different segments
+ int seg1Index = oneCoin.fSegments[oneIdx];
+ int seg2Index = twoCoin.fSegments[twoIdx];
+ if (seg1Index == seg2Index) {
+ return;
+ }
+ SkOpContour* contour1 = oneIdx ? oneCoin.fOther : this;
+ SkOpContour* contour2 = twoIdx ? twoCoin.fOther : this;
+ SkOpSegment* segment1 = &contour1->fSegments[seg1Index];
+ SkOpSegment* segment2 = &contour2->fSegments[seg2Index];
+ // find opposite t value ranges corresponding to reference min/max range
+ const SkOpContour* refContour = oneIdx ? this : oneCoin.fOther;
+ const int refSegIndex = oneCoin.fSegments[!oneIdx];
+ const SkOpSegment* refSegment = &refContour->fSegments[refSegIndex];
+ int seg1Start = segment1->findOtherT(min, refSegment);
+ int seg1End = segment1->findOtherT(max, refSegment);
+ int seg2Start = segment2->findOtherT(min, refSegment);
+ int seg2End = segment2->findOtherT(max, refSegment);
+ // if the opposite pairs already contain min/max, we're done
+ if (seg1Start >= 0 && seg1End >= 0 && seg2Start >= 0 && seg2End >= 0) {
+ return;
+ }
+ double loEnd = SkTMin(min1, min2);
+ double hiEnd = SkTMax(max1, max2);
+ // insert the missing coincident point(s)
+ double missingT1 = -1;
+ double otherT1 = -1;
+ if (seg1Start < 0) {
+ if (seg2Start < 0) {
+ return;
+ }
+ missingT1 = segment1->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
+ segment2, seg1End);
+ if (missingT1 < 0) {
+ return;
+ }
+ const SkOpSpan* missingSpan = &segment2->span(seg2Start);
+ otherT1 = missingSpan->fT;
+ } else if (seg2Start < 0) {
+ SkASSERT(seg1Start >= 0);
+ missingT1 = segment2->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
+ segment1, seg2End);
+ if (missingT1 < 0) {
+ return;
+ }
+ const SkOpSpan* missingSpan = &segment1->span(seg1Start);
+ otherT1 = missingSpan->fT;
+ }
+ SkPoint missingPt1;
+ SkOpSegment* addTo1 = NULL;
+ SkOpSegment* addOther1 = seg1Start < 0 ? segment2 : segment1;
+ int minTIndex = refSegment->findExactT(min, addOther1);
+ SkASSERT(minTIndex >= 0);
+ if (missingT1 >= 0) {
+ missingPt1 = refSegment->span(minTIndex).fPt;
+ addTo1 = seg1Start < 0 ? segment1 : segment2;
+ }
+ double missingT2 = -1;
+ double otherT2 = -1;
+ if (seg1End < 0) {
+ if (seg2End < 0) {
+ return;
+ }
+ missingT2 = segment1->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
+ segment2, seg1Start);
+ if (missingT2 < 0) {
+ return;
+ }
+ const SkOpSpan* missingSpan = &segment2->span(seg2End);
+ otherT2 = missingSpan->fT;
+ } else if (seg2End < 0) {
+ SkASSERT(seg1End >= 0);
+ missingT2 = segment2->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
+ segment1, seg2Start);
+ if (missingT2 < 0) {
+ return;
+ }
+ const SkOpSpan* missingSpan = &segment1->span(seg1End);
+ otherT2 = missingSpan->fT;
+ }
+ SkPoint missingPt2;
+ SkOpSegment* addTo2 = NULL;
+ SkOpSegment* addOther2 = seg1End < 0 ? segment2 : segment1;
+ int maxTIndex = refSegment->findExactT(max, addOther2);
+ SkASSERT(maxTIndex >= 0);
+ if (missingT2 >= 0) {
+ missingPt2 = refSegment->span(maxTIndex).fPt;
+ addTo2 = seg1End < 0 ? segment1 : segment2;
+ }
+ if (missingT1 >= 0) {
+ addTo1->pinT(missingPt1, &missingT1);
+ addTo1->addTPair(missingT1, addOther1, otherT1, false, missingPt1);
+ } else {
+ SkASSERT(minTIndex >= 0);
+ missingPt1 = refSegment->span(minTIndex).fPt;
+ }
+ if (missingT2 >= 0) {
+ addTo2->pinT(missingPt2, &missingT2);
+ addTo2->addTPair(missingT2, addOther2, otherT2, false, missingPt2);
+ } else {
+ SkASSERT(minTIndex >= 0);
+ missingPt2 = refSegment->span(maxTIndex).fPt;
+ }
+ if (!partial) {
+ return;
+ }
+ if (cancelers) {
+ if (missingT1 >= 0) {
+ if (addTo1->reversePoints(missingPt1, missingPt2)) {
+ SkTSwap(missingPt1, missingPt2);
+ }
+ addTo1->addTCancel(missingPt1, missingPt2, addOther1);
+ } else {
+ if (addTo2->reversePoints(missingPt1, missingPt2)) {
+ SkTSwap(missingPt1, missingPt2);
+ }
+ addTo2->addTCancel(missingPt1, missingPt2, addOther2);
+ }
+ } else if (missingT1 >= 0) {
+ SkAssertResult(addTo1->addTCoincident(missingPt1, missingPt2,
+ addTo1 == addTo2 ? missingT2 : otherT2, addOther1));
+ } else {
+ SkAssertResult(addTo2->addTCoincident(missingPt2, missingPt1,
+ addTo2 == addTo1 ? missingT1 : otherT1, addOther2));
+ }
+}
+
+void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) {
+ int count = coincidences.count();
+#if DEBUG_CONCIDENT
+ if (count > 0) {
+ SkDebugf("%s count=%d\n", __FUNCTION__, count);
+ }
+#endif
+ // look for a lineup where the partial implies another adjoining coincidence
+ for (int index = 0; index < count; ++index) {
+ const SkCoincidence& coincidence = coincidences[index];
+ int thisIndex = coincidence.fSegments[0];
+ SkOpSegment& thisOne = fSegments[thisIndex];
+ if (thisOne.done()) {
+ continue;
+ }
+ SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ SkOpSegment& other = otherContour->fSegments[otherIndex];
+ if (other.done()) {
+ continue;
+ }
+ double startT = coincidence.fTs[0][0];
+ double endT = coincidence.fTs[0][1];
+ if (startT == endT) { // this can happen in very large compares
+ continue;
+ }
+ double oStartT = coincidence.fTs[1][0];
+ double oEndT = coincidence.fTs[1][1];
+ if (oStartT == oEndT) {
+ continue;
+ }
+ bool swapStart = startT > endT;
+ bool swapOther = oStartT > oEndT;
+ const SkPoint* startPt = &coincidence.fPts[0][0];
+ const SkPoint* endPt = &coincidence.fPts[0][1];
+ if (swapStart) {
+ SkTSwap(startT, endT);
+ SkTSwap(oStartT, oEndT);
+ SkTSwap(startPt, endPt);
+ }
+ bool cancel = swapOther != swapStart;
+ int step = swapStart ? -1 : 1;
+ int oStep = swapOther ? -1 : 1;
+ double oMatchStart = cancel ? oEndT : oStartT;
+ if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) {
+ bool added = false;
+ if (oMatchStart != 0) {
+ const SkPoint& oMatchStartPt = cancel ? *endPt : *startPt;
+ added = thisOne.joinCoincidence(&other, oMatchStart, oMatchStartPt, oStep, cancel);
+ }
+ if (!cancel && startT != 0 && !added) {
+ (void) other.joinCoincidence(&thisOne, startT, *startPt, step, cancel);
+ }
+ }
+ double oMatchEnd = cancel ? oStartT : oEndT;
+ if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) {
+ bool added = false;
+ if (cancel && endT != 1 && !added) {
+ (void) other.joinCoincidence(&thisOne, endT, *endPt, -step, cancel);
+ }
+ }
+ }
+}
+
+bool SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
+ if (coincidence.fNearly[0] && coincidence.fNearly[1]) {
+ return true;
+ }
+ int thisIndex = coincidence.fSegments[0];
+ SkOpSegment& thisOne = fSegments[thisIndex];
+ if (thisOne.done()) {
+ return true;
+ }
+ SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ SkOpSegment& other = otherContour->fSegments[otherIndex];
+ if (other.done()) {
+ return true;
+ }
+ double startT = coincidence.fTs[0][0];
+ double endT = coincidence.fTs[0][1];
+ const SkPoint* startPt = &coincidence.fPts[0][0];
+ const SkPoint* endPt = &coincidence.fPts[0][1];
+ bool cancelers;
+ if ((cancelers = startT > endT)) {
+ SkTSwap<double>(startT, endT);
+ SkTSwap<const SkPoint*>(startPt, endPt);
+ }
+ bump_out_close_span(&startT, &endT);
+ SkASSERT(!approximately_negative(endT - startT));
+ double oStartT = coincidence.fTs[1][0];
+ double oEndT = coincidence.fTs[1][1];
+ if (oStartT > oEndT) {
+ SkTSwap<double>(oStartT, oEndT);
+ cancelers ^= true;
+ }
+ bump_out_close_span(&oStartT, &oEndT);
+ SkASSERT(!approximately_negative(oEndT - oStartT));
+ bool success = true;
+ if (cancelers) {
+ thisOne.addTCancel(*startPt, *endPt, &other);
+ } else {
+ success = thisOne.addTCoincident(*startPt, *endPt, endT, &other);
+ }
+#if DEBUG_CONCIDENT
+ thisOne.debugShowTs("p");
+ other.debugShowTs("o");
+#endif
+ return success;
+}
+
+void SkOpContour::resolveNearCoincidence() {
+ int count = fCoincidences.count();
+ for (int index = 0; index < count; ++index) {
+ SkCoincidence& coincidence = fCoincidences[index];
+ if (!coincidence.fNearly[0] || !coincidence.fNearly[1]) {
+ continue;
+ }
+ int thisIndex = coincidence.fSegments[0];
+ SkOpSegment& thisOne = fSegments[thisIndex];
+ SkOpContour* otherContour = coincidence.fOther;
+ int otherIndex = coincidence.fSegments[1];
+ SkOpSegment& other = otherContour->fSegments[otherIndex];
+ if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
+ // OPTIMIZATION: remove from coincidence array
+ continue;
+ }
+ #if DEBUG_CONCIDENT
+ thisOne.debugShowTs("-");
+ other.debugShowTs("o");
+ #endif
+ double startT = coincidence.fTs[0][0];
+ double endT = coincidence.fTs[0][1];
+ bool cancelers;
+ if ((cancelers = startT > endT)) {
+ SkTSwap<double>(startT, endT);
+ }
+ if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
+ if (endT <= 1 - FLT_EPSILON) {
+ endT += FLT_EPSILON;
+ SkASSERT(endT <= 1);
+ } else {
+ startT -= FLT_EPSILON;
+ SkASSERT(startT >= 0);
+ }
+ }
+ SkASSERT(!approximately_negative(endT - startT));
+ double oStartT = coincidence.fTs[1][0];
+ double oEndT = coincidence.fTs[1][1];
+ if (oStartT > oEndT) {
+ SkTSwap<double>(oStartT, oEndT);
+ cancelers ^= true;
+ }
+ SkASSERT(!approximately_negative(oEndT - oStartT));
+ if (cancelers) {
+ thisOne.blindCancel(coincidence, &other);
+ } else {
+ thisOne.blindCoincident(coincidence, &other);
+ }
+ }
+}
+
+void SkOpContour::sortAngles() {
+ int segmentCount = fSegments.count();
+ for (int test = 0; test < segmentCount; ++test) {
+ fSegments[test].sortAngles();
+ }
+}
+
+void SkOpContour::sortSegments() {
+ int segmentCount = fSegments.count();
+ fSortedSegments.push_back_n(segmentCount);
+ for (int test = 0; test < segmentCount; ++test) {
+ fSortedSegments[test] = &fSegments[test];
+ }
+ SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
+ fFirstSorted = 0;
+}
+
void SkOpContour::toPath(SkPathWriter* path) const {
- const SkPoint& pt = fHead.pts()[0];
+ int segmentCount = fSegments.count();
+ const SkPoint& pt = fSegments.front().pts()[0];
path->deferredMove(pt);
- const SkOpSegment* segment = &fHead;
- do {
- segment->addCurveTo(segment->head(), segment->tail(), path, true);
- } while ((segment = segment->next()));
+ for (int test = 0; test < segmentCount; ++test) {
+ fSegments[test].addCurveTo(0, 1, path, true);
+ }
path->close();
}
@@ -99,14 +706,57 @@ void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
}
}
-SkOpSegment* SkOpContour::undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr) {
- SkOpSegment* segment = &fHead;
- do {
- if (segment->done()) {
+SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
+ int segmentCount = fSegments.count();
+ for (int test = 0; test < segmentCount; ++test) {
+ SkOpSegment* testSegment = &fSegments[test];
+ if (testSegment->done()) {
continue;
}
- segment->undoneSpan(startPtr, endPtr);
- return segment;
- } while ((segment = segment->next()));
+ testSegment->undoneSpan(start, end);
+ return testSegment;
+ }
return NULL;
}
+
+#if DEBUG_SHOW_WINDING
+int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
+ int count = fSegments.count();
+ int sum = 0;
+ for (int index = 0; index < count; ++index) {
+ sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
+ }
+// SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
+ return sum;
+}
+
+void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
+// int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
+// int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
+ int ofInterest = 1 << 5 | 1 << 8;
+ int total = 0;
+ int index;
+ for (index = 0; index < contourList.count(); ++index) {
+ total += contourList[index]->segments().count();
+ }
+ int sum = 0;
+ for (index = 0; index < contourList.count(); ++index) {
+ sum += contourList[index]->debugShowWindingValues(total, ofInterest);
+ }
+// SkDebugf("%s total=%d\n", __FUNCTION__, sum);
+}
+#endif
+
+void SkOpContour::setBounds() {
+ int count = fSegments.count();
+ if (count == 0) {
+ SkDebugf("%s empty contour\n", __FUNCTION__);
+ SkASSERT(0);
+ // FIXME: delete empty contour?
+ return;
+ }
+ fBounds = fSegments.front().bounds();
+ for (int index = 1; index < count; ++index) {
+ fBounds.add(fSegments[index].bounds());
+ }
+}