SkPathOpsCommon.cpp revision 7eaa53d8f7e48fd17d02b5e3bd91f90e9c1899ef
1/*
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"
10#include "SkTSort.h"
11
12static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, SkOpSegment** currentPtr,
13                              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);
20    SkPoint basePt = current->ptAtT(tAtMid);
21    int contourCount = contourList.count();
22    SkScalar bestY = SK_ScalarMin;
23    SkOpSegment* bestSeg = NULL;
24    int bestTIndex = 0;
25    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);
92        SkASSERT(result == SK_MinS32 || *bestDx);
93    }
94    double baseT = current->t(index);
95    double endT = current->t(endIndex);
96    *bestHit = baseT + mid * (endT - baseT);
97    return result;
98}
99
100SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end) {
101    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;
120        SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
121        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        }
136        SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
137        bool sortable = SkOpSegment::SortAngles(angles, &sorted,
138                SkOpSegment::kMayBeUnordered_SortAngleKind);
139        int angleCount = sorted.count();
140#if DEBUG_SORT
141        sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0, sortable);
142#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
165        segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0, sortable);
166    #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
210#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
211void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList) {
212    int index;
213    for (index = 0; index < contourList.count(); ++ index) {
214        contourList[index]->debugShowActiveSpans();
215    }
216}
217#endif
218
219static SkOpSegment* findSortableTop(const SkTArray<SkOpContour*, true>& contourList,
220                                    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);
253    if (result) {
254        *unsortable = false;
255    }
256    return result;
257}
258
259static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList,
260                             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
276static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
277        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
294SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
295        SkOpAngle::IncludeType angleIncludeType, bool* firstContour, int* indexPtr,
296        int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done) {
297    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    }
314    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) {
319        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);
334
335        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        }
342        if (angleIncludeType < SkOpAngle::kBinarySingle) {
343            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
353void 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
363// 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
372void FixOtherTIndex(SkTArray<SkOpContour*, true>* contourList) {
373    int contourCount = (*contourList).count();
374    for (int cTest = 0; cTest < contourCount; ++cTest) {
375        SkOpContour* contour = (*contourList)[cTest];
376        contour->fixOtherTIndex();
377    }
378}
379
380void SortSegments(SkTArray<SkOpContour*, true>* contourList) {
381    int contourCount = (*contourList).count();
382    for (int cTest = 0; cTest < contourCount; ++cTest) {
383        SkOpContour* contour = (*contourList)[cTest];
384        contour->sortSegments();
385    }
386}
387
388void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
389                     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);
397        list.push_back(&contour);
398    }
399    SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
400}
401
402class DistanceLessThan {
403public:
404    DistanceLessThan(double* distances) : fDistances(distances) { }
405    double* fDistances;
406    bool operator()(const int one, const int two) {
407        return fDistances[one] < fDistances[two];
408    }
409};
410
411    /*
412        check start and end of each contour
413        if not the same, record them
414        match them up
415        connect closest
416        reassemble contour pieces into new path
417    */
418void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
419#if DEBUG_PATH_CONSTRUCTION
420    SkDebugf("%s\n", __FUNCTION__);
421#endif
422    SkTArray<SkOpContour> contours;
423    SkOpEdgeBuilder builder(path, contours);
424    builder.finish();
425    int count = contours.count();
426    int outer;
427    SkTArray<int, true> runs(count);  // indices of partial contours
428    for (outer = 0; outer < count; ++outer) {
429        const SkOpContour& eContour = contours[outer];
430        const SkPoint& eStart = eContour.start();
431        const SkPoint& eEnd = eContour.end();
432#if DEBUG_ASSEMBLE
433        SkDebugf("%s contour", __FUNCTION__);
434        if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
435            SkDebugf("[%d]", runs.count());
436        } else {
437            SkDebugf("   ");
438        }
439        SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
440                eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
441#endif
442        if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
443            eContour.toPath(simple);
444            continue;
445        }
446        runs.push_back(outer);
447    }
448    count = runs.count();
449    if (count == 0) {
450        return;
451    }
452    SkTArray<int, true> sLink, eLink;
453    sLink.push_back_n(count);
454    eLink.push_back_n(count);
455    int rIndex, iIndex;
456    for (rIndex = 0; rIndex < count; ++rIndex) {
457        sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
458    }
459    const int ends = count * 2;  // all starts and ends
460    const int entries = (ends - 1) * count;  // folded triangle : n * (n - 1) / 2
461    SkTArray<double, true> distances;
462    distances.push_back_n(entries);
463    for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
464        outer = runs[rIndex >> 1];
465        const SkOpContour& oContour = contours[outer];
466        const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
467        const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
468                * ends - rIndex - 1;
469        for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
470            int inner = runs[iIndex >> 1];
471            const SkOpContour& iContour = contours[inner];
472            const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
473            double dx = iPt.fX - oPt.fX;
474            double dy = iPt.fY - oPt.fY;
475            double dist = dx * dx + dy * dy;
476            distances[row + iIndex] = dist;  // oStart distance from iStart
477        }
478    }
479    SkTArray<int, true> sortedDist;
480    sortedDist.push_back_n(entries);
481    for (rIndex = 0; rIndex < entries; ++rIndex) {
482        sortedDist[rIndex] = rIndex;
483    }
484    SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
485    int remaining = count;  // number of start/end pairs
486    for (rIndex = 0; rIndex < entries; ++rIndex) {
487        int pair = sortedDist[rIndex];
488        int row = pair / ends;
489        int col = pair - row * ends;
490        int thingOne = row < col ? row : ends - row - 2;
491        int ndxOne = thingOne >> 1;
492        bool endOne = thingOne & 1;
493        int* linkOne = endOne ? eLink.begin() : sLink.begin();
494        if (linkOne[ndxOne] != SK_MaxS32) {
495            continue;
496        }
497        int thingTwo = row < col ? col : ends - row + col - 1;
498        int ndxTwo = thingTwo >> 1;
499        bool endTwo = thingTwo & 1;
500        int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
501        if (linkTwo[ndxTwo] != SK_MaxS32) {
502            continue;
503        }
504        SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
505        bool flip = endOne == endTwo;
506        linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
507        linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
508        if (!--remaining) {
509            break;
510        }
511    }
512    SkASSERT(!remaining);
513#if DEBUG_ASSEMBLE
514    for (rIndex = 0; rIndex < count; ++rIndex) {
515        int s = sLink[rIndex];
516        int e = eLink[rIndex];
517        SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
518                s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
519    }
520#endif
521    rIndex = 0;
522    do {
523        bool forward = true;
524        bool first = true;
525        int sIndex = sLink[rIndex];
526        SkASSERT(sIndex != SK_MaxS32);
527        sLink[rIndex] = SK_MaxS32;
528        int eIndex;
529        if (sIndex < 0) {
530            eIndex = sLink[~sIndex];
531            sLink[~sIndex] = SK_MaxS32;
532        } else {
533            eIndex = eLink[sIndex];
534            eLink[sIndex] = SK_MaxS32;
535        }
536        SkASSERT(eIndex != SK_MaxS32);
537#if DEBUG_ASSEMBLE
538        SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
539                    sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
540                    eIndex < 0 ? ~eIndex : eIndex);
541#endif
542        do {
543            outer = runs[rIndex];
544            const SkOpContour& contour = contours[outer];
545            if (first) {
546                first = false;
547                const SkPoint* startPtr = &contour.start();
548                simple->deferredMove(startPtr[0]);
549            }
550            if (forward) {
551                contour.toPartialForward(simple);
552            } else {
553                contour.toPartialBackward(simple);
554            }
555#if DEBUG_ASSEMBLE
556            SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
557                eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
558                sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
559#endif
560            if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
561                simple->close();
562                break;
563            }
564            if (forward) {
565                eIndex = eLink[rIndex];
566                SkASSERT(eIndex != SK_MaxS32);
567                eLink[rIndex] = SK_MaxS32;
568                if (eIndex >= 0) {
569                    SkASSERT(sLink[eIndex] == rIndex);
570                    sLink[eIndex] = SK_MaxS32;
571                } else {
572                    SkASSERT(eLink[~eIndex] == ~rIndex);
573                    eLink[~eIndex] = SK_MaxS32;
574                }
575            } else {
576                eIndex = sLink[rIndex];
577                SkASSERT(eIndex != SK_MaxS32);
578                sLink[rIndex] = SK_MaxS32;
579                if (eIndex >= 0) {
580                    SkASSERT(eLink[eIndex] == rIndex);
581                    eLink[eIndex] = SK_MaxS32;
582                } else {
583                    SkASSERT(sLink[~eIndex] == ~rIndex);
584                    sLink[~eIndex] = SK_MaxS32;
585                }
586            }
587            rIndex = eIndex;
588            if (rIndex < 0) {
589                forward ^= 1;
590                rIndex = ~rIndex;
591            }
592        } while (true);
593        for (rIndex = 0; rIndex < count; ++rIndex) {
594            if (sLink[rIndex] != SK_MaxS32) {
595                break;
596            }
597        }
598    } while (rIndex < count);
599#if DEBUG_ASSEMBLE
600    for (rIndex = 0; rIndex < count; ++rIndex) {
601       SkASSERT(sLink[rIndex] == SK_MaxS32);
602       SkASSERT(eLink[rIndex] == SK_MaxS32);
603    }
604#endif
605}
606