blob: 826a19070622ba8974df564053aa12a07706e147 [file] [log] [blame]
caryclark@google.com235f56a2012-09-14 14:19:30 +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 */
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +00007
caryclark@google.com235f56a2012-09-14 14:19:30 +00008#include "Simplify.h"
9
10namespace Op {
11
caryclark@google.com7ba591e2012-11-20 14:21:54 +000012#define INCLUDED_BY_SHAPE_OPS 1
13
caryclark@google.com235f56a2012-09-14 14:19:30 +000014#include "Simplify.cpp"
15
caryclark@google.com31143cf2012-11-09 22:14:19 +000016// FIXME: this and find chase should be merge together, along with
17// other code that walks winding in angles
18// OPTIMIZATION: Probably, the walked winding should be rolled into the angle structure
19// so it isn't duplicated by walkers like this one
20static Segment* findChaseOp(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
21 while (chase.count()) {
22 Span* span;
23 chase.pop(&span);
24 const Span& backPtr = span->fOther->span(span->fOtherIndex);
25 Segment* segment = backPtr.fOther;
26 tIndex = backPtr.fOtherIndex;
27 SkTDArray<Angle> angles;
28 int done = 0;
29 if (segment->activeAngle(tIndex, done, angles)) {
30 Angle* last = angles.end() - 1;
31 tIndex = last->start();
32 endIndex = last->end();
33 #if TRY_ROTATE
34 *chase.insert(0) = span;
35 #else
36 *chase.append() = span;
37 #endif
38 return last->segment();
39 }
40 if (done == angles.count()) {
41 continue;
42 }
43 SkTDArray<Angle*> sorted;
44 bool sortable = Segment::SortAngles(angles, sorted);
45#if DEBUG_SORT
46 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
47#endif
48 if (!sortable) {
49 continue;
50 }
51 // find first angle, initialize winding to computed fWindSum
52 int firstIndex = -1;
53 const Angle* angle;
54 int winding;
55 do {
56 angle = sorted[++firstIndex];
57 segment = angle->segment();
58 winding = segment->windSum(angle);
59 } while (winding == SK_MinS32);
60 int spanWinding = segment->spanSign(angle->start(), angle->end());
61 #if DEBUG_WINDING
62 SkDebugf("%s winding=%d spanWinding=%d\n",
63 __FUNCTION__, winding, spanWinding);
64 #endif
65 // turn span winding into contour winding
66 if (spanWinding * winding < 0) {
67 winding += spanWinding;
68 }
69 // we care about first sign and whether wind sum indicates this
70 // edge is inside or outside. Maybe need to pass span winding
71 // or first winding or something into this function?
72 // advance to first undone angle, then return it and winding
73 // (to set whether edges are active or not)
74 int nextIndex = firstIndex + 1;
75 int angleCount = sorted.count();
76 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
77 angle = sorted[firstIndex];
78 segment = angle->segment();
79 int oWinding = segment->oppSum(angle);
80 #if DEBUG_SORT
81 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
82 #endif
83 winding -= segment->spanSign(angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +000084 oWinding -= segment->oppSign(angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +000085 bool firstOperand = segment->operand();
86 do {
87 SkASSERT(nextIndex != firstIndex);
88 if (nextIndex == angleCount) {
89 nextIndex = 0;
90 }
91 angle = sorted[nextIndex];
92 segment = angle->segment();
93 int deltaSum = segment->spanSign(angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +000094 int deltaOppSum = segment->oppSign(angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +000095 bool angleIsOp = segment->operand() ^ firstOperand;
96 int maxWinding;
97 if (angleIsOp) {
98 maxWinding = oWinding;
99 oWinding -= deltaSum;
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000100 winding -= deltaOppSum;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000101 } else {
102 maxWinding = winding;
103 winding -= deltaSum;
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000104 oWinding -= deltaOppSum;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000105 }
106 #if DEBUG_SORT
107 SkDebugf("%s id=%d maxWinding=%d winding=%d oWinding=%d sign=%d\n", __FUNCTION__,
108 segment->debugID(), maxWinding, winding, oWinding, angle->sign());
109 #endif
110 tIndex = angle->start();
111 endIndex = angle->end();
112 int lesser = SkMin32(tIndex, endIndex);
113 const Span& nextSpan = segment->span(lesser);
114 if (!nextSpan.fDone) {
115 if (angleIsOp) {
116 SkTSwap(winding, oWinding);
117 }
118 if (useInnerWinding(maxWinding, winding)) {
119 maxWinding = winding;
120 }
121 segment->markWinding(lesser, maxWinding, oWinding);
122 break;
123 }
124 } while (++nextIndex != lastIndex);
125 #if TRY_ROTATE
126 *chase.insert(0) = span;
127 #else
128 *chase.append() = span;
129 #endif
130 return segment;
131 }
132 return NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000133}
134
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000135static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000136 bool windingIsOp, ShapeOp op) {
137 bool active = windingIsActive(winding, spanWinding);
138 if (!active) {
139 return false;
140 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000141 if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
142 return op == kIntersect_Op || op == kUnion_Op;
143 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000144 bool opActive = oppWinding != 0;
145 return gOpLookup[op][opActive][windingIsOp];
146}
147
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000148static int updateWindings(const Segment* current, int index, int endIndex,
149 int& spanWinding, int& oppWinding, int& oppSpanWinding) {
150 int winding = updateWindings(current, index, endIndex, spanWinding);
151 int lesser = SkMin32(index, endIndex);
152 oppWinding = current->oppSum(lesser);
153 oppSpanWinding = current->oppSign(index, endIndex);
154 if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)) {
155 oppWinding -= oppSpanWinding;
156 }
157 return winding;
158}
159
caryclark@google.com31143cf2012-11-09 22:14:19 +0000160static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
caryclark@google.comf839c032012-10-26 21:03:50 +0000161 const int aXorMask, const int bXorMask, PathWrapper& simple) {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000162 bool firstContour = true;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000163 bool unsortable = false;
164 bool closable = true;
caryclark@google.comf839c032012-10-26 21:03:50 +0000165 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com235f56a2012-09-14 14:19:30 +0000166 do {
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000167 int index, endIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +0000168 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000169 if (!current) {
170 break;
171 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000172 int contourWinding, oppContourWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000173 if (firstContour) {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000174 contourWinding = oppContourWinding = 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000175 firstContour = false;
176 } else {
caryclark@google.com6ec15262012-11-16 20:16:50 +0000177 int minIndex = SkMin32(index, endIndex);
178 int sumWinding = current->windSum(minIndex);
179 int oppSumWinding = current->oppSum(minIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000180 // FIXME: don't I have to adjust windSum to get contourWinding?
181 if (sumWinding == SK_MinS32) {
caryclark@google.com6ec15262012-11-16 20:16:50 +0000182 sumWinding = current->computeSum(index, endIndex, &oppSumWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000183 }
184 if (sumWinding == SK_MinS32) {
185 contourWinding = innerContourCheck(contourList, current,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000186 index, endIndex, false);
187 oppContourWinding = innerContourCheck(contourList, current,
188 index, endIndex, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000189 } else {
caryclark@google.com6ec15262012-11-16 20:16:50 +0000190 int spanWinding, oppWinding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000191 contourWinding = updateWindings(current, index, endIndex, spanWinding,
192 oppContourWinding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000193#if DEBUG_WINDING
caryclark@google.com6ec15262012-11-16 20:16:50 +0000194 SkDebugf("%s contourWinding=%d oppContourWinding=%d spanWinding=%d oppWinding=%d\n",
195 __FUNCTION__, contourWinding, oppContourWinding, spanWinding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000196#endif
197 }
198#if DEBUG_WINDING
199 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
200 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
201#endif
202 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000203 int winding = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000204 int oppWinding = oppContourWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000205 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000206 int oppSpanWinding = current->oppSign(index, endIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000207 SkTDArray<Span*> chaseArray;
208 do {
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000209 bool active = windingIsActive(winding, oppWinding, spanWinding, oppSpanWinding,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000210 current->operand(), op);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000211 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000212 SkDebugf("%s active=%s winding=%d oppWinding=%d spanWinding=%d oppSpanWinding=%d\n",
caryclark@google.com235f56a2012-09-14 14:19:30 +0000213 __FUNCTION__, active ? "true" : "false",
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000214 winding, oppWinding, spanWinding, oppSpanWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000215 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000216 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000217 #if DEBUG_ACTIVE_SPANS
218 if (!unsortable && current->done()) {
219 debugShowActiveSpans(contourList);
220 }
221 #endif
222 SkASSERT(unsortable || !current->done());
caryclark@google.com235f56a2012-09-14 14:19:30 +0000223 int nextStart = index;
224 int nextEnd = endIndex;
225 Segment* next = current->findNextOp(chaseArray, active,
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000226 nextStart, nextEnd, winding, oppWinding, spanWinding, oppSpanWinding,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000227 unsortable, op, aXorMask, bXorMask);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000228 if (!next) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000229 SkASSERT(!unsortable);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000230 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +0000231 && current->verb() != SkPath::kLine_Verb
232 && !simple.isClosed()) {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000233 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.comf839c032012-10-26 21:03:50 +0000234 SkASSERT(simple.isClosed());
caryclark@google.com235f56a2012-09-14 14:19:30 +0000235 }
236 break;
237 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000238 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000239 current = next;
240 index = nextStart;
241 endIndex = nextEnd;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000242 } while (!simple.isClosed()
243 && ((active && !unsortable) || !current->done()));
244 if (active) {
245 if (!simple.isClosed()) {
246 SkASSERT(unsortable);
247 int min = SkMin32(index, endIndex);
248 if (!current->done(min)) {
249 current->addCurveTo(index, endIndex, simple, true);
250 current->markDone(SkMin32(index, endIndex), winding ? winding : spanWinding);
251 }
252 closable = false;
253 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000254 simple.close();
255 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000256 current = findChaseOp(chaseArray, index, endIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000257 #if DEBUG_ACTIVE_SPANS
258 debugShowActiveSpans(contourList);
259 #endif
260 if (!current) {
261 break;
262 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000263 winding = updateWindings(current, index, endIndex, spanWinding, oppWinding,
264 oppSpanWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000265 } while (true);
266 } while (true);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000267 return closable;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000268}
269
270} // end of Op namespace
271
272
273void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
274 result.reset();
275 result.setFillType(SkPath::kEvenOdd_FillType);
276 // turn path into list of segments
277 SkTArray<Op::Contour> contours;
278 // FIXME: add self-intersecting cubics' T values to segment
279 Op::EdgeBuilder builder(one, contours);
280 const int aXorMask = builder.xorMask();
281 builder.addOperand(two);
282 const int bXorMask = builder.xorMask();
283 builder.finish();
284 SkTDArray<Op::Contour*> contourList;
285 makeContourList(contours, contourList);
286 Op::Contour** currentPtr = contourList.begin();
287 if (!currentPtr) {
288 return;
289 }
290 Op::Contour** listEnd = contourList.end();
291 // find all intersections between segments
292 do {
293 Op::Contour** nextPtr = currentPtr;
294 Op::Contour* current = *currentPtr++;
295 Op::Contour* next;
296 do {
297 next = *nextPtr++;
298 } while (addIntersectTs(current, next) && nextPtr != listEnd);
299 } while (currentPtr != listEnd);
300 // eat through coincident edges
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +0000301
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000302 int total = 0;
303 int index;
304 for (index = 0; index < contourList.count(); ++index) {
305 total += contourList[index]->segments().count();
306 }
307#if DEBUG_WINDING
308 Op::Contour::debugShowWindingValues(contourList);
309#endif
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000310 coincidenceCheck(contourList, (aXorMask == kEvenOdd_Mask)
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000311 ^ (bXorMask == kEvenOdd_Mask), total);
312#if DEBUG_WINDING
313 Op::Contour::debugShowWindingValues(contourList);
314#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000315 fixOtherTIndex(contourList);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000316 sortSegments(contourList);
317#if DEBUG_ACTIVE_SPANS
318 debugShowActiveSpans(contourList);
319#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000320 // construct closed contours
caryclark@google.comf839c032012-10-26 21:03:50 +0000321 Op::PathWrapper wrapper(result);
322 bridgeOp(contourList, op, aXorMask, bXorMask, wrapper);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000323}