blob: db55635d89183523224e238c1f574c1c41304be6 [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
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 "SkOpEdgeBuilder.h"
9#include "SkPathOpsCommon.h"
10#include "SkPathWriter.h"
11
12// FIXME: this and find chase should be merge together, along with
13// other code that walks winding in angles
14// OPTIMIZATION: Probably, the walked winding should be rolled into the angle structure
15// so it isn't duplicated by walkers like this one
16static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int& nextStart, int& nextEnd) {
17 while (chase.count()) {
18 SkOpSpan* span;
19 chase.pop(&span);
20 const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
21 SkOpSegment* segment = backPtr.fOther;
22 nextStart = backPtr.fOtherIndex;
23 SkTDArray<SkOpAngle> angles;
24 int done = 0;
25 if (segment->activeAngle(nextStart, &done, &angles)) {
26 SkOpAngle* last = angles.end() - 1;
27 nextStart = last->start();
28 nextEnd = last->end();
29 #if TRY_ROTATE
30 *chase.insert(0) = span;
31 #else
32 *chase.append() = span;
33 #endif
34 return last->segment();
35 }
36 if (done == angles.count()) {
37 continue;
38 }
39 SkTDArray<SkOpAngle*> sorted;
40 bool sortable = SkOpSegment::SortAngles(angles, &sorted);
41 int angleCount = sorted.count();
42#if DEBUG_SORT
43 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0);
44#endif
45 if (!sortable) {
46 continue;
47 }
48 // find first angle, initialize winding to computed fWindSum
49 int firstIndex = -1;
50 const SkOpAngle* angle;
51 do {
52 angle = sorted[++firstIndex];
53 segment = angle->segment();
54 } while (segment->windSum(angle) == SK_MinS32);
55 #if DEBUG_SORT
56 segment->debugShowSort(__FUNCTION__, sorted, firstIndex);
57 #endif
58 int sumMiWinding = segment->updateWindingReverse(angle);
59 int sumSuWinding = segment->updateOppWindingReverse(angle);
60 if (segment->operand()) {
61 SkTSwap<int>(sumMiWinding, sumSuWinding);
62 }
63 int nextIndex = firstIndex + 1;
64 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
65 SkOpSegment* first = NULL;
66 do {
67 SkASSERT(nextIndex != firstIndex);
68 if (nextIndex == angleCount) {
69 nextIndex = 0;
70 }
71 angle = sorted[nextIndex];
72 segment = angle->segment();
73 int start = angle->start();
74 int end = angle->end();
75 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
76 segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
77 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
78 if (!segment->done(angle)) {
79 if (!first) {
80 first = segment;
81 nextStart = start;
82 nextEnd = end;
83 }
84 (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
85 oppSumWinding, true, angle);
86 }
87 } while (++nextIndex != lastIndex);
88 if (first) {
89 #if TRY_ROTATE
90 *chase.insert(0) = span;
91 #else
92 *chase.append() = span;
93 #endif
94 return first;
95 }
96 }
97 return NULL;
98}
99
100/*
101static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
102 bool windingIsOp, PathOp op) {
103 bool active = windingIsActive(winding, spanWinding);
104 if (!active) {
105 return false;
106 }
107 if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
108 switch (op) {
109 case kIntersect_Op:
110 case kUnion_Op:
111 return true;
112 case kDifference_Op: {
113 int absSpan = abs(spanWinding);
114 int absOpp = abs(oppSpanWinding);
115 return windingIsOp ? absSpan < absOpp : absSpan > absOpp;
116 }
117 case kXor_Op:
118 return spanWinding != oppSpanWinding;
119 default:
120 SkASSERT(0);
121 }
122 }
123 bool opActive = oppWinding != 0;
124 return gOpLookup[op][opActive][windingIsOp];
125}
126*/
127
128static bool bridgeOp(SkTDArray<SkOpContour*>& contourList, const SkPathOp op,
129 const int xorMask, const int xorOpMask, SkPathWriter* simple) {
130 bool firstContour = true;
131 bool unsortable = false;
132 bool topUnsortable = false;
133 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
134 do {
135 int index, endIndex;
136 bool done;
137 SkOpSegment* current = FindSortableTop(contourList, &firstContour, &index, &endIndex,
138 &topLeft, &topUnsortable, &done, true);
139 if (!current) {
140 if (topUnsortable || !done) {
141 topUnsortable = false;
142 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
143 topLeft.fX = topLeft.fY = SK_ScalarMin;
144 continue;
145 }
146 break;
147 }
148 SkTDArray<SkOpSpan*> chaseArray;
149 do {
150 if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
151 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000152 if (!unsortable && current->done()) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000153 #if DEBUG_ACTIVE_SPANS
caryclark@google.com07393ca2013-04-08 11:47:37 +0000154 DebugShowActiveSpans(contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000155 #endif
caryclark@google.coma5e55922013-05-07 18:51:31 +0000156 if (simple->isEmpty()) {
157 simple->init();
158 break;
159 }
160 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000161 SkASSERT(unsortable || !current->done());
162 int nextStart = index;
163 int nextEnd = endIndex;
164 SkOpSegment* next = current->findNextOp(&chaseArray, &nextStart, &nextEnd,
165 &unsortable, op, xorMask, xorOpMask);
166 if (!next) {
167 if (!unsortable && simple->hasMove()
168 && current->verb() != SkPath::kLine_Verb
169 && !simple->isClosed()) {
170 current->addCurveTo(index, endIndex, simple, true);
171 SkASSERT(simple->isClosed());
172 }
173 break;
174 }
175 #if DEBUG_FLOW
176 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
177 current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
178 current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
179 #endif
180 current->addCurveTo(index, endIndex, simple, true);
181 current = next;
182 index = nextStart;
183 endIndex = nextEnd;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000184 } while (!simple->isClosed() && (!unsortable
caryclark@google.com07393ca2013-04-08 11:47:37 +0000185 || !current->done(SkMin32(index, endIndex))));
186 if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000187 SkASSERT(unsortable || simple->isEmpty());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000188 int min = SkMin32(index, endIndex);
189 if (!current->done(min)) {
190 current->addCurveTo(index, endIndex, simple, true);
191 current->markDoneBinary(min);
192 }
193 }
194 simple->close();
195 } else {
196 SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
197 if (last && !last->fLoop) {
198 *chaseArray.append() = last;
199 }
200 }
201 current = findChaseOp(chaseArray, index, endIndex);
202 #if DEBUG_ACTIVE_SPANS
203 DebugShowActiveSpans(contourList);
204 #endif
205 if (!current) {
206 break;
207 }
208 } while (true);
209 } while (true);
210 return simple->someAssemblyRequired();
211}
212
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000213// pretty picture:
214// https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b-7vD9JP2V-kn9Ss/edit?usp=sharing
215static const SkPathOp gOpInverse[kReverseDifference_PathOp + 1][2][2] = {
216// inside minuend outside minuend
217// inside subtrahend outside subtrahend inside subtrahend outside subtrahend
218 {{ kDifference_PathOp, kIntersect_PathOp }, { kUnion_PathOp, kReverseDifference_PathOp }},
219 {{ kIntersect_PathOp, kDifference_PathOp }, { kReverseDifference_PathOp, kUnion_PathOp }},
220 {{ kUnion_PathOp, kReverseDifference_PathOp }, { kDifference_PathOp, kIntersect_PathOp }},
221 {{ kXOR_PathOp, kXOR_PathOp }, { kXOR_PathOp, kXOR_PathOp }},
222 {{ kReverseDifference_PathOp, kUnion_PathOp }, { kIntersect_PathOp, kDifference_PathOp }},
223};
224
225static const bool gOutInverse[kReverseDifference_PathOp + 1][2][2] = {
226 {{ false, false }, { true, false }}, // diff
227 {{ false, false }, { false, true }}, // sect
228 {{ false, true }, { true, true }}, // union
229 {{ false, true }, { true, false }}, // xor
230 {{ false, true }, { false, false }}, // rev diff
231};
232
caryclark@google.com66560ca2013-04-26 19:51:16 +0000233bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000234#if DEBUG_SHOW_PATH
235 ShowPath(one, "path");
236 ShowPath(two, "pathB");
237 ShowOp(op, "path", "pathB");
238#endif
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000239 op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000240 SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]
241 ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000242 const SkPath* minuend = &one;
243 const SkPath* subtrahend = &two;
244 if (op == kReverseDifference_PathOp) {
245 minuend = &two;
246 subtrahend = &one;
247 op = kDifference_PathOp;
248 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000249#if DEBUG_SORT || DEBUG_SWAP_TOP
250 gDebugSortCount = gDebugSortCountDefault;
251#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000252 // turn path into list of segments
253 SkTArray<SkOpContour> contours;
254 // FIXME: add self-intersecting cubics' T values to segment
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000255 SkOpEdgeBuilder builder(*minuend, contours);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000256 const int xorMask = builder.xorMask();
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000257 builder.addOperand(*subtrahend);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000258 if (!builder.finish()) {
259 return false;
260 }
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000261 result->reset();
262 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000263 const int xorOpMask = builder.xorMask();
264 SkTDArray<SkOpContour*> contourList;
265 MakeContourList(contours, contourList, xorMask == kEvenOdd_PathOpsMask,
266 xorOpMask == kEvenOdd_PathOpsMask);
267 SkOpContour** currentPtr = contourList.begin();
268 if (!currentPtr) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000269 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000270 }
271 SkOpContour** listEnd = contourList.end();
272 // find all intersections between segments
273 do {
274 SkOpContour** nextPtr = currentPtr;
275 SkOpContour* current = *currentPtr++;
276 if (current->containsCubics()) {
277 AddSelfIntersectTs(current);
278 }
279 SkOpContour* next;
280 do {
281 next = *nextPtr++;
282 } while (AddIntersectTs(current, next) && nextPtr != listEnd);
283 } while (currentPtr != listEnd);
284 // eat through coincident edges
285
286 int total = 0;
287 int index;
288 for (index = 0; index < contourList.count(); ++index) {
289 total += contourList[index]->segments().count();
290 }
291#if DEBUG_SHOW_WINDING
292 SkOpContour::debugShowWindingValues(contourList);
293#endif
294 CoincidenceCheck(&contourList, total);
295#if DEBUG_SHOW_WINDING
296 SkOpContour::debugShowWindingValues(contourList);
297#endif
298 FixOtherTIndex(&contourList);
299 SortSegments(&contourList);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000300#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
caryclark@google.com07393ca2013-04-08 11:47:37 +0000301 DebugShowActiveSpans(contourList);
302#endif
303 // construct closed contours
304 SkPathWriter wrapper(*result);
305 bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
306 { // if some edges could not be resolved, assemble remaining fragments
307 SkPath temp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000308 temp.setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000309 SkPathWriter assembled(temp);
310 Assemble(wrapper, &assembled);
311 *result = *assembled.nativePath();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000312 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000313 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000314 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000315}