blob: 0df4859cdfcd90b46356575ca97f9ee3c6f42208 [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;
caryclark@google.comd892bd82013-06-17 14:10:36 +000023 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
caryclark@google.com07393ca2013-04-08 11:47:37 +000024 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 }
caryclark@google.comd892bd82013-06-17 14:10:36 +000039 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
caryclark@google.comcffbcc32013-06-04 17:59:42 +000040 bool sortable = SkOpSegment::SortAngles(angles, &sorted,
41 SkOpSegment::kMayBeUnordered_SortAngleKind);
caryclark@google.com07393ca2013-04-08 11:47:37 +000042 int angleCount = sorted.count();
43#if DEBUG_SORT
caryclark@google.com07e97fc2013-07-08 17:17:02 +000044 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +000045#endif
46 if (!sortable) {
47 continue;
48 }
49 // find first angle, initialize winding to computed fWindSum
50 int firstIndex = -1;
51 const SkOpAngle* angle;
52 do {
53 angle = sorted[++firstIndex];
54 segment = angle->segment();
55 } while (segment->windSum(angle) == SK_MinS32);
56 #if DEBUG_SORT
caryclark@google.com07e97fc2013-07-08 17:17:02 +000057 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +000058 #endif
59 int sumMiWinding = segment->updateWindingReverse(angle);
60 int sumSuWinding = segment->updateOppWindingReverse(angle);
61 if (segment->operand()) {
62 SkTSwap<int>(sumMiWinding, sumSuWinding);
63 }
64 int nextIndex = firstIndex + 1;
65 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
66 SkOpSegment* first = NULL;
67 do {
68 SkASSERT(nextIndex != firstIndex);
69 if (nextIndex == angleCount) {
70 nextIndex = 0;
71 }
72 angle = sorted[nextIndex];
73 segment = angle->segment();
74 int start = angle->start();
75 int end = angle->end();
76 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
77 segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
78 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
79 if (!segment->done(angle)) {
80 if (!first) {
81 first = segment;
82 nextStart = start;
83 nextEnd = end;
84 }
85 (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
86 oppSumWinding, true, angle);
87 }
88 } while (++nextIndex != lastIndex);
89 if (first) {
90 #if TRY_ROTATE
91 *chase.insert(0) = span;
92 #else
93 *chase.append() = span;
94 #endif
95 return first;
96 }
97 }
98 return NULL;
99}
100
101/*
102static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
103 bool windingIsOp, PathOp op) {
104 bool active = windingIsActive(winding, spanWinding);
105 if (!active) {
106 return false;
107 }
108 if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
109 switch (op) {
110 case kIntersect_Op:
111 case kUnion_Op:
112 return true;
113 case kDifference_Op: {
114 int absSpan = abs(spanWinding);
115 int absOpp = abs(oppSpanWinding);
116 return windingIsOp ? absSpan < absOpp : absSpan > absOpp;
117 }
118 case kXor_Op:
119 return spanWinding != oppSpanWinding;
120 default:
121 SkASSERT(0);
122 }
123 }
124 bool opActive = oppWinding != 0;
125 return gOpLookup[op][opActive][windingIsOp];
126}
127*/
128
caryclark@google.comd892bd82013-06-17 14:10:36 +0000129static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp op,
caryclark@google.com07393ca2013-04-08 11:47:37 +0000130 const int xorMask, const int xorOpMask, SkPathWriter* simple) {
131 bool firstContour = true;
132 bool unsortable = false;
133 bool topUnsortable = false;
134 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
135 do {
136 int index, endIndex;
137 bool done;
138 SkOpSegment* current = FindSortableTop(contourList, &firstContour, &index, &endIndex,
139 &topLeft, &topUnsortable, &done, true);
140 if (!current) {
141 if (topUnsortable || !done) {
142 topUnsortable = false;
143 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
144 topLeft.fX = topLeft.fY = SK_ScalarMin;
145 continue;
146 }
147 break;
148 }
149 SkTDArray<SkOpSpan*> chaseArray;
150 do {
151 if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
152 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000153 if (!unsortable && current->done()) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000154 #if DEBUG_ACTIVE_SPANS
caryclark@google.com07393ca2013-04-08 11:47:37 +0000155 DebugShowActiveSpans(contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000156 #endif
caryclark@google.coma5e55922013-05-07 18:51:31 +0000157 if (simple->isEmpty()) {
158 simple->init();
159 break;
160 }
161 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000162 SkASSERT(unsortable || !current->done());
163 int nextStart = index;
164 int nextEnd = endIndex;
165 SkOpSegment* next = current->findNextOp(&chaseArray, &nextStart, &nextEnd,
166 &unsortable, op, xorMask, xorOpMask);
167 if (!next) {
168 if (!unsortable && simple->hasMove()
169 && current->verb() != SkPath::kLine_Verb
170 && !simple->isClosed()) {
171 current->addCurveTo(index, endIndex, simple, true);
172 SkASSERT(simple->isClosed());
173 }
174 break;
175 }
176 #if DEBUG_FLOW
177 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
178 current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
179 current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
180 #endif
181 current->addCurveTo(index, endIndex, simple, true);
182 current = next;
183 index = nextStart;
184 endIndex = nextEnd;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000185 } while (!simple->isClosed() && (!unsortable
caryclark@google.com07393ca2013-04-08 11:47:37 +0000186 || !current->done(SkMin32(index, endIndex))));
187 if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000188 SkASSERT(unsortable || simple->isEmpty());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000189 int min = SkMin32(index, endIndex);
190 if (!current->done(min)) {
191 current->addCurveTo(index, endIndex, simple, true);
192 current->markDoneBinary(min);
193 }
194 }
195 simple->close();
196 } else {
197 SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
198 if (last && !last->fLoop) {
199 *chaseArray.append() = last;
200 }
201 }
202 current = findChaseOp(chaseArray, index, endIndex);
203 #if DEBUG_ACTIVE_SPANS
204 DebugShowActiveSpans(contourList);
205 #endif
206 if (!current) {
207 break;
208 }
209 } while (true);
210 } while (true);
211 return simple->someAssemblyRequired();
212}
213
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000214// pretty picture:
215// https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b-7vD9JP2V-kn9Ss/edit?usp=sharing
216static const SkPathOp gOpInverse[kReverseDifference_PathOp + 1][2][2] = {
217// inside minuend outside minuend
218// inside subtrahend outside subtrahend inside subtrahend outside subtrahend
219 {{ kDifference_PathOp, kIntersect_PathOp }, { kUnion_PathOp, kReverseDifference_PathOp }},
220 {{ kIntersect_PathOp, kDifference_PathOp }, { kReverseDifference_PathOp, kUnion_PathOp }},
221 {{ kUnion_PathOp, kReverseDifference_PathOp }, { kDifference_PathOp, kIntersect_PathOp }},
222 {{ kXOR_PathOp, kXOR_PathOp }, { kXOR_PathOp, kXOR_PathOp }},
223 {{ kReverseDifference_PathOp, kUnion_PathOp }, { kIntersect_PathOp, kDifference_PathOp }},
224};
225
226static const bool gOutInverse[kReverseDifference_PathOp + 1][2][2] = {
227 {{ false, false }, { true, false }}, // diff
228 {{ false, false }, { false, true }}, // sect
229 {{ false, true }, { true, true }}, // union
230 {{ false, true }, { true, false }}, // xor
231 {{ false, true }, { false, false }}, // rev diff
232};
233
caryclark@google.com66560ca2013-04-26 19:51:16 +0000234bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000235#if DEBUG_SHOW_TEST_NAME
236 char* debugName = DEBUG_FILENAME_STRING;
237 if (debugName && debugName[0]) {
238 DebugBumpTestName(debugName);
239 DebugShowPath(one, two, op, debugName);
240 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000241#endif
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000242 op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000243 SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]
244 ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000245 const SkPath* minuend = &one;
246 const SkPath* subtrahend = &two;
247 if (op == kReverseDifference_PathOp) {
248 minuend = &two;
249 subtrahend = &one;
250 op = kDifference_PathOp;
251 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000252#if DEBUG_SORT || DEBUG_SWAP_TOP
253 gDebugSortCount = gDebugSortCountDefault;
254#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000255 // turn path into list of segments
256 SkTArray<SkOpContour> contours;
257 // FIXME: add self-intersecting cubics' T values to segment
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000258 SkOpEdgeBuilder builder(*minuend, contours);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000259 const int xorMask = builder.xorMask();
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000260 builder.addOperand(*subtrahend);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000261 if (!builder.finish()) {
262 return false;
263 }
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000264 result->reset();
265 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000266 const int xorOpMask = builder.xorMask();
caryclark@google.comd892bd82013-06-17 14:10:36 +0000267 SkTArray<SkOpContour*, true> contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000268 MakeContourList(contours, contourList, xorMask == kEvenOdd_PathOpsMask,
269 xorOpMask == kEvenOdd_PathOpsMask);
270 SkOpContour** currentPtr = contourList.begin();
271 if (!currentPtr) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000272 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000273 }
274 SkOpContour** listEnd = contourList.end();
275 // find all intersections between segments
276 do {
277 SkOpContour** nextPtr = currentPtr;
278 SkOpContour* current = *currentPtr++;
279 if (current->containsCubics()) {
280 AddSelfIntersectTs(current);
281 }
282 SkOpContour* next;
283 do {
284 next = *nextPtr++;
285 } while (AddIntersectTs(current, next) && nextPtr != listEnd);
286 } while (currentPtr != listEnd);
287 // eat through coincident edges
288
289 int total = 0;
290 int index;
291 for (index = 0; index < contourList.count(); ++index) {
292 total += contourList[index]->segments().count();
293 }
294#if DEBUG_SHOW_WINDING
295 SkOpContour::debugShowWindingValues(contourList);
296#endif
297 CoincidenceCheck(&contourList, total);
298#if DEBUG_SHOW_WINDING
299 SkOpContour::debugShowWindingValues(contourList);
300#endif
301 FixOtherTIndex(&contourList);
302 SortSegments(&contourList);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000303#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
caryclark@google.com07393ca2013-04-08 11:47:37 +0000304 DebugShowActiveSpans(contourList);
305#endif
306 // construct closed contours
307 SkPathWriter wrapper(*result);
308 bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
309 { // if some edges could not be resolved, assemble remaining fragments
310 SkPath temp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000311 temp.setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000312 SkPathWriter assembled(temp);
313 Assemble(wrapper, &assembled);
314 *result = *assembled.nativePath();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000315 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000316 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000317 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000318}