SkPathOpsCommon.cpp revision cffbcc3b9665f2c928544b6fc6b8a0e22a4210fb
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 SkTDArray<SkOpContour*>& 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->xyAtT(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(SkTDArray<SkOpContour*>& 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        SkTDArray<SkOpAngle> 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        SkTDArray<SkOpAngle*> 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);
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);
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(SkTDArray<SkOpContour*>& 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 SkTDArray<SkOpContour*>& 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
256static int rightAngleWinding(const SkTDArray<SkOpContour*>& 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
273static void skipVertical(const SkTDArray<SkOpContour*>& 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
291SkOpSegment* FindSortableTop(const SkTDArray<SkOpContour*>& 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
347void FixOtherTIndex(SkTDArray<SkOpContour*>* contourList) {
348    int contourCount = (*contourList).count();
349    for (int cTest = 0; cTest < contourCount; ++cTest) {
350        SkOpContour* contour = (*contourList)[cTest];
351        contour->fixOtherTIndex();
352    }
353}
354
355void SortSegments(SkTDArray<SkOpContour*>* contourList) {
356    int contourCount = (*contourList).count();
357    for (int cTest = 0; cTest < contourCount; ++cTest) {
358        SkOpContour* contour = (*contourList)[cTest];
359        contour->sortSegments();
360    }
361}
362
363void MakeContourList(SkTArray<SkOpContour>& contours, SkTDArray<SkOpContour*>& list,
364                     bool evenOdd, bool oppEvenOdd) {
365    int count = contours.count();
366    if (count == 0) {
367        return;
368    }
369    for (int index = 0; index < count; ++index) {
370        SkOpContour& contour = contours[index];
371        contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
372        *list.append() = &contour;
373    }
374    SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
375}
376
377static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
378    return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
379}
380
381class DistanceLessThan {
382public:
383    DistanceLessThan(double* distances) : fDistances(distances) { }
384    double* fDistances;
385    bool operator()(const int one, const int two) {
386        return fDistances[one] < fDistances[two];
387    }
388};
389
390    /*
391        check start and end of each contour
392        if not the same, record them
393        match them up
394        connect closest
395        reassemble contour pieces into new path
396    */
397void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
398#if DEBUG_PATH_CONSTRUCTION
399    SkDebugf("%s\n", __FUNCTION__);
400#endif
401    SkTArray<SkOpContour> contours;
402    SkOpEdgeBuilder builder(path, contours);
403    builder.finish();
404    int count = contours.count();
405    int outer;
406    SkTDArray<int> runs;  // indices of partial contours
407    for (outer = 0; outer < count; ++outer) {
408        const SkOpContour& eContour = contours[outer];
409        const SkPoint& eStart = eContour.start();
410        const SkPoint& eEnd = eContour.end();
411#if DEBUG_ASSEMBLE
412        SkDebugf("%s contour", __FUNCTION__);
413        if (!approximatelyEqual(eStart, eEnd)) {
414            SkDebugf("[%d]", runs.count());
415        } else {
416            SkDebugf("   ");
417        }
418        SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
419                eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
420#endif
421        if (approximatelyEqual(eStart, eEnd)) {
422            eContour.toPath(simple);
423            continue;
424        }
425        *runs.append() = outer;
426    }
427    count = runs.count();
428    if (count == 0) {
429        return;
430    }
431    SkTDArray<int> sLink, eLink;
432    sLink.setCount(count);
433    eLink.setCount(count);
434    int rIndex, iIndex;
435    for (rIndex = 0; rIndex < count; ++rIndex) {
436        sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
437    }
438    SkTDArray<double> distances;
439    const int ends = count * 2;  // all starts and ends
440    const int entries = (ends - 1) * count;  // folded triangle : n * (n - 1) / 2
441    distances.setCount(entries);
442    for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
443        outer = runs[rIndex >> 1];
444        const SkOpContour& oContour = contours[outer];
445        const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
446        const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
447                * ends - rIndex - 1;
448        for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
449            int inner = runs[iIndex >> 1];
450            const SkOpContour& iContour = contours[inner];
451            const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
452            double dx = iPt.fX - oPt.fX;
453            double dy = iPt.fY - oPt.fY;
454            double dist = dx * dx + dy * dy;
455            distances[row + iIndex] = dist;  // oStart distance from iStart
456        }
457    }
458    SkTDArray<int> sortedDist;
459    sortedDist.setCount(entries);
460    for (rIndex = 0; rIndex < entries; ++rIndex) {
461        sortedDist[rIndex] = rIndex;
462    }
463    SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
464    int remaining = count;  // number of start/end pairs
465    for (rIndex = 0; rIndex < entries; ++rIndex) {
466        int pair = sortedDist[rIndex];
467        int row = pair / ends;
468        int col = pair - row * ends;
469        int thingOne = row < col ? row : ends - row - 2;
470        int ndxOne = thingOne >> 1;
471        bool endOne = thingOne & 1;
472        int* linkOne = endOne ? eLink.begin() : sLink.begin();
473        if (linkOne[ndxOne] != SK_MaxS32) {
474            continue;
475        }
476        int thingTwo = row < col ? col : ends - row + col - 1;
477        int ndxTwo = thingTwo >> 1;
478        bool endTwo = thingTwo & 1;
479        int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
480        if (linkTwo[ndxTwo] != SK_MaxS32) {
481            continue;
482        }
483        SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
484        bool flip = endOne == endTwo;
485        linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
486        linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
487        if (!--remaining) {
488            break;
489        }
490    }
491    SkASSERT(!remaining);
492#if DEBUG_ASSEMBLE
493    for (rIndex = 0; rIndex < count; ++rIndex) {
494        int s = sLink[rIndex];
495        int e = eLink[rIndex];
496        SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
497                s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
498    }
499#endif
500    rIndex = 0;
501    do {
502        bool forward = true;
503        bool first = true;
504        int sIndex = sLink[rIndex];
505        SkASSERT(sIndex != SK_MaxS32);
506        sLink[rIndex] = SK_MaxS32;
507        int eIndex;
508        if (sIndex < 0) {
509            eIndex = sLink[~sIndex];
510            sLink[~sIndex] = SK_MaxS32;
511        } else {
512            eIndex = eLink[sIndex];
513            eLink[sIndex] = SK_MaxS32;
514        }
515        SkASSERT(eIndex != SK_MaxS32);
516#if DEBUG_ASSEMBLE
517        SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
518                    sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
519                    eIndex < 0 ? ~eIndex : eIndex);
520#endif
521        do {
522            outer = runs[rIndex];
523            const SkOpContour& contour = contours[outer];
524            if (first) {
525                first = false;
526                const SkPoint* startPtr = &contour.start();
527                simple->deferredMove(startPtr[0]);
528            }
529            if (forward) {
530                contour.toPartialForward(simple);
531            } else {
532                contour.toPartialBackward(simple);
533            }
534#if DEBUG_ASSEMBLE
535            SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
536                eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
537                sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
538#endif
539            if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
540                simple->close();
541                break;
542            }
543            if (forward) {
544                eIndex = eLink[rIndex];
545                SkASSERT(eIndex != SK_MaxS32);
546                eLink[rIndex] = SK_MaxS32;
547                if (eIndex >= 0) {
548                    SkASSERT(sLink[eIndex] == rIndex);
549                    sLink[eIndex] = SK_MaxS32;
550                } else {
551                    SkASSERT(eLink[~eIndex] == ~rIndex);
552                    eLink[~eIndex] = SK_MaxS32;
553                }
554            } else {
555                eIndex = sLink[rIndex];
556                SkASSERT(eIndex != SK_MaxS32);
557                sLink[rIndex] = SK_MaxS32;
558                if (eIndex >= 0) {
559                    SkASSERT(eLink[eIndex] == rIndex);
560                    eLink[eIndex] = SK_MaxS32;
561                } else {
562                    SkASSERT(sLink[~eIndex] == ~rIndex);
563                    sLink[~eIndex] = SK_MaxS32;
564                }
565            }
566            rIndex = eIndex;
567            if (rIndex < 0) {
568                forward ^= 1;
569                rIndex = ~rIndex;
570            }
571        } while (true);
572        for (rIndex = 0; rIndex < count; ++rIndex) {
573            if (sLink[rIndex] != SK_MaxS32) {
574                break;
575            }
576        }
577    } while (rIndex < count);
578#if DEBUG_ASSEMBLE
579    for (rIndex = 0; rIndex < count; ++rIndex) {
580       SkASSERT(sLink[rIndex] == SK_MaxS32);
581       SkASSERT(eLink[rIndex] == SK_MaxS32);
582    }
583#endif
584}
585