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