SkPathOpsCommon.cpp revision 3f0753d3eccece8ac7f02f6af36d66a96c3dfb26
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 "SkAddIntersections.h"
8#include "SkOpCoincidence.h"
9#include "SkOpEdgeBuilder.h"
10#include "SkPathOpsCommon.h"
11#include "SkPathWriter.h"
12#include "SkTSort.h"
13
14const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
15        bool* sortablePtr) {
16    // find first angle, initialize winding to computed fWindSum
17    SkOpSegment* segment = start->segment();
18    const SkOpAngle* angle = segment->spanToAngle(start, end);
19    if (!angle) {
20        *windingPtr = SK_MinS32;
21        return nullptr;
22    }
23    bool computeWinding = false;
24    const SkOpAngle* firstAngle = angle;
25    bool loop = false;
26    bool unorderable = false;
27    int winding = SK_MinS32;
28    do {
29        angle = angle->next();
30        unorderable |= angle->unorderable();
31        if ((computeWinding = unorderable || (angle == firstAngle && loop))) {
32            break;    // if we get here, there's no winding, loop is unorderable
33        }
34        loop |= angle == firstAngle;
35        segment = angle->segment();
36        winding = segment->windSum(angle);
37    } while (winding == SK_MinS32);
38    // if the angle loop contains an unorderable span, the angle order may be useless
39    // directly compute the winding in this case for each span
40    if (computeWinding) {
41        firstAngle = angle;
42        winding = SK_MinS32;
43        do {
44            SkOpSpanBase* startSpan = angle->start();
45            SkOpSpanBase* endSpan = angle->end();
46            SkOpSpan* lesser = startSpan->starter(endSpan);
47            int testWinding = lesser->windSum();
48            if (testWinding == SK_MinS32) {
49                testWinding = lesser->computeWindSum();
50            }
51            if (testWinding != SK_MinS32) {
52                segment = angle->segment();
53                winding = testWinding;
54            }
55            angle = angle->next();
56        } while (angle != firstAngle);
57    }
58    *sortablePtr = !unorderable;
59    *windingPtr = winding;
60    return angle;
61}
62
63SkOpSegment* FindUndone(SkOpContourHead* contourList, SkOpSpanBase** startPtr,
64         SkOpSpanBase** endPtr) {
65    SkOpSegment* result;
66    SkOpContour* contour = contourList;
67    do {
68        result = contour->undoneSegment(startPtr, endPtr);
69        if (result) {
70            return result;
71        }
72    } while ((contour = contour->next()));
73    return nullptr;
74}
75
76SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
77        SkOpSpanBase** endPtr) {
78    while (chase->count()) {
79        SkOpSpanBase* span;
80        chase->pop(&span);
81        SkOpSegment* segment = span->segment();
82        *startPtr = span->ptT()->next()->span();
83        bool done = true;
84        *endPtr = nullptr;
85        if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
86            *startPtr = last->start();
87            *endPtr = last->end();
88    #if TRY_ROTATE
89            *chase->insert(0) = span;
90    #else
91            *chase->append() = span;
92    #endif
93            return last->segment();
94        }
95        if (done) {
96            continue;
97        }
98        // find first angle, initialize winding to computed wind sum
99        int winding;
100        bool sortable;
101        const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
102        if (winding == SK_MinS32) {
103            continue;
104        }
105        int sumWinding SK_INIT_TO_AVOID_WARNING;
106        if (sortable) {
107            segment = angle->segment();
108            sumWinding = segment->updateWindingReverse(angle);
109        }
110        SkOpSegment* first = nullptr;
111        const SkOpAngle* firstAngle = angle;
112        while ((angle = angle->next()) != firstAngle) {
113            segment = angle->segment();
114            SkOpSpanBase* start = angle->start();
115            SkOpSpanBase* end = angle->end();
116            int maxWinding SK_INIT_TO_AVOID_WARNING;
117            if (sortable) {
118                segment->setUpWinding(start, end, &maxWinding, &sumWinding);
119            }
120            if (!segment->done(angle)) {
121                if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
122                    first = segment;
123                    *startPtr = start;
124                    *endPtr = end;
125                }
126                // OPTIMIZATION: should this also add to the chase?
127                if (sortable) {
128                    (void) segment->markAngle(maxWinding, sumWinding, angle);
129                }
130            }
131        }
132        if (first) {
133       #if TRY_ROTATE
134            *chase->insert(0) = span;
135       #else
136            *chase->append() = span;
137       #endif
138            return first;
139        }
140    }
141    return nullptr;
142}
143
144#if DEBUG_ACTIVE_SPANS
145void DebugShowActiveSpans(SkOpContourHead* contourList) {
146    SkOpContour* contour = contourList;
147    do {
148        contour->debugShowActiveSpans();
149    } while ((contour = contour->next()));
150}
151#endif
152
153bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) {
154    SkTDArray<SkOpContour* > list;
155    SkOpContour* contour = *contourList;
156    do {
157        if (contour->count()) {
158            contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
159            *list.append() = contour;
160        }
161    } while ((contour = contour->next()));
162    int count = list.count();
163    if (!count) {
164        return false;
165    }
166    if (count > 1) {
167        SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
168    }
169    contour = list[0];
170    SkOpContourHead* contourHead = static_cast<SkOpContourHead*>(contour);
171    contour->globalState()->setContourHead(contourHead);
172    *contourList = contourHead;
173    for (int index = 1; index < count; ++index) {
174        SkOpContour* next = list[index];
175        contour->setNext(next);
176        contour = next;
177    }
178    contour->setNext(nullptr);
179    return true;
180}
181
182class DistanceLessThan {
183public:
184    DistanceLessThan(double* distances) : fDistances(distances) { }
185    double* fDistances;
186    bool operator()(const int one, const int two) {
187        return fDistances[one] < fDistances[two];
188    }
189};
190
191    /*
192        check start and end of each contour
193        if not the same, record them
194        match them up
195        connect closest
196        reassemble contour pieces into new path
197    */
198void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
199    SkChunkAlloc allocator(4096);  // FIXME: constant-ize, tune
200    SkOpContourHead contour;
201    SkOpGlobalState globalState(nullptr, &contour  SkDEBUGPARAMS(false)
202                                SkDEBUGPARAMS(nullptr));
203#if DEBUG_SHOW_TEST_NAME
204    SkDebugf("</div>\n");
205#endif
206#if DEBUG_PATH_CONSTRUCTION
207    SkDebugf("%s\n", __FUNCTION__);
208#endif
209    SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
210    builder.finish(&allocator);
211    SkTDArray<const SkOpContour* > runs;  // indices of partial contours
212    const SkOpContour* eContour = builder.head();
213    do {
214        if (!eContour->count()) {
215            continue;
216        }
217        const SkPoint& eStart = eContour->start();
218        const SkPoint& eEnd = eContour->end();
219#if DEBUG_ASSEMBLE
220        SkDebugf("%s contour", __FUNCTION__);
221        if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
222            SkDebugf("[%d]", runs.count());
223        } else {
224            SkDebugf("   ");
225        }
226        SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
227                eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
228#endif
229        if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
230            eContour->toPath(simple);
231            continue;
232        }
233        *runs.append() = eContour;
234    } while ((eContour = eContour->next()));
235    int count = runs.count();
236    if (count == 0) {
237        return;
238    }
239    SkTDArray<int> sLink, eLink;
240    sLink.append(count);
241    eLink.append(count);
242    int rIndex, iIndex;
243    for (rIndex = 0; rIndex < count; ++rIndex) {
244        sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
245    }
246    const int ends = count * 2;  // all starts and ends
247    const int entries = (ends - 1) * count;  // folded triangle : n * (n - 1) / 2
248    SkTDArray<double> distances;
249    distances.append(entries);
250    for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
251        const SkOpContour* oContour = runs[rIndex >> 1];
252        const SkPoint& oPt = rIndex & 1 ? oContour->end() : oContour->start();
253        const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
254                * ends - rIndex - 1;
255        for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
256            const SkOpContour* iContour = runs[iIndex >> 1];
257            const SkPoint& iPt = iIndex & 1 ? iContour->end() : iContour->start();
258            double dx = iPt.fX - oPt.fX;
259            double dy = iPt.fY - oPt.fY;
260            double dist = dx * dx + dy * dy;
261            distances[row + iIndex] = dist;  // oStart distance from iStart
262        }
263    }
264    SkTDArray<int> sortedDist;
265    sortedDist.append(entries);
266    for (rIndex = 0; rIndex < entries; ++rIndex) {
267        sortedDist[rIndex] = rIndex;
268    }
269    SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
270    int remaining = count;  // number of start/end pairs
271    for (rIndex = 0; rIndex < entries; ++rIndex) {
272        int pair = sortedDist[rIndex];
273        int row = pair / ends;
274        int col = pair - row * ends;
275        int thingOne = row < col ? row : ends - row - 2;
276        int ndxOne = thingOne >> 1;
277        bool endOne = thingOne & 1;
278        int* linkOne = endOne ? eLink.begin() : sLink.begin();
279        if (linkOne[ndxOne] != SK_MaxS32) {
280            continue;
281        }
282        int thingTwo = row < col ? col : ends - row + col - 1;
283        int ndxTwo = thingTwo >> 1;
284        bool endTwo = thingTwo & 1;
285        int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
286        if (linkTwo[ndxTwo] != SK_MaxS32) {
287            continue;
288        }
289        SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
290        bool flip = endOne == endTwo;
291        linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
292        linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
293        if (!--remaining) {
294            break;
295        }
296    }
297    SkASSERT(!remaining);
298#if DEBUG_ASSEMBLE
299    for (rIndex = 0; rIndex < count; ++rIndex) {
300        int s = sLink[rIndex];
301        int e = eLink[rIndex];
302        SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
303                s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
304    }
305#endif
306    rIndex = 0;
307    do {
308        bool forward = true;
309        bool first = true;
310        int sIndex = sLink[rIndex];
311        SkASSERT(sIndex != SK_MaxS32);
312        sLink[rIndex] = SK_MaxS32;
313        int eIndex;
314        if (sIndex < 0) {
315            eIndex = sLink[~sIndex];
316            sLink[~sIndex] = SK_MaxS32;
317        } else {
318            eIndex = eLink[sIndex];
319            eLink[sIndex] = SK_MaxS32;
320        }
321        SkASSERT(eIndex != SK_MaxS32);
322#if DEBUG_ASSEMBLE
323        SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
324                    sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
325                    eIndex < 0 ? ~eIndex : eIndex);
326#endif
327        do {
328            const SkOpContour* contour = runs[rIndex];
329            if (first) {
330                first = false;
331                const SkPoint* startPtr = &contour->start();
332                simple->deferredMove(startPtr[0]);
333            }
334            if (forward) {
335                contour->toPartialForward(simple);
336            } else {
337                contour->toPartialBackward(simple);
338            }
339#if DEBUG_ASSEMBLE
340            SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
341                eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
342                sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
343#endif
344            if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
345                simple->close();
346                break;
347            }
348            if (forward) {
349                eIndex = eLink[rIndex];
350                SkASSERT(eIndex != SK_MaxS32);
351                eLink[rIndex] = SK_MaxS32;
352                if (eIndex >= 0) {
353                    SkASSERT(sLink[eIndex] == rIndex);
354                    sLink[eIndex] = SK_MaxS32;
355                } else {
356                    SkASSERT(eLink[~eIndex] == ~rIndex);
357                    eLink[~eIndex] = SK_MaxS32;
358                }
359            } else {
360                eIndex = sLink[rIndex];
361                SkASSERT(eIndex != SK_MaxS32);
362                sLink[rIndex] = SK_MaxS32;
363                if (eIndex >= 0) {
364                    SkASSERT(eLink[eIndex] == rIndex);
365                    eLink[eIndex] = SK_MaxS32;
366                } else {
367                    SkASSERT(sLink[~eIndex] == ~rIndex);
368                    sLink[~eIndex] = SK_MaxS32;
369                }
370            }
371            rIndex = eIndex;
372            if (rIndex < 0) {
373                forward ^= 1;
374                rIndex = ~rIndex;
375            }
376        } while (true);
377        for (rIndex = 0; rIndex < count; ++rIndex) {
378            if (sLink[rIndex] != SK_MaxS32) {
379                break;
380            }
381        }
382    } while (rIndex < count);
383#if DEBUG_ASSEMBLE
384    for (rIndex = 0; rIndex < count; ++rIndex) {
385       SkASSERT(sLink[rIndex] == SK_MaxS32);
386       SkASSERT(eLink[rIndex] == SK_MaxS32);
387    }
388#endif
389}
390
391static void align(SkOpContourHead* contourList) {
392    SkOpContour* contour = contourList;
393    do {
394        contour->align();
395    } while ((contour = contour->next()));
396}
397
398static void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
399    SkOpContour* contour = contourList;
400    do {
401        contour->addAlignIntersections(contourList, allocator);
402    } while ((contour = contour->next()));
403}
404
405static void calcAngles(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
406    SkOpContour* contour = contourList;
407    do {
408        contour->calcAngles(allocator);
409    } while ((contour = contour->next()));
410}
411
412static void findCollapsed(SkOpContourHead* contourList) {
413    SkOpContour* contour = contourList;
414    do {
415        contour->findCollapsed();
416    } while ((contour = contour->next()));
417}
418
419static bool missingCoincidence(SkOpContourHead* contourList,
420        SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
421    SkOpContour* contour = contourList;
422    bool result = false;
423    do {
424        result |= contour->missingCoincidence(coincidence, allocator);
425    } while ((contour = contour->next()));
426    return result;
427}
428
429static bool moveMultiples(SkOpContourHead* contourList) {
430    SkOpContour* contour = contourList;
431    do {
432        if (!contour->moveMultiples()) {
433            return false;
434        }
435    } while ((contour = contour->next()));
436    return true;
437}
438
439static void moveNearby(SkOpContourHead* contourList) {
440    SkOpContour* contour = contourList;
441    do {
442        contour->moveNearby();
443    } while ((contour = contour->next()));
444}
445
446static void sortAngles(SkOpContourHead* contourList) {
447    SkOpContour* contour = contourList;
448    do {
449        contour->sortAngles();
450    } while ((contour = contour->next()));
451}
452
453bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence,
454        SkChunkAlloc* allocator) {
455    SkOpGlobalState* globalState = contourList->globalState();
456    // combine t values when multiple intersections occur on some segments but not others
457    DEBUG_COINCIDENCE_HEALTH(contourList, "start");
458    if (!moveMultiples(contourList)) {
459        return false;
460    }
461    DEBUG_COINCIDENCE_HEALTH(contourList, "moveMultiples");
462    findCollapsed(contourList);
463    DEBUG_COINCIDENCE_HEALTH(contourList, "findCollapsed");
464    // move t values and points together to eliminate small/tiny gaps
465    moveNearby(contourList);
466    DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby");
467    align(contourList);  // give all span members common values
468    DEBUG_COINCIDENCE_HEALTH(contourList, "align");
469    if (!coincidence->fixAligned()) { // aligning may have marked a coincidence pt-t deleted
470        return false;
471    }
472    DEBUG_COINCIDENCE_HEALTH(contourList, "fixAligned");
473#if DEBUG_VALIDATE
474    globalState->setPhase(SkOpGlobalState::kIntersecting);
475#endif
476    // look for intersections on line segments formed by moving end points
477    addAlignIntersections(contourList, allocator);
478    DEBUG_COINCIDENCE_HEALTH(contourList, "addAlignIntersections");
479    if (coincidence->addMissing(allocator)) {
480        DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing");
481        moveNearby(contourList);
482        DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby2");
483        align(contourList);  // give all span members common values
484        DEBUG_COINCIDENCE_HEALTH(contourList, "align2");
485        if (!coincidence->fixAligned()) {  // aligning may have marked a coincidence pt-t deleted
486            return false;
487        }
488        DEBUG_COINCIDENCE_HEALTH(contourList, "fixAligned2");
489    }
490#if DEBUG_VALIDATE
491    globalState->setPhase(SkOpGlobalState::kWalking);
492#endif
493    // check to see if, loosely, coincident ranges may be expanded
494    if (coincidence->expand()) {
495        DEBUG_COINCIDENCE_HEALTH(contourList, "expand1");
496        if (!coincidence->addExpanded(allocator  PATH_OPS_DEBUG_VALIDATE_PARAMS(globalState))) {
497            return false;
498        }
499    }
500    DEBUG_COINCIDENCE_HEALTH(contourList, "expand2");
501    // the expanded ranges may not align -- add the missing spans
502    if (!coincidence->mark()) {  // mark spans of coincident segments as coincident
503        return false;
504    }
505    DEBUG_COINCIDENCE_HEALTH(contourList, "mark1");
506    // look for coincidence missed earlier
507    if (missingCoincidence(contourList, coincidence, allocator)) {
508        DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence1");
509        (void) coincidence->expand();
510        DEBUG_COINCIDENCE_HEALTH(contourList, "expand3");
511        if (!coincidence->addExpanded(allocator  PATH_OPS_DEBUG_VALIDATE_PARAMS(globalState))) {
512            return false;
513        }
514        DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded2");
515        coincidence->mark();
516    }
517    DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence2");
518    SkOpCoincidence overlaps;
519    do {
520        SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
521        if (!pairs->apply()) {  // adjust the winding value to account for coincident edges
522            return false;
523        }
524        DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->apply");
525        // For each coincident pair that overlaps another, when the receivers (the 1st of the pair)
526        // are different, construct a new pair to resolve their mutual span
527        if (!pairs->findOverlaps(&overlaps, allocator)) {
528            return false;
529        }
530        DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->findOverlaps");
531    } while (!overlaps.isEmpty());
532    calcAngles(contourList, allocator);
533    sortAngles(contourList);
534    if (globalState->angleCoincidence()) {
535        (void) missingCoincidence(contourList, coincidence, allocator);
536        if (!coincidence->apply()) {
537            return false;
538        }
539    }
540#if DEBUG_ACTIVE_SPANS
541    coincidence->debugShowCoincidence();
542    DebugShowActiveSpans(contourList);
543#endif
544    return true;
545}
546