• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ScaleFactor(const SkPath & path)14 SkScalar 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 
ScalePath(const SkPath & path,SkScalar scale,SkPath * scaled)29 void 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 
AngleWinding(SkOpSpanBase * start,SkOpSpanBase * end,int * windingPtr,bool * sortablePtr)36 const 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 
FindUndone(SkOpContourHead * contourHead)88 SkOpSpan* 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 
FindChase(SkTDArray<SkOpSpanBase * > * chase,SkOpSpanBase ** startPtr,SkOpSpanBase ** endPtr)102 SkOpSegment* 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 
SortContourList(SkOpContourHead ** contourList,bool evenOdd,bool oppEvenOdd)173 bool 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 
calc_angles(SkOpContourHead * contourList DEBUG_COIN_DECLARE_PARAMS ())202 static 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 
missing_coincidence(SkOpContourHead * contourList DEBUG_COIN_DECLARE_PARAMS ())210 static 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 
move_multiples(SkOpContourHead * contourList DEBUG_COIN_DECLARE_PARAMS ())220 static 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 
move_nearby(SkOpContourHead * contourList DEBUG_COIN_DECLARE_PARAMS ())231 static bool move_nearby(SkOpContourHead* contourList  DEBUG_COIN_DECLARE_PARAMS()) {
232     DEBUG_STATIC_SET_PHASE(contourList);
233     SkOpContour* contour = contourList;
234     do {
235         if (!contour->moveNearby()) {
236             return false;
237         }
238     } while ((contour = contour->next()));
239     return true;
240 }
241 
sort_angles(SkOpContourHead * contourList)242 static bool sort_angles(SkOpContourHead* contourList) {
243     SkOpContour* contour = contourList;
244     do {
245         if (!contour->sortAngles()) {
246             return false;
247         }
248     } while ((contour = contour->next()));
249     return true;
250 }
251 
HandleCoincidence(SkOpContourHead * contourList,SkOpCoincidence * coincidence)252 bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence) {
253     SkOpGlobalState* globalState = contourList->globalState();
254     // match up points within the coincident runs
255     if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kIntersecting))) {
256         return false;
257     }
258     // combine t values when multiple intersections occur on some segments but not others
259     if (!move_multiples(contourList  DEBUG_PHASE_PARAMS(kWalking))) {
260         return false;
261     }
262     // move t values and points together to eliminate small/tiny gaps
263     if (!move_nearby(contourList  DEBUG_COIN_PARAMS())) {
264         return false;
265     }
266     // add coincidence formed by pairing on curve points and endpoints
267     coincidence->correctEnds(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
268     if (!coincidence->addEndMovedSpans(DEBUG_COIN_ONLY_PARAMS())) {
269         return false;
270     }
271     const int SAFETY_COUNT = 3;
272     int safetyHatch = SAFETY_COUNT;
273     // look for coincidence present in A-B and A-C but missing in B-C
274     do {
275         bool added;
276         if (!coincidence->addMissing(&added  DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
277             return false;
278         }
279         if (!added) {
280             break;
281         }
282         if (!--safetyHatch) {
283             SkASSERT(globalState->debugSkipAssert());
284             return false;
285         }
286         move_nearby(contourList  DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch - 1));
287     } while (true);
288     // check to see if, loosely, coincident ranges may be expanded
289     if (coincidence->expand(DEBUG_COIN_ONLY_PARAMS())) {
290         bool added;
291         if (!coincidence->addMissing(&added  DEBUG_COIN_PARAMS())) {
292             return false;
293         }
294         if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
295             return false;
296         }
297         if (!move_multiples(contourList  DEBUG_COIN_PARAMS())) {
298             return false;
299         }
300         move_nearby(contourList  DEBUG_COIN_PARAMS());
301     }
302     // the expanded ranges may not align -- add the missing spans
303     if (!coincidence->addExpanded(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
304         return false;
305     }
306     // mark spans of coincident segments as coincident
307     coincidence->mark(DEBUG_COIN_ONLY_PARAMS());
308     // look for coincidence lines and curves undetected by intersection
309     if (missing_coincidence(contourList  DEBUG_COIN_PARAMS())) {
310         (void) coincidence->expand(DEBUG_PHASE_ONLY_PARAMS(kIntersecting));
311         if (!coincidence->addExpanded(DEBUG_COIN_ONLY_PARAMS())) {
312             return false;
313         }
314         if (!coincidence->mark(DEBUG_PHASE_ONLY_PARAMS(kWalking))) {
315             return false;
316         }
317     } else {
318         (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
319     }
320     (void) coincidence->expand(DEBUG_COIN_ONLY_PARAMS());
321 
322     SkOpCoincidence overlaps(globalState);
323     safetyHatch = SAFETY_COUNT;
324     do {
325         SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
326         // adjust the winding value to account for coincident edges
327         if (!pairs->apply(DEBUG_ITER_ONLY_PARAMS(SAFETY_COUNT - safetyHatch))) {
328             return false;
329         }
330         // For each coincident pair that overlaps another, when the receivers (the 1st of the pair)
331         // are different, construct a new pair to resolve their mutual span
332         if (!pairs->findOverlaps(&overlaps  DEBUG_ITER_PARAMS(SAFETY_COUNT - safetyHatch))) {
333             return false;
334         }
335         if (!--safetyHatch) {
336             SkASSERT(globalState->debugSkipAssert());
337             return false;
338         }
339     } while (!overlaps.isEmpty());
340     calc_angles(contourList  DEBUG_COIN_PARAMS());
341     if (!sort_angles(contourList)) {
342         return false;
343     }
344 #if DEBUG_COINCIDENCE_VERBOSE
345     coincidence->debugShowCoincidence();
346 #endif
347 #if DEBUG_COINCIDENCE
348     coincidence->debugValidate();
349 #endif
350     SkPathOpsDebug::ShowActiveSpans(contourList);
351     return true;
352 }
353