blob: 7dd13a7fe8dd6958788c006d35c605f9e153da68 [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 */
7#include "SkOpEdgeBuilder.h"
8#include "SkPathOpsCommon.h"
9#include "SkPathWriter.h"
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +000010#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000011
caryclark@google.comd892bd82013-06-17 14:10:36 +000012static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, SkOpSegment** currentPtr,
caryclark@google.com07393ca2013-04-08 11:47:37 +000013 int* indexPtr, int* endIndexPtr, double* bestHit, SkScalar* bestDx,
14 bool* tryAgain, double* midPtr, bool opp) {
15 const int index = *indexPtr;
16 const int endIndex = *endIndexPtr;
17 const double mid = *midPtr;
18 const SkOpSegment* current = *currentPtr;
19 double tAtMid = current->tAtMid(index, endIndex, mid);
caryclark@google.com4fdbb222013-07-23 15:27:41 +000020 SkPoint basePt = current->ptAtT(tAtMid);
caryclark@google.com07393ca2013-04-08 11:47:37 +000021 int contourCount = contourList.count();
22 SkScalar bestY = SK_ScalarMin;
23 SkOpSegment* bestSeg = NULL;
caryclark@google.com3e475dc2013-04-11 14:09:50 +000024 int bestTIndex = 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +000025 bool bestOpp;
26 bool hitSomething = false;
27 for (int cTest = 0; cTest < contourCount; ++cTest) {
28 SkOpContour* contour = contourList[cTest];
29 bool testOpp = contour->operand() ^ current->operand() ^ opp;
30 if (basePt.fY < contour->bounds().fTop) {
31 continue;
32 }
33 if (bestY > contour->bounds().fBottom) {
34 continue;
35 }
36 int segmentCount = contour->segments().count();
37 for (int test = 0; test < segmentCount; ++test) {
38 SkOpSegment* testSeg = &contour->segments()[test];
39 SkScalar testY = bestY;
40 double testHit;
41 int testTIndex = testSeg->crossedSpanY(basePt, &testY, &testHit, &hitSomething, tAtMid,
42 testOpp, testSeg == current);
43 if (testTIndex < 0) {
44 if (testTIndex == SK_MinS32) {
45 hitSomething = true;
46 bestSeg = NULL;
47 goto abortContours; // vertical encountered, return and try different point
48 }
49 continue;
50 }
51 if (testSeg == current && current->betweenTs(index, testHit, endIndex)) {
52 double baseT = current->t(index);
53 double endT = current->t(endIndex);
54 double newMid = (testHit - baseT) / (endT - baseT);
55#if DEBUG_WINDING
56 double midT = current->tAtMid(index, endIndex, mid);
57 SkPoint midXY = current->xyAtT(midT);
58 double newMidT = current->tAtMid(index, endIndex, newMid);
59 SkPoint newXY = current->xyAtT(newMidT);
60 SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)"
61 " n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__,
62 current->debugID(), mid, newMid,
63 baseT, current->xAtT(index), current->yAtT(index),
64 baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
65 baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
66 endT, current->xAtT(endIndex), current->yAtT(endIndex));
67#endif
68 *midPtr = newMid * 2; // calling loop with divide by 2 before continuing
69 return SK_MinS32;
70 }
71 bestSeg = testSeg;
72 *bestHit = testHit;
73 bestOpp = testOpp;
74 bestTIndex = testTIndex;
75 bestY = testY;
76 }
77 }
78abortContours:
79 int result;
80 if (!bestSeg) {
81 result = hitSomething ? SK_MinS32 : 0;
82 } else {
83 if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
84 *currentPtr = bestSeg;
85 *indexPtr = bestTIndex;
86 *endIndexPtr = bestSeg->nextSpan(bestTIndex, 1);
87 SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
88 *tryAgain = true;
89 return 0;
90 }
91 result = bestSeg->windingAtT(*bestHit, bestTIndex, bestOpp, bestDx);
caryclark@google.comf11a5af2013-04-10 18:55:11 +000092 SkASSERT(result == SK_MinS32 || *bestDx);
caryclark@google.com07393ca2013-04-08 11:47:37 +000093 }
94 double baseT = current->t(index);
95 double endT = current->t(endIndex);
96 *bestHit = baseT + mid * (endT - baseT);
97 return result;
98}
99
caryclark@google.comd892bd82013-06-17 14:10:36 +0000100SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000101 int contourCount = contourList.count();
102 SkOpSegment* result;
103 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
104 SkOpContour* contour = contourList[cIndex];
105 result = contour->undoneSegment(start, end);
106 if (result) {
107 return result;
108 }
109 }
110 return NULL;
111}
112
113SkOpSegment* FindChase(SkTDArray<SkOpSpan*>& chase, int& tIndex, int& endIndex) {
114 while (chase.count()) {
115 SkOpSpan* span;
116 chase.pop(&span);
117 const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
118 SkOpSegment* segment = backPtr.fOther;
119 tIndex = backPtr.fOtherIndex;
caryclark@google.comd892bd82013-06-17 14:10:36 +0000120 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000121 int done = 0;
122 if (segment->activeAngle(tIndex, &done, &angles)) {
123 SkOpAngle* last = angles.end() - 1;
124 tIndex = last->start();
125 endIndex = last->end();
126 #if TRY_ROTATE
127 *chase.insert(0) = span;
128 #else
129 *chase.append() = span;
130 #endif
131 return last->segment();
132 }
133 if (done == angles.count()) {
134 continue;
135 }
caryclark@google.comd892bd82013-06-17 14:10:36 +0000136 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000137 bool sortable = SkOpSegment::SortAngles(angles, &sorted,
138 SkOpSegment::kMayBeUnordered_SortAngleKind);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000139 int angleCount = sorted.count();
140#if DEBUG_SORT
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000141 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000142#endif
143 if (!sortable) {
144 continue;
145 }
146 // find first angle, initialize winding to computed fWindSum
147 int firstIndex = -1;
148 const SkOpAngle* angle;
149 int winding;
150 do {
151 angle = sorted[++firstIndex];
152 segment = angle->segment();
153 winding = segment->windSum(angle);
154 } while (winding == SK_MinS32);
155 int spanWinding = segment->spanSign(angle->start(), angle->end());
156 #if DEBUG_WINDING
157 SkDebugf("%s winding=%d spanWinding=%d\n",
158 __FUNCTION__, winding, spanWinding);
159 #endif
160 // turn span winding into contour winding
161 if (spanWinding * winding < 0) {
162 winding += spanWinding;
163 }
164 #if DEBUG_SORT
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000165 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000166 #endif
167 // we care about first sign and whether wind sum indicates this
168 // edge is inside or outside. Maybe need to pass span winding
169 // or first winding or something into this function?
170 // advance to first undone angle, then return it and winding
171 // (to set whether edges are active or not)
172 int nextIndex = firstIndex + 1;
173 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
174 angle = sorted[firstIndex];
175 winding -= angle->segment()->spanSign(angle);
176 do {
177 SkASSERT(nextIndex != firstIndex);
178 if (nextIndex == angleCount) {
179 nextIndex = 0;
180 }
181 angle = sorted[nextIndex];
182 segment = angle->segment();
183 int maxWinding = winding;
184 winding -= segment->spanSign(angle);
185 #if DEBUG_SORT
186 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
187 segment->debugID(), maxWinding, winding, angle->sign());
188 #endif
189 tIndex = angle->start();
190 endIndex = angle->end();
191 int lesser = SkMin32(tIndex, endIndex);
192 const SkOpSpan& nextSpan = segment->span(lesser);
193 if (!nextSpan.fDone) {
194 // FIXME: this be wrong? assign startWinding if edge is in
195 // same direction. If the direction is opposite, winding to
196 // assign is flipped sign or +/- 1?
197 if (SkOpSegment::UseInnerWinding(maxWinding, winding)) {
198 maxWinding = winding;
199 }
200 segment->markAndChaseWinding(angle, maxWinding, 0);
201 break;
202 }
203 } while (++nextIndex != lastIndex);
204 *chase.insert(0) = span;
205 return segment;
206 }
207 return NULL;
208}
209
caryclark@google.coma5e55922013-05-07 18:51:31 +0000210#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
caryclark@google.comd892bd82013-06-17 14:10:36 +0000211void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000212 int index;
213 for (index = 0; index < contourList.count(); ++ index) {
214 contourList[index]->debugShowActiveSpans();
215 }
216}
217#endif
218
caryclark@google.comd892bd82013-06-17 14:10:36 +0000219static SkOpSegment* findSortableTop(const SkTArray<SkOpContour*, true>& contourList,
caryclark@google.com07393ca2013-04-08 11:47:37 +0000220 int* index, int* endIndex, SkPoint* topLeft, bool* unsortable,
221 bool* done, bool onlySortable) {
222 SkOpSegment* result;
223 do {
224 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
225 int contourCount = contourList.count();
226 SkOpSegment* topStart = NULL;
227 *done = true;
228 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
229 SkOpContour* contour = contourList[cIndex];
230 if (contour->done()) {
231 continue;
232 }
233 const SkPathOpsBounds& bounds = contour->bounds();
234 if (bounds.fBottom < topLeft->fY) {
235 *done = false;
236 continue;
237 }
238 if (bounds.fBottom == topLeft->fY && bounds.fRight < topLeft->fX) {
239 *done = false;
240 continue;
241 }
242 contour->topSortableSegment(*topLeft, &bestXY, &topStart);
243 if (!contour->done()) {
244 *done = false;
245 }
246 }
247 if (!topStart) {
248 return NULL;
249 }
250 *topLeft = bestXY;
251 result = topStart->findTop(index, endIndex, unsortable, onlySortable);
252 } while (!result);
caryclark@google.com570863f2013-09-16 15:55:01 +0000253 if (result) {
254 *unsortable = false;
255 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000256 return result;
257}
258
caryclark@google.comd892bd82013-06-17 14:10:36 +0000259static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList,
caryclark@google.com07393ca2013-04-08 11:47:37 +0000260 SkOpSegment** current, int* index, int* endIndex, double* tHit,
261 SkScalar* hitDx, bool* tryAgain, bool opp) {
262 double test = 0.9;
263 int contourWinding;
264 do {
265 contourWinding = contourRangeCheckY(contourList, current, index, endIndex, tHit, hitDx,
266 tryAgain, &test, opp);
267 if (contourWinding != SK_MinS32 || *tryAgain) {
268 return contourWinding;
269 }
270 test /= 2;
271 } while (!approximately_negative(test));
272 SkASSERT(0); // should be OK to comment out, but interested when this hits
273 return contourWinding;
274}
275
caryclark@google.comd892bd82013-06-17 14:10:36 +0000276static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
caryclark@google.com07393ca2013-04-08 11:47:37 +0000277 SkOpSegment** current, int* index, int* endIndex) {
278 if (!(*current)->isVertical(*index, *endIndex)) {
279 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 }
287 *current = contour->nonVerticalSegment(index, endIndex);
288 if (*current) {
289 return;
290 }
291 }
292}
293
caryclark@google.com570863f2013-09-16 15:55:01 +0000294SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
295 SkOpAngle::IncludeType angleIncludeType, bool* firstContour, int* indexPtr,
296 int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000297 SkOpSegment* current = findSortableTop(contourList, indexPtr, endIndexPtr, topLeft, unsortable,
298 done, true);
299 if (!current) {
300 return NULL;
301 }
302 const int index = *indexPtr;
303 const int endIndex = *endIndexPtr;
304 if (*firstContour) {
305 current->initWinding(index, endIndex);
306 *firstContour = false;
307 return current;
308 }
309 int minIndex = SkMin32(index, endIndex);
310 int sumWinding = current->windSum(minIndex);
311 if (sumWinding != SK_MinS32) {
312 return current;
313 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000314 SkASSERT(current->windSum(SkMin32(index, endIndex)) == SK_MinS32);
315 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
316 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
317 sumWinding = current->computeSum(index, endIndex, angleIncludeType, &angles, &sorted);
318 if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000319 return current;
320 }
321 int contourWinding;
322 int oppContourWinding = 0;
323 // the simple upward projection of the unresolved points hit unsortable angles
324 // shoot rays at right angles to the segment to find its winding, ignoring angle cases
325 bool tryAgain;
326 double tHit;
327 SkScalar hitDx = 0;
328 SkScalar hitOppDx = 0;
329 do {
330 // if current is vertical, find another candidate which is not
331 // if only remaining candidates are vertical, then they can be marked done
332 SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
333 skipVertical(contourList, &current, indexPtr, endIndexPtr);
skia.committer@gmail.com32840172013-04-09 07:01:27 +0000334
caryclark@google.com07393ca2013-04-08 11:47:37 +0000335 SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
336 tryAgain = false;
337 contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
338 &hitDx, &tryAgain, false);
339 if (tryAgain) {
340 continue;
341 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000342 if (angleIncludeType < SkOpAngle::kBinarySingle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000343 break;
344 }
345 oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
346 &hitOppDx, &tryAgain, true);
347 } while (tryAgain);
348 current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx, oppContourWinding,
349 hitOppDx);
350 return current;
351}
352
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000353void CheckEnds(SkTArray<SkOpContour*, true>* contourList) {
354 // it's hard to determine if the end of a cubic or conic nearly intersects another curve.
355 // instead, look to see if the connecting curve intersected at that same end.
356 int contourCount = (*contourList).count();
357 for (int cTest = 0; cTest < contourCount; ++cTest) {
358 SkOpContour* contour = (*contourList)[cTest];
359 contour->checkEnds();
360 }
361}
362
caryclark@google.com570863f2013-09-16 15:55:01 +0000363// A tiny interval may indicate an undiscovered coincidence. Find and fix.
364void CheckTiny(SkTArray<SkOpContour*, true>* contourList) {
365 int contourCount = (*contourList).count();
366 for (int cTest = 0; cTest < contourCount; ++cTest) {
367 SkOpContour* contour = (*contourList)[cTest];
368 contour->checkTiny();
369 }
370}
371
caryclark@google.comd892bd82013-06-17 14:10:36 +0000372void FixOtherTIndex(SkTArray<SkOpContour*, true>* contourList) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000373 int contourCount = (*contourList).count();
374 for (int cTest = 0; cTest < contourCount; ++cTest) {
375 SkOpContour* contour = (*contourList)[cTest];
376 contour->fixOtherTIndex();
377 }
378}
379
caryclark@google.comd892bd82013-06-17 14:10:36 +0000380void SortSegments(SkTArray<SkOpContour*, true>* contourList) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000381 int contourCount = (*contourList).count();
382 for (int cTest = 0; cTest < contourCount; ++cTest) {
383 SkOpContour* contour = (*contourList)[cTest];
384 contour->sortSegments();
385 }
386}
387
caryclark@google.comd892bd82013-06-17 14:10:36 +0000388void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
caryclark@google.com07393ca2013-04-08 11:47:37 +0000389 bool evenOdd, bool oppEvenOdd) {
390 int count = contours.count();
391 if (count == 0) {
392 return;
393 }
394 for (int index = 0; index < count; ++index) {
395 SkOpContour& contour = contours[index];
396 contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
caryclark@google.comd892bd82013-06-17 14:10:36 +0000397 list.push_back(&contour);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000398 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000399 SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000400}
401
402static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
403 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
404}
405
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000406class DistanceLessThan {
407public:
408 DistanceLessThan(double* distances) : fDistances(distances) { }
409 double* fDistances;
410 bool operator()(const int one, const int two) {
411 return fDistances[one] < fDistances[two];
412 }
413};
414
caryclark@google.com07393ca2013-04-08 11:47:37 +0000415 /*
416 check start and end of each contour
417 if not the same, record them
418 match them up
419 connect closest
420 reassemble contour pieces into new path
421 */
422void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
423#if DEBUG_PATH_CONSTRUCTION
424 SkDebugf("%s\n", __FUNCTION__);
425#endif
426 SkTArray<SkOpContour> contours;
427 SkOpEdgeBuilder builder(path, contours);
428 builder.finish();
429 int count = contours.count();
430 int outer;
caryclark@google.comd892bd82013-06-17 14:10:36 +0000431 SkTArray<int, true> runs(count); // indices of partial contours
caryclark@google.com07393ca2013-04-08 11:47:37 +0000432 for (outer = 0; outer < count; ++outer) {
433 const SkOpContour& eContour = contours[outer];
434 const SkPoint& eStart = eContour.start();
435 const SkPoint& eEnd = eContour.end();
436#if DEBUG_ASSEMBLE
437 SkDebugf("%s contour", __FUNCTION__);
438 if (!approximatelyEqual(eStart, eEnd)) {
439 SkDebugf("[%d]", runs.count());
440 } else {
441 SkDebugf(" ");
442 }
443 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
444 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
445#endif
446 if (approximatelyEqual(eStart, eEnd)) {
447 eContour.toPath(simple);
448 continue;
449 }
caryclark@google.comd892bd82013-06-17 14:10:36 +0000450 runs.push_back(outer);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000451 }
452 count = runs.count();
453 if (count == 0) {
454 return;
455 }
caryclark@google.comd892bd82013-06-17 14:10:36 +0000456 SkTArray<int, true> sLink, eLink;
457 sLink.push_back_n(count);
458 eLink.push_back_n(count);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000459 int rIndex, iIndex;
460 for (rIndex = 0; rIndex < count; ++rIndex) {
461 sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
462 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000463 const int ends = count * 2; // all starts and ends
464 const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
caryclark@google.comd892bd82013-06-17 14:10:36 +0000465 SkTArray<double, true> distances;
466 distances.push_back_n(entries);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000467 for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
468 outer = runs[rIndex >> 1];
469 const SkOpContour& oContour = contours[outer];
470 const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
471 const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
472 * ends - rIndex - 1;
473 for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
474 int inner = runs[iIndex >> 1];
475 const SkOpContour& iContour = contours[inner];
476 const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
477 double dx = iPt.fX - oPt.fX;
478 double dy = iPt.fY - oPt.fY;
479 double dist = dx * dx + dy * dy;
480 distances[row + iIndex] = dist; // oStart distance from iStart
481 }
482 }
caryclark@google.comd892bd82013-06-17 14:10:36 +0000483 SkTArray<int, true> sortedDist;
484 sortedDist.push_back_n(entries);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000485 for (rIndex = 0; rIndex < entries; ++rIndex) {
486 sortedDist[rIndex] = rIndex;
487 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000488 SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000489 int remaining = count; // number of start/end pairs
490 for (rIndex = 0; rIndex < entries; ++rIndex) {
491 int pair = sortedDist[rIndex];
492 int row = pair / ends;
493 int col = pair - row * ends;
494 int thingOne = row < col ? row : ends - row - 2;
495 int ndxOne = thingOne >> 1;
496 bool endOne = thingOne & 1;
497 int* linkOne = endOne ? eLink.begin() : sLink.begin();
498 if (linkOne[ndxOne] != SK_MaxS32) {
499 continue;
500 }
501 int thingTwo = row < col ? col : ends - row + col - 1;
502 int ndxTwo = thingTwo >> 1;
503 bool endTwo = thingTwo & 1;
504 int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
505 if (linkTwo[ndxTwo] != SK_MaxS32) {
506 continue;
507 }
508 SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
509 bool flip = endOne == endTwo;
510 linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
511 linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
512 if (!--remaining) {
513 break;
514 }
515 }
516 SkASSERT(!remaining);
517#if DEBUG_ASSEMBLE
518 for (rIndex = 0; rIndex < count; ++rIndex) {
519 int s = sLink[rIndex];
520 int e = eLink[rIndex];
521 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
522 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
523 }
524#endif
525 rIndex = 0;
526 do {
527 bool forward = true;
528 bool first = true;
529 int sIndex = sLink[rIndex];
530 SkASSERT(sIndex != SK_MaxS32);
531 sLink[rIndex] = SK_MaxS32;
532 int eIndex;
533 if (sIndex < 0) {
534 eIndex = sLink[~sIndex];
535 sLink[~sIndex] = SK_MaxS32;
536 } else {
537 eIndex = eLink[sIndex];
538 eLink[sIndex] = SK_MaxS32;
539 }
540 SkASSERT(eIndex != SK_MaxS32);
541#if DEBUG_ASSEMBLE
542 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
543 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
544 eIndex < 0 ? ~eIndex : eIndex);
545#endif
546 do {
547 outer = runs[rIndex];
548 const SkOpContour& contour = contours[outer];
549 if (first) {
550 first = false;
551 const SkPoint* startPtr = &contour.start();
552 simple->deferredMove(startPtr[0]);
553 }
554 if (forward) {
555 contour.toPartialForward(simple);
556 } else {
557 contour.toPartialBackward(simple);
558 }
559#if DEBUG_ASSEMBLE
560 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
561 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
562 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
563#endif
564 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
565 simple->close();
566 break;
567 }
568 if (forward) {
569 eIndex = eLink[rIndex];
570 SkASSERT(eIndex != SK_MaxS32);
571 eLink[rIndex] = SK_MaxS32;
572 if (eIndex >= 0) {
573 SkASSERT(sLink[eIndex] == rIndex);
574 sLink[eIndex] = SK_MaxS32;
575 } else {
576 SkASSERT(eLink[~eIndex] == ~rIndex);
577 eLink[~eIndex] = SK_MaxS32;
578 }
579 } else {
580 eIndex = sLink[rIndex];
581 SkASSERT(eIndex != SK_MaxS32);
582 sLink[rIndex] = SK_MaxS32;
583 if (eIndex >= 0) {
584 SkASSERT(eLink[eIndex] == rIndex);
585 eLink[eIndex] = SK_MaxS32;
586 } else {
587 SkASSERT(sLink[~eIndex] == ~rIndex);
588 sLink[~eIndex] = SK_MaxS32;
589 }
590 }
591 rIndex = eIndex;
592 if (rIndex < 0) {
593 forward ^= 1;
594 rIndex = ~rIndex;
595 }
596 } while (true);
597 for (rIndex = 0; rIndex < count; ++rIndex) {
598 if (sLink[rIndex] != SK_MaxS32) {
599 break;
600 }
601 }
602 } while (rIndex < count);
603#if DEBUG_ASSEMBLE
604 for (rIndex = 0; rIndex < count; ++rIndex) {
605 SkASSERT(sLink[rIndex] == SK_MaxS32);
606 SkASSERT(eLink[rIndex] == SK_MaxS32);
607 }
608#endif
609}