blob: fa29ab769c31ade35ca6f8d393bc2b94c728bfa8 [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 {
174 int sumWinding = current->windSum(SkMin32(index, endIndex));
175 // FIXME: don't I have to adjust windSum to get contourWinding?
176 if (sumWinding == SK_MinS32) {
177 sumWinding = current->computeSum(index, endIndex);
178 }
179 if (sumWinding == SK_MinS32) {
180 contourWinding = innerContourCheck(contourList, current,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000181 index, endIndex, false);
182 oppContourWinding = innerContourCheck(contourList, current,
183 index, endIndex, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000184 } else {
185 contourWinding = sumWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000186 oppContourWinding = 0;
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000187 SkASSERT(0);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000188 // FIXME: need to get oppContourWinding by building sort wheel and
189 // retrieving sumWinding of uphill opposite span, calling inner contour check
190 // if need be
caryclark@google.com235f56a2012-09-14 14:19:30 +0000191 int spanWinding = current->spanSign(index, endIndex);
192 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
193 if (inner) {
194 contourWinding -= spanWinding;
195 }
196#if DEBUG_WINDING
197 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
198 sumWinding, spanWinding, SkSign32(index - endIndex),
199 inner, contourWinding);
200#endif
201 }
202#if DEBUG_WINDING
203 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
204 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
205#endif
206 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000207 int winding = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000208 int oppWinding = oppContourWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000209 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000210 int oppSpanWinding = current->oppSign(index, endIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000211 SkTDArray<Span*> chaseArray;
212 do {
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000213 bool active = windingIsActive(winding, oppWinding, spanWinding, oppSpanWinding,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000214 current->operand(), op);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000215 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000216 SkDebugf("%s active=%s winding=%d oppWinding=%d spanWinding=%d oppSpanWinding=%d\n",
caryclark@google.com235f56a2012-09-14 14:19:30 +0000217 __FUNCTION__, active ? "true" : "false",
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000218 winding, oppWinding, spanWinding, oppSpanWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000219 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000220 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000221 #if DEBUG_ACTIVE_SPANS
222 if (!unsortable && current->done()) {
223 debugShowActiveSpans(contourList);
224 }
225 #endif
226 SkASSERT(unsortable || !current->done());
caryclark@google.com235f56a2012-09-14 14:19:30 +0000227 int nextStart = index;
228 int nextEnd = endIndex;
229 Segment* next = current->findNextOp(chaseArray, active,
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000230 nextStart, nextEnd, winding, oppWinding, spanWinding, oppSpanWinding,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000231 unsortable, op, aXorMask, bXorMask);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000232 if (!next) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000233 SkASSERT(!unsortable);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000234 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +0000235 && current->verb() != SkPath::kLine_Verb
236 && !simple.isClosed()) {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000237 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.comf839c032012-10-26 21:03:50 +0000238 SkASSERT(simple.isClosed());
caryclark@google.com235f56a2012-09-14 14:19:30 +0000239 }
240 break;
241 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000242 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000243 current = next;
244 index = nextStart;
245 endIndex = nextEnd;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000246 } while (!simple.isClosed()
247 && ((active && !unsortable) || !current->done()));
248 if (active) {
249 if (!simple.isClosed()) {
250 SkASSERT(unsortable);
251 int min = SkMin32(index, endIndex);
252 if (!current->done(min)) {
253 current->addCurveTo(index, endIndex, simple, true);
254 current->markDone(SkMin32(index, endIndex), winding ? winding : spanWinding);
255 }
256 closable = false;
257 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000258 simple.close();
259 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000260 current = findChaseOp(chaseArray, index, endIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000261 #if DEBUG_ACTIVE_SPANS
262 debugShowActiveSpans(contourList);
263 #endif
264 if (!current) {
265 break;
266 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000267 winding = updateWindings(current, index, endIndex, spanWinding, oppWinding,
268 oppSpanWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000269 } while (true);
270 } while (true);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000271 return closable;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000272}
273
274} // end of Op namespace
275
276
277void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
278 result.reset();
279 result.setFillType(SkPath::kEvenOdd_FillType);
280 // turn path into list of segments
281 SkTArray<Op::Contour> contours;
282 // FIXME: add self-intersecting cubics' T values to segment
283 Op::EdgeBuilder builder(one, contours);
284 const int aXorMask = builder.xorMask();
285 builder.addOperand(two);
286 const int bXorMask = builder.xorMask();
287 builder.finish();
288 SkTDArray<Op::Contour*> contourList;
289 makeContourList(contours, contourList);
290 Op::Contour** currentPtr = contourList.begin();
291 if (!currentPtr) {
292 return;
293 }
294 Op::Contour** listEnd = contourList.end();
295 // find all intersections between segments
296 do {
297 Op::Contour** nextPtr = currentPtr;
298 Op::Contour* current = *currentPtr++;
299 Op::Contour* next;
300 do {
301 next = *nextPtr++;
302 } while (addIntersectTs(current, next) && nextPtr != listEnd);
303 } while (currentPtr != listEnd);
304 // eat through coincident edges
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000305 coincidenceCheck(contourList, (aXorMask == kEvenOdd_Mask)
306 ^ (bXorMask == kEvenOdd_Mask));
caryclark@google.com235f56a2012-09-14 14:19:30 +0000307 fixOtherTIndex(contourList);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000308 sortSegments(contourList);
309#if DEBUG_ACTIVE_SPANS
310 debugShowActiveSpans(contourList);
311#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000312 // construct closed contours
caryclark@google.comf839c032012-10-26 21:03:50 +0000313 Op::PathWrapper wrapper(result);
314 bridgeOp(contourList, op, aXorMask, bXorMask, wrapper);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000315}