SkPathOpsCommon.cpp revision ab2d73b06fe6c518be1d399a79c9cc39db21abb6
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
14SkScalar ScaleFactor(const SkPath& path) {
15    static const SkScalar twoTo10 = 1024.f;
16    SkScalar largest = 0;
17    const SkScalar* oneBounds = &path.getBounds().fLeft;
18    for (int index = 0; index < 4; ++index) {
19        largest = SkTMax(largest, SkScalarAbs(oneBounds[index]));
20    }
21    SkScalar scale = twoTo10;
22    SkScalar next;
23    while ((next = scale * twoTo10) < largest) {
24        scale = next;
25    }
26    return scale == twoTo10 ? SK_Scalar1 : scale;
27}
28
29void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled) {
30    SkMatrix matrix;
31    matrix.setScale(scale, scale);
32    *scaled = path;
33    scaled->transform(matrix);
34}
35
36const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
37        bool* sortablePtr) {
38    // find first angle, initialize winding to computed fWindSum
39    SkOpSegment* segment = start->segment();
40    const SkOpAngle* angle = segment->spanToAngle(start, end);
41    if (!angle) {
42        *windingPtr = SK_MinS32;
43        return nullptr;
44    }
45    bool computeWinding = false;
46    const SkOpAngle* firstAngle = angle;
47    bool loop = false;
48    bool unorderable = false;
49    int winding = SK_MinS32;
50    do {
51        angle = angle->next();
52        if (!angle) {
53            return nullptr;
54        }
55        unorderable |= angle->unorderable();
56        if ((computeWinding = unorderable || (angle == firstAngle && loop))) {
57            break;    // if we get here, there's no winding, loop is unorderable
58        }
59        loop |= angle == firstAngle;
60        segment = angle->segment();
61        winding = segment->windSum(angle);
62    } while (winding == SK_MinS32);
63    // if the angle loop contains an unorderable span, the angle order may be useless
64    // directly compute the winding in this case for each span
65    if (computeWinding) {
66        firstAngle = angle;
67        winding = SK_MinS32;
68        do {
69            SkOpSpanBase* startSpan = angle->start();
70            SkOpSpanBase* endSpan = angle->end();
71            SkOpSpan* lesser = startSpan->starter(endSpan);
72            int testWinding = lesser->windSum();
73            if (testWinding == SK_MinS32) {
74                testWinding = lesser->computeWindSum();
75            }
76            if (testWinding != SK_MinS32) {
77                segment = angle->segment();
78                winding = testWinding;
79            }
80            angle = angle->next();
81        } while (angle != firstAngle);
82    }
83    *sortablePtr = !unorderable;
84    *windingPtr = winding;
85    return angle;
86}
87
88SkOpSpan* FindUndone(SkOpContourHead* contourHead) {
89    SkOpContour* contour = contourHead;
90    do {
91        if (contour->done()) {
92            continue;
93        }
94        SkOpSpan* result = contour->undoneSpan();
95        if (result) {
96            return result;
97        }
98    } while ((contour = contour->next()));
99    return nullptr;
100}
101
102SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
103        SkOpSpanBase** endPtr) {
104    while (chase->count()) {
105        SkOpSpanBase* span;
106        chase->pop(&span);
107        SkOpSegment* segment = span->segment();
108        *startPtr = span->ptT()->next()->span();
109        bool done = true;
110        *endPtr = nullptr;
111        if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
112            *startPtr = last->start();
113            *endPtr = last->end();
114    #if TRY_ROTATE
115            *chase->insert(0) = span;
116    #else
117            *chase->append() = span;
118    #endif
119            return last->segment();
120        }
121        if (done) {
122            continue;
123        }
124        // find first angle, initialize winding to computed wind sum
125        int winding;
126        bool sortable;
127        const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
128        if (!angle) {
129            return nullptr;
130        }
131        if (winding == SK_MinS32) {
132            continue;
133        }
134        int sumWinding SK_INIT_TO_AVOID_WARNING;
135        if (sortable) {
136            segment = angle->segment();
137            sumWinding = segment->updateWindingReverse(angle);
138        }
139        SkOpSegment* first = nullptr;
140        const SkOpAngle* firstAngle = angle;
141        while ((angle = angle->next()) != firstAngle) {
142            segment = angle->segment();
143            SkOpSpanBase* start = angle->start();
144            SkOpSpanBase* end = angle->end();
145            int maxWinding SK_INIT_TO_AVOID_WARNING;
146            if (sortable) {
147                segment->setUpWinding(start, end, &maxWinding, &sumWinding);
148            }
149            if (!segment->done(angle)) {
150                if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
151                    first = segment;
152                    *startPtr = start;
153                    *endPtr = end;
154                }
155                // OPTIMIZATION: should this also add to the chase?
156                if (sortable) {
157                    (void) segment->markAngle(maxWinding, sumWinding, angle);
158                }
159            }
160        }
161        if (first) {
162       #if TRY_ROTATE
163            *chase->insert(0) = span;
164       #else
165            *chase->append() = span;
166       #endif
167            return first;
168        }
169    }
170    return nullptr;
171}
172
173bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) {
174    SkTDArray<SkOpContour* > list;
175    SkOpContour* contour = *contourList;
176    do {
177        if (contour->count()) {
178            contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
179            *list.append() = contour;
180        }
181    } while ((contour = contour->next()));
182    int count = list.count();
183    if (!count) {
184        return false;
185    }
186    if (count > 1) {
187        SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
188    }
189    contour = list[0];
190    SkOpContourHead* contourHead = static_cast<SkOpContourHead*>(contour);
191    contour->globalState()->setContourHead(contourHead);
192    *contourList = contourHead;
193    for (int index = 1; index < count; ++index) {
194        SkOpContour* next = list[index];
195        contour->setNext(next);
196        contour = next;
197    }
198    contour->setNext(nullptr);
199    return true;
200}
201
202static void calc_angles(SkOpContourHead* contourList  DEBUG_COIN_DECLARE_PARAMS()) {
203    DEBUG_STATIC_SET_PHASE(contourList);
204    SkOpContour* contour = contourList;
205    do {
206        contour->calcAngles();
207    } while ((contour = contour->next()));
208}
209
210static bool missing_coincidence(SkOpContourHead* contourList  DEBUG_COIN_DECLARE_PARAMS()) {
211    DEBUG_STATIC_SET_PHASE(contourList);
212    SkOpContour* contour = contourList;
213    bool result = false;
214    do {
215        result |= contour->missingCoincidence();
216    } while ((contour = contour->next()));
217    return result;
218}
219
220static bool move_multiples(SkOpContourHead* contourList  DEBUG_COIN_DECLARE_PARAMS()) {
221    DEBUG_STATIC_SET_PHASE(contourList);
222    SkOpContour* contour = contourList;
223    do {
224        if (!contour->moveMultiples()) {
225            return false;
226        }
227    } while ((contour = contour->next()));
228    return true;
229}
230
231static void move_nearby(SkOpContourHead* contourList  DEBUG_COIN_DECLARE_PARAMS()) {
232    DEBUG_STATIC_SET_PHASE(contourList);
233    SkOpContour* contour = contourList;
234    do {
235        contour->moveNearby();
236    } while ((contour = contour->next()));
237}
238
239static bool sort_angles(SkOpContourHead* contourList) {
240    SkOpContour* contour = contourList;
241    do {
242        if (!contour->sortAngles()) {
243            return false;
244        }
245    } while ((contour = contour->next()));
246    return true;
247}
248
249bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence) {
250    SkOpGlobalState* globalState = contourList->globalState();
251    // match up points within the coincident runs
252    if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kIntersecting))) {
253        return false;
254    }
255    // combine t values when multiple intersections occur on some segments but not others
256    if (!move_multiples(contourList  DEBUG_PHASE_PARAMS(kWalking))) {
257        return false;
258    }
259    // move t values and points together to eliminate small/tiny gaps
260    move_nearby(contourList  DEBUG_COIN_PARAMS());
261    // add coincidence formed by pairing on curve points and endpoints
262    coincidence->correctEnds(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
263    if (!coincidence->addEndMovedSpans(DEBUG_COIN_ONLY_PARAMS())) {
264        return false;
265    }
266    const int SAFETY_COUNT = 3;
267    int safetyHatch = SAFETY_COUNT;
268    // look for coincidence present in A-B and A-C but missing in B-C
269    do {
270        bool added;
271        if (!coincidence->addMissing(&added  DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
272            return false;
273        }
274        if (!added) {
275            break;
276        }
277        if (!--safetyHatch) {
278            SkASSERT(globalState->debugSkipAssert());
279            return false;
280        }
281        move_nearby(contourList  DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch - 1));
282    } while (true);
283    // check to see if, loosely, coincident ranges may be expanded
284    if (coincidence->expand(DEBUG_COIN_ONLY_PARAMS())) {
285        bool added;
286        if (!coincidence->addMissing(&added  DEBUG_COIN_PARAMS())) {
287            return false;
288        }
289        if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
290            return false;
291        }
292        if (!move_multiples(contourList  DEBUG_COIN_PARAMS())) {
293            return false;
294        }
295        move_nearby(contourList  DEBUG_COIN_PARAMS());
296    }
297    // the expanded ranges may not align -- add the missing spans
298    if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
299        return false;
300    }
301    // mark spans of coincident segments as coincident
302    coincidence->mark(DEBUG_COIN_ONLY_PARAMS());
303    // look for coincidence lines and curves undetected by intersection
304    if (missing_coincidence(contourList  DEBUG_COIN_PARAMS())) {
305        (void) coincidence->expand(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
306        if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
307            return false;
308        }
309        if (!coincidence->mark(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
310            return false;
311        }
312    } else {
313        (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
314    }
315    (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
316
317    SkOpCoincidence overlaps(globalState);
318    safetyHatch = SAFETY_COUNT;
319    do {
320        SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
321        // adjust the winding value to account for coincident edges
322        if (!pairs->apply(DEBUG_ITER_ONLY_PARAMS(SAFETY_COUNT - safetyHatch))) {
323            return false;
324        }
325        // For each coincident pair that overlaps another, when the receivers (the 1st of the pair)
326        // are different, construct a new pair to resolve their mutual span
327        if (!pairs->findOverlaps(&overlaps  DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
328            return false;
329        }
330        if (!--safetyHatch) {
331            SkASSERT(globalState->debugSkipAssert());
332            return false;
333        }
334    } while (!overlaps.isEmpty());
335    calc_angles(contourList  DEBUG_COIN_PARAMS());
336    if (!sort_angles(contourList)) {
337        return false;
338    }
339#if DEBUG_COINCIDENCE_VERBOSE
340    coincidence->debugShowCoincidence();
341#endif
342#if DEBUG_COINCIDENCE
343    coincidence->debugValidate();
344#endif
345    SkPathOpsDebug::ShowActiveSpans(contourList);
346    return true;
347}
348