blob: 5356ca6bb6fba976b257cc52bb843b98ef29dd57 [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);
81 bool firstOperand = segment->operand();
82 do {
83 SkASSERT(nextIndex != firstIndex);
84 if (nextIndex == angleCount) {
85 nextIndex = 0;
86 }
87 angle = sorted[nextIndex];
88 segment = angle->segment();
89 int deltaSum = segment->spanSign(angle);
90 bool angleIsOp = segment->operand() ^ firstOperand;
91 int maxWinding;
92 if (angleIsOp) {
93 maxWinding = oWinding;
94 oWinding -= deltaSum;
95 } else {
96 maxWinding = winding;
97 winding -= deltaSum;
98 }
99 #if DEBUG_SORT
100 SkDebugf("%s id=%d maxWinding=%d winding=%d oWinding=%d sign=%d\n", __FUNCTION__,
101 segment->debugID(), maxWinding, winding, oWinding, angle->sign());
102 #endif
103 tIndex = angle->start();
104 endIndex = angle->end();
105 int lesser = SkMin32(tIndex, endIndex);
106 const Span& nextSpan = segment->span(lesser);
107 if (!nextSpan.fDone) {
108 if (angleIsOp) {
109 SkTSwap(winding, oWinding);
110 }
111 if (useInnerWinding(maxWinding, winding)) {
112 maxWinding = winding;
113 }
114 segment->markWinding(lesser, maxWinding, oWinding);
115 break;
116 }
117 } while (++nextIndex != lastIndex);
118 #if TRY_ROTATE
119 *chase.insert(0) = span;
120 #else
121 *chase.append() = span;
122 #endif
123 return segment;
124 }
125 return NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000126}
127
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000128static bool windingIsActive(int winding, int oppWinding, int spanWinding,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000129 bool windingIsOp, ShapeOp op) {
130 bool active = windingIsActive(winding, spanWinding);
131 if (!active) {
132 return false;
133 }
134 bool opActive = oppWinding != 0;
135 return gOpLookup[op][opActive][windingIsOp];
136}
137
138static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
caryclark@google.comf839c032012-10-26 21:03:50 +0000139 const int aXorMask, const int bXorMask, PathWrapper& simple) {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000140 bool firstContour = true;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000141 bool unsortable = false;
142 bool closable = true;
caryclark@google.comf839c032012-10-26 21:03:50 +0000143 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com235f56a2012-09-14 14:19:30 +0000144 do {
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000145 int index, endIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +0000146 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000147 if (!current) {
148 break;
149 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000150 int contourWinding, oppContourWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000151 if (firstContour) {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000152 contourWinding = oppContourWinding = 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000153 firstContour = false;
154 } else {
155 int sumWinding = current->windSum(SkMin32(index, endIndex));
156 // FIXME: don't I have to adjust windSum to get contourWinding?
157 if (sumWinding == SK_MinS32) {
158 sumWinding = current->computeSum(index, endIndex);
159 }
160 if (sumWinding == SK_MinS32) {
161 contourWinding = innerContourCheck(contourList, current,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000162 index, endIndex, false);
163 oppContourWinding = innerContourCheck(contourList, current,
164 index, endIndex, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000165 } else {
166 contourWinding = sumWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000167 oppContourWinding = 0;
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000168 SkASSERT(0);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000169 // FIXME: need to get oppContourWinding by building sort wheel and
170 // retrieving sumWinding of uphill opposite span, calling inner contour check
171 // if need be
caryclark@google.com235f56a2012-09-14 14:19:30 +0000172 int spanWinding = current->spanSign(index, endIndex);
173 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
174 if (inner) {
175 contourWinding -= spanWinding;
176 }
177#if DEBUG_WINDING
178 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
179 sumWinding, spanWinding, SkSign32(index - endIndex),
180 inner, contourWinding);
181#endif
182 }
183#if DEBUG_WINDING
184 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
185 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
186#endif
187 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000188 int winding = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000189 int oppWinding = oppContourWinding;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000190 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000191 SkTDArray<Span*> chaseArray;
192 do {
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000193 bool active = windingIsActive(winding, oppWinding, spanWinding,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000194 current->operand(), op);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000195 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +0000196 SkDebugf("%s active=%s winding=%d oppWinding=%d spanWinding=%d\n",
caryclark@google.com235f56a2012-09-14 14:19:30 +0000197 __FUNCTION__, active ? "true" : "false",
caryclark@google.com31143cf2012-11-09 22:14:19 +0000198 winding, oppWinding, spanWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000199 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000200 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000201 #if DEBUG_ACTIVE_SPANS
202 if (!unsortable && current->done()) {
203 debugShowActiveSpans(contourList);
204 }
205 #endif
206 SkASSERT(unsortable || !current->done());
caryclark@google.com235f56a2012-09-14 14:19:30 +0000207 int nextStart = index;
208 int nextEnd = endIndex;
209 Segment* next = current->findNextOp(chaseArray, active,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000210 nextStart, nextEnd, winding, oppWinding, spanWinding,
211 unsortable, op, aXorMask, bXorMask);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000212 if (!next) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000213 SkASSERT(!unsortable);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000214 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +0000215 && current->verb() != SkPath::kLine_Verb
216 && !simple.isClosed()) {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000217 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.comf839c032012-10-26 21:03:50 +0000218 SkASSERT(simple.isClosed());
caryclark@google.com235f56a2012-09-14 14:19:30 +0000219 }
220 break;
221 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000222 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000223 current = next;
224 index = nextStart;
225 endIndex = nextEnd;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000226 } while (!simple.isClosed()
227 && ((active && !unsortable) || !current->done()));
228 if (active) {
229 if (!simple.isClosed()) {
230 SkASSERT(unsortable);
231 int min = SkMin32(index, endIndex);
232 if (!current->done(min)) {
233 current->addCurveTo(index, endIndex, simple, true);
234 current->markDone(SkMin32(index, endIndex), winding ? winding : spanWinding);
235 }
236 closable = false;
237 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000238 simple.close();
239 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000240 current = findChaseOp(chaseArray, index, endIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000241 #if DEBUG_ACTIVE_SPANS
242 debugShowActiveSpans(contourList);
243 #endif
244 if (!current) {
245 break;
246 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000247 winding = updateWindings(current, index, endIndex, spanWinding, &oppWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000248 } while (true);
249 } while (true);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000250 return closable;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000251}
252
253} // end of Op namespace
254
255
256void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
257 result.reset();
258 result.setFillType(SkPath::kEvenOdd_FillType);
259 // turn path into list of segments
260 SkTArray<Op::Contour> contours;
261 // FIXME: add self-intersecting cubics' T values to segment
262 Op::EdgeBuilder builder(one, contours);
263 const int aXorMask = builder.xorMask();
264 builder.addOperand(two);
265 const int bXorMask = builder.xorMask();
266 builder.finish();
267 SkTDArray<Op::Contour*> contourList;
268 makeContourList(contours, contourList);
269 Op::Contour** currentPtr = contourList.begin();
270 if (!currentPtr) {
271 return;
272 }
273 Op::Contour** listEnd = contourList.end();
274 // find all intersections between segments
275 do {
276 Op::Contour** nextPtr = currentPtr;
277 Op::Contour* current = *currentPtr++;
278 Op::Contour* next;
279 do {
280 next = *nextPtr++;
281 } while (addIntersectTs(current, next) && nextPtr != listEnd);
282 } while (currentPtr != listEnd);
283 // eat through coincident edges
284 coincidenceCheck(contourList);
285 fixOtherTIndex(contourList);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000286 sortSegments(contourList);
287#if DEBUG_ACTIVE_SPANS
288 debugShowActiveSpans(contourList);
289#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000290 // construct closed contours
caryclark@google.comf839c032012-10-26 21:03:50 +0000291 Op::PathWrapper wrapper(result);
292 bridgeOp(contourList, op, aXorMask, bXorMask, wrapper);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000293}