blob: e766f5abe2c383eb3ce2d782aea6e68986bb026c [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));
caryclark03b03ca2015-04-23 09:13:37 -0700449#if DEBUG_SHOW_TEST_NAME
450 SkDebugf("</div>\n");
451#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000452#if DEBUG_PATH_CONSTRUCTION
453 SkDebugf("%s\n", __FUNCTION__);
454#endif
caryclark54359292015-03-26 07:52:43 -0700455 SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
456 builder.finish(&allocator);
457 SkTDArray<const SkOpContour* > runs; // indices of partial contours
458 const SkOpContour* eContour = builder.head();
459 do {
460 if (!eContour->count()) {
461 continue;
462 }
463 const SkPoint& eStart = eContour->start();
464 const SkPoint& eEnd = eContour->end();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000465#if DEBUG_ASSEMBLE
466 SkDebugf("%s contour", __FUNCTION__);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000467 if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000468 SkDebugf("[%d]", runs.count());
469 } else {
470 SkDebugf(" ");
471 }
472 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
473 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
474#endif
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000475 if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
caryclark54359292015-03-26 07:52:43 -0700476 eContour->toPath(simple);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000477 continue;
478 }
caryclark54359292015-03-26 07:52:43 -0700479 *runs.append() = eContour;
480 } while ((eContour = eContour->next()));
481 int count = runs.count();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000482 if (count == 0) {
483 return;
484 }
caryclark54359292015-03-26 07:52:43 -0700485 SkTDArray<int> sLink, eLink;
486 sLink.append(count);
487 eLink.append(count);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000488 int rIndex, iIndex;
489 for (rIndex = 0; rIndex < count; ++rIndex) {
490 sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
491 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000492 const int ends = count * 2; // all starts and ends
493 const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
caryclark54359292015-03-26 07:52:43 -0700494 SkTDArray<double> distances;
495 distances.append(entries);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000496 for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
caryclark54359292015-03-26 07:52:43 -0700497 const SkOpContour* oContour = runs[rIndex >> 1];
498 const SkPoint& oPt = rIndex & 1 ? oContour->end() : oContour->start();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000499 const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
500 * ends - rIndex - 1;
501 for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
caryclark54359292015-03-26 07:52:43 -0700502 const SkOpContour* iContour = runs[iIndex >> 1];
503 const SkPoint& iPt = iIndex & 1 ? iContour->end() : iContour->start();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000504 double dx = iPt.fX - oPt.fX;
505 double dy = iPt.fY - oPt.fY;
506 double dist = dx * dx + dy * dy;
507 distances[row + iIndex] = dist; // oStart distance from iStart
508 }
509 }
caryclark54359292015-03-26 07:52:43 -0700510 SkTDArray<int> sortedDist;
511 sortedDist.append(entries);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000512 for (rIndex = 0; rIndex < entries; ++rIndex) {
513 sortedDist[rIndex] = rIndex;
514 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000515 SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000516 int remaining = count; // number of start/end pairs
517 for (rIndex = 0; rIndex < entries; ++rIndex) {
518 int pair = sortedDist[rIndex];
519 int row = pair / ends;
520 int col = pair - row * ends;
521 int thingOne = row < col ? row : ends - row - 2;
522 int ndxOne = thingOne >> 1;
523 bool endOne = thingOne & 1;
524 int* linkOne = endOne ? eLink.begin() : sLink.begin();
525 if (linkOne[ndxOne] != SK_MaxS32) {
526 continue;
527 }
528 int thingTwo = row < col ? col : ends - row + col - 1;
529 int ndxTwo = thingTwo >> 1;
530 bool endTwo = thingTwo & 1;
531 int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
532 if (linkTwo[ndxTwo] != SK_MaxS32) {
533 continue;
534 }
535 SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
536 bool flip = endOne == endTwo;
537 linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
538 linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
539 if (!--remaining) {
540 break;
541 }
542 }
543 SkASSERT(!remaining);
544#if DEBUG_ASSEMBLE
545 for (rIndex = 0; rIndex < count; ++rIndex) {
546 int s = sLink[rIndex];
547 int e = eLink[rIndex];
548 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
549 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
550 }
551#endif
552 rIndex = 0;
553 do {
554 bool forward = true;
555 bool first = true;
556 int sIndex = sLink[rIndex];
557 SkASSERT(sIndex != SK_MaxS32);
558 sLink[rIndex] = SK_MaxS32;
559 int eIndex;
560 if (sIndex < 0) {
561 eIndex = sLink[~sIndex];
562 sLink[~sIndex] = SK_MaxS32;
563 } else {
564 eIndex = eLink[sIndex];
565 eLink[sIndex] = SK_MaxS32;
566 }
567 SkASSERT(eIndex != SK_MaxS32);
568#if DEBUG_ASSEMBLE
569 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
570 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
571 eIndex < 0 ? ~eIndex : eIndex);
572#endif
573 do {
caryclark54359292015-03-26 07:52:43 -0700574 const SkOpContour* contour = runs[rIndex];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000575 if (first) {
576 first = false;
caryclark54359292015-03-26 07:52:43 -0700577 const SkPoint* startPtr = &contour->start();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000578 simple->deferredMove(startPtr[0]);
579 }
580 if (forward) {
caryclark54359292015-03-26 07:52:43 -0700581 contour->toPartialForward(simple);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000582 } else {
caryclark54359292015-03-26 07:52:43 -0700583 contour->toPartialBackward(simple);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000584 }
585#if DEBUG_ASSEMBLE
586 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
587 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
588 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
589#endif
590 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
591 simple->close();
592 break;
593 }
594 if (forward) {
595 eIndex = eLink[rIndex];
596 SkASSERT(eIndex != SK_MaxS32);
597 eLink[rIndex] = SK_MaxS32;
598 if (eIndex >= 0) {
599 SkASSERT(sLink[eIndex] == rIndex);
600 sLink[eIndex] = SK_MaxS32;
601 } else {
602 SkASSERT(eLink[~eIndex] == ~rIndex);
603 eLink[~eIndex] = SK_MaxS32;
604 }
605 } else {
606 eIndex = sLink[rIndex];
607 SkASSERT(eIndex != SK_MaxS32);
608 sLink[rIndex] = SK_MaxS32;
609 if (eIndex >= 0) {
610 SkASSERT(eLink[eIndex] == rIndex);
611 eLink[eIndex] = SK_MaxS32;
612 } else {
613 SkASSERT(sLink[~eIndex] == ~rIndex);
614 sLink[~eIndex] = SK_MaxS32;
615 }
616 }
617 rIndex = eIndex;
618 if (rIndex < 0) {
619 forward ^= 1;
620 rIndex = ~rIndex;
621 }
622 } while (true);
623 for (rIndex = 0; rIndex < count; ++rIndex) {
624 if (sLink[rIndex] != SK_MaxS32) {
625 break;
626 }
627 }
628 } while (rIndex < count);
629#if DEBUG_ASSEMBLE
630 for (rIndex = 0; rIndex < count; ++rIndex) {
631 SkASSERT(sLink[rIndex] == SK_MaxS32);
632 SkASSERT(eLink[rIndex] == SK_MaxS32);
633 }
634#endif
635}
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000636
caryclark54359292015-03-26 07:52:43 -0700637static void align(SkTDArray<SkOpContour* >* contourList) {
638 int contourCount = (*contourList).count();
639 for (int cTest = 0; cTest < contourCount; ++cTest) {
640 SkOpContour* contour = (*contourList)[cTest];
641 contour->align();
642 }
643}
644
645static void calcAngles(SkTDArray<SkOpContour* >* contourList, SkChunkAlloc* allocator) {
646 int contourCount = (*contourList).count();
647 for (int cTest = 0; cTest < contourCount; ++cTest) {
648 SkOpContour* contour = (*contourList)[cTest];
649 contour->calcAngles(allocator);
650 }
651}
652
653static void missingCoincidence(SkTDArray<SkOpContour* >* contourList,
654 SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
655 int contourCount = (*contourList).count();
656 for (int cTest = 0; cTest < contourCount; ++cTest) {
657 SkOpContour* contour = (*contourList)[cTest];
658 contour->missingCoincidence(coincidence, allocator);
659 }
660}
661
662static bool moveNearby(SkTDArray<SkOpContour* >* contourList) {
663 int contourCount = (*contourList).count();
664 for (int cTest = 0; cTest < contourCount; ++cTest) {
665 SkOpContour* contour = (*contourList)[cTest];
666 if (!contour->moveNearby()) {
667 return false;
668 }
669 }
670 return true;
671}
672
673static void sortAngles(SkTDArray<SkOpContour* >* contourList) {
674 int contourCount = (*contourList).count();
675 for (int cTest = 0; cTest < contourCount; ++cTest) {
676 SkOpContour* contour = (*contourList)[cTest];
677 contour->sortAngles();
678 }
679}
680
681static void sortSegments(SkTDArray<SkOpContour* >* contourList) {
682 int contourCount = (*contourList).count();
683 for (int cTest = 0; cTest < contourCount; ++cTest) {
684 SkOpContour* contour = (*contourList)[cTest];
685 contour->sortSegments();
686 }
687}
688
689bool HandleCoincidence(SkTDArray<SkOpContour* >* contourList, SkOpCoincidence* coincidence,
690 SkChunkAlloc* allocator, SkOpGlobalState* globalState) {
691 // move t values and points together to eliminate small/tiny gaps
692 if (!moveNearby(contourList)) {
caryclark630240d2014-09-19 06:33:31 -0700693 return false;
694 }
caryclark54359292015-03-26 07:52:43 -0700695 align(contourList); // give all span members common values
696#if DEBUG_VALIDATE
697 globalState->setPhase(SkOpGlobalState::kIntersecting);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000698#endif
caryclark54359292015-03-26 07:52:43 -0700699 coincidence->addMissing(allocator);
700#if DEBUG_VALIDATE
701 globalState->setPhase(SkOpGlobalState::kWalking);
702#endif
703 coincidence->expand(); // check to see if, loosely, coincident ranges may be expanded
704 coincidence->mark(); // mark spans of coincident segments as coincident
705 missingCoincidence(contourList, coincidence, allocator); // look for coincidence missed earlier
706 if (!coincidence->apply()) { // adjust the winding value to account for coincident edges
caryclark65f55312014-11-13 06:58:52 -0800707 return false;
708 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000709 sortSegments(contourList);
caryclark54359292015-03-26 07:52:43 -0700710 calcAngles(contourList, allocator);
reed0dc4dd62015-03-24 13:55:33 -0700711 sortAngles(contourList);
caryclark54359292015-03-26 07:52:43 -0700712 if (globalState->angleCoincidence()) {
713 missingCoincidence(contourList, coincidence, allocator);
714 if (!coincidence->apply()) {
715 return false;
716 }
717 }
718#if DEBUG_ACTIVE_SPANS
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000719 DebugShowActiveSpans(*contourList);
720#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000721 return true;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000722}