blob: 6814f7dcaa0d03ea7dfa6a54fe000615b97a1ecc [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
caryclark@google.com7fce0de2012-11-29 14:31:50 +000020static Segment* findChaseOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd) {
caryclark@google.com31143cf2012-11-09 22:14:19 +000021 while (chase.count()) {
22 Span* span;
23 chase.pop(&span);
24 const Span& backPtr = span->fOther->span(span->fOtherIndex);
25 Segment* segment = backPtr.fOther;
caryclark@google.com7fce0de2012-11-29 14:31:50 +000026 nextStart = backPtr.fOtherIndex;
caryclark@google.com31143cf2012-11-09 22:14:19 +000027 SkTDArray<Angle> angles;
28 int done = 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +000029 if (segment->activeAngle(nextStart, done, angles)) {
caryclark@google.com31143cf2012-11-09 22:14:19 +000030 Angle* last = angles.end() - 1;
caryclark@google.com7fce0de2012-11-29 14:31:50 +000031 nextStart = last->start();
32 nextEnd = last->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +000033 #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);
caryclark@google.com7fce0de2012-11-29 14:31:50 +000045 int angleCount = sorted.count();
caryclark@google.com31143cf2012-11-09 22:14:19 +000046#if DEBUG_SORT
caryclark@google.com7fce0de2012-11-29 14:31:50 +000047 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0);
caryclark@google.com31143cf2012-11-09 22:14:19 +000048#endif
49 if (!sortable) {
50 continue;
51 }
52 // find first angle, initialize winding to computed fWindSum
53 int firstIndex = -1;
54 const Angle* angle;
caryclark@google.com31143cf2012-11-09 22:14:19 +000055 do {
56 angle = sorted[++firstIndex];
57 segment = angle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +000058 } while (segment->windSum(angle) == SK_MinS32);
caryclark@google.com31143cf2012-11-09 22:14:19 +000059 #if DEBUG_SORT
caryclark@google.com7fce0de2012-11-29 14:31:50 +000060 segment->debugShowSort(__FUNCTION__, sorted, firstIndex);
caryclark@google.com31143cf2012-11-09 22:14:19 +000061 #endif
caryclark@google.com7fce0de2012-11-29 14:31:50 +000062 int sumMiWinding = segment->updateWindingReverse(angle);
63 int sumSuWinding = segment->updateOppWindingReverse(angle);
64 if (segment->operand()) {
65 SkTSwap<int>(sumMiWinding, sumSuWinding);
66 }
67 int nextIndex = firstIndex + 1;
68 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
69 Segment* first = NULL;
caryclark@google.com31143cf2012-11-09 22:14:19 +000070 do {
71 SkASSERT(nextIndex != firstIndex);
72 if (nextIndex == angleCount) {
73 nextIndex = 0;
74 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +000075 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +000076 angle = sorted[nextIndex];
77 segment = angle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +000078 int start = angle->start();
79 int end = angle->end();
80 segment->setUpWindings(start, end, sumMiWinding, sumSuWinding,
81 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
82 if (!segment->done(angle)) {
83 if (!first) {
84 first = segment;
85 nextStart = start;
86 nextEnd = end;
caryclark@google.com31143cf2012-11-09 22:14:19 +000087 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +000088 (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
89 oppSumWinding, true, angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +000090 }
91 } while (++nextIndex != lastIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +000092 if (first) {
93 #if TRY_ROTATE
94 *chase.insert(0) = span;
95 #else
96 *chase.append() = span;
97 #endif
98 return first;
99 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000100 }
101 return NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000102}
103
caryclark@google.com7fce0de2012-11-29 14:31:50 +0000104/*
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000105static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
caryclark@google.com31143cf2012-11-09 22:14:19 +0000106 bool windingIsOp, ShapeOp op) {
107 bool active = windingIsActive(winding, spanWinding);
108 if (!active) {
109 return false;
110 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000111 if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +0000112 switch (op) {
113 case kIntersect_Op:
114 case kUnion_Op:
115 return true;
116 case kDifference_Op: {
117 int absSpan = abs(spanWinding);
118 int absOpp = abs(oppSpanWinding);
119 return windingIsOp ? absSpan < absOpp : absSpan > absOpp;
120 }
121 case kXor_Op:
122 return spanWinding != oppSpanWinding;
123 default:
124 SkASSERT(0);
125 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000126 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000127 bool opActive = oppWinding != 0;
128 return gOpLookup[op][opActive][windingIsOp];
129}
caryclark@google.com7fce0de2012-11-29 14:31:50 +0000130*/
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000131
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000132static Segment* findSortableTopNew(SkTDArray<Contour*>& contourList, bool& firstContour, int& index,
133 int& endIndex, SkPoint& topLeft, bool& unsortable) {
134 Segment* current;
135 bool allowTies = true;
136 bool first = true;
137 do {
138 current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, allowTies,
139 true);
140 if (!current) {
141 if (first) {
142 return NULL;
143 }
144 break;
145 }
146 first = false;
147 if (firstContour) {
148 current->initWinding(index, endIndex, 0, 0);
149 firstContour = false;
150 return current;
151 }
152 int minIndex = SkMin32(index, endIndex);
153 int sumWinding = current->windSum(minIndex);
154 if (sumWinding == SK_MinS32) {
155 sumWinding = current->computeSum(index, endIndex, true);
156 if (sumWinding != SK_MinS32) {
157 return current;
158 }
159 }
160 allowTies = false;
161 int contourWinding = innerContourCheck(contourList, current, index, endIndex, false);
162 if (contourWinding == SK_MinS32) {
163 continue;
164 }
165 int oppContourWinding = innerContourCheck(contourList, current, index, endIndex, true);
166 if (oppContourWinding == SK_MinS32) {
167 continue;
168 }
169 current->initWinding(index, endIndex, contourWinding, oppContourWinding);
170 return current;
171 } while (true);
172 // the simple upward projection of the unresolved points hit unsortable angles
173 // shoot rays at right angles to the segment to find its winding, ignoring angle cases
174 SkASSERT(0); // steal code from findSortableTopOld and put it here
175 return current;
176}
177
caryclark@google.com31143cf2012-11-09 22:14:19 +0000178static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
caryclark@google.com7fce0de2012-11-29 14:31:50 +0000179 const int xorMask, const int xorOpMask, PathWrapper& simple) {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000180 bool firstContour = true;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000181 bool unsortable = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000182 bool topUnsortable = false;
183 bool firstRetry = false;
caryclark@google.com31143cf2012-11-09 22:14:19 +0000184 bool closable = true;
caryclark@google.comf839c032012-10-26 21:03:50 +0000185 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com235f56a2012-09-14 14:19:30 +0000186 do {
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000187 int index, endIndex;
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000188 Segment* current = findSortableTopNew(contourList, firstContour, index, endIndex, topLeft,
189 topUnsortable);
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000190 if (!current) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000191 if (topUnsortable) {
192 topUnsortable = false;
193 SkASSERT(!firstRetry);
194 firstRetry = true;
195 topLeft.fX = topLeft.fY = SK_ScalarMin;
196 continue;
197 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000198 break;
199 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000200 SkTDArray<Span*> chaseArray;
201 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +0000202 if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
203 bool active = true;
204 do {
205 #if DEBUG_ACTIVE_SPANS
206 if (!unsortable && current->done()) {
207 debugShowActiveSpans(contourList);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000208 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +0000209 #endif
210 SkASSERT(unsortable || !current->done());
211 int nextStart = index;
212 int nextEnd = endIndex;
213 Segment* next = current->findNextOp(chaseArray, nextStart, nextEnd,
214 unsortable, op, xorMask, xorOpMask);
215 if (!next) {
216 SkASSERT(!unsortable);
217 if (!unsortable && simple.hasMove()
218 && current->verb() != SkPath::kLine_Verb
219 && !simple.isClosed()) {
220 current->addCurveTo(index, endIndex, simple, true);
221 SkASSERT(simple.isClosed());
222 }
223 active = false;
224 break;
225 }
226 current->addCurveTo(index, endIndex, simple, true);
227 current = next;
228 index = nextStart;
229 endIndex = nextEnd;
230 } while (!simple.isClosed() && ((!unsortable) || !current->done()));
231 if (active && !simple.isClosed()) {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000232 SkASSERT(unsortable);
233 int min = SkMin32(index, endIndex);
234 if (!current->done(min)) {
235 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.com7fce0de2012-11-29 14:31:50 +0000236 current->markDoneBinary(min);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000237 }
238 closable = false;
239 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000240 simple.close();
caryclark@google.com7fce0de2012-11-29 14:31:50 +0000241 } else {
242 Span* last = current->markAndChaseDoneBinary(index, endIndex);
243 if (last) {
244 *chaseArray.append() = last;
245 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000246 }
caryclark@google.com31143cf2012-11-09 22:14:19 +0000247 current = findChaseOp(chaseArray, index, endIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000248 #if DEBUG_ACTIVE_SPANS
249 debugShowActiveSpans(contourList);
250 #endif
251 if (!current) {
252 break;
253 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000254 } while (true);
255 } while (true);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000256 return closable;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000257}
258
259} // end of Op namespace
260
261
262void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
263 result.reset();
264 result.setFillType(SkPath::kEvenOdd_FillType);
265 // turn path into list of segments
266 SkTArray<Op::Contour> contours;
267 // FIXME: add self-intersecting cubics' T values to segment
268 Op::EdgeBuilder builder(one, contours);
caryclark@google.com7fce0de2012-11-29 14:31:50 +0000269 const int xorMask = builder.xorMask();
caryclark@google.com235f56a2012-09-14 14:19:30 +0000270 builder.addOperand(two);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000271 builder.finish();
caryclark@google.com7fce0de2012-11-29 14:31:50 +0000272 const int xorOpMask = builder.xorMask();
caryclark@google.com235f56a2012-09-14 14:19:30 +0000273 SkTDArray<Op::Contour*> contourList;
caryclark@google.com4eeda372012-12-06 21:47:48 +0000274 makeContourList(contours, contourList, xorMask == kEvenOdd_Mask,
275 xorOpMask == kEvenOdd_Mask);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000276 Op::Contour** currentPtr = contourList.begin();
277 if (!currentPtr) {
278 return;
279 }
280 Op::Contour** listEnd = contourList.end();
281 // find all intersections between segments
282 do {
283 Op::Contour** nextPtr = currentPtr;
284 Op::Contour* current = *currentPtr++;
285 Op::Contour* next;
286 do {
287 next = *nextPtr++;
288 } while (addIntersectTs(current, next) && nextPtr != listEnd);
289 } while (currentPtr != listEnd);
290 // eat through coincident edges
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +0000291
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000292 int total = 0;
293 int index;
294 for (index = 0; index < contourList.count(); ++index) {
295 total += contourList[index]->segments().count();
296 }
caryclark@google.com729e1c42012-11-21 21:36:34 +0000297#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000298 Op::Contour::debugShowWindingValues(contourList);
299#endif
caryclark@google.com4eeda372012-12-06 21:47:48 +0000300 coincidenceCheck(contourList, total);
caryclark@google.com729e1c42012-11-21 21:36:34 +0000301#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000302 Op::Contour::debugShowWindingValues(contourList);
303#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000304 fixOtherTIndex(contourList);
caryclark@google.com31143cf2012-11-09 22:14:19 +0000305 sortSegments(contourList);
306#if DEBUG_ACTIVE_SPANS
307 debugShowActiveSpans(contourList);
308#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000309 // construct closed contours
caryclark@google.comf839c032012-10-26 21:03:50 +0000310 Op::PathWrapper wrapper(result);
caryclark@google.com7fce0de2012-11-29 14:31:50 +0000311 bridgeOp(contourList, op, xorMask, xorOpMask, wrapper);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000312}