blob: 17a19e83696383f23dbe2a738d33a71e2bbfd15e [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 */
7#include "Simplify.h"
8
9namespace Op {
10
11#include "Simplify.cpp"
12
caryclark@google.com31143cf2012-11-09 22:14:19 +000013// FIXME: this and find chase should be merge together, along with
14// other code that walks winding in angles
15// OPTIMIZATION: Probably, the walked winding should be rolled into the angle structure
16// so it isn't duplicated by walkers like this one
17static Segment* findChaseOp(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
18 while (chase.count()) {
19 Span* span;
20 chase.pop(&span);
21 const Span& backPtr = span->fOther->span(span->fOtherIndex);
22 Segment* segment = backPtr.fOther;
23 tIndex = backPtr.fOtherIndex;
24 SkTDArray<Angle> angles;
25 int done = 0;
26 if (segment->activeAngle(tIndex, done, angles)) {
27 Angle* last = angles.end() - 1;
28 tIndex = last->start();
29 endIndex = last->end();
30 #if TRY_ROTATE
31 *chase.insert(0) = span;
32 #else
33 *chase.append() = span;
34 #endif
35 return last->segment();
36 }
37 if (done == angles.count()) {
38 continue;
39 }
40 SkTDArray<Angle*> sorted;
41 bool sortable = Segment::SortAngles(angles, sorted);
42#if DEBUG_SORT
43 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
44#endif
45 if (!sortable) {
46 continue;
47 }
48 // find first angle, initialize winding to computed fWindSum
49 int firstIndex = -1;
50 const Angle* angle;
51 int winding;
52 do {
53 angle = sorted[++firstIndex];
54 segment = angle->segment();
55 winding = segment->windSum(angle);
56 } while (winding == SK_MinS32);
57 int spanWinding = segment->spanSign(angle->start(), angle->end());
58 #if DEBUG_WINDING
59 SkDebugf("%s winding=%d spanWinding=%d\n",
60 __FUNCTION__, winding, spanWinding);
61 #endif
62 // turn span winding into contour winding
63 if (spanWinding * winding < 0) {
64 winding += spanWinding;
65 }
66 // we care about first sign and whether wind sum indicates this
67 // edge is inside or outside. Maybe need to pass span winding
68 // or first winding or something into this function?
69 // advance to first undone angle, then return it and winding
70 // (to set whether edges are active or not)
71 int nextIndex = firstIndex + 1;
72 int angleCount = sorted.count();
73 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
74 angle = sorted[firstIndex];
75 segment = angle->segment();
76 int oWinding = segment->oppSum(angle);
77 #if DEBUG_SORT
78 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
79 #endif
80 winding -= segment->spanSign(angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +000081 oWinding -= segment->oppSign(angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +000082 bool firstOperand = segment->operand();
83 do {
84 SkASSERT(nextIndex != firstIndex);
85 if (nextIndex == angleCount) {
86 nextIndex = 0;
87 }
88 angle = sorted[nextIndex];
89 segment = angle->segment();
90 int deltaSum = segment->spanSign(angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +000091 int deltaOppSum = segment->oppSign(angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +000092 bool angleIsOp = segment->operand() ^ firstOperand;
93 int maxWinding;
94 if (angleIsOp) {
95 maxWinding = oWinding;
96 oWinding -= deltaSum;
caryclark@google.com57cff8d2012-11-14 21:14:56 +000097 winding -= deltaOppSum;
caryclark@google.com31143cf2012-11-09 22:14:19 +000098 } else {
99 maxWinding = winding;
100 winding -= deltaSum;
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000101 oWinding -= deltaOppSum;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000102 }
103 #if DEBUG_SORT
104 SkDebugf("%s id=%d maxWinding=%d winding=%d oWinding=%d sign=%d\n", __FUNCTION__,
105 segment->debugID(), maxWinding, winding, oWinding, angle->sign());
106 #endif
107 tIndex = angle->start();
108 endIndex = angle->end();
109 int lesser = SkMin32(tIndex, endIndex);
110 const Span& nextSpan = segment->span(lesser);
111 if (!nextSpan.fDone) {
112 if (angleIsOp) {
113 SkTSwap(winding, oWinding);
114 }
115 if (useInnerWinding(maxWinding, winding)) {
116 maxWinding = winding;
117 }
118 segment->markWinding(lesser, maxWinding, oWinding);
119 break;
120 }
121 } while (++nextIndex != lastIndex);
122 #if TRY_ROTATE
123 *chase.insert(0) = span;
124 #else
125 *chase.append() = span;
126 #endif
127 return segment;
128 }
129 return NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000130}
131
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000132static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000133 bool windingIsOp, ShapeOp op) {
134 bool active = windingIsActive(winding, spanWinding);
135 if (!active) {
136 return false;
137 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000138 if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
139 return op == kIntersect_Op || op == kUnion_Op;
140 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000141 bool opActive = oppWinding != 0;
142 return gOpLookup[op][opActive][windingIsOp];
143}
144
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000145static int updateWindings(const Segment* current, int index, int endIndex,
146 int& spanWinding, int& oppWinding, int& oppSpanWinding) {
147 int winding = updateWindings(current, index, endIndex, spanWinding);
148 int lesser = SkMin32(index, endIndex);
149 oppWinding = current->oppSum(lesser);
150 oppSpanWinding = current->oppSign(index, endIndex);
151 if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)) {
152 oppWinding -= oppSpanWinding;
153 }
154 return winding;
155}
156
caryclark@google.com31143cf2012-11-09 22:14:19 +0000157static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
caryclark@google.comf839c032012-10-26 21:03:50 +0000158 const int aXorMask, const int bXorMask, PathWrapper& simple) {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000159 bool firstContour = true;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000160 bool unsortable = false;
161 bool closable = true;
caryclark@google.comf839c032012-10-26 21:03:50 +0000162 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com235f56a2012-09-14 14:19:30 +0000163 do {
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000164 int index, endIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +0000165 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000166 if (!current) {
167 break;
168 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000169 int contourWinding, oppContourWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000170 if (firstContour) {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000171 contourWinding = oppContourWinding = 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000172 firstContour = false;
173 } else {
caryclark@google.com6ec15262012-11-16 20:16:50 +0000174 int minIndex = SkMin32(index, endIndex);
175 int sumWinding = current->windSum(minIndex);
176 int oppSumWinding = current->oppSum(minIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000177 // FIXME: don't I have to adjust windSum to get contourWinding?
178 if (sumWinding == SK_MinS32) {
caryclark@google.com6ec15262012-11-16 20:16:50 +0000179 sumWinding = current->computeSum(index, endIndex, &oppSumWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000180 }
181 if (sumWinding == SK_MinS32) {
182 contourWinding = innerContourCheck(contourList, current,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000183 index, endIndex, false);
184 oppContourWinding = innerContourCheck(contourList, current,
185 index, endIndex, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000186 } else {
caryclark@google.com6ec15262012-11-16 20:16:50 +0000187 int spanWinding, oppWinding;
188 contourWinding = updateWindings(current, index, endIndex, spanWinding, oppWinding,
189 oppContourWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000190#if DEBUG_WINDING
caryclark@google.com6ec15262012-11-16 20:16:50 +0000191 SkDebugf("%s contourWinding=%d oppContourWinding=%d spanWinding=%d oppWinding=%d\n",
192 __FUNCTION__, contourWinding, oppContourWinding, spanWinding, oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000193#endif
194 }
195#if DEBUG_WINDING
196 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
197 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
198#endif
199 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000200 int winding = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000201 int oppWinding = oppContourWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000202 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000203 int oppSpanWinding = current->oppSign(index, endIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000204 SkTDArray<Span*> chaseArray;
205 do {
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000206 bool active = windingIsActive(winding, oppWinding, spanWinding, oppSpanWinding,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000207 current->operand(), op);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000208 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000209 SkDebugf("%s active=%s winding=%d oppWinding=%d spanWinding=%d oppSpanWinding=%d\n",
caryclark@google.com235f56a2012-09-14 14:19:30 +0000210 __FUNCTION__, active ? "true" : "false",
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000211 winding, oppWinding, spanWinding, oppSpanWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000212 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000213 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000214 #if DEBUG_ACTIVE_SPANS
215 if (!unsortable && current->done()) {
216 debugShowActiveSpans(contourList);
217 }
218 #endif
219 SkASSERT(unsortable || !current->done());
caryclark@google.com235f56a2012-09-14 14:19:30 +0000220 int nextStart = index;
221 int nextEnd = endIndex;
222 Segment* next = current->findNextOp(chaseArray, active,
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000223 nextStart, nextEnd, winding, oppWinding, spanWinding, oppSpanWinding,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000224 unsortable, op, aXorMask, bXorMask);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000225 if (!next) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000226 SkASSERT(!unsortable);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000227 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +0000228 && current->verb() != SkPath::kLine_Verb
229 && !simple.isClosed()) {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000230 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.comf839c032012-10-26 21:03:50 +0000231 SkASSERT(simple.isClosed());
caryclark@google.com235f56a2012-09-14 14:19:30 +0000232 }
233 break;
234 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000235 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000236 current = next;
237 index = nextStart;
238 endIndex = nextEnd;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000239 } while (!simple.isClosed()
240 && ((active && !unsortable) || !current->done()));
241 if (active) {
242 if (!simple.isClosed()) {
243 SkASSERT(unsortable);
244 int min = SkMin32(index, endIndex);
245 if (!current->done(min)) {
246 current->addCurveTo(index, endIndex, simple, true);
247 current->markDone(SkMin32(index, endIndex), winding ? winding : spanWinding);
248 }
249 closable = false;
250 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000251 simple.close();
252 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000253 current = findChaseOp(chaseArray, index, endIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000254 #if DEBUG_ACTIVE_SPANS
255 debugShowActiveSpans(contourList);
256 #endif
257 if (!current) {
258 break;
259 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000260 winding = updateWindings(current, index, endIndex, spanWinding, oppWinding,
261 oppSpanWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000262 } while (true);
263 } while (true);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000264 return closable;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000265}
266
267} // end of Op namespace
268
269
270void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
271 result.reset();
272 result.setFillType(SkPath::kEvenOdd_FillType);
273 // turn path into list of segments
274 SkTArray<Op::Contour> contours;
275 // FIXME: add self-intersecting cubics' T values to segment
276 Op::EdgeBuilder builder(one, contours);
277 const int aXorMask = builder.xorMask();
278 builder.addOperand(two);
279 const int bXorMask = builder.xorMask();
280 builder.finish();
281 SkTDArray<Op::Contour*> contourList;
282 makeContourList(contours, contourList);
283 Op::Contour** currentPtr = contourList.begin();
284 if (!currentPtr) {
285 return;
286 }
287 Op::Contour** listEnd = contourList.end();
288 // find all intersections between segments
289 do {
290 Op::Contour** nextPtr = currentPtr;
291 Op::Contour* current = *currentPtr++;
292 Op::Contour* next;
293 do {
294 next = *nextPtr++;
295 } while (addIntersectTs(current, next) && nextPtr != listEnd);
296 } while (currentPtr != listEnd);
297 // eat through coincident edges
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000298 coincidenceCheck(contourList, (aXorMask == kEvenOdd_Mask)
299 ^ (bXorMask == kEvenOdd_Mask));
caryclark@google.com235f56a2012-09-14 14:19:30 +0000300 fixOtherTIndex(contourList);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000301 sortSegments(contourList);
302#if DEBUG_ACTIVE_SPANS
303 debugShowActiveSpans(contourList);
304#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000305 // construct closed contours
caryclark@google.comf839c032012-10-26 21:03:50 +0000306 Op::PathWrapper wrapper(result);
307 bridgeOp(contourList, op, aXorMask, bXorMask, wrapper);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000308}