blob: a16e811ea96e76033763332686c664edb3478d2b [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00007#include "SkAddIntersections.h"
caryclark54359292015-03-26 07:52:43 -07008#include "SkOpCoincidence.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +00009#include "SkOpEdgeBuilder.h"
10#include "SkPathOpsCommon.h"
11#include "SkPathWriter.h"
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +000012#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000013
caryclark54359292015-03-26 07:52:43 -070014static int contourRangeCheckY(const SkTDArray<SkOpContour* >& contourList,
15 SkOpSegment** currentPtr, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
16 double* bestHit, SkScalar* bestDx, bool* tryAgain, double* midPtr, bool opp) {
17 SkOpSpanBase* start = *startPtr;
18 SkOpSpanBase* end = *endPtr;
caryclark@google.com07393ca2013-04-08 11:47:37 +000019 const double mid = *midPtr;
20 const SkOpSegment* current = *currentPtr;
caryclark54359292015-03-26 07:52:43 -070021 double tAtMid = SkOpSegment::TAtMid(start, end, mid);
caryclark@google.com4fdbb222013-07-23 15:27:41 +000022 SkPoint basePt = current->ptAtT(tAtMid);
caryclark@google.com07393ca2013-04-08 11:47:37 +000023 int contourCount = contourList.count();
24 SkScalar bestY = SK_ScalarMin;
25 SkOpSegment* bestSeg = NULL;
caryclark54359292015-03-26 07:52:43 -070026 SkOpSpan* bestTSpan = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +000027 bool bestOpp;
28 bool hitSomething = false;
29 for (int cTest = 0; cTest < contourCount; ++cTest) {
30 SkOpContour* contour = contourList[cTest];
31 bool testOpp = contour->operand() ^ current->operand() ^ opp;
32 if (basePt.fY < contour->bounds().fTop) {
33 continue;
34 }
35 if (bestY > contour->bounds().fBottom) {
36 continue;
37 }
caryclark54359292015-03-26 07:52:43 -070038 SkOpSegment* testSeg = contour->first();
39 SkASSERT(testSeg);
40 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +000041 SkScalar testY = bestY;
42 double testHit;
caryclark54359292015-03-26 07:52:43 -070043 bool vertical;
44 SkOpSpan* testTSpan = testSeg->crossedSpanY(basePt, tAtMid, testOpp,
45 testSeg == current, &testY, &testHit, &hitSomething, &vertical);
46 if (!testTSpan) {
47 if (vertical) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000048 hitSomething = true;
49 bestSeg = NULL;
50 goto abortContours; // vertical encountered, return and try different point
51 }
52 continue;
53 }
caryclark54359292015-03-26 07:52:43 -070054 if (testSeg == current && SkOpSegment::BetweenTs(start, testHit, end)) {
55 double baseT = start->t();
56 double endT = end->t();
caryclark@google.com07393ca2013-04-08 11:47:37 +000057 double newMid = (testHit - baseT) / (endT - baseT);
58#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -070059 double midT = SkOpSegment::TAtMid(start, end, mid);
60 SkPoint midXY = current->ptAtT(midT);
61 double newMidT = SkOpSegment::TAtMid(start, end, newMid);
62 SkPoint newXY = current->ptAtT(newMidT);
caryclark@google.com07393ca2013-04-08 11:47:37 +000063 SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)"
64 " n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__,
65 current->debugID(), mid, newMid,
caryclark54359292015-03-26 07:52:43 -070066 baseT, start->pt().fX, start->pt().fY,
caryclark@google.com07393ca2013-04-08 11:47:37 +000067 baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
68 baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
caryclark54359292015-03-26 07:52:43 -070069 endT, end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +000070#endif
71 *midPtr = newMid * 2; // calling loop with divide by 2 before continuing
72 return SK_MinS32;
73 }
74 bestSeg = testSeg;
75 *bestHit = testHit;
76 bestOpp = testOpp;
caryclark54359292015-03-26 07:52:43 -070077 bestTSpan = testTSpan;
caryclark@google.com07393ca2013-04-08 11:47:37 +000078 bestY = testY;
caryclark54359292015-03-26 07:52:43 -070079 } while ((testSeg = testSeg->next()));
caryclark@google.com07393ca2013-04-08 11:47:37 +000080 }
81abortContours:
82 int result;
83 if (!bestSeg) {
84 result = hitSomething ? SK_MinS32 : 0;
85 } else {
caryclark54359292015-03-26 07:52:43 -070086 if (bestTSpan->windSum() == SK_MinS32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000087 *currentPtr = bestSeg;
caryclark54359292015-03-26 07:52:43 -070088 *startPtr = bestTSpan;
89 *endPtr = bestTSpan->next();
90 SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
caryclark@google.com07393ca2013-04-08 11:47:37 +000091 *tryAgain = true;
92 return 0;
93 }
caryclark54359292015-03-26 07:52:43 -070094 result = bestSeg->windingAtT(*bestHit, bestTSpan, bestOpp, bestDx);
caryclark@google.comf11a5af2013-04-10 18:55:11 +000095 SkASSERT(result == SK_MinS32 || *bestDx);
caryclark@google.com07393ca2013-04-08 11:47:37 +000096 }
caryclark54359292015-03-26 07:52:43 -070097 double baseT = (*startPtr)->t();
98 double endT = (*endPtr)->t();
caryclark@google.com07393ca2013-04-08 11:47:37 +000099 *bestHit = baseT + mid * (endT - baseT);
100 return result;
101}
102
caryclark54359292015-03-26 07:52:43 -0700103SkOpSegment* FindUndone(SkTDArray<SkOpContour* >& contourList, SkOpSpanBase** startPtr,
104 SkOpSpanBase** endPtr) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000105 int contourCount = contourList.count();
106 SkOpSegment* result;
107 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
108 SkOpContour* contour = contourList[cIndex];
caryclark54359292015-03-26 07:52:43 -0700109 result = contour->undoneSegment(startPtr, endPtr);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000110 if (result) {
111 return result;
112 }
113 }
114 return NULL;
115}
116
caryclark54359292015-03-26 07:52:43 -0700117SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
118 SkOpSpanBase** endPtr) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000119 while (chase->count()) {
caryclark54359292015-03-26 07:52:43 -0700120 SkOpSpanBase* span;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000121 chase->pop(&span);
caryclark54359292015-03-26 07:52:43 -0700122 SkOpSegment* segment = span->segment();
123 *startPtr = span->ptT()->next()->span();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000124 bool sortable = true;
125 bool done = true;
caryclark54359292015-03-26 07:52:43 -0700126 *endPtr = NULL;
127 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000128 &sortable)) {
caryclark54359292015-03-26 07:52:43 -0700129 if (last->unorderable()) {
130 continue;
131 }
132 *startPtr = last->start();
133 *endPtr = last->end();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000134 #if TRY_ROTATE
135 *chase->insert(0) = span;
136 #else
137 *chase->append() = span;
138 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000139 return last->segment();
140 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000141 if (done) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000142 continue;
143 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000144 if (!sortable) {
145 continue;
146 }
caryclark65f55312014-11-13 06:58:52 -0800147 // find first angle, initialize winding to computed wind sum
caryclark54359292015-03-26 07:52:43 -0700148 const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr);
149 if (!angle) {
150 continue;
151 }
152 const SkOpAngle* firstAngle = angle;
153 bool loop = false;
154 int winding = SK_MinS32;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000155 do {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000156 angle = angle->next();
caryclark54359292015-03-26 07:52:43 -0700157 if (angle == firstAngle && loop) {
158 break; // if we get here, there's no winding, loop is unorderable
159 }
160 loop |= angle == firstAngle;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000161 segment = angle->segment();
162 winding = segment->windSum(angle);
163 } while (winding == SK_MinS32);
caryclark54359292015-03-26 07:52:43 -0700164 if (winding == SK_MinS32) {
165 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000166 }
caryclark54359292015-03-26 07:52:43 -0700167 int sumWinding = segment->updateWindingReverse(angle);
168 SkOpSegment* first = NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000169 firstAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000170 while ((angle = angle->next()) != firstAngle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000171 segment = angle->segment();
caryclark54359292015-03-26 07:52:43 -0700172 SkOpSpanBase* start = angle->start();
173 SkOpSpanBase* end = angle->end();
174 int maxWinding;
175 segment->setUpWinding(start, end, &maxWinding, &sumWinding);
176 if (!segment->done(angle)) {
177 if (!first) {
178 first = segment;
179 *startPtr = start;
180 *endPtr = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000181 }
caryclark54359292015-03-26 07:52:43 -0700182 // OPTIMIZATION: should this also add to the chase?
183 (void) segment->markAngle(maxWinding, sumWinding, angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000184 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000185 }
caryclark54359292015-03-26 07:52:43 -0700186 if (first) {
187 #if TRY_ROTATE
188 *chase->insert(0) = span;
189 #else
190 *chase->append() = span;
191 #endif
192 return first;
193 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000194 }
195 return NULL;
196}
197
caryclark54359292015-03-26 07:52:43 -0700198#if DEBUG_ACTIVE_SPANS
199void DebugShowActiveSpans(SkTDArray<SkOpContour* >& contourList) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000200 int index;
201 for (index = 0; index < contourList.count(); ++ index) {
202 contourList[index]->debugShowActiveSpans();
203 }
204}
205#endif
206
caryclark54359292015-03-26 07:52:43 -0700207static SkOpSegment* findTopSegment(const SkTDArray<SkOpContour* >& contourList,
208 bool firstPass, SkOpSpanBase** start, SkOpSpanBase** end, SkPoint* topLeft,
209 bool* unsortable, bool* done, SkChunkAlloc* allocator) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000210 SkOpSegment* result;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000211 const SkOpSegment* lastTopStart = NULL;
caryclark54359292015-03-26 07:52:43 -0700212 SkOpSpanBase* lastStart = NULL, * lastEnd = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000213 do {
214 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
215 int contourCount = contourList.count();
216 SkOpSegment* topStart = NULL;
217 *done = true;
218 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
219 SkOpContour* contour = contourList[cIndex];
220 if (contour->done()) {
221 continue;
222 }
223 const SkPathOpsBounds& bounds = contour->bounds();
224 if (bounds.fBottom < topLeft->fY) {
225 *done = false;
226 continue;
227 }
228 if (bounds.fBottom == topLeft->fY && bounds.fRight < topLeft->fX) {
229 *done = false;
230 continue;
231 }
232 contour->topSortableSegment(*topLeft, &bestXY, &topStart);
233 if (!contour->done()) {
234 *done = false;
235 }
236 }
237 if (!topStart) {
238 return NULL;
239 }
240 *topLeft = bestXY;
caryclark54359292015-03-26 07:52:43 -0700241 result = topStart->findTop(firstPass, start, end, unsortable, allocator);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000242 if (!result) {
caryclark54359292015-03-26 07:52:43 -0700243 if (lastTopStart == topStart && lastStart == *start && lastEnd == *end) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000244 *done = true;
245 return NULL;
246 }
247 lastTopStart = topStart;
caryclark54359292015-03-26 07:52:43 -0700248 lastStart = *start;
249 lastEnd = *end;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000250 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000251 } while (!result);
252 return result;
253}
254
caryclark54359292015-03-26 07:52:43 -0700255static int rightAngleWinding(const SkTDArray<SkOpContour* >& contourList,
256 SkOpSegment** currentPtr, SkOpSpanBase** start, SkOpSpanBase** end, double* tHit,
caryclarkdac1d172014-06-17 05:15:38 -0700257 SkScalar* hitDx, bool* tryAgain, bool* onlyVertical, bool opp) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000258 double test = 0.9;
259 int contourWinding;
260 do {
caryclark54359292015-03-26 07:52:43 -0700261 contourWinding = contourRangeCheckY(contourList, currentPtr, start, end,
caryclarkdac1d172014-06-17 05:15:38 -0700262 tHit, hitDx, tryAgain, &test, opp);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000263 if (contourWinding != SK_MinS32 || *tryAgain) {
264 return contourWinding;
265 }
caryclarkdac1d172014-06-17 05:15:38 -0700266 if (*currentPtr && (*currentPtr)->isVertical()) {
267 *onlyVertical = true;
268 return contourWinding;
269 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000270 test /= 2;
271 } while (!approximately_negative(test));
caryclarkdac1d172014-06-17 05:15:38 -0700272 SkASSERT(0); // FIXME: incomplete functionality
caryclark@google.com07393ca2013-04-08 11:47:37 +0000273 return contourWinding;
274}
275
caryclark54359292015-03-26 07:52:43 -0700276static void skipVertical(const SkTDArray<SkOpContour* >& contourList,
277 SkOpSegment** current, SkOpSpanBase** start, SkOpSpanBase** end) {
278 if (!(*current)->isVertical(*start, *end)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000279 return;
280 }
281 int contourCount = contourList.count();
282 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
283 SkOpContour* contour = contourList[cIndex];
284 if (contour->done()) {
285 continue;
286 }
caryclark54359292015-03-26 07:52:43 -0700287 SkOpSegment* nonVertical = contour->nonVerticalSegment(start, end);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000288 if (nonVertical) {
289 *current = nonVertical;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000290 return;
291 }
292 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000293 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000294}
295
caryclark54359292015-03-26 07:52:43 -0700296struct SortableTop2 { // error if local in pre-C++11
297 SkOpSpanBase* fStart;
298 SkOpSpanBase* fEnd;
caryclark65f55312014-11-13 06:58:52 -0800299};
300
caryclark54359292015-03-26 07:52:43 -0700301SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour* >& contourList, bool firstPass,
302 SkOpAngle::IncludeType angleIncludeType, bool* firstContour, SkOpSpanBase** startPtr,
303 SkOpSpanBase** endPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical,
304 SkChunkAlloc* allocator) {
305 SkOpSegment* current = findTopSegment(contourList, firstPass, startPtr, endPtr, topLeft,
306 unsortable, done, allocator);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000307 if (!current) {
308 return NULL;
309 }
caryclark54359292015-03-26 07:52:43 -0700310 SkOpSpanBase* start = *startPtr;
311 SkOpSpanBase* end = *endPtr;
312 SkASSERT(current == start->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000313 if (*firstContour) {
caryclark54359292015-03-26 07:52:43 -0700314 current->initWinding(start, end, angleIncludeType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000315 *firstContour = false;
316 return current;
317 }
caryclark54359292015-03-26 07:52:43 -0700318 SkOpSpan* minSpan = start->starter(end);
319 int sumWinding = minSpan->windSum();
caryclarkdac1d172014-06-17 05:15:38 -0700320 if (sumWinding == SK_MinS32) {
caryclark54359292015-03-26 07:52:43 -0700321 SkOpSpanBase* iSpan = end;
322 SkOpSpanBase* oSpan = start;
323 do {
324 bool checkFrom = oSpan->t() < iSpan->t();
325 if ((checkFrom ? iSpan->fromAngle() : iSpan->upCast()->toAngle()) == NULL) {
caryclark1049f122015-04-20 08:31:59 -0700326 if (!iSpan->addSimpleAngle(checkFrom, allocator)) {
327 *unsortable = true;
328 return NULL;
329 }
caryclarkdac1d172014-06-17 05:15:38 -0700330 }
caryclark54359292015-03-26 07:52:43 -0700331 sumWinding = current->computeSum(oSpan, iSpan, angleIncludeType);
332 SkTSwap(iSpan, oSpan);
333 } while (sumWinding == SK_MinS32 && iSpan == start);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000334 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000335 if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000336 return current;
337 }
338 int contourWinding;
339 int oppContourWinding = 0;
340 // the simple upward projection of the unresolved points hit unsortable angles
341 // shoot rays at right angles to the segment to find its winding, ignoring angle cases
342 bool tryAgain;
343 double tHit;
344 SkScalar hitDx = 0;
345 SkScalar hitOppDx = 0;
caryclark65f55312014-11-13 06:58:52 -0800346 // keep track of subsequent returns to detect infinite loops
caryclark54359292015-03-26 07:52:43 -0700347 SkTDArray<SortableTop2> sortableTops;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000348 do {
349 // if current is vertical, find another candidate which is not
350 // if only remaining candidates are vertical, then they can be marked done
caryclark54359292015-03-26 07:52:43 -0700351 SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
352 SkASSERT(current == (*startPtr)->segment());
353 skipVertical(contourList, &current, startPtr, endPtr);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000354 SkASSERT(current); // FIXME: if null, all remaining are vertical
caryclark54359292015-03-26 07:52:43 -0700355 SkASSERT(*startPtr != *endPtr && *startPtr && *endPtr);
356 SkASSERT(current == (*startPtr)->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000357 tryAgain = false;
caryclark54359292015-03-26 07:52:43 -0700358 contourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &tHit,
caryclarkdac1d172014-06-17 05:15:38 -0700359 &hitDx, &tryAgain, onlyVertical, false);
caryclark54359292015-03-26 07:52:43 -0700360 SkASSERT(current == (*startPtr)->segment());
caryclark65f55312014-11-13 06:58:52 -0800361 if (tryAgain) {
362 bool giveUp = false;
363 int count = sortableTops.count();
364 for (int index = 0; index < count; ++index) {
caryclark54359292015-03-26 07:52:43 -0700365 const SortableTop2& prev = sortableTops[index];
caryclark65f55312014-11-13 06:58:52 -0800366 if (giveUp) {
caryclark54359292015-03-26 07:52:43 -0700367 prev.fStart->segment()->markDone(prev.fStart->starter(prev.fEnd));
368 } else if (prev.fStart == *startPtr || prev.fEnd == *endPtr) {
caryclark65f55312014-11-13 06:58:52 -0800369 // remaining edges are non-vertical and cannot have their winding computed
370 // mark them as done and return, and hope that assembly can fill the holes
371 giveUp = true;
372 index = -1;
373 }
374 }
375 if (giveUp) {
376 *done = true;
377 return NULL;
378 }
379 }
caryclark54359292015-03-26 07:52:43 -0700380 SortableTop2* sortableTop = sortableTops.append();
381 sortableTop->fStart = *startPtr;
382 sortableTop->fEnd = *endPtr;
caryclark65f55312014-11-13 06:58:52 -0800383#if DEBUG_SORT
384 SkDebugf("%s current=%d index=%d endIndex=%d tHit=%1.9g hitDx=%1.9g try=%d vert=%d\n",
caryclark54359292015-03-26 07:52:43 -0700385 __FUNCTION__, current->debugID(), (*startPtr)->debugID(), (*endPtr)->debugID(),
386 tHit, hitDx, tryAgain, *onlyVertical);
caryclark65f55312014-11-13 06:58:52 -0800387#endif
caryclarkdac1d172014-06-17 05:15:38 -0700388 if (*onlyVertical) {
389 return current;
390 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000391 if (tryAgain) {
392 continue;
393 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000394 if (angleIncludeType < SkOpAngle::kBinarySingle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000395 break;
396 }
caryclark54359292015-03-26 07:52:43 -0700397 oppContourWinding = rightAngleWinding(contourList, &current, startPtr, endPtr, &tHit,
caryclarkdac1d172014-06-17 05:15:38 -0700398 &hitOppDx, &tryAgain, NULL, true);
caryclark54359292015-03-26 07:52:43 -0700399 SkASSERT(current == (*startPtr)->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000400 } while (tryAgain);
caryclark54359292015-03-26 07:52:43 -0700401 bool success = current->initWinding(*startPtr, *endPtr, tHit, contourWinding, hitDx,
caryclark65f55312014-11-13 06:58:52 -0800402 oppContourWinding, hitOppDx);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000403 if (current->done()) {
404 return NULL;
caryclark65f55312014-11-13 06:58:52 -0800405 } else if (!success) { // check if the span has a valid winding
caryclark54359292015-03-26 07:52:43 -0700406 SkOpSpan* minSpan = (*startPtr)->t() < (*endPtr)->t() ? (*startPtr)->upCast()
407 : (*endPtr)->upCast();
408 if (minSpan->windSum() == SK_MinS32) {
caryclark65f55312014-11-13 06:58:52 -0800409 return NULL;
410 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000411 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000412 return current;
413}
414
caryclark54359292015-03-26 07:52:43 -0700415void MakeContourList(SkOpContour* contour, SkTDArray<SkOpContour* >& list,
reed0dc4dd62015-03-24 13:55:33 -0700416 bool evenOdd, bool oppEvenOdd) {
caryclark54359292015-03-26 07:52:43 -0700417 do {
418 if (contour->count()) {
419 contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
420 *list.append() = contour;
421 }
422 } while ((contour = contour->next()));
423 if (list.count() < 2) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000424 return;
425 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000426 SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000427}
428
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000429class DistanceLessThan {
430public:
431 DistanceLessThan(double* distances) : fDistances(distances) { }
432 double* fDistances;
433 bool operator()(const int one, const int two) {
434 return fDistances[one] < fDistances[two];
435 }
436};
437
caryclark@google.com07393ca2013-04-08 11:47:37 +0000438 /*
439 check start and end of each contour
440 if not the same, record them
441 match them up
442 connect closest
443 reassemble contour pieces into new path
444 */
445void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
caryclark54359292015-03-26 07:52:43 -0700446 SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
447 SkOpContour contour;
caryclark1049f122015-04-20 08:31:59 -0700448 SkOpGlobalState globalState(NULL SkDEBUGPARAMS(&contour));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000449#if DEBUG_PATH_CONSTRUCTION
450 SkDebugf("%s\n", __FUNCTION__);
451#endif
caryclark54359292015-03-26 07:52:43 -0700452 SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
453 builder.finish(&allocator);
454 SkTDArray<const SkOpContour* > runs; // indices of partial contours
455 const SkOpContour* eContour = builder.head();
456 do {
457 if (!eContour->count()) {
458 continue;
459 }
460 const SkPoint& eStart = eContour->start();
461 const SkPoint& eEnd = eContour->end();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000462#if DEBUG_ASSEMBLE
463 SkDebugf("%s contour", __FUNCTION__);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000464 if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000465 SkDebugf("[%d]", runs.count());
466 } else {
467 SkDebugf(" ");
468 }
469 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
470 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
471#endif
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000472 if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
caryclark54359292015-03-26 07:52:43 -0700473 eContour->toPath(simple);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000474 continue;
475 }
caryclark54359292015-03-26 07:52:43 -0700476 *runs.append() = eContour;
477 } while ((eContour = eContour->next()));
478 int count = runs.count();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000479 if (count == 0) {
480 return;
481 }
caryclark54359292015-03-26 07:52:43 -0700482 SkTDArray<int> sLink, eLink;
483 sLink.append(count);
484 eLink.append(count);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000485 int rIndex, iIndex;
486 for (rIndex = 0; rIndex < count; ++rIndex) {
487 sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
488 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000489 const int ends = count * 2; // all starts and ends
490 const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
caryclark54359292015-03-26 07:52:43 -0700491 SkTDArray<double> distances;
492 distances.append(entries);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000493 for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
caryclark54359292015-03-26 07:52:43 -0700494 const SkOpContour* oContour = runs[rIndex >> 1];
495 const SkPoint& oPt = rIndex & 1 ? oContour->end() : oContour->start();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000496 const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
497 * ends - rIndex - 1;
498 for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
caryclark54359292015-03-26 07:52:43 -0700499 const SkOpContour* iContour = runs[iIndex >> 1];
500 const SkPoint& iPt = iIndex & 1 ? iContour->end() : iContour->start();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000501 double dx = iPt.fX - oPt.fX;
502 double dy = iPt.fY - oPt.fY;
503 double dist = dx * dx + dy * dy;
504 distances[row + iIndex] = dist; // oStart distance from iStart
505 }
506 }
caryclark54359292015-03-26 07:52:43 -0700507 SkTDArray<int> sortedDist;
508 sortedDist.append(entries);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000509 for (rIndex = 0; rIndex < entries; ++rIndex) {
510 sortedDist[rIndex] = rIndex;
511 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000512 SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000513 int remaining = count; // number of start/end pairs
514 for (rIndex = 0; rIndex < entries; ++rIndex) {
515 int pair = sortedDist[rIndex];
516 int row = pair / ends;
517 int col = pair - row * ends;
518 int thingOne = row < col ? row : ends - row - 2;
519 int ndxOne = thingOne >> 1;
520 bool endOne = thingOne & 1;
521 int* linkOne = endOne ? eLink.begin() : sLink.begin();
522 if (linkOne[ndxOne] != SK_MaxS32) {
523 continue;
524 }
525 int thingTwo = row < col ? col : ends - row + col - 1;
526 int ndxTwo = thingTwo >> 1;
527 bool endTwo = thingTwo & 1;
528 int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
529 if (linkTwo[ndxTwo] != SK_MaxS32) {
530 continue;
531 }
532 SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
533 bool flip = endOne == endTwo;
534 linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
535 linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
536 if (!--remaining) {
537 break;
538 }
539 }
540 SkASSERT(!remaining);
541#if DEBUG_ASSEMBLE
542 for (rIndex = 0; rIndex < count; ++rIndex) {
543 int s = sLink[rIndex];
544 int e = eLink[rIndex];
545 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
546 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
547 }
548#endif
549 rIndex = 0;
550 do {
551 bool forward = true;
552 bool first = true;
553 int sIndex = sLink[rIndex];
554 SkASSERT(sIndex != SK_MaxS32);
555 sLink[rIndex] = SK_MaxS32;
556 int eIndex;
557 if (sIndex < 0) {
558 eIndex = sLink[~sIndex];
559 sLink[~sIndex] = SK_MaxS32;
560 } else {
561 eIndex = eLink[sIndex];
562 eLink[sIndex] = SK_MaxS32;
563 }
564 SkASSERT(eIndex != SK_MaxS32);
565#if DEBUG_ASSEMBLE
566 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
567 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
568 eIndex < 0 ? ~eIndex : eIndex);
569#endif
570 do {
caryclark54359292015-03-26 07:52:43 -0700571 const SkOpContour* contour = runs[rIndex];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000572 if (first) {
573 first = false;
caryclark54359292015-03-26 07:52:43 -0700574 const SkPoint* startPtr = &contour->start();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000575 simple->deferredMove(startPtr[0]);
576 }
577 if (forward) {
caryclark54359292015-03-26 07:52:43 -0700578 contour->toPartialForward(simple);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000579 } else {
caryclark54359292015-03-26 07:52:43 -0700580 contour->toPartialBackward(simple);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000581 }
582#if DEBUG_ASSEMBLE
583 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
584 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
585 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
586#endif
587 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
588 simple->close();
589 break;
590 }
591 if (forward) {
592 eIndex = eLink[rIndex];
593 SkASSERT(eIndex != SK_MaxS32);
594 eLink[rIndex] = SK_MaxS32;
595 if (eIndex >= 0) {
596 SkASSERT(sLink[eIndex] == rIndex);
597 sLink[eIndex] = SK_MaxS32;
598 } else {
599 SkASSERT(eLink[~eIndex] == ~rIndex);
600 eLink[~eIndex] = SK_MaxS32;
601 }
602 } else {
603 eIndex = sLink[rIndex];
604 SkASSERT(eIndex != SK_MaxS32);
605 sLink[rIndex] = SK_MaxS32;
606 if (eIndex >= 0) {
607 SkASSERT(eLink[eIndex] == rIndex);
608 eLink[eIndex] = SK_MaxS32;
609 } else {
610 SkASSERT(sLink[~eIndex] == ~rIndex);
611 sLink[~eIndex] = SK_MaxS32;
612 }
613 }
614 rIndex = eIndex;
615 if (rIndex < 0) {
616 forward ^= 1;
617 rIndex = ~rIndex;
618 }
619 } while (true);
620 for (rIndex = 0; rIndex < count; ++rIndex) {
621 if (sLink[rIndex] != SK_MaxS32) {
622 break;
623 }
624 }
625 } while (rIndex < count);
626#if DEBUG_ASSEMBLE
627 for (rIndex = 0; rIndex < count; ++rIndex) {
628 SkASSERT(sLink[rIndex] == SK_MaxS32);
629 SkASSERT(eLink[rIndex] == SK_MaxS32);
630 }
631#endif
632}
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000633
caryclark54359292015-03-26 07:52:43 -0700634static void align(SkTDArray<SkOpContour* >* contourList) {
635 int contourCount = (*contourList).count();
636 for (int cTest = 0; cTest < contourCount; ++cTest) {
637 SkOpContour* contour = (*contourList)[cTest];
638 contour->align();
639 }
640}
641
642static void calcAngles(SkTDArray<SkOpContour* >* contourList, SkChunkAlloc* allocator) {
643 int contourCount = (*contourList).count();
644 for (int cTest = 0; cTest < contourCount; ++cTest) {
645 SkOpContour* contour = (*contourList)[cTest];
646 contour->calcAngles(allocator);
647 }
648}
649
650static void missingCoincidence(SkTDArray<SkOpContour* >* contourList,
651 SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
652 int contourCount = (*contourList).count();
653 for (int cTest = 0; cTest < contourCount; ++cTest) {
654 SkOpContour* contour = (*contourList)[cTest];
655 contour->missingCoincidence(coincidence, allocator);
656 }
657}
658
659static bool moveNearby(SkTDArray<SkOpContour* >* contourList) {
660 int contourCount = (*contourList).count();
661 for (int cTest = 0; cTest < contourCount; ++cTest) {
662 SkOpContour* contour = (*contourList)[cTest];
663 if (!contour->moveNearby()) {
664 return false;
665 }
666 }
667 return true;
668}
669
670static void sortAngles(SkTDArray<SkOpContour* >* contourList) {
671 int contourCount = (*contourList).count();
672 for (int cTest = 0; cTest < contourCount; ++cTest) {
673 SkOpContour* contour = (*contourList)[cTest];
674 contour->sortAngles();
675 }
676}
677
678static void sortSegments(SkTDArray<SkOpContour* >* contourList) {
679 int contourCount = (*contourList).count();
680 for (int cTest = 0; cTest < contourCount; ++cTest) {
681 SkOpContour* contour = (*contourList)[cTest];
682 contour->sortSegments();
683 }
684}
685
686bool HandleCoincidence(SkTDArray<SkOpContour* >* contourList, SkOpCoincidence* coincidence,
687 SkChunkAlloc* allocator, SkOpGlobalState* globalState) {
688 // move t values and points together to eliminate small/tiny gaps
689 if (!moveNearby(contourList)) {
caryclark630240d2014-09-19 06:33:31 -0700690 return false;
691 }
caryclark54359292015-03-26 07:52:43 -0700692 align(contourList); // give all span members common values
693#if DEBUG_VALIDATE
694 globalState->setPhase(SkOpGlobalState::kIntersecting);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000695#endif
caryclark54359292015-03-26 07:52:43 -0700696 coincidence->addMissing(allocator);
697#if DEBUG_VALIDATE
698 globalState->setPhase(SkOpGlobalState::kWalking);
699#endif
700 coincidence->expand(); // check to see if, loosely, coincident ranges may be expanded
701 coincidence->mark(); // mark spans of coincident segments as coincident
702 missingCoincidence(contourList, coincidence, allocator); // look for coincidence missed earlier
703 if (!coincidence->apply()) { // adjust the winding value to account for coincident edges
caryclark65f55312014-11-13 06:58:52 -0800704 return false;
705 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000706 sortSegments(contourList);
caryclark54359292015-03-26 07:52:43 -0700707 calcAngles(contourList, allocator);
reed0dc4dd62015-03-24 13:55:33 -0700708 sortAngles(contourList);
caryclark54359292015-03-26 07:52:43 -0700709 if (globalState->angleCoincidence()) {
710 missingCoincidence(contourList, coincidence, allocator);
711 if (!coincidence->apply()) {
712 return false;
713 }
714 }
715#if DEBUG_ACTIVE_SPANS
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000716 DebugShowActiveSpans(*contourList);
717#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000718 return true;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000719}