Home | History | Annotate | Download | only in pathops
      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 
     12 static 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     }
     78 abortContours:
     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 
    100 SkOpSegment* 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 
    113 SkOpSegment* 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
    211 void 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 
    219 static 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     return result;
    254 }
    255 
    256 static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList,
    257                              SkOpSegment** current, int* index, int* endIndex, double* tHit,
    258                              SkScalar* hitDx, bool* tryAgain, bool opp) {
    259     double test = 0.9;
    260     int contourWinding;
    261     do {
    262         contourWinding = contourRangeCheckY(contourList, current, index, endIndex, tHit, hitDx,
    263                 tryAgain, &test, opp);
    264         if (contourWinding != SK_MinS32 || *tryAgain) {
    265             return contourWinding;
    266         }
    267         test /= 2;
    268     } while (!approximately_negative(test));
    269     SkASSERT(0);  // should be OK to comment out, but interested when this hits
    270     return contourWinding;
    271 }
    272 
    273 static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
    274         SkOpSegment** current, int* index, int* endIndex) {
    275     if (!(*current)->isVertical(*index, *endIndex)) {
    276         return;
    277     }
    278     int contourCount = contourList.count();
    279     for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
    280         SkOpContour* contour = contourList[cIndex];
    281         if (contour->done()) {
    282             continue;
    283         }
    284         *current = contour->nonVerticalSegment(index, endIndex);
    285         if (*current) {
    286             return;
    287         }
    288     }
    289 }
    290 
    291 SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList, bool* firstContour,
    292                              int* indexPtr, int* endIndexPtr, SkPoint* topLeft, bool* unsortable,
    293                              bool* done,  bool binary) {
    294     SkOpSegment* current = findSortableTop(contourList, indexPtr, endIndexPtr, topLeft, unsortable,
    295             done, true);
    296     if (!current) {
    297         return NULL;
    298     }
    299     const int index = *indexPtr;
    300     const int endIndex = *endIndexPtr;
    301     if (*firstContour) {
    302         current->initWinding(index, endIndex);
    303         *firstContour = false;
    304         return current;
    305     }
    306     int minIndex = SkMin32(index, endIndex);
    307     int sumWinding = current->windSum(minIndex);
    308     if (sumWinding != SK_MinS32) {
    309         return current;
    310     }
    311     sumWinding = current->computeSum(index, endIndex, binary);
    312     if (sumWinding != SK_MinS32) {
    313         return current;
    314     }
    315     int contourWinding;
    316     int oppContourWinding = 0;
    317     // the simple upward projection of the unresolved points hit unsortable angles
    318     // shoot rays at right angles to the segment to find its winding, ignoring angle cases
    319     bool tryAgain;
    320     double tHit;
    321     SkScalar hitDx = 0;
    322     SkScalar hitOppDx = 0;
    323     do {
    324         // if current is vertical, find another candidate which is not
    325         // if only remaining candidates are vertical, then they can be marked done
    326         SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
    327         skipVertical(contourList, &current, indexPtr, endIndexPtr);
    328 
    329         SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
    330         tryAgain = false;
    331         contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
    332                 &hitDx, &tryAgain, false);
    333         if (tryAgain) {
    334             continue;
    335         }
    336         if (!binary) {
    337             break;
    338         }
    339         oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
    340                 &hitOppDx, &tryAgain, true);
    341     } while (tryAgain);
    342     current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx, oppContourWinding,
    343             hitOppDx);
    344     return current;
    345 }
    346 
    347 void CheckEnds(SkTArray<SkOpContour*, true>* contourList) {
    348     // it's hard to determine if the end of a cubic or conic nearly intersects another curve.
    349     // instead, look to see if the connecting curve intersected at that same end.
    350     int contourCount = (*contourList).count();
    351     for (int cTest = 0; cTest < contourCount; ++cTest) {
    352         SkOpContour* contour = (*contourList)[cTest];
    353         contour->checkEnds();
    354     }
    355 }
    356 
    357 void FixOtherTIndex(SkTArray<SkOpContour*, true>* contourList) {
    358     int contourCount = (*contourList).count();
    359     for (int cTest = 0; cTest < contourCount; ++cTest) {
    360         SkOpContour* contour = (*contourList)[cTest];
    361         contour->fixOtherTIndex();
    362     }
    363 }
    364 
    365 void SortSegments(SkTArray<SkOpContour*, true>* contourList) {
    366     int contourCount = (*contourList).count();
    367     for (int cTest = 0; cTest < contourCount; ++cTest) {
    368         SkOpContour* contour = (*contourList)[cTest];
    369         contour->sortSegments();
    370     }
    371 }
    372 
    373 void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
    374                      bool evenOdd, bool oppEvenOdd) {
    375     int count = contours.count();
    376     if (count == 0) {
    377         return;
    378     }
    379     for (int index = 0; index < count; ++index) {
    380         SkOpContour& contour = contours[index];
    381         contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
    382         list.push_back(&contour);
    383     }
    384     SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
    385 }
    386 
    387 static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
    388     return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
    389 }
    390 
    391 class DistanceLessThan {
    392 public:
    393     DistanceLessThan(double* distances) : fDistances(distances) { }
    394     double* fDistances;
    395     bool operator()(const int one, const int two) {
    396         return fDistances[one] < fDistances[two];
    397     }
    398 };
    399 
    400     /*
    401         check start and end of each contour
    402         if not the same, record them
    403         match them up
    404         connect closest
    405         reassemble contour pieces into new path
    406     */
    407 void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
    408 #if DEBUG_PATH_CONSTRUCTION
    409     SkDebugf("%s\n", __FUNCTION__);
    410 #endif
    411     SkTArray<SkOpContour> contours;
    412     SkOpEdgeBuilder builder(path, contours);
    413     builder.finish();
    414     int count = contours.count();
    415     int outer;
    416     SkTArray<int, true> runs(count);  // indices of partial contours
    417     for (outer = 0; outer < count; ++outer) {
    418         const SkOpContour& eContour = contours[outer];
    419         const SkPoint& eStart = eContour.start();
    420         const SkPoint& eEnd = eContour.end();
    421 #if DEBUG_ASSEMBLE
    422         SkDebugf("%s contour", __FUNCTION__);
    423         if (!approximatelyEqual(eStart, eEnd)) {
    424             SkDebugf("[%d]", runs.count());
    425         } else {
    426             SkDebugf("   ");
    427         }
    428         SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
    429                 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
    430 #endif
    431         if (approximatelyEqual(eStart, eEnd)) {
    432             eContour.toPath(simple);
    433             continue;
    434         }
    435         runs.push_back(outer);
    436     }
    437     count = runs.count();
    438     if (count == 0) {
    439         return;
    440     }
    441     SkTArray<int, true> sLink, eLink;
    442     sLink.push_back_n(count);
    443     eLink.push_back_n(count);
    444     int rIndex, iIndex;
    445     for (rIndex = 0; rIndex < count; ++rIndex) {
    446         sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
    447     }
    448     const int ends = count * 2;  // all starts and ends
    449     const int entries = (ends - 1) * count;  // folded triangle : n * (n - 1) / 2
    450     SkTArray<double, true> distances;
    451     distances.push_back_n(entries);
    452     for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
    453         outer = runs[rIndex >> 1];
    454         const SkOpContour& oContour = contours[outer];
    455         const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
    456         const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
    457                 * ends - rIndex - 1;
    458         for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
    459             int inner = runs[iIndex >> 1];
    460             const SkOpContour& iContour = contours[inner];
    461             const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
    462             double dx = iPt.fX - oPt.fX;
    463             double dy = iPt.fY - oPt.fY;
    464             double dist = dx * dx + dy * dy;
    465             distances[row + iIndex] = dist;  // oStart distance from iStart
    466         }
    467     }
    468     SkTArray<int, true> sortedDist;
    469     sortedDist.push_back_n(entries);
    470     for (rIndex = 0; rIndex < entries; ++rIndex) {
    471         sortedDist[rIndex] = rIndex;
    472     }
    473     SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
    474     int remaining = count;  // number of start/end pairs
    475     for (rIndex = 0; rIndex < entries; ++rIndex) {
    476         int pair = sortedDist[rIndex];
    477         int row = pair / ends;
    478         int col = pair - row * ends;
    479         int thingOne = row < col ? row : ends - row - 2;
    480         int ndxOne = thingOne >> 1;
    481         bool endOne = thingOne & 1;
    482         int* linkOne = endOne ? eLink.begin() : sLink.begin();
    483         if (linkOne[ndxOne] != SK_MaxS32) {
    484             continue;
    485         }
    486         int thingTwo = row < col ? col : ends - row + col - 1;
    487         int ndxTwo = thingTwo >> 1;
    488         bool endTwo = thingTwo & 1;
    489         int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
    490         if (linkTwo[ndxTwo] != SK_MaxS32) {
    491             continue;
    492         }
    493         SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
    494         bool flip = endOne == endTwo;
    495         linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
    496         linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
    497         if (!--remaining) {
    498             break;
    499         }
    500     }
    501     SkASSERT(!remaining);
    502 #if DEBUG_ASSEMBLE
    503     for (rIndex = 0; rIndex < count; ++rIndex) {
    504         int s = sLink[rIndex];
    505         int e = eLink[rIndex];
    506         SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
    507                 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
    508     }
    509 #endif
    510     rIndex = 0;
    511     do {
    512         bool forward = true;
    513         bool first = true;
    514         int sIndex = sLink[rIndex];
    515         SkASSERT(sIndex != SK_MaxS32);
    516         sLink[rIndex] = SK_MaxS32;
    517         int eIndex;
    518         if (sIndex < 0) {
    519             eIndex = sLink[~sIndex];
    520             sLink[~sIndex] = SK_MaxS32;
    521         } else {
    522             eIndex = eLink[sIndex];
    523             eLink[sIndex] = SK_MaxS32;
    524         }
    525         SkASSERT(eIndex != SK_MaxS32);
    526 #if DEBUG_ASSEMBLE
    527         SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
    528                     sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
    529                     eIndex < 0 ? ~eIndex : eIndex);
    530 #endif
    531         do {
    532             outer = runs[rIndex];
    533             const SkOpContour& contour = contours[outer];
    534             if (first) {
    535                 first = false;
    536                 const SkPoint* startPtr = &contour.start();
    537                 simple->deferredMove(startPtr[0]);
    538             }
    539             if (forward) {
    540                 contour.toPartialForward(simple);
    541             } else {
    542                 contour.toPartialBackward(simple);
    543             }
    544 #if DEBUG_ASSEMBLE
    545             SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
    546                 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
    547                 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
    548 #endif
    549             if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
    550                 simple->close();
    551                 break;
    552             }
    553             if (forward) {
    554                 eIndex = eLink[rIndex];
    555                 SkASSERT(eIndex != SK_MaxS32);
    556                 eLink[rIndex] = SK_MaxS32;
    557                 if (eIndex >= 0) {
    558                     SkASSERT(sLink[eIndex] == rIndex);
    559                     sLink[eIndex] = SK_MaxS32;
    560                 } else {
    561                     SkASSERT(eLink[~eIndex] == ~rIndex);
    562                     eLink[~eIndex] = SK_MaxS32;
    563                 }
    564             } else {
    565                 eIndex = sLink[rIndex];
    566                 SkASSERT(eIndex != SK_MaxS32);
    567                 sLink[rIndex] = SK_MaxS32;
    568                 if (eIndex >= 0) {
    569                     SkASSERT(eLink[eIndex] == rIndex);
    570                     eLink[eIndex] = SK_MaxS32;
    571                 } else {
    572                     SkASSERT(sLink[~eIndex] == ~rIndex);
    573                     sLink[~eIndex] = SK_MaxS32;
    574                 }
    575             }
    576             rIndex = eIndex;
    577             if (rIndex < 0) {
    578                 forward ^= 1;
    579                 rIndex = ~rIndex;
    580             }
    581         } while (true);
    582         for (rIndex = 0; rIndex < count; ++rIndex) {
    583             if (sLink[rIndex] != SK_MaxS32) {
    584                 break;
    585             }
    586         }
    587     } while (rIndex < count);
    588 #if DEBUG_ASSEMBLE
    589     for (rIndex = 0; rIndex < count; ++rIndex) {
    590        SkASSERT(sLink[rIndex] == SK_MaxS32);
    591        SkASSERT(eLink[rIndex] == SK_MaxS32);
    592     }
    593 #endif
    594 }
    595