blob: 6fe1fbb49d46d893c08da9fa9cfac2cf35c30425 [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +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 "SkIntersections.h"
8#include "SkOpSegment.h"
9#include "SkPathWriter.h"
caryclark@google.com7dfbb072013-04-22 14:37:05 +000010#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000011
12#define F (false) // discard the edge
13#define T (true) // keep the edge
14
15static const bool gUnaryActiveEdge[2][2] = {
16// from=0 from=1
17// to=0,1 to=0,1
18 {F, T}, {T, F},
19};
20
caryclark@google.com07393ca2013-04-08 11:47:37 +000021static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
22// miFrom=0 miFrom=1
23// miTo=0 miTo=1 miTo=0 miTo=1
24// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
25// suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1
26 {{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su
27 {{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su
28 {{{{F, T}, {T, F}}, {{T, T}, {F, F}}}, {{{T, F}, {T, F}}, {{F, F}, {F, F}}}}, // mi | su
29 {{{{F, T}, {T, F}}, {{T, F}, {F, T}}}, {{{T, F}, {F, T}}, {{F, T}, {T, F}}}}, // mi ^ su
30};
31
32#undef F
33#undef T
34
caryclark@google.com570863f2013-09-16 15:55:01 +000035enum {
36 kOutsideTrackedTCount = 16, // FIXME: determine what this should be
37 kMissingSpanCount = 4, // FIXME: determine what this should be
38};
caryclark@google.comd892bd82013-06-17 14:10:36 +000039
caryclark@google.com570863f2013-09-16 15:55:01 +000040// note that this follows the same logic flow as build angles
caryclark@google.comd892bd82013-06-17 14:10:36 +000041bool SkOpSegment::activeAngle(int index, int* done, SkTArray<SkOpAngle, true>* angles) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000042 if (activeAngleInner(index, done, angles)) {
43 return true;
44 }
caryclark@google.com570863f2013-09-16 15:55:01 +000045 double referenceT = fTs[index].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +000046 int lesser = index;
caryclark@google.com570863f2013-09-16 15:55:01 +000047 while (--lesser >= 0
48 && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000049 if (activeAngleOther(lesser, done, angles)) {
50 return true;
51 }
52 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000053 do {
54 if (activeAngleOther(index, done, angles)) {
55 return true;
56 }
caryclark@google.com570863f2013-09-16 15:55:01 +000057 if (++index == fTs.count()) {
58 break;
59 }
60 if (fTs[index - 1].fTiny) {
61 referenceT = fTs[index].fT;
62 continue;
63 }
64 } while (precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com07393ca2013-04-08 11:47:37 +000065 return false;
66}
67
caryclark@google.comd892bd82013-06-17 14:10:36 +000068bool SkOpSegment::activeAngleOther(int index, int* done, SkTArray<SkOpAngle, true>* angles) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000069 SkOpSpan* span = &fTs[index];
70 SkOpSegment* other = span->fOther;
71 int oIndex = span->fOtherIndex;
72 return other->activeAngleInner(oIndex, done, angles);
73}
74
caryclark@google.comd892bd82013-06-17 14:10:36 +000075bool SkOpSegment::activeAngleInner(int index, int* done, SkTArray<SkOpAngle, true>* angles) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000076 int next = nextExactSpan(index, 1);
77 if (next > 0) {
78 SkOpSpan& upSpan = fTs[index];
79 if (upSpan.fWindValue || upSpan.fOppValue) {
80 addAngle(angles, index, next);
81 if (upSpan.fDone || upSpan.fUnsortableEnd) {
82 (*done)++;
83 } else if (upSpan.fWindSum != SK_MinS32) {
84 return true;
85 }
86 } else if (!upSpan.fDone) {
87 upSpan.fDone = true;
88 fDoneSpans++;
89 }
90 }
91 int prev = nextExactSpan(index, -1);
92 // edge leading into junction
93 if (prev >= 0) {
94 SkOpSpan& downSpan = fTs[prev];
95 if (downSpan.fWindValue || downSpan.fOppValue) {
96 addAngle(angles, index, prev);
97 if (downSpan.fDone) {
98 (*done)++;
99 } else if (downSpan.fWindSum != SK_MinS32) {
100 return true;
101 }
102 } else if (!downSpan.fDone) {
103 downSpan.fDone = true;
104 fDoneSpans++;
105 }
106 }
107 return false;
108}
109
110SkPoint SkOpSegment::activeLeftTop(bool onlySortable, int* firstT) const {
111 SkASSERT(!done());
112 SkPoint topPt = {SK_ScalarMax, SK_ScalarMax};
113 int count = fTs.count();
114 // see if either end is not done since we want smaller Y of the pair
115 bool lastDone = true;
116 bool lastUnsortable = false;
117 double lastT = -1;
118 for (int index = 0; index < count; ++index) {
119 const SkOpSpan& span = fTs[index];
120 if (onlySortable && (span.fUnsortableStart || lastUnsortable)) {
121 goto next;
122 }
123 if (span.fDone && lastDone) {
124 goto next;
125 }
126 if (approximately_negative(span.fT - lastT)) {
127 goto next;
128 }
129 {
130 const SkPoint& xy = xyAtT(&span);
131 if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
132 topPt = xy;
133 if (firstT) {
134 *firstT = index;
135 }
136 }
137 if (fVerb != SkPath::kLine_Verb && !lastDone) {
reed@google.com277c3f82013-05-31 15:17:50 +0000138 SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000139 if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
140 && topPt.fX > curveTop.fX)) {
141 topPt = curveTop;
142 if (firstT) {
143 *firstT = index;
144 }
145 }
146 }
147 lastT = span.fT;
148 }
149next:
150 lastDone = span.fDone;
151 lastUnsortable = span.fUnsortableEnd;
152 }
153 return topPt;
154}
155
156bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
157 int sumMiWinding = updateWinding(endIndex, index);
158 int sumSuWinding = updateOppWinding(endIndex, index);
159 if (fOperand) {
160 SkTSwap<int>(sumMiWinding, sumSuWinding);
161 }
162 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
163 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding,
164 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
165}
166
167bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
168 int* sumMiWinding, int* sumSuWinding,
169 int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
170 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
171 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
172 bool miFrom;
173 bool miTo;
174 bool suFrom;
175 bool suTo;
176 if (operand()) {
177 miFrom = (*oppMaxWinding & xorMiMask) != 0;
178 miTo = (*oppSumWinding & xorMiMask) != 0;
179 suFrom = (*maxWinding & xorSuMask) != 0;
180 suTo = (*sumWinding & xorSuMask) != 0;
181 } else {
182 miFrom = (*maxWinding & xorMiMask) != 0;
183 miTo = (*sumWinding & xorMiMask) != 0;
184 suFrom = (*oppMaxWinding & xorSuMask) != 0;
185 suTo = (*oppSumWinding & xorSuMask) != 0;
186 }
187 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
188#if DEBUG_ACTIVE_OP
189 SkDebugf("%s op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n", __FUNCTION__,
caryclark@google.com570863f2013-09-16 15:55:01 +0000190 SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000191#endif
192 return result;
193}
194
195bool SkOpSegment::activeWinding(int index, int endIndex) {
196 int sumWinding = updateWinding(endIndex, index);
197 int maxWinding;
198 return activeWinding(index, endIndex, &maxWinding, &sumWinding);
199}
200
201bool SkOpSegment::activeWinding(int index, int endIndex, int* maxWinding, int* sumWinding) {
202 setUpWinding(index, endIndex, maxWinding, sumWinding);
203 bool from = *maxWinding != 0;
204 bool to = *sumWinding != 0;
205 bool result = gUnaryActiveEdge[from][to];
206 return result;
207}
208
caryclark@google.comd892bd82013-06-17 14:10:36 +0000209void SkOpSegment::addAngle(SkTArray<SkOpAngle, true>* anglesPtr, int start, int end) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000210 SkASSERT(start != end);
caryclark@google.comd892bd82013-06-17 14:10:36 +0000211 SkOpAngle& angle = anglesPtr->push_back();
caryclark@google.comd892bd82013-06-17 14:10:36 +0000212 angle.set(this, start, end);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000213}
214
caryclark@google.com570863f2013-09-16 15:55:01 +0000215void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt,
216 SkOpSegment* other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000217 int tIndex = -1;
218 int tCount = fTs.count();
219 int oIndex = -1;
220 int oCount = other->fTs.count();
221 do {
222 ++tIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000223 } while (startPt != fTs[tIndex].fPt && tIndex < tCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000224 int tIndexStart = tIndex;
225 do {
226 ++oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000227 } while (endPt != other->fTs[oIndex].fPt && oIndex < oCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000228 int oIndexStart = oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000229 const SkPoint* nextPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000230 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000231 nextPt = &fTs[++tIndex].fPt;
232 SkASSERT(fTs[tIndex].fT < 1 || startPt != *nextPt);
233 } while (startPt == *nextPt);
234 double nextT = fTs[tIndex].fT;
235 const SkPoint* oNextPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000236 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000237 oNextPt = &other->fTs[++oIndex].fPt;
238 SkASSERT(other->fTs[oIndex].fT < 1 || endPt != *oNextPt);
239 } while (endPt == *oNextPt);
240 double oNextT = other->fTs[oIndex].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000241 // at this point, spans before and after are at:
242 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
243 // if tIndexStart == 0, no prior span
244 // if nextT == 1, no following span
245
246 // advance the span with zero winding
247 // if the following span exists (not past the end, non-zero winding)
248 // connect the two edges
249 if (!fTs[tIndexStart].fWindValue) {
250 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
251#if DEBUG_CONCIDENT
252 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
253 __FUNCTION__, fID, other->fID, tIndexStart - 1,
254 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
255 xyAtT(tIndexStart).fY);
256#endif
257 addTPair(fTs[tIndexStart].fT, other, other->fTs[oIndex].fT, false,
258 fTs[tIndexStart].fPt);
259 }
260 if (nextT < 1 && fTs[tIndex].fWindValue) {
261#if DEBUG_CONCIDENT
262 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
263 __FUNCTION__, fID, other->fID, tIndex,
264 fTs[tIndex].fT, xyAtT(tIndex).fX,
265 xyAtT(tIndex).fY);
266#endif
267 addTPair(fTs[tIndex].fT, other, other->fTs[oIndexStart].fT, false, fTs[tIndex].fPt);
268 }
269 } else {
270 SkASSERT(!other->fTs[oIndexStart].fWindValue);
271 if (oIndexStart > 0 && other->fTs[oIndexStart - 1].fWindValue) {
272#if DEBUG_CONCIDENT
273 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
274 __FUNCTION__, fID, other->fID, oIndexStart - 1,
275 other->fTs[oIndexStart].fT, other->xyAtT(oIndexStart).fX,
276 other->xyAtT(oIndexStart).fY);
277 other->debugAddTPair(other->fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
278#endif
279 }
280 if (oNextT < 1 && other->fTs[oIndex].fWindValue) {
281#if DEBUG_CONCIDENT
282 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
283 __FUNCTION__, fID, other->fID, oIndex,
284 other->fTs[oIndex].fT, other->xyAtT(oIndex).fX,
285 other->xyAtT(oIndex).fY);
286 other->debugAddTPair(other->fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
287#endif
288 }
289 }
290}
291
caryclark@google.com570863f2013-09-16 15:55:01 +0000292void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
293 SkOpSegment* other) {
294 // walk this to startPt
295 // walk other to startPt
caryclark@google.com07393ca2013-04-08 11:47:37 +0000296 // if either is > 0, add a pointer to the other, copying adjacent winding
297 int tIndex = -1;
298 int oIndex = -1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000299 do {
300 ++tIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000301 } while (startPt != fTs[tIndex].fPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000302 do {
303 ++oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000304 } while (startPt != other->fTs[oIndex].fPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000305 if (tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000306 addTPair(fTs[tIndex].fT, other, other->fTs[oIndex].fT, false, startPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000307 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000308 SkPoint nextPt = startPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000309 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000310 const SkPoint* workPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000311 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000312 workPt = &fTs[++tIndex].fPt;
313 } while (nextPt == *workPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000314 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000315 workPt = &other->fTs[++oIndex].fPt;
316 } while (nextPt == *workPt);
317 nextPt = *workPt;
318 double tStart = fTs[tIndex].fT;
319 double oStart = other->fTs[oIndex].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000320 if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) {
321 break;
322 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000323 addTPair(tStart, other, oStart, false, nextPt);
324 } while (endPt != nextPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000325}
326
327void SkOpSegment::addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
328 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
329 fBounds.setCubicBounds(pts);
330}
331
332void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active) const {
333 SkPoint edge[4];
334 const SkPoint* ePtr;
335 int lastT = fTs.count() - 1;
336 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
337 ePtr = fPts;
338 } else {
339 // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
340 subDivide(start, end, edge);
341 ePtr = edge;
342 }
343 if (active) {
344 bool reverse = ePtr == fPts && start != 0;
345 if (reverse) {
reed@google.com277c3f82013-05-31 15:17:50 +0000346 path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000347 switch (fVerb) {
348 case SkPath::kLine_Verb:
349 path->deferredLine(ePtr[0]);
350 break;
351 case SkPath::kQuad_Verb:
352 path->quadTo(ePtr[1], ePtr[0]);
353 break;
354 case SkPath::kCubic_Verb:
355 path->cubicTo(ePtr[2], ePtr[1], ePtr[0]);
356 break;
357 default:
358 SkASSERT(0);
359 }
360 // return ePtr[0];
361 } else {
362 path->deferredMoveLine(ePtr[0]);
363 switch (fVerb) {
364 case SkPath::kLine_Verb:
365 path->deferredLine(ePtr[1]);
366 break;
367 case SkPath::kQuad_Verb:
368 path->quadTo(ePtr[1], ePtr[2]);
369 break;
370 case SkPath::kCubic_Verb:
371 path->cubicTo(ePtr[1], ePtr[2], ePtr[3]);
372 break;
373 default:
374 SkASSERT(0);
375 }
376 }
377 }
reed@google.com277c3f82013-05-31 15:17:50 +0000378 // return ePtr[SkPathOpsVerbToPoints(fVerb)];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000379}
380
381void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
382 init(pts, SkPath::kLine_Verb, operand, evenOdd);
383 fBounds.set(pts, 2);
384}
385
386// add 2 to edge or out of range values to get T extremes
387void SkOpSegment::addOtherT(int index, double otherT, int otherIndex) {
388 SkOpSpan& span = fTs[index];
caryclark@google.com03610322013-04-18 15:58:21 +0000389 if (precisely_zero(otherT)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000390 otherT = 0;
caryclark@google.com03610322013-04-18 15:58:21 +0000391 } else if (precisely_equal(otherT, 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000392 otherT = 1;
393 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000394 span.fOtherT = otherT;
395 span.fOtherIndex = otherIndex;
396}
397
398void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
399 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
400 fBounds.setQuadBounds(pts);
401}
402
403 // Defer all coincident edge processing until
404 // after normal intersections have been computed
405
406// no need to be tricky; insert in normal T order
407// resolve overlapping ts when considering coincidence later
408
409 // add non-coincident intersection. Resulting edges are sorted in T.
caryclark@google.com570863f2013-09-16 15:55:01 +0000410int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT, bool isNear) {
caryclark@google.com03610322013-04-18 15:58:21 +0000411 if (precisely_zero(newT)) {
412 newT = 0;
413 } else if (precisely_equal(newT, 1)) {
414 newT = 1;
415 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000416 // FIXME: in the pathological case where there is a ton of intercepts,
417 // binary search?
418 int insertedAt = -1;
419 size_t tCount = fTs.count();
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000420 const SkPoint& firstPt = fPts[0];
421 const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000422 for (size_t index = 0; index < tCount; ++index) {
423 // OPTIMIZATION: if there are three or more identical Ts, then
424 // the fourth and following could be further insertion-sorted so
425 // that all the edges are clockwise or counterclockwise.
426 // This could later limit segment tests to the two adjacent
427 // neighbors, although it doesn't help with determining which
428 // circular direction to go in.
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000429 const SkOpSpan& span = fTs[index];
430 if (newT < span.fT) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000431 insertedAt = index;
432 break;
433 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000434 if (newT == span.fT) {
435 if (pt == span.fPt) {
436 insertedAt = index;
437 break;
438 }
439 if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
440 insertedAt = index;
441 break;
442 }
443 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000444 }
445 SkOpSpan* span;
446 if (insertedAt >= 0) {
447 span = fTs.insert(insertedAt);
448 } else {
449 insertedAt = tCount;
450 span = fTs.append();
451 }
452 span->fT = newT;
453 span->fOther = other;
454 span->fPt = pt;
caryclark@google.com570863f2013-09-16 15:55:01 +0000455 span->fNear = isNear;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000456#if 0
457 // cubics, for instance, may not be exact enough to satisfy this check (e.g., cubicOp69d)
458 SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX)
459 && approximately_equal(xyAtT(newT).fY, pt.fY));
460#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000461 span->fWindSum = SK_MinS32;
462 span->fOppSum = SK_MinS32;
463 span->fWindValue = 1;
464 span->fOppValue = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000465 span->fSmall = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000466 span->fTiny = false;
467 span->fLoop = false;
468 if ((span->fDone = newT == 1)) {
469 ++fDoneSpans;
470 }
471 span->fUnsortableStart = false;
472 span->fUnsortableEnd = false;
473 int less = -1;
caryclark@google.com570863f2013-09-16 15:55:01 +0000474 while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, span->fPt)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000475 if (span[less].fDone) {
476 break;
477 }
478 double tInterval = newT - span[less].fT;
479 if (precisely_negative(tInterval)) {
480 break;
481 }
482 if (fVerb == SkPath::kCubic_Verb) {
483 double tMid = newT - tInterval / 2;
484 SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
485 if (!midPt.approximatelyEqual(xyAtT(span))) {
486 break;
487 }
488 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000489 span[less].fSmall = true;
490 bool tiny = span[less].fPt == span->fPt;
491 span[less].fTiny = tiny;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000492 span[less].fDone = true;
caryclark@google.com570863f2013-09-16 15:55:01 +0000493 if (approximately_negative(newT - span[less].fT) && tiny) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000494 if (approximately_greater_than_one(newT)) {
495 SkASSERT(&span[less] - fTs.begin() < fTs.count());
496 span[less].fUnsortableStart = true;
497 if (&span[less - 1] - fTs.begin() >= 0) {
498 span[less - 1].fUnsortableEnd = true;
499 }
500 }
501 if (approximately_less_than_zero(span[less].fT)) {
502 SkASSERT(&span[less + 1] - fTs.begin() < fTs.count());
503 SkASSERT(&span[less] - fTs.begin() >= 0);
504 span[less + 1].fUnsortableStart = true;
505 span[less].fUnsortableEnd = true;
506 }
507 }
508 ++fDoneSpans;
509 --less;
510 }
511 int more = 1;
caryclark@google.com570863f2013-09-16 15:55:01 +0000512 while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].fPt, span->fPt)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000513 if (span[more - 1].fDone) {
514 break;
515 }
516 double tEndInterval = span[more].fT - newT;
517 if (precisely_negative(tEndInterval)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000518 if ((span->fTiny = span[more].fTiny)) {
519 span->fDone = true;
520 ++fDoneSpans;
521 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000522 break;
523 }
524 if (fVerb == SkPath::kCubic_Verb) {
525 double tMid = newT - tEndInterval / 2;
526 SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
527 if (!midEndPt.approximatelyEqual(xyAtT(span))) {
528 break;
529 }
530 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000531 span[more - 1].fSmall = true;
532 bool tiny = span[more].fPt == span->fPt;
533 span[more - 1].fTiny = tiny;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000534 span[more - 1].fDone = true;
caryclark@google.com570863f2013-09-16 15:55:01 +0000535 if (approximately_negative(span[more].fT - newT) && tiny) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000536 if (approximately_greater_than_one(span[more].fT)) {
537 span[more + 1].fUnsortableStart = true;
538 span[more].fUnsortableEnd = true;
539 }
540 if (approximately_less_than_zero(newT)) {
541 span[more].fUnsortableStart = true;
542 span[more - 1].fUnsortableEnd = true;
543 }
544 }
545 ++fDoneSpans;
546 ++more;
547 }
548 return insertedAt;
549}
550
551// set spans from start to end to decrement by one
552// note this walks other backwards
caryclark@google.coma5e55922013-05-07 18:51:31 +0000553// FIXME: there's probably an edge case that can be constructed where
caryclark@google.com07393ca2013-04-08 11:47:37 +0000554// two span in one segment are separated by float epsilon on one span but
555// not the other, if one segment is very small. For this
556// case the counts asserted below may or may not be enough to separate the
557// spans. Even if the counts work out, what if the spans aren't correctly
558// sorted? It feels better in such a case to match the span's other span
559// pointer since both coincident segments must contain the same spans.
caryclark@google.coma5e55922013-05-07 18:51:31 +0000560// FIXME? It seems that decrementing by one will fail for complex paths that
561// have three or more coincident edges. Shouldn't this subtract the difference
562// between the winding values?
caryclark@google.com570863f2013-09-16 15:55:01 +0000563/* |--> |-->
564this 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4
565other 2<<<<1<<<<0 2<<<<1<<<<0 2<<<<1<<<<0
566 ^ ^ <--| <--|
567 startPt endPt test/oTest first pos test/oTest final pos
568*/
569void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000570 bool binary = fOperand != other->fOperand;
571 int index = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000572 while (startPt != fTs[index].fPt) {
573 SkASSERT(index < fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000574 ++index;
575 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000576 while (index > 0 && fTs[index].fT == fTs[index - 1].fT) {
577 --index;
578 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000579 int oIndex = other->fTs.count();
caryclark@google.com570863f2013-09-16 15:55:01 +0000580 while (startPt != other->fTs[--oIndex].fPt) { // look for startPt match
581 SkASSERT(oIndex > 0);
582 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000583 double oStartT = other->fTs[oIndex].fT;
584 // look for first point beyond match
585 while (startPt == other->fTs[--oIndex].fPt || oStartT == other->fTs[oIndex].fT) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000586 SkASSERT(oIndex > 0);
587 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000588 SkOpSpan* test = &fTs[index];
589 SkOpSpan* oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +0000590 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
591 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000592 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000593 SkASSERT(test->fT < 1);
594 SkASSERT(oTest->fT < 1);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000595 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000596 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000597 bool bigger = test->fWindValue >= oTest->fWindValue;
caryclark@google.com570863f2013-09-16 15:55:01 +0000598 const SkPoint& testPt = test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000599 double testT = test->fT;
caryclark@google.com570863f2013-09-16 15:55:01 +0000600 const SkPoint& oTestPt = oTest->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000601 double oTestT = oTest->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000602 do {
603 if (decrement) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000604 if (binary && bigger) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000605 test->fOppValue--;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000606 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +0000607 decrementSpan(test);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000608 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000609 } else if (track) {
610 TrackOutsidePair(&outsidePts, testPt, oTestPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000611 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000612 SkASSERT(index < fTs.count() - 1);
613 test = &fTs[++index];
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000614 } while (testPt == test->fPt || testT == test->fT);
caryclark@google.com570863f2013-09-16 15:55:01 +0000615 SkDEBUGCODE(int originalWindValue = oTest->fWindValue);
616 do {
617 SkASSERT(oTest->fT < 1);
618 SkASSERT(originalWindValue == oTest->fWindValue);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000619 if (decrement) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000620 if (binary && !bigger) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000621 oTest->fOppValue--;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000622 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +0000623 other->decrementSpan(oTest);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000624 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000625 } else if (track) {
626 TrackOutsidePair(&oOutsidePts, oTestPt, testPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000627 }
628 if (!oIndex) {
629 break;
630 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000631 oTest = &other->fTs[--oIndex];
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000632 } while (oTestPt == oTest->fPt || oTestT == oTest->fT);
633 } while (endPt != test->fPt && test->fT < 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000634 // FIXME: determine if canceled edges need outside ts added
caryclark@google.com570863f2013-09-16 15:55:01 +0000635 int outCount = outsidePts.count();
636 if (!done() && outCount) {
637 addCancelOutsides(outsidePts[0], outsidePts[1], other);
638 if (outCount > 2) {
639 addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000640 }
641 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000642 if (!other->done() && oOutsidePts.count()) {
643 other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000644 }
645}
646
647int SkOpSegment::addSelfT(SkOpSegment* other, const SkPoint& pt, double newT) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000648 // if the tail nearly intersects itself but not quite, the caller records this separately
649 int result = addT(other, pt, newT, SkOpSpan::kPointIsExact);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000650 SkOpSpan* span = &fTs[result];
651 span->fLoop = true;
652 return result;
653}
654
caryclark@google.com570863f2013-09-16 15:55:01 +0000655void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
656 SkTArray<SkPoint, true>* outsideTs) {
657 int index = *indexPtr;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000658 int oWindValue = oTest.fWindValue;
659 int oOppValue = oTest.fOppValue;
caryclark@google.com570863f2013-09-16 15:55:01 +0000660 if (binary) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000661 SkTSwap<int>(oWindValue, oOppValue);
662 }
663 SkOpSpan* const test = &fTs[index];
664 SkOpSpan* end = test;
caryclark@google.com570863f2013-09-16 15:55:01 +0000665 const SkPoint& oStartPt = oTest.fPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000666 do {
667 if (bumpSpan(end, oWindValue, oOppValue)) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000668 TrackOutside(outsideTs, oStartPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000669 }
670 end = &fTs[++index];
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000671 } while ((end->fPt == test->fPt || end->fT == test->fT) && end->fT < 1);
caryclark@google.com570863f2013-09-16 15:55:01 +0000672 *indexPtr = index;
673}
674
675bool SkOpSegment::bumpCoincident(SkOpSpan* test, bool bigger, bool binary) {
676 if (bigger) {
677 if (binary) {
678 if (fOppXor) {
679 test->fOppValue ^= 1;
680 } else {
681 test->fOppValue++;
682 }
683 } else {
684 if (fXor) {
685 test->fWindValue ^= 1;
686 } else {
687 test->fWindValue++;
688 }
689 }
690 if (!test->fWindValue && !test->fOppValue) {
691 test->fDone = true;
692 ++fDoneSpans;
693 return true;
694 }
695 return false;
696 }
697 return decrementSpan(test);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000698}
699
700// because of the order in which coincidences are resolved, this and other
701// may not have the same intermediate points. Compute the corresponding
702// intermediate T values (using this as the master, other as the follower)
703// and walk other conditionally -- hoping that it catches up in the end
caryclark@google.com570863f2013-09-16 15:55:01 +0000704void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
705 SkTArray<SkPoint, true>* oOutsidePts) {
706 int oIndex = *oIndexPtr;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000707 SkOpSpan* const oTest = &fTs[oIndex];
708 SkOpSpan* oEnd = oTest;
caryclark@google.com570863f2013-09-16 15:55:01 +0000709 const SkPoint& startPt = test.fPt;
710 const SkPoint& oStartPt = oTest->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000711 double oStartT = oTest->fT;
712 if (oStartPt == oEnd->fPt || oStartT == oEnd->fT) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000713 TrackOutside(oOutsidePts, startPt);
714 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000715 while (oStartPt == oEnd->fPt || oStartT == oEnd->fT) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000716 zeroSpan(oEnd);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000717 oEnd = &fTs[++oIndex];
718 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000719 *oIndexPtr = oIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000720}
721
722// FIXME: need to test this case:
723// contourA has two segments that are coincident
724// contourB has two segments that are coincident in the same place
725// each ends up with +2/0 pairs for winding count
726// since logic below doesn't transfer count (only increments/decrements) can this be
727// resolved to +4/0 ?
728
729// set spans from start to end to increment the greater by one and decrement
730// the lesser
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000731void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
caryclark@google.com570863f2013-09-16 15:55:01 +0000732 SkOpSegment* other) {
733 bool binary = fOperand != other->fOperand;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000734 int index = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000735 while (startPt != fTs[index].fPt) {
736 SkASSERT(index < fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000737 ++index;
738 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000739 double startT = fTs[index].fT;
740 while (index > 0 && fTs[index - 1].fT == startT) {
741 --index;
742 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000743 int oIndex = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000744 while (startPt != other->fTs[oIndex].fPt) {
745 SkASSERT(oIndex < other->fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000746 ++oIndex;
747 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000748 double oStartT = other->fTs[oIndex].fT;
749 while (oIndex > 0 && other->fTs[oIndex - 1].fT == oStartT) {
750 --oIndex;
751 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000752 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
753 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000754 SkOpSpan* test = &fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +0000755 const SkPoint* testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000756 double testT = test->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000757 SkOpSpan* oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +0000758 const SkPoint* oTestPt = &oTest->fPt;
759 SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000760 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000761 SkASSERT(test->fT < 1);
762 SkASSERT(oTest->fT < 1);
763#if 0
caryclark@google.com07393ca2013-04-08 11:47:37 +0000764 if (test->fDone || oTest->fDone) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000765#else // consolidate the winding count even if done
766 if ((test->fWindValue == 0 && test->fOppValue == 0)
767 || (oTest->fWindValue == 0 && oTest->fOppValue == 0)) {
768#endif
769 SkDEBUGCODE(int firstWind = test->fWindValue);
770 SkDEBUGCODE(int firstOpp = test->fOppValue);
771 do {
772 SkASSERT(firstWind == fTs[index].fWindValue);
773 SkASSERT(firstOpp == fTs[index].fOppValue);
774 ++index;
775 SkASSERT(index < fTs.count());
776 } while (*testPt == fTs[index].fPt);
777 SkDEBUGCODE(firstWind = oTest->fWindValue);
778 SkDEBUGCODE(firstOpp = oTest->fOppValue);
779 do {
780 SkASSERT(firstWind == other->fTs[oIndex].fWindValue);
781 SkASSERT(firstOpp == other->fTs[oIndex].fOppValue);
782 ++oIndex;
783 SkASSERT(oIndex < other->fTs.count());
784 } while (*oTestPt == other->fTs[oIndex].fPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000785 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +0000786 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
787 bumpCoincidentThis(*oTest, binary, &index, &outsidePts);
788 other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts);
789 } else {
790 other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts);
791 bumpCoincidentOther(*oTest, &index, &outsidePts);
792 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000793 }
794 test = &fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +0000795 testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000796 testT = test->fT;
797 if (endPt == *testPt || endT == testT) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000798 break;
799 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000800 oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +0000801 oTestPt = &oTest->fPt;
802 SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
803 } while (endPt != *oTestPt);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000804 if (endPt != *testPt && endT != testT) { // in rare cases, one may have ended before the other
805 int lastWind = test[-1].fWindValue;
806 int lastOpp = test[-1].fOppValue;
807 bool zero = lastWind == 0 && lastOpp == 0;
808 do {
809 if (test->fWindValue || test->fOppValue) {
810 test->fWindValue = lastWind;
811 test->fOppValue = lastOpp;
812 if (zero) {
813 test->fDone = true;
814 ++fDoneSpans;
815 }
816 }
817 test = &fTs[++index];
818 testPt = &test->fPt;
819 } while (endPt != *testPt);
820 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000821 int outCount = outsidePts.count();
822 if (!done() && outCount) {
823 addCoinOutsides(outsidePts[0], endPt, other);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000824 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000825 if (!other->done() && oOutsidePts.count()) {
826 other->addCoinOutsides(oOutsidePts[0], endPt, this);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000827 }
828}
829
830// FIXME: this doesn't prevent the same span from being added twice
831// fix in caller, SkASSERT here?
832void SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
833 const SkPoint& pt) {
834 int tCount = fTs.count();
835 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
836 const SkOpSpan& span = fTs[tIndex];
837 if (!approximately_negative(span.fT - t)) {
838 break;
839 }
840 if (approximately_negative(span.fT - t) && span.fOther == other
841 && approximately_equal(span.fOtherT, otherT)) {
842#if DEBUG_ADD_T_PAIR
843 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
844 __FUNCTION__, fID, t, other->fID, otherT);
845#endif
846 return;
847 }
848 }
849#if DEBUG_ADD_T_PAIR
850 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
851 __FUNCTION__, fID, t, other->fID, otherT);
852#endif
caryclark@google.com570863f2013-09-16 15:55:01 +0000853 int insertedAt = addT(other, pt, t, SkOpSpan::kPointIsExact);
854 int otherInsertedAt = other->addT(this, pt, otherT, SkOpSpan::kPointIsExact);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000855 addOtherT(insertedAt, otherT, otherInsertedAt);
856 other->addOtherT(otherInsertedAt, t, insertedAt);
857 matchWindingValue(insertedAt, t, borrowWind);
858 other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
859}
860
caryclark@google.comd892bd82013-06-17 14:10:36 +0000861void SkOpSegment::addTwoAngles(int start, int end, SkTArray<SkOpAngle, true>* angles) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000862 // add edge leading into junction
863 int min = SkMin32(end, start);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000864 if (fTs[min].fWindValue > 0 || fTs[min].fOppValue != 0) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000865 addAngle(angles, end, start);
866 }
867 // add edge leading away from junction
868 int step = SkSign32(end - start);
869 int tIndex = nextExactSpan(end, step);
870 min = SkMin32(end, tIndex);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000871 if (tIndex >= 0 && (fTs[min].fWindValue > 0 || fTs[min].fOppValue != 0)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000872 addAngle(angles, end, tIndex);
873 }
874}
875
caryclark@google.com570863f2013-09-16 15:55:01 +0000876SkOpSegment::MissingSpan::Command SkOpSegment::adjustThisNear(double startT, const SkPoint& startPt,
877 const SkPoint& endPt, SkTArray<MissingSpan, true>* missingSpans) {
878 // see if endPt exists on this curve, and if it has the same t or a different T than the startT
879 int count = this->count();
880 SkASSERT(count > 0);
881 int startIndex, endIndex, step;
882 if (startT == 0) {
883 startIndex = 0;
884 endIndex = count;
885 step = 1;
886 } else {
887 SkASSERT(startT == 1);
888 startIndex = count - 1;
889 endIndex = -1;
890 step = -1;
891 }
892 int index = startIndex;
893 do {
894 const SkOpSpan& span = fTs[index];
895 if (span.fPt != endPt) {
896 continue;
897 }
898 if (span.fT == startT) {
899 // check to see if otherT matches some other mid curve intersection
900 int inner = startIndex;
901 do {
902 if (inner == index) {
903 continue;
904 }
905 const SkOpSpan& matchSpan = fTs[inner];
906 double matchT = span.fOther->missingNear(span.fOtherT, matchSpan.fOther, startPt,
907 endPt);
908 if (matchT >= 0) {
909 SkASSERT(missingSpans);
910 MissingSpan& missingSpan = missingSpans->push_back();
911 SkDEBUGCODE(sk_bzero(&missingSpan, sizeof(missingSpan)));
912 missingSpan.fCommand = MissingSpan::kRemoveNear;
913 missingSpan.fT = startT;
914 missingSpan.fSegment = this;
915 missingSpan.fOther = span.fOther;
916 missingSpan.fOtherT = matchT;
917 return missingSpan.fCommand;
918 }
919 } while ((inner += step) != endIndex);
920 break;
921 }
922 double midT = (startT + span.fT) / 2;
923 if (betweenPoints(midT, startPt, endPt)) {
924 if (!missingSpans) {
925 return MissingSpan::kZeroSpan;
926 }
927 MissingSpan& missingSpan = missingSpans->push_back();
928 SkDEBUGCODE(sk_bzero(&missingSpan, sizeof(missingSpan)));
929 missingSpan.fCommand = MissingSpan::kZeroSpan;
930 missingSpan.fT = SkTMin(startT, span.fT);
931 missingSpan.fEndT = SkTMax(startT, span.fT);
932 missingSpan.fSegment = this;
933 return missingSpan.fCommand;
934 }
935 } while ((index += step) != endIndex);
936 return MissingSpan::kNoAction;
937}
938
939void SkOpSegment::adjustOtherNear(double startT, const SkPoint& startPt, const SkPoint& endPt,
940 SkTArray<MissingSpan, true>* missingSpans) {
941 int count = this->count();
942 SkASSERT(count > 0);
943 int startIndex, endIndex, step;
944 if (startT == 0) {
945 startIndex = 0;
946 endIndex = count;
947 step = 1;
948 } else {
949 SkASSERT(startT == 1);
950 startIndex = count - 1;
951 endIndex = -1;
952 step = -1;
953 }
954 int index = startIndex;
955 do {
956 const SkOpSpan& span = fTs[index];
957 if (span.fT != startT) {
958 return;
959 }
960 SkOpSegment* other = span.fOther;
961 if (other->fPts[0] == endPt) {
962 other->adjustThisNear(0, endPt, startPt, missingSpans);
963 } else if (other->fPts[0] == startPt) {
964 other->adjustThisNear(0, startPt, endPt, missingSpans);
965 }
966 if (other->ptAtT(1) == endPt) {
967 other->adjustThisNear(1, endPt, startPt, missingSpans);
968 } else if (other->ptAtT(1) == startPt) {
969 other->adjustThisNear(1, startPt, endPt, missingSpans);
970 }
971 } while ((index += step) != endIndex);
972}
973
974void SkOpSegment::adjustMissingNear(const SkPoint& startPt, const SkPoint& endPt,
975 SkTArray<MissingSpan, true>* missingSpans) {
976 int count = missingSpans->count();
977 for (int index = 0; index < count; ) {
978 MissingSpan& missing = (*missingSpans)[index];
979 SkOpSegment* other = missing.fOther;
980 MissingSpan::Command command = MissingSpan::kNoAction;
981 if (missing.fPt == startPt) {
982 if (missingNear(missing.fT, other, startPt, endPt) >= 0) {
983 command = MissingSpan::kZeroSpan;
984 } else if (other->ptAtT(0) == endPt) {
985 command = other->adjustThisNear(0, endPt, startPt, NULL);
986 } else if (other->ptAtT(1) == endPt) {
987 command = other->adjustThisNear(1, endPt, startPt, NULL);
988 }
989 } else if (missing.fPt == endPt) {
990 if (missingNear(missing.fT, other, endPt, startPt) >= 0) {
991 command = MissingSpan::kZeroSpan;
992 } else if (other->ptAtT(0) == startPt) {
993 command = other->adjustThisNear(0, startPt, endPt, NULL);
994 } else if (other->ptAtT(1) == startPt) {
995 command = other->adjustThisNear(1, startPt, endPt, NULL);
996 }
997 }
998 if (command == MissingSpan::kZeroSpan) {
999#if 1
1000 missing = missingSpans->back();
1001 missingSpans->pop_back();
1002#else // if this is supported in the future ...
1003 missingSpans->removeShuffle(index);
1004#endif
1005 --count;
1006 continue;
1007 }
1008 ++index;
1009 }
1010}
1011
1012void SkOpSegment::adjustNear(double startT, const SkPoint& endPt,
1013 SkTArray<MissingSpan, true>* missingSpans) {
1014 const SkPoint startPt = ptAtT(startT);
1015 adjustMissingNear(startPt, endPt, missingSpans);
1016 adjustThisNear(startT, startPt, endPt, missingSpans);
1017 adjustOtherNear(startT, startPt, endPt, missingSpans);
1018}
1019
1020int SkOpSegment::advanceCoincidentThis(int index) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001021 SkOpSpan* const test = &fTs[index];
1022 SkOpSpan* end;
1023 do {
1024 end = &fTs[++index];
1025 } while (approximately_negative(end->fT - test->fT));
1026 return index;
1027}
1028
caryclark@google.com570863f2013-09-16 15:55:01 +00001029int SkOpSegment::advanceCoincidentOther(double oEndT, int oIndex) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001030 SkOpSpan* const oTest = &fTs[oIndex];
1031 SkOpSpan* oEnd = oTest;
1032 const double oStartT = oTest->fT;
1033 while (!approximately_negative(oEndT - oEnd->fT)
1034 && approximately_negative(oEnd->fT - oStartT)) {
1035 oEnd = &fTs[++oIndex];
1036 }
1037 return oIndex;
1038}
1039
caryclark@google.com570863f2013-09-16 15:55:01 +00001040bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
1041 const SkPoint midPt = ptAtT(midT);
1042 SkPathOpsBounds bounds;
1043 bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1044 bounds.sort();
1045 return bounds.almostContains(midPt);
1046}
1047
caryclark@google.com07393ca2013-04-08 11:47:37 +00001048bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const {
1049 if (lesser > greater) {
1050 SkTSwap<int>(lesser, greater);
1051 }
1052 return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
1053}
1054
caryclark@google.com570863f2013-09-16 15:55:01 +00001055// note that this follows the same logic flow as active angle
1056bool SkOpSegment::buildAngles(int index, SkTArray<SkOpAngle, true>* angles, bool allowOpp) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001057 double referenceT = fTs[index].fT;
caryclark@google.com570863f2013-09-16 15:55:01 +00001058 const SkPoint& referencePt = fTs[index].fPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001059 int lesser = index;
caryclark@google.com570863f2013-09-16 15:55:01 +00001060 while (--lesser >= 0 && (allowOpp || fTs[lesser].fOther->fOperand == fOperand)
1061 && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001062 buildAnglesInner(lesser, angles);
1063 }
1064 do {
1065 buildAnglesInner(index, angles);
caryclark@google.com570863f2013-09-16 15:55:01 +00001066 if (++index == fTs.count()) {
1067 break;
1068 }
1069 if (!allowOpp && fTs[index].fOther->fOperand != fOperand) {
1070 break;
1071 }
1072 if (fTs[index - 1].fTiny) {
1073 referenceT = fTs[index].fT;
1074 continue;
1075 }
1076 if (!precisely_negative(fTs[index].fT - referenceT) && fTs[index].fPt == referencePt) {
1077 // FIXME
1078 // testQuad8 generates the wrong output unless false is returned here. Other tests will
1079 // take this path although they aren't required to. This means that many go much slower
1080 // because of this sort fail.
1081 // SkDebugf("!!!\n");
1082 return false;
1083 }
1084 } while (precisely_negative(fTs[index].fT - referenceT));
1085 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001086}
1087
caryclark@google.comd892bd82013-06-17 14:10:36 +00001088void SkOpSegment::buildAnglesInner(int index, SkTArray<SkOpAngle, true>* angles) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001089 const SkOpSpan* span = &fTs[index];
1090 SkOpSegment* other = span->fOther;
1091// if there is only one live crossing, and no coincidence, continue
1092// in the same direction
1093// if there is coincidence, the only choice may be to reverse direction
1094 // find edge on either side of intersection
1095 int oIndex = span->fOtherIndex;
1096 // if done == -1, prior span has already been processed
1097 int step = 1;
1098 int next = other->nextExactSpan(oIndex, step);
1099 if (next < 0) {
1100 step = -step;
1101 next = other->nextExactSpan(oIndex, step);
1102 }
1103 // add candidate into and away from junction
1104 other->addTwoAngles(next, oIndex, angles);
1105}
1106
caryclark@google.com570863f2013-09-16 15:55:01 +00001107int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType,
1108 SkTArray<SkOpAngle, true>* angles, SkTArray<SkOpAngle*, true>* sorted) {
1109 addTwoAngles(startIndex, endIndex, angles);
1110 if (!buildAngles(endIndex, angles, includeType == SkOpAngle::kBinaryOpp)) {
1111 return SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001112 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001113 int angleCount = angles->count();
1114 // abort early before sorting if an unsortable (not an unorderable) angle is found
1115 if (includeType != SkOpAngle::kUnaryXor) {
1116 int firstIndex = -1;
1117 while (++firstIndex < angleCount) {
1118 const SkOpAngle& angle = (*angles)[firstIndex];
1119 if (angle.segment()->windSum(&angle) != SK_MinS32) {
1120 break;
1121 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001122 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001123 if (firstIndex == angleCount) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001124 return SK_MinS32;
1125 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001126 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001127 bool sortable = SortAngles2(*angles, sorted);
1128#if DEBUG_SORT_RAW
1129 if (sorted->count() > 0) {
1130 (*sorted)[0]->segment()->debugShowSort(__FUNCTION__, *sorted, 0, 0, 0, sortable);
1131 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001132#endif
caryclark@google.com570863f2013-09-16 15:55:01 +00001133 if (!sortable) {
1134 return SK_NaN32;
1135 }
1136 if (includeType == SkOpAngle::kUnaryXor) {
1137 return SK_MinS32;
1138 }
1139 // if all angles have a computed winding,
1140 // or if no adjacent angles are orderable,
1141 // or if adjacent orderable angles have no computed winding,
1142 // there's nothing to do
1143 // if two orderable angles are adjacent, and one has winding computed, transfer to the other
1144 const SkOpAngle* baseAngle = NULL;
1145 int last = angleCount;
1146 int finalInitialOrderable = -1;
1147 bool tryReverse = false;
1148 // look for counterclockwise transfers
caryclark@google.com07393ca2013-04-08 11:47:37 +00001149 do {
caryclark@google.com570863f2013-09-16 15:55:01 +00001150 int index = 0;
1151 do {
1152 SkOpAngle* testAngle = (*sorted)[index];
1153 int testWinding = testAngle->segment()->windSum(testAngle);
1154 if (SK_MinS32 != testWinding && !testAngle->unorderable()) {
1155 baseAngle = testAngle;
1156 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001157 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001158 if (testAngle->unorderable()) {
1159 baseAngle = NULL;
1160 tryReverse = true;
1161 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001162 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001163 if (baseAngle) {
1164 ComputeOneSum(baseAngle, testAngle, includeType);
1165 baseAngle = SK_MinS32 != testAngle->segment()->windSum(testAngle) ? testAngle
1166 : NULL;
1167 tryReverse |= !baseAngle;
1168 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001169 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001170 if (finalInitialOrderable + 1 == index) {
1171 finalInitialOrderable = index;
1172 }
1173 } while (++index != last);
1174 if (finalInitialOrderable < 0) {
1175 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001176 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001177 last = finalInitialOrderable + 1;
1178 finalInitialOrderable = -2; // make this always negative the second time through
1179 } while (baseAngle);
1180 if (tryReverse) {
1181 baseAngle = NULL;
1182 last = 0;
1183 finalInitialOrderable = angleCount;
1184 do {
1185 int index = angleCount;
1186 while (--index >= last) {
1187 SkOpAngle* testAngle = (*sorted)[index];
1188 int testWinding = testAngle->segment()->windSum(testAngle);
1189 if (SK_MinS32 != testWinding) {
1190 baseAngle = testAngle;
1191 continue;
1192 }
1193 if (testAngle->unorderable()) {
1194 baseAngle = NULL;
1195 continue;
1196 }
1197 if (baseAngle) {
1198 ComputeOneSumReverse(baseAngle, testAngle, includeType);
1199 baseAngle = SK_MinS32 != testAngle->segment()->windSum(testAngle) ? testAngle
1200 : NULL;
1201 continue;
1202 }
1203 if (finalInitialOrderable - 1 == index) {
1204 finalInitialOrderable = index;
1205 }
1206 }
1207 if (finalInitialOrderable >= angleCount) {
1208 break;
1209 }
1210 last = finalInitialOrderable;
1211 finalInitialOrderable = angleCount + 1; // make this inactive 2nd time through
1212 } while (baseAngle);
1213 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001214 int minIndex = SkMin32(startIndex, endIndex);
1215 return windSum(minIndex);
1216}
1217
caryclark@google.com570863f2013-09-16 15:55:01 +00001218void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
1219 SkOpAngle::IncludeType includeType) {
1220 const SkOpSegment* baseSegment = baseAngle->segment();
1221 int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
1222 int sumSuWinding;
1223 bool binary = includeType >= SkOpAngle::kBinarySingle;
1224 if (binary) {
1225 sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
1226 if (baseSegment->operand()) {
1227 SkTSwap<int>(sumMiWinding, sumSuWinding);
1228 }
1229 }
1230 SkOpSegment* nextSegment = nextAngle->segment();
1231 int maxWinding, sumWinding;
1232 SkOpSpan* last;
1233 if (binary) {
1234 int oppMaxWinding, oppSumWinding;
1235 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
1236 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1237 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
1238 true, nextAngle);
1239 } else {
1240 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
1241 &maxWinding, &sumWinding);
1242 last = nextSegment->markAngle(maxWinding, sumWinding, true, nextAngle);
1243 }
1244 nextAngle->setLastMarked(last);
1245}
1246
1247void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
1248 SkOpAngle::IncludeType includeType) {
1249 const SkOpSegment* baseSegment = baseAngle->segment();
1250 int sumMiWinding = baseSegment->updateWinding(baseAngle);
1251 int sumSuWinding;
1252 bool binary = includeType >= SkOpAngle::kBinarySingle;
1253 if (binary) {
1254 sumSuWinding = baseSegment->updateOppWinding(baseAngle);
1255 if (baseSegment->operand()) {
1256 SkTSwap<int>(sumMiWinding, sumSuWinding);
1257 }
1258 }
1259 SkOpSegment* nextSegment = nextAngle->segment();
1260 int maxWinding, sumWinding;
1261 SkOpSpan* last;
1262 if (binary) {
1263 int oppMaxWinding, oppSumWinding;
1264 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
1265 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1266 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
1267 true, nextAngle);
1268 } else {
1269 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
1270 &maxWinding, &sumWinding);
1271 last = nextSegment->markAngle(maxWinding, sumWinding, true, nextAngle);
1272 }
1273 nextAngle->setLastMarked(last);
1274}
1275
caryclark@google.com07393ca2013-04-08 11:47:37 +00001276int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
1277 bool* hitSomething, double mid, bool opp, bool current) const {
1278 SkScalar bottom = fBounds.fBottom;
1279 int bestTIndex = -1;
1280 if (bottom <= *bestY) {
1281 return bestTIndex;
1282 }
1283 SkScalar top = fBounds.fTop;
1284 if (top >= basePt.fY) {
1285 return bestTIndex;
1286 }
1287 if (fBounds.fLeft > basePt.fX) {
1288 return bestTIndex;
1289 }
1290 if (fBounds.fRight < basePt.fX) {
1291 return bestTIndex;
1292 }
1293 if (fBounds.fLeft == fBounds.fRight) {
1294 // if vertical, and directly above test point, wait for another one
1295 return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
1296 }
1297 // intersect ray starting at basePt with edge
1298 SkIntersections intersections;
1299 // OPTIMIZE: use specialty function that intersects ray with curve,
1300 // returning t values only for curve (we don't care about t on ray)
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00001301 intersections.allowNear(false);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001302 int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
1303 (fPts, top, bottom, basePt.fX, false);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001304 if (pts == 0 || (current && pts == 1)) {
1305 return bestTIndex;
1306 }
1307 if (current) {
1308 SkASSERT(pts > 1);
1309 int closestIdx = 0;
1310 double closest = fabs(intersections[0][0] - mid);
1311 for (int idx = 1; idx < pts; ++idx) {
1312 double test = fabs(intersections[0][idx] - mid);
1313 if (closest > test) {
1314 closestIdx = idx;
1315 closest = test;
1316 }
1317 }
1318 intersections.quickRemoveOne(closestIdx, --pts);
1319 }
1320 double bestT = -1;
1321 for (int index = 0; index < pts; ++index) {
1322 double foundT = intersections[0][index];
1323 if (approximately_less_than_zero(foundT)
1324 || approximately_greater_than_one(foundT)) {
1325 continue;
1326 }
reed@google.com277c3f82013-05-31 15:17:50 +00001327 SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001328 if (approximately_negative(testY - *bestY)
1329 || approximately_negative(basePt.fY - testY)) {
1330 continue;
1331 }
1332 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1333 return SK_MinS32; // if the intersection is edge on, wait for another one
1334 }
1335 if (fVerb > SkPath::kLine_Verb) {
reed@google.com277c3f82013-05-31 15:17:50 +00001336 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001337 if (approximately_zero(dx)) {
1338 return SK_MinS32; // hit vertical, wait for another one
1339 }
1340 }
1341 *bestY = testY;
1342 bestT = foundT;
1343 }
1344 if (bestT < 0) {
1345 return bestTIndex;
1346 }
1347 SkASSERT(bestT >= 0);
1348 SkASSERT(bestT <= 1);
1349 int start;
1350 int end = 0;
1351 do {
1352 start = end;
1353 end = nextSpan(start, 1);
1354 } while (fTs[end].fT < bestT);
1355 // FIXME: see next candidate for a better pattern to find the next start/end pair
1356 while (start + 1 < end && fTs[start].fDone) {
1357 ++start;
1358 }
1359 if (!isCanceled(start)) {
1360 *hitT = bestT;
1361 bestTIndex = start;
1362 *hitSomething = true;
1363 }
1364 return bestTIndex;
1365}
1366
caryclark@google.com570863f2013-09-16 15:55:01 +00001367bool SkOpSegment::decrementSpan(SkOpSpan* span) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001368 SkASSERT(span->fWindValue > 0);
1369 if (--(span->fWindValue) == 0) {
1370 if (!span->fOppValue && !span->fDone) {
1371 span->fDone = true;
1372 ++fDoneSpans;
caryclark@google.com570863f2013-09-16 15:55:01 +00001373 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001374 }
1375 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001376 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001377}
1378
1379bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001380 SkASSERT(!span->fDone || span->fTiny || span->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001381 span->fWindValue += windDelta;
1382 SkASSERT(span->fWindValue >= 0);
1383 span->fOppValue += oppDelta;
1384 SkASSERT(span->fOppValue >= 0);
1385 if (fXor) {
1386 span->fWindValue &= 1;
1387 }
1388 if (fOppXor) {
1389 span->fOppValue &= 1;
1390 }
1391 if (!span->fWindValue && !span->fOppValue) {
1392 span->fDone = true;
1393 ++fDoneSpans;
1394 return true;
1395 }
1396 return false;
1397}
1398
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001399// look to see if the curve end intersects an intermediary that intersects the other
1400void SkOpSegment::checkEnds() {
caryclark@google.com4fdbb222013-07-23 15:27:41 +00001401 debugValidate();
caryclark@google.com570863f2013-09-16 15:55:01 +00001402 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001403 int count = fTs.count();
1404 for (int index = 0; index < count; ++index) {
1405 const SkOpSpan& span = fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +00001406 double otherT = span.fOtherT;
1407 if (otherT != 0 && otherT != 1) { // only check ends
1408 continue;
1409 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001410 const SkOpSegment* other = span.fOther;
caryclark@google.com570863f2013-09-16 15:55:01 +00001411 // peek start/last describe the range of spans that match the other t of this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001412 int peekStart = span.fOtherIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00001413 while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT)
1414 ;
1415 int otherCount = other->fTs.count();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001416 int peekLast = span.fOtherIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00001417 while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT)
1418 ;
1419 if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001420 continue;
1421 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001422 // t start/last describe the range of spans that match the t of this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001423 double t = span.fT;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00001424 double tBottom = -1;
1425 int tStart = -1;
1426 int tLast = count;
1427 bool lastSmall = false;
1428 double afterT = t;
1429 for (int inner = 0; inner < count; ++inner) {
1430 double innerT = fTs[inner].fT;
1431 if (innerT <= t && innerT > tBottom) {
1432 if (innerT < t || !lastSmall) {
1433 tStart = inner - 1;
1434 }
1435 tBottom = innerT;
1436 }
1437 if (innerT > afterT) {
1438 if (t == afterT && lastSmall) {
1439 afterT = innerT;
1440 } else {
1441 tLast = inner;
1442 break;
1443 }
1444 }
1445 lastSmall = innerT <= t ? fTs[inner].fSmall : false;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001446 }
1447 for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001448 if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001449 continue;
1450 }
1451 const SkOpSpan& peekSpan = other->fTs[peekIndex];
1452 SkOpSegment* match = peekSpan.fOther;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001453 if (match->done()) {
1454 continue; // if the edge has already been eaten (likely coincidence), ignore it
1455 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001456 const double matchT = peekSpan.fOtherT;
caryclark@google.com570863f2013-09-16 15:55:01 +00001457 // see if any of the spans match the other spans
1458 for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001459 const SkOpSpan& tSpan = fTs[tIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +00001460 if (tSpan.fOther == match) {
1461 if (tSpan.fOtherT == matchT) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001462 goto nextPeekIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00001463 }
1464 double midT = (tSpan.fOtherT + matchT) / 2;
1465 if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001466 goto nextPeekIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00001467 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001468 }
1469 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001470 if (missingSpans.count() > 0) {
1471 const MissingSpan& lastMissing = missingSpans.back();
1472 if (lastMissing.fCommand == MissingSpan::kAddMissing
1473 && lastMissing.fT == t
1474 && lastMissing.fOther == match
1475 && lastMissing.fOtherT == matchT) {
1476 SkASSERT(lastMissing.fPt == peekSpan.fPt);
1477 continue;
1478 }
1479 }
1480#if DEBUG_CHECK_ENDS
1481 SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n",
1482 __FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY);
1483#endif
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001484 // this segment is missing a entry that the other contains
1485 // remember so we can add the missing one and recompute the indices
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001486 {
1487 MissingSpan& missing = missingSpans.push_back();
1488 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
1489 missing.fCommand = MissingSpan::kAddMissing;
1490 missing.fT = t;
1491 missing.fOther = match;
1492 missing.fOtherT = matchT;
1493 missing.fPt = peekSpan.fPt;
1494 }
1495 break;
1496nextPeekIndex:
1497 ;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001498 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001499 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001500 if (missingSpans.count() == 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001501 debugValidate();
caryclark@google.com4fdbb222013-07-23 15:27:41 +00001502 return;
1503 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001504 // if one end is near the other point, look for a coincident span
1505 for (int index = 0; index < count; ++index) {
1506 const SkOpSpan& span = fTs[index];
1507 if (span.fT > 0) {
1508 break;
1509 }
1510 const SkOpSpan& otherSpan = span.fOther->span(span.fOtherIndex);
1511 if (span.fNear) {
1512 SkASSERT(otherSpan.fPt == fPts[0]);
1513 adjustNear(0, span.fPt, &missingSpans);
1514 continue;
1515 }
1516 if (otherSpan.fNear) {
1517 SkASSERT(span.fPt == fPts[0]);
1518 adjustNear(0, otherSpan.fPt, &missingSpans);
1519 }
1520 }
1521 for (int index = count; --index >= 0; ) {
1522 const SkOpSpan& span = fTs[index];
1523 if (span.fT < 1) {
1524 break;
1525 }
1526 const SkOpSegment* other = span.fOther;
1527 if (span.fNear) {
1528 SkASSERT(other->ptAtT(span.fOtherT) == ptAtT(1));
1529 const SkPoint& otherPt = other->xyAtT(span.fOtherIndex);
1530 SkASSERT(otherPt != ptAtT(1));
1531 adjustNear(1, otherPt, &missingSpans);
1532 continue;
1533 }
1534 const SkOpSpan& otherSpan = other->span(span.fOtherIndex);
1535 if (otherSpan.fNear) {
1536 SkASSERT(otherSpan.fPt == ptAtT(1));
1537 SkPoint otherPt = other->ptAtT(span.fOtherT);
1538 SkASSERT(otherPt != ptAtT(1));
1539 adjustNear(1, otherPt, &missingSpans);
1540 }
1541 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00001542 debugValidate();
caryclark@google.com570863f2013-09-16 15:55:01 +00001543 int missingCount = missingSpans.count();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001544 for (int index = 0; index < missingCount; ++index) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001545 MissingSpan& missing = missingSpans[index];
1546 switch (missing.fCommand) {
1547 case MissingSpan::kNoAction:
1548 break;
1549 case MissingSpan::kAddMissing:
1550 addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
1551 break;
1552 case MissingSpan::kRemoveNear: {
1553 SkOpSegment* segment = missing.fSegment;
1554 int count = segment->count();
1555 for (int inner = 0; inner < count; ++inner) {
1556 const SkOpSpan& span = segment->span(inner);
1557 if (span.fT != missing.fT && span.fOther != missing.fOther) {
1558 continue;
1559 }
1560 SkASSERT(span.fNear);
1561 SkOpSegment* other = span.fOther;
1562 int otherCount = other->count();
1563 for (int otherIndex = 0; otherIndex < otherCount; ++otherIndex) {
1564 const SkOpSpan& otherSpan = other->span(otherIndex);
1565 if (otherSpan.fT == span.fOtherT && otherSpan.fOther == segment
1566 && otherSpan.fOtherT == span.fT) {
1567 if (otherSpan.fDone) {
1568 other->fDoneSpans--;
1569 }
1570 other->fTs.remove(otherIndex);
1571 // FIXME: remove may leave a tiny dangling -- recompute tiny w/index
1572 break;
1573 }
1574 }
1575 if (span.fDone) {
1576 segment->fDoneSpans--;
1577 }
1578 segment->fTs.remove(inner);
1579 // FIXME: remove may leave a tiny dangling -- recompute tiny w/index
1580 break;
1581 }
1582 break;
1583 }
1584 case MissingSpan::kZeroSpan: {
1585 SkOpSegment* segment = missing.fSegment;
1586 int count = segment->count();
1587 for (int inner = 0; inner < count; ++inner) {
1588 SkOpSpan& span = segment->fTs[inner];
1589 if (span.fT < missing.fT) {
1590 continue;
1591 }
1592 if (span.fT >= missing.fEndT) {
1593 break;
1594 }
1595 span.fWindValue = span.fOppValue = 0;
1596 if (!span.fDone) {
1597 span.fDone = true;
1598 ++segment->fDoneSpans;
1599 }
1600 }
1601 break;
1602 }
1603 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001604 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00001605 fixOtherTIndex();
caryclark@google.com570863f2013-09-16 15:55:01 +00001606 // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
1607 // avoid this
caryclark@google.com4fdbb222013-07-23 15:27:41 +00001608 for (int index = 0; index < missingCount; ++index) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001609 const MissingSpan& missing = missingSpans[index];
1610 switch (missing.fCommand) {
1611 case MissingSpan::kNoAction:
1612 break;
1613 case MissingSpan::kAddMissing:
1614 missing.fOther->fixOtherTIndex();
1615 break;
1616 case MissingSpan::kRemoveNear:
1617 missing.fSegment->fixOtherTIndex();
1618 missing.fOther->fixOtherTIndex();
1619 break;
1620 case MissingSpan::kZeroSpan:
1621 break;
1622 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001623 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00001624 debugValidate();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001625}
1626
caryclark@google.com570863f2013-09-16 15:55:01 +00001627bool SkOpSegment::checkSmall(int index) const {
1628 if (fTs[index].fSmall) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001629 return true;
1630 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001631 double tBase = fTs[index].fT;
1632 while (index > 0 && precisely_negative(tBase - fTs[--index].fT))
1633 ;
1634 return fTs[index].fSmall;
1635}
1636
1637// if pair of spans on either side of tiny have the same end point and mid point, mark
1638// them as parallel
1639// OPTIMIZATION : mark the segment to note that some span is tiny
1640void SkOpSegment::checkTiny() {
1641 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
1642 SkOpSpan* thisSpan = fTs.begin() - 1;
1643 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be tiny
1644 while (++thisSpan < endSpan) {
1645 if (!thisSpan->fTiny) {
1646 continue;
1647 }
1648 SkOpSpan* nextSpan = thisSpan + 1;
1649 double thisT = thisSpan->fT;
1650 double nextT = nextSpan->fT;
1651 if (thisT == nextT) {
1652 continue;
1653 }
1654 SkASSERT(thisT < nextT);
1655 SkASSERT(thisSpan->fPt == nextSpan->fPt);
1656 SkOpSegment* thisOther = thisSpan->fOther;
1657 SkOpSegment* nextOther = nextSpan->fOther;
1658 int oIndex = thisSpan->fOtherIndex;
1659 for (int oStep = -1; oStep <= 1; oStep += 2) {
1660 int oEnd = thisOther->nextExactSpan(oIndex, oStep);
1661 if (oEnd < 0) {
1662 continue;
1663 }
1664 const SkOpSpan& oSpan = thisOther->span(oEnd);
1665 int nIndex = nextSpan->fOtherIndex;
1666 for (int nStep = -1; nStep <= 1; nStep += 2) {
1667 int nEnd = nextOther->nextExactSpan(nIndex, nStep);
1668 if (nEnd < 0) {
1669 continue;
1670 }
1671 const SkOpSpan& nSpan = nextOther->span(nEnd);
1672 if (oSpan.fPt != nSpan.fPt) {
1673 continue;
1674 }
1675 double oMidT = (thisSpan->fOtherT + oSpan.fT) / 2;
1676 const SkPoint& oPt = thisOther->ptAtT(oMidT);
1677 double nMidT = (nextSpan->fOtherT + nSpan.fT) / 2;
1678 const SkPoint& nPt = nextOther->ptAtT(nMidT);
1679 if (!AlmostEqualUlps(oPt, nPt)) {
1680 continue;
1681 }
1682#if DEBUG_CHECK_TINY
1683 SkDebugf("%s [%d] add coincidence [%d] [%d]\n", __FUNCTION__, fID,
1684 thisOther->fID, nextOther->fID);
1685#endif
1686 // this segment is missing a entry that the other contains
1687 // remember so we can add the missing one and recompute the indices
1688 MissingSpan& missing = missingSpans.push_back();
1689 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
1690 missing.fCommand = MissingSpan::kAddMissing;
1691 missing.fSegment = thisOther;
1692 missing.fT = thisSpan->fOtherT;
1693 missing.fOther = nextOther;
1694 missing.fOtherT = nextSpan->fOtherT;
1695 missing.fPt = thisSpan->fPt;
1696 }
1697 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001698 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001699 int missingCount = missingSpans.count();
1700 if (!missingCount) {
1701 return;
1702 }
1703 for (int index = 0; index < missingCount; ++index) {
1704 MissingSpan& missing = missingSpans[index];
1705 missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
1706 }
1707 for (int index = 0; index < missingCount; ++index) {
1708 MissingSpan& missing = missingSpans[index];
1709 missing.fSegment->fixOtherTIndex();
1710 missing.fOther->fixOtherTIndex();
1711 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001712}
1713
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00001714bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart,
1715 int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const {
1716 SkASSERT(span->fT == 0 || span->fT == 1);
1717 SkASSERT(span->fOtherT == 0 || span->fOtherT == 1);
1718 const SkOpSpan* otherSpan = &other->span(oEnd);
1719 double refT = otherSpan->fT;
1720 const SkPoint& refPt = otherSpan->fPt;
1721 const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0);
1722 do {
1723 const SkOpSegment* match = span->fOther;
1724 if (match == otherSpan->fOther) {
1725 // find start of respective spans and see if both have winding
1726 int startIndex, endIndex;
1727 if (span->fOtherT == 1) {
1728 endIndex = span->fOtherIndex;
1729 startIndex = match->nextExactSpan(endIndex, -1);
1730 } else {
1731 startIndex = span->fOtherIndex;
1732 endIndex = match->nextExactSpan(startIndex, 1);
1733 }
1734 const SkOpSpan& startSpan = match->span(startIndex);
1735 if (startSpan.fWindValue != 0) {
1736 // draw ray from endSpan.fPt perpendicular to end tangent and measure distance
1737 // to other segment.
1738 const SkOpSpan& endSpan = match->span(endIndex);
1739 SkDLine ray;
1740 SkVector dxdy;
1741 if (span->fOtherT == 1) {
1742 ray.fPts[0].set(startSpan.fPt);
1743 dxdy = match->dxdy(startIndex);
1744 } else {
1745 ray.fPts[0].set(endSpan.fPt);
1746 dxdy = match->dxdy(endIndex);
1747 }
1748 ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY;
1749 ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX;
1750 SkIntersections i;
1751 int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray);
1752 for (int index = 0; index < roots; ++index) {
1753 if (ray.fPts[0].approximatelyEqual(i.pt(index))) {
1754 double matchMidT = (match->span(startIndex).fT
1755 + match->span(endIndex).fT) / 2;
1756 SkPoint matchMidPt = match->ptAtT(matchMidT);
1757 double otherMidT = (i[0][index] + other->span(oStart).fT) / 2;
1758 SkPoint otherMidPt = other->ptAtT(otherMidT);
1759 if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) {
1760 *startPt = startSpan.fPt;
1761 *endPt = endSpan.fPt;
1762 *endT = endSpan.fT;
1763 return true;
1764 }
1765 }
1766 }
1767 }
1768 return false;
1769 }
1770 if (otherSpan == lastSpan) {
1771 break;
1772 }
1773 otherSpan += step;
1774 } while (otherSpan->fT == refT || otherSpan->fPt == refPt);
1775 return false;
1776}
1777
caryclark@google.com07393ca2013-04-08 11:47:37 +00001778/*
1779 The M and S variable name parts stand for the operators.
1780 Mi stands for Minuend (see wiki subtraction, analogous to difference)
1781 Su stands for Subtrahend
1782 The Opp variable name part designates that the value is for the Opposite operator.
1783 Opposite values result from combining coincident spans.
1784 */
1785SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
1786 bool* unsortable, SkPathOp op, const int xorMiMask,
1787 const int xorSuMask) {
1788 const int startIndex = *nextStart;
1789 const int endIndex = *nextEnd;
1790 SkASSERT(startIndex != endIndex);
1791 SkDEBUGCODE(const int count = fTs.count());
1792 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
1793 const int step = SkSign32(endIndex - startIndex);
1794 const int end = nextExactSpan(startIndex, step);
1795 SkASSERT(end >= 0);
1796 SkOpSpan* endSpan = &fTs[end];
1797 SkOpSegment* other;
1798 if (isSimple(end)) {
1799 // mark the smaller of startIndex, endIndex done, and all adjacent
1800 // spans with the same T value (but not 'other' spans)
1801#if DEBUG_WINDING
1802 SkDebugf("%s simple\n", __FUNCTION__);
1803#endif
1804 int min = SkMin32(startIndex, endIndex);
1805 if (fTs[min].fDone) {
1806 return NULL;
1807 }
1808 markDoneBinary(min);
1809 other = endSpan->fOther;
1810 *nextStart = endSpan->fOtherIndex;
1811 double startT = other->fTs[*nextStart].fT;
1812 *nextEnd = *nextStart;
1813 do {
1814 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00001815 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001816 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001817 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +00001818 *unsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001819 return NULL;
1820 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001821 return other;
1822 }
1823 // more than one viable candidate -- measure angles to find best
caryclark@google.comd892bd82013-06-17 14:10:36 +00001824 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001825 SkASSERT(startIndex - endIndex != 0);
1826 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.comd892bd82013-06-17 14:10:36 +00001827 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
caryclark@google.com570863f2013-09-16 15:55:01 +00001828 int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp, &angles, &sorted);
1829 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001830 if (sortable && sorted.count() == 0) {
1831 // if no edge has a computed winding sum, we can go no further
1832 *unsortable = true;
1833 return NULL;
1834 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001835 int angleCount = angles.count();
1836 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com570863f2013-09-16 15:55:01 +00001837 SkASSERT(!sortable || firstIndex >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001838#if DEBUG_SORT
caryclark@google.com07e97fc2013-07-08 17:17:02 +00001839 debugShowSort(__FUNCTION__, sorted, firstIndex, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001840#endif
1841 if (!sortable) {
1842 *unsortable = true;
1843 return NULL;
1844 }
1845 SkASSERT(sorted[firstIndex]->segment() == this);
1846#if DEBUG_WINDING
1847 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
1848 sorted[firstIndex]->sign());
1849#endif
1850 int sumMiWinding = updateWinding(endIndex, startIndex);
1851 int sumSuWinding = updateOppWinding(endIndex, startIndex);
1852 if (operand()) {
1853 SkTSwap<int>(sumMiWinding, sumSuWinding);
1854 }
1855 int nextIndex = firstIndex + 1;
1856 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1857 const SkOpAngle* foundAngle = NULL;
1858 bool foundDone = false;
1859 // iterate through the angle, and compute everyone's winding
1860 SkOpSegment* nextSegment;
1861 int activeCount = 0;
1862 do {
1863 SkASSERT(nextIndex != firstIndex);
1864 if (nextIndex == angleCount) {
1865 nextIndex = 0;
1866 }
1867 const SkOpAngle* nextAngle = sorted[nextIndex];
1868 nextSegment = nextAngle->segment();
1869 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
1870 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
1871 nextAngle->end(), op, &sumMiWinding, &sumSuWinding,
1872 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1873 if (activeAngle) {
1874 ++activeCount;
1875 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001876 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001877 *unsortable = true;
1878 return NULL;
1879 }
1880 foundAngle = nextAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +00001881 foundDone = nextSegment->done(nextAngle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001882 }
1883 }
1884 if (nextSegment->done()) {
1885 continue;
1886 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001887 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001888 continue;
1889 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001890 if (!activeAngle) {
1891 nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
1892 }
1893 SkOpSpan* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001894 if (last) {
1895 *chase->append() = last;
1896#if DEBUG_WINDING
caryclark@google.com570863f2013-09-16 15:55:01 +00001897 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
1898 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
1899 last->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001900#endif
1901 }
1902 } while (++nextIndex != lastIndex);
1903 markDoneBinary(SkMin32(startIndex, endIndex));
1904 if (!foundAngle) {
1905 return NULL;
1906 }
1907 *nextStart = foundAngle->start();
1908 *nextEnd = foundAngle->end();
1909 nextSegment = foundAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001910#if DEBUG_WINDING
1911 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
1912 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
1913 #endif
1914 return nextSegment;
1915}
1916
1917SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart,
1918 int* nextEnd, bool* unsortable) {
1919 const int startIndex = *nextStart;
1920 const int endIndex = *nextEnd;
1921 SkASSERT(startIndex != endIndex);
1922 SkDEBUGCODE(const int count = fTs.count());
1923 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
1924 const int step = SkSign32(endIndex - startIndex);
1925 const int end = nextExactSpan(startIndex, step);
1926 SkASSERT(end >= 0);
1927 SkOpSpan* endSpan = &fTs[end];
1928 SkOpSegment* other;
1929 if (isSimple(end)) {
1930 // mark the smaller of startIndex, endIndex done, and all adjacent
1931 // spans with the same T value (but not 'other' spans)
1932#if DEBUG_WINDING
1933 SkDebugf("%s simple\n", __FUNCTION__);
1934#endif
1935 int min = SkMin32(startIndex, endIndex);
1936 if (fTs[min].fDone) {
1937 return NULL;
1938 }
1939 markDoneUnary(min);
1940 other = endSpan->fOther;
1941 *nextStart = endSpan->fOtherIndex;
1942 double startT = other->fTs[*nextStart].fT;
1943 *nextEnd = *nextStart;
1944 do {
1945 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00001946 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001947 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
caryclark@google.com570863f2013-09-16 15:55:01 +00001948 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
1949 *unsortable = true;
1950 return NULL;
1951 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001952 return other;
1953 }
1954 // more than one viable candidate -- measure angles to find best
caryclark@google.comd892bd82013-06-17 14:10:36 +00001955 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001956 SkASSERT(startIndex - endIndex != 0);
1957 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.comd892bd82013-06-17 14:10:36 +00001958 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
caryclark@google.com570863f2013-09-16 15:55:01 +00001959 int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding, &angles, &sorted);
1960 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001961 int angleCount = angles.count();
1962 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com570863f2013-09-16 15:55:01 +00001963 SkASSERT(!sortable || firstIndex >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001964#if DEBUG_SORT
caryclark@google.com07e97fc2013-07-08 17:17:02 +00001965 debugShowSort(__FUNCTION__, sorted, firstIndex, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001966#endif
1967 if (!sortable) {
1968 *unsortable = true;
1969 return NULL;
1970 }
1971 SkASSERT(sorted[firstIndex]->segment() == this);
1972#if DEBUG_WINDING
1973 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
1974 sorted[firstIndex]->sign());
1975#endif
1976 int sumWinding = updateWinding(endIndex, startIndex);
1977 int nextIndex = firstIndex + 1;
1978 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1979 const SkOpAngle* foundAngle = NULL;
1980 bool foundDone = false;
1981 // iterate through the angle, and compute everyone's winding
1982 SkOpSegment* nextSegment;
1983 int activeCount = 0;
1984 do {
1985 SkASSERT(nextIndex != firstIndex);
1986 if (nextIndex == angleCount) {
1987 nextIndex = 0;
1988 }
1989 const SkOpAngle* nextAngle = sorted[nextIndex];
1990 nextSegment = nextAngle->segment();
1991 int maxWinding;
1992 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
1993 &maxWinding, &sumWinding);
1994 if (activeAngle) {
1995 ++activeCount;
1996 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001997 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001998 *unsortable = true;
1999 return NULL;
2000 }
2001 foundAngle = nextAngle;
2002 foundDone = nextSegment->done(nextAngle);
2003 }
2004 }
2005 if (nextSegment->done()) {
2006 continue;
2007 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002008 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002009 continue;
2010 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002011 if (!activeAngle) {
2012 nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
2013 }
2014 SkOpSpan* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002015 if (last) {
2016 *chase->append() = last;
2017#if DEBUG_WINDING
caryclark@google.com570863f2013-09-16 15:55:01 +00002018 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
2019 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
2020 last->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002021#endif
2022 }
2023 } while (++nextIndex != lastIndex);
2024 markDoneUnary(SkMin32(startIndex, endIndex));
2025 if (!foundAngle) {
2026 return NULL;
2027 }
2028 *nextStart = foundAngle->start();
2029 *nextEnd = foundAngle->end();
2030 nextSegment = foundAngle->segment();
2031#if DEBUG_WINDING
2032 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2033 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
2034 #endif
2035 return nextSegment;
2036}
2037
2038SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsortable) {
2039 const int startIndex = *nextStart;
2040 const int endIndex = *nextEnd;
2041 SkASSERT(startIndex != endIndex);
2042 SkDEBUGCODE(int count = fTs.count());
caryclark@google.com570863f2013-09-16 15:55:01 +00002043 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002044 int step = SkSign32(endIndex - startIndex);
2045 int end = nextExactSpan(startIndex, step);
2046 SkASSERT(end >= 0);
2047 SkOpSpan* endSpan = &fTs[end];
2048 SkOpSegment* other;
2049 if (isSimple(end)) {
2050#if DEBUG_WINDING
2051 SkDebugf("%s simple\n", __FUNCTION__);
2052#endif
2053 int min = SkMin32(startIndex, endIndex);
2054 if (fTs[min].fDone) {
2055 return NULL;
2056 }
2057 markDone(min, 1);
2058 other = endSpan->fOther;
2059 *nextStart = endSpan->fOtherIndex;
2060 double startT = other->fTs[*nextStart].fT;
2061 // FIXME: I don't know why the logic here is difference from the winding case
2062 SkDEBUGCODE(bool firstLoop = true;)
2063 if ((approximately_less_than_zero(startT) && step < 0)
2064 || (approximately_greater_than_one(startT) && step > 0)) {
2065 step = -step;
2066 SkDEBUGCODE(firstLoop = false;)
2067 }
2068 do {
2069 *nextEnd = *nextStart;
2070 do {
2071 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00002072 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002073 if (other->fTs[SkMin32(*nextStart, *nextEnd)].fWindValue) {
2074 break;
2075 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002076 SkASSERT(firstLoop);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002077 SkDEBUGCODE(firstLoop = false;)
2078 step = -step;
2079 } while (true);
2080 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
2081 return other;
2082 }
caryclark@google.comd892bd82013-06-17 14:10:36 +00002083 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002084 SkASSERT(startIndex - endIndex != 0);
2085 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.comd892bd82013-06-17 14:10:36 +00002086 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
caryclark@google.com570863f2013-09-16 15:55:01 +00002087 int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryXor, &angles, &sorted);
2088 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002089 int angleCount = angles.count();
2090 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com570863f2013-09-16 15:55:01 +00002091 SkASSERT(!sortable || firstIndex >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002092#if DEBUG_SORT
caryclark@google.com07e97fc2013-07-08 17:17:02 +00002093 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002094#endif
caryclark@google.com570863f2013-09-16 15:55:01 +00002095 if (!sortable) {
2096 *unsortable = true;
2097 return NULL;
2098 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002099 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com570863f2013-09-16 15:55:01 +00002100#if DEBUG_WINDING
2101 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
2102 sorted[firstIndex]->sign());
2103#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00002104 int nextIndex = firstIndex + 1;
2105 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2106 const SkOpAngle* foundAngle = NULL;
2107 bool foundDone = false;
2108 SkOpSegment* nextSegment;
2109 int activeCount = 0;
2110 do {
2111 SkASSERT(nextIndex != firstIndex);
2112 if (nextIndex == angleCount) {
2113 nextIndex = 0;
2114 }
2115 const SkOpAngle* nextAngle = sorted[nextIndex];
2116 nextSegment = nextAngle->segment();
2117 ++activeCount;
2118 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002119 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002120 *unsortable = true;
2121 return NULL;
2122 }
2123 foundAngle = nextAngle;
2124 foundDone = nextSegment->done(nextAngle);
2125 }
2126 if (nextSegment->done()) {
2127 continue;
2128 }
2129 } while (++nextIndex != lastIndex);
2130 markDone(SkMin32(startIndex, endIndex), 1);
2131 if (!foundAngle) {
2132 return NULL;
2133 }
2134 *nextStart = foundAngle->start();
2135 *nextEnd = foundAngle->end();
2136 nextSegment = foundAngle->segment();
2137#if DEBUG_WINDING
2138 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2139 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
2140 #endif
2141 return nextSegment;
2142}
2143
caryclark@google.comd892bd82013-06-17 14:10:36 +00002144int SkOpSegment::findStartingEdge(const SkTArray<SkOpAngle*, true>& sorted, int start, int end) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002145 int angleCount = sorted.count();
2146 int firstIndex = -1;
2147 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2148 const SkOpAngle* angle = sorted[angleIndex];
2149 if (angle->segment() == this && angle->start() == end &&
2150 angle->end() == start) {
2151 firstIndex = angleIndex;
2152 break;
2153 }
2154 }
2155 return firstIndex;
2156}
2157
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002158int SkOpSegment::findT(double t, const SkOpSegment* match) const {
2159 int count = this->count();
2160 for (int index = 0; index < count; ++index) {
2161 const SkOpSpan& span = fTs[index];
2162 if (span.fT == t && span.fOther == match) {
2163 return index;
2164 }
2165 }
2166 SkASSERT(0);
2167 return -1;
2168}
2169
caryclark@google.com07393ca2013-04-08 11:47:37 +00002170// FIXME: either:
2171// a) mark spans with either end unsortable as done, or
2172// b) rewrite findTop / findTopSegment / findTopContour to iterate further
2173// when encountering an unsortable span
2174
2175// OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2176// and use more concise logic like the old edge walker code?
2177// FIXME: this needs to deal with coincident edges
2178SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable,
2179 bool onlySortable) {
2180 // iterate through T intersections and return topmost
2181 // topmost tangent from y-min to first pt is closer to horizontal
2182 SkASSERT(!done());
2183 int firstT = -1;
2184 /* SkPoint topPt = */ activeLeftTop(onlySortable, &firstT);
2185 if (firstT < 0) {
2186 *unsortable = true;
2187 firstT = 0;
2188 while (fTs[firstT].fDone) {
2189 SkASSERT(firstT < fTs.count());
2190 ++firstT;
2191 }
2192 *tIndexPtr = firstT;
2193 *endIndexPtr = nextExactSpan(firstT, 1);
2194 return this;
2195 }
2196 // sort the edges to find the leftmost
2197 int step = 1;
2198 int end = nextSpan(firstT, step);
2199 if (end == -1) {
2200 step = -1;
2201 end = nextSpan(firstT, step);
2202 SkASSERT(end != -1);
2203 }
2204 // if the topmost T is not on end, or is three-way or more, find left
2205 // look for left-ness from tLeft to firstT (matching y of other)
caryclark@google.comd892bd82013-06-17 14:10:36 +00002206 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002207 SkASSERT(firstT - end != 0);
2208 addTwoAngles(end, firstT, &angles);
caryclark@google.com570863f2013-09-16 15:55:01 +00002209 if (!buildAngles(firstT, &angles, true) && onlySortable) {
2210// *unsortable = true;
2211// return NULL;
2212 }
caryclark@google.comd892bd82013-06-17 14:10:36 +00002213 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002214 bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMayBeUnordered_SortAngleKind);
caryclark@google.com570863f2013-09-16 15:55:01 +00002215 if (onlySortable && !sortable) {
2216 *unsortable = true;
2217 return NULL;
2218 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002219 int first = SK_MaxS32;
2220 SkScalar top = SK_ScalarMax;
2221 int count = sorted.count();
2222 for (int index = 0; index < count; ++index) {
2223 const SkOpAngle* angle = sorted[index];
caryclark@google.com570863f2013-09-16 15:55:01 +00002224 if (onlySortable && angle->unorderable()) {
2225 continue;
2226 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002227 SkOpSegment* next = angle->segment();
2228 SkPathOpsBounds bounds;
2229 next->subDivideBounds(angle->end(), angle->start(), &bounds);
2230 if (approximately_greater(top, bounds.fTop)) {
2231 top = bounds.fTop;
2232 first = index;
2233 }
2234 }
2235 SkASSERT(first < SK_MaxS32);
2236#if DEBUG_SORT // || DEBUG_SWAP_TOP
caryclark@google.com07e97fc2013-07-08 17:17:02 +00002237 sorted[first]->segment()->debugShowSort(__FUNCTION__, sorted, first, 0, 0, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002238#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00002239 // skip edges that have already been processed
2240 firstT = first - 1;
2241 SkOpSegment* leftSegment;
2242 do {
2243 if (++firstT == count) {
2244 firstT = 0;
2245 }
2246 const SkOpAngle* angle = sorted[firstT];
2247 SkASSERT(!onlySortable || !angle->unsortable());
2248 leftSegment = angle->segment();
2249 *tIndexPtr = angle->end();
2250 *endIndexPtr = angle->start();
2251 } while (leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone);
2252 if (leftSegment->verb() >= SkPath::kQuad_Verb) {
2253 const int tIndex = *tIndexPtr;
2254 const int endIndex = *endIndexPtr;
2255 if (!leftSegment->clockwise(tIndex, endIndex)) {
2256 bool swap = !leftSegment->monotonicInY(tIndex, endIndex)
2257 && !leftSegment->serpentine(tIndex, endIndex);
2258 #if DEBUG_SWAP_TOP
2259 SkDebugf("%s swap=%d serpentine=%d containedByEnds=%d monotonic=%d\n", __FUNCTION__,
2260 swap,
2261 leftSegment->serpentine(tIndex, endIndex),
2262 leftSegment->controlsContainedByEnds(tIndex, endIndex),
2263 leftSegment->monotonicInY(tIndex, endIndex));
2264 #endif
2265 if (swap) {
2266 // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
2267 // sorted but merely the first not already processed (i.e., not done)
2268 SkTSwap(*tIndexPtr, *endIndexPtr);
2269 }
2270 }
2271 }
2272 SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
2273 return leftSegment;
2274}
2275
2276// FIXME: not crazy about this
2277// when the intersections are performed, the other index is into an
2278// incomplete array. As the array grows, the indices become incorrect
2279// while the following fixes the indices up again, it isn't smart about
2280// skipping segments whose indices are already correct
2281// assuming we leave the code that wrote the index in the first place
caryclark@google.com570863f2013-09-16 15:55:01 +00002282// FIXME: if called after remove, this needs to correct tiny
caryclark@google.com07393ca2013-04-08 11:47:37 +00002283void SkOpSegment::fixOtherTIndex() {
2284 int iCount = fTs.count();
2285 for (int i = 0; i < iCount; ++i) {
2286 SkOpSpan& iSpan = fTs[i];
2287 double oT = iSpan.fOtherT;
2288 SkOpSegment* other = iSpan.fOther;
2289 int oCount = other->fTs.count();
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002290 SkDEBUGCODE(iSpan.fOtherIndex = -1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002291 for (int o = 0; o < oCount; ++o) {
2292 SkOpSpan& oSpan = other->fTs[o];
2293 if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) {
2294 iSpan.fOtherIndex = o;
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002295 oSpan.fOtherIndex = i;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002296 break;
2297 }
2298 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002299 SkASSERT(iSpan.fOtherIndex >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002300 }
2301}
2302
2303void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
2304 fDoneSpans = 0;
2305 fOperand = operand;
2306 fXor = evenOdd;
2307 fPts = pts;
2308 fVerb = verb;
2309}
2310
2311void SkOpSegment::initWinding(int start, int end) {
2312 int local = spanSign(start, end);
2313 int oppLocal = oppSign(start, end);
2314 (void) markAndChaseWinding(start, end, local, oppLocal);
2315 // OPTIMIZATION: the reverse mark and chase could skip the first marking
2316 (void) markAndChaseWinding(end, start, local, oppLocal);
2317}
2318
2319/*
2320when we start with a vertical intersect, we try to use the dx to determine if the edge is to
2321the left or the right of vertical. This determines if we need to add the span's
2322sign or not. However, this isn't enough.
2323If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
2324If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
2325from has the same x direction as this span, the winding should change. If the dx is opposite, then
2326the same winding is shared by both.
2327*/
2328void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
2329 int oppWind, SkScalar hitOppDx) {
2330 SkASSERT(hitDx || !winding);
reed@google.com277c3f82013-05-31 15:17:50 +00002331 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002332 SkASSERT(dx);
2333 int windVal = windValue(SkMin32(start, end));
2334#if DEBUG_WINDING_AT_T
2335 SkDebugf("%s oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, winding,
2336 hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
2337#endif
2338 if (!winding) {
2339 winding = dx < 0 ? windVal : -windVal;
2340 } else if (winding * dx < 0) {
2341 int sideWind = winding + (dx < 0 ? windVal : -windVal);
2342 if (abs(winding) < abs(sideWind)) {
2343 winding = sideWind;
2344 }
2345 }
2346#if DEBUG_WINDING_AT_T
2347 SkDebugf(" winding=%d\n", winding);
2348#endif
2349 SkDEBUGCODE(int oppLocal = oppSign(start, end));
2350 SkASSERT(hitOppDx || !oppWind || !oppLocal);
2351 int oppWindVal = oppValue(SkMin32(start, end));
2352 if (!oppWind) {
2353 oppWind = dx < 0 ? oppWindVal : -oppWindVal;
2354 } else if (hitOppDx * dx >= 0) {
2355 int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
2356 if (abs(oppWind) < abs(oppSideWind)) {
2357 oppWind = oppSideWind;
2358 }
2359 }
2360 (void) markAndChaseWinding(start, end, winding, oppWind);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002361 // OPTIMIZATION: the reverse mark and chase could skip the first marking
2362 (void) markAndChaseWinding(end, start, winding, oppWind);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002363}
2364
caryclark@google.com07393ca2013-04-08 11:47:37 +00002365// OPTIMIZE: successive calls could start were the last leaves off
2366// or calls could specialize to walk forwards or backwards
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002367bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002368 size_t tCount = fTs.count();
2369 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002370 const SkOpSpan& span = fTs[index];
2371 if (approximately_zero(startT - span.fT) && pt == span.fPt) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002372 return false;
2373 }
2374 }
2375 return true;
2376}
2377
2378bool SkOpSegment::isSimple(int end) const {
2379 int count = fTs.count();
2380 if (count == 2) {
2381 return true;
2382 }
2383 double t = fTs[end].fT;
2384 if (approximately_less_than_zero(t)) {
2385 return !approximately_less_than_zero(fTs[1].fT);
2386 }
2387 if (approximately_greater_than_one(t)) {
2388 return !approximately_greater_than_one(fTs[count - 2].fT);
2389 }
2390 return false;
2391}
2392
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002393bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
2394 int start = angle->start();
2395 int end = angle->end();
2396 const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
2397 return mSpan.fTiny;
2398}
2399
2400bool SkOpSegment::isTiny(int index) const {
2401 return fTs[index].fTiny;
2402}
2403
2404// look pair of active edges going away from coincident edge
2405// one of them should be the continuation of other
2406// if both are active, look to see if they both the connect to another coincident pair
2407// if one at least one is a line, then make the pair coincident
2408// if neither is a line, test for coincidence
2409bool SkOpSegment::joinCoincidence(bool end, SkOpSegment* other, double otherT, int step,
2410 bool cancel) {
2411 int otherTIndex = other->findT(otherT, this);
2412 int next = other->nextExactSpan(otherTIndex, step);
2413 int otherMin = SkTMin(otherTIndex, next);
2414 int otherWind = other->span(otherMin).fWindValue;
2415 if (otherWind == 0) {
2416 return false;
2417 }
2418 SkASSERT(next >= 0);
2419 if (end) {
2420 int tIndex = count() - 1;
2421 do {
2422 SkOpSpan* test = &fTs[tIndex];
2423 SkASSERT(test->fT == 1);
2424 if (test->fOther == other || test->fOtherT != 0) {
2425 continue;
2426 }
2427 SkPoint startPt, endPt;
2428 double endT;
2429 if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) {
2430 SkOpSegment* match = test->fOther;
2431 if (cancel) {
2432 match->addTCancel(startPt, endPt, other);
2433 } else {
2434 match->addTCoincident(startPt, endPt, endT, other);
2435 }
2436 return true;
2437 }
2438 } while (fTs[--tIndex].fT == 1);
2439 } else {
2440 int tIndex = 0;
2441 do {
2442 SkOpSpan* test = &fTs[tIndex];
2443 SkASSERT(test->fT == 0);
2444 if (test->fOther == other || test->fOtherT != 1) {
2445 continue;
2446 }
2447 SkPoint startPt, endPt;
2448 double endT;
2449 if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) {
2450 SkOpSegment* match = test->fOther;
2451 if (cancel) {
2452 match->addTCancel(startPt, endPt, other);
2453 } else {
2454 match->addTCoincident(startPt, endPt, endT, other);
2455 }
2456 return true;
2457 }
2458 } while (fTs[++tIndex].fT == 0);
2459 }
2460 return false;
2461}
2462
caryclark@google.com07393ca2013-04-08 11:47:37 +00002463// this span is excluded by the winding rule -- chase the ends
2464// as long as they are unambiguous to mark connections as done
2465// and give them the same winding value
2466SkOpSpan* SkOpSegment::markAndChaseDone(int index, int endIndex, int winding) {
2467 int step = SkSign32(endIndex - index);
2468 int min = SkMin32(index, endIndex);
2469 markDone(min, winding);
2470 SkOpSpan* last;
2471 SkOpSegment* other = this;
2472 while ((other = other->nextChase(&index, step, &min, &last))) {
2473 other->markDone(min, winding);
2474 }
2475 return last;
2476}
2477
2478SkOpSpan* SkOpSegment::markAndChaseDoneBinary(const SkOpAngle* angle, int winding, int oppWinding) {
2479 int index = angle->start();
2480 int endIndex = angle->end();
2481 int step = SkSign32(endIndex - index);
2482 int min = SkMin32(index, endIndex);
2483 markDoneBinary(min, winding, oppWinding);
2484 SkOpSpan* last;
2485 SkOpSegment* other = this;
2486 while ((other = other->nextChase(&index, step, &min, &last))) {
2487 other->markDoneBinary(min, winding, oppWinding);
2488 }
2489 return last;
2490}
2491
2492SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
2493 int step = SkSign32(endIndex - index);
2494 int min = SkMin32(index, endIndex);
2495 markDoneBinary(min);
2496 SkOpSpan* last;
2497 SkOpSegment* other = this;
2498 while ((other = other->nextChase(&index, step, &min, &last))) {
2499 if (other->done()) {
2500 return NULL;
2501 }
2502 other->markDoneBinary(min);
2503 }
2504 return last;
2505}
2506
2507SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
2508 int step = SkSign32(endIndex - index);
2509 int min = SkMin32(index, endIndex);
2510 markDoneUnary(min);
2511 SkOpSpan* last;
2512 SkOpSegment* other = this;
2513 while ((other = other->nextChase(&index, step, &min, &last))) {
2514 if (other->done()) {
2515 return NULL;
2516 }
2517 other->markDoneUnary(min);
2518 }
2519 return last;
2520}
2521
2522SkOpSpan* SkOpSegment::markAndChaseDoneUnary(const SkOpAngle* angle, int winding) {
2523 int index = angle->start();
2524 int endIndex = angle->end();
2525 return markAndChaseDone(index, endIndex, winding);
2526}
2527
2528SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, const int winding) {
2529 int index = angle->start();
2530 int endIndex = angle->end();
2531 int step = SkSign32(endIndex - index);
2532 int min = SkMin32(index, endIndex);
2533 markWinding(min, winding);
2534 SkOpSpan* last;
2535 SkOpSegment* other = this;
2536 while ((other = other->nextChase(&index, step, &min, &last))) {
2537 if (other->fTs[min].fWindSum != SK_MinS32) {
2538 SkASSERT(other->fTs[min].fWindSum == winding);
2539 return NULL;
2540 }
2541 other->markWinding(min, winding);
2542 }
2543 return last;
2544}
2545
2546SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
2547 int min = SkMin32(index, endIndex);
2548 int step = SkSign32(endIndex - index);
2549 markWinding(min, winding, oppWinding);
2550 SkOpSpan* last;
2551 SkOpSegment* other = this;
2552 while ((other = other->nextChase(&index, step, &min, &last))) {
2553 if (other->fTs[min].fWindSum != SK_MinS32) {
2554 SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
2555 return NULL;
2556 }
2557 other->markWinding(min, winding, oppWinding);
2558 }
2559 return last;
2560}
2561
2562SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding) {
2563 int start = angle->start();
2564 int end = angle->end();
2565 return markAndChaseWinding(start, end, winding, oppWinding);
2566}
2567
2568SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, bool activeAngle,
2569 const SkOpAngle* angle) {
2570 SkASSERT(angle->segment() == this);
2571 if (UseInnerWinding(maxWinding, sumWinding)) {
2572 maxWinding = sumWinding;
2573 }
2574 SkOpSpan* last;
2575 if (activeAngle) {
2576 last = markAndChaseWinding(angle, maxWinding);
2577 } else {
2578 last = markAndChaseDoneUnary(angle, maxWinding);
2579 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002580#if DEBUG_WINDING
2581 if (last) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002582 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
2583 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2584 SkPathOpsDebug::WindingPrintf(last->fWindSum);
2585 SkDebugf(" small=%d\n", last->fSmall);
caryclark@google.com570863f2013-09-16 15:55:01 +00002586 }
2587#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00002588 return last;
2589}
2590
2591SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
2592 int oppSumWinding, bool activeAngle, const SkOpAngle* angle) {
2593 SkASSERT(angle->segment() == this);
2594 if (UseInnerWinding(maxWinding, sumWinding)) {
2595 maxWinding = sumWinding;
2596 }
2597 if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
2598 oppMaxWinding = oppSumWinding;
2599 }
2600 SkOpSpan* last;
2601 if (activeAngle) {
2602 last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
2603 } else {
2604 last = markAndChaseDoneBinary(angle, maxWinding, oppMaxWinding);
2605 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002606#if DEBUG_WINDING
2607 if (last) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002608 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
2609 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2610 SkPathOpsDebug::WindingPrintf(last->fWindSum);
2611 SkDebugf(" small=%d\n", last->fSmall);
caryclark@google.com570863f2013-09-16 15:55:01 +00002612 }
2613#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00002614 return last;
2615}
2616
2617// FIXME: this should also mark spans with equal (x,y)
2618// This may be called when the segment is already marked done. While this
2619// wastes time, it shouldn't do any more than spin through the T spans.
2620// OPTIMIZATION: abort on first done found (assuming that this code is
2621// always called to mark segments done).
2622void SkOpSegment::markDone(int index, int winding) {
2623 // SkASSERT(!done());
2624 SkASSERT(winding);
2625 double referenceT = fTs[index].fT;
2626 int lesser = index;
2627 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2628 markOneDone(__FUNCTION__, lesser, winding);
2629 }
2630 do {
2631 markOneDone(__FUNCTION__, index, winding);
2632 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2633}
2634
2635void SkOpSegment::markDoneBinary(int index, int winding, int oppWinding) {
2636 // SkASSERT(!done());
2637 SkASSERT(winding || oppWinding);
2638 double referenceT = fTs[index].fT;
2639 int lesser = index;
2640 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2641 markOneDoneBinary(__FUNCTION__, lesser, winding, oppWinding);
2642 }
2643 do {
2644 markOneDoneBinary(__FUNCTION__, index, winding, oppWinding);
2645 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2646}
2647
2648void SkOpSegment::markDoneBinary(int index) {
2649 double referenceT = fTs[index].fT;
2650 int lesser = index;
2651 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2652 markOneDoneBinary(__FUNCTION__, lesser);
2653 }
2654 do {
2655 markOneDoneBinary(__FUNCTION__, index);
2656 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2657}
2658
2659void SkOpSegment::markDoneUnary(int index) {
2660 double referenceT = fTs[index].fT;
2661 int lesser = index;
2662 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2663 markOneDoneUnary(__FUNCTION__, lesser);
2664 }
2665 do {
2666 markOneDoneUnary(__FUNCTION__, index);
2667 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2668}
2669
2670void SkOpSegment::markOneDone(const char* funName, int tIndex, int winding) {
2671 SkOpSpan* span = markOneWinding(funName, tIndex, winding);
2672 if (!span) {
2673 return;
2674 }
2675 span->fDone = true;
2676 fDoneSpans++;
2677}
2678
2679void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
2680 SkOpSpan* span = verifyOneWinding(funName, tIndex);
2681 if (!span) {
2682 return;
2683 }
2684 span->fDone = true;
2685 fDoneSpans++;
2686}
2687
2688void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding) {
2689 SkOpSpan* span = markOneWinding(funName, tIndex, winding, oppWinding);
2690 if (!span) {
2691 return;
2692 }
2693 span->fDone = true;
2694 fDoneSpans++;
2695}
2696
2697void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
2698 SkOpSpan* span = verifyOneWindingU(funName, tIndex);
2699 if (!span) {
2700 return;
2701 }
2702 span->fDone = true;
2703 fDoneSpans++;
2704}
2705
2706SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding) {
2707 SkOpSpan& span = fTs[tIndex];
2708 if (span.fDone) {
2709 return NULL;
2710 }
2711#if DEBUG_MARK_DONE
2712 debugShowNewWinding(funName, span, winding);
2713#endif
2714 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com570863f2013-09-16 15:55:01 +00002715 SkASSERT(abs(winding) <= SkPathOpsDebug::gMaxWindSum);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002716 span.fWindSum = winding;
2717 return &span;
2718}
2719
2720SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
2721 int oppWinding) {
2722 SkOpSpan& span = fTs[tIndex];
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002723 if (span.fDone && !span.fSmall) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002724 return NULL;
2725 }
2726#if DEBUG_MARK_DONE
2727 debugShowNewWinding(funName, span, winding, oppWinding);
2728#endif
2729 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com570863f2013-09-16 15:55:01 +00002730 SkASSERT(abs(winding) <= SkPathOpsDebug::gMaxWindSum);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002731 span.fWindSum = winding;
2732 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00002733 SkASSERT(abs(oppWinding) <= SkPathOpsDebug::gMaxWindSum);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002734 span.fOppSum = oppWinding;
2735 return &span;
2736}
2737
2738// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
2739bool SkOpSegment::clockwise(int tStart, int tEnd) const {
2740 SkASSERT(fVerb != SkPath::kLine_Verb);
2741 SkPoint edge[4];
2742 subDivide(tStart, tEnd, edge);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002743 int points = SkPathOpsVerbToPoints(fVerb);
2744 double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002745 if (fVerb == SkPath::kCubic_Verb) {
2746 SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
2747 if (edge[1].fY < lesser && edge[2].fY < lesser) {
2748 SkDLine tangent1 = {{ {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} }};
2749 SkDLine tangent2 = {{ {edge[2].fX, edge[2].fY}, {edge[3].fX, edge[3].fY} }};
2750 if (SkIntersections::Test(tangent1, tangent2)) {
2751 SkPoint topPt = cubic_top(fPts, fTs[tStart].fT, fTs[tEnd].fT);
2752 sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
2753 sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
2754 return sum <= 0;
2755 }
2756 }
2757 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002758 for (int idx = 0; idx < points; ++idx){
caryclark@google.com07393ca2013-04-08 11:47:37 +00002759 sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
2760 }
2761 return sum <= 0;
2762}
2763
2764bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
2765 if (fVerb == SkPath::kLine_Verb) {
2766 return false;
2767 }
2768 if (fVerb == SkPath::kQuad_Verb) {
2769 SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
2770 return dst.monotonicInY();
2771 }
2772 SkASSERT(fVerb == SkPath::kCubic_Verb);
2773 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
2774 return dst.monotonicInY();
2775}
2776
2777bool SkOpSegment::serpentine(int tStart, int tEnd) const {
2778 if (fVerb != SkPath::kCubic_Verb) {
2779 return false;
2780 }
2781 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
2782 return dst.serpentine();
2783}
2784
2785SkOpSpan* SkOpSegment::verifyOneWinding(const char* funName, int tIndex) {
2786 SkOpSpan& span = fTs[tIndex];
2787 if (span.fDone) {
2788 return NULL;
2789 }
2790#if DEBUG_MARK_DONE
2791 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
2792#endif
2793 SkASSERT(span.fWindSum != SK_MinS32);
2794 SkASSERT(span.fOppSum != SK_MinS32);
2795 return &span;
2796}
2797
2798SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
2799 SkOpSpan& span = fTs[tIndex];
2800 if (span.fDone) {
2801 return NULL;
2802 }
2803#if DEBUG_MARK_DONE
2804 debugShowNewWinding(funName, span, span.fWindSum);
2805#endif
2806 SkASSERT(span.fWindSum != SK_MinS32);
2807 return &span;
2808}
2809
2810// note that just because a span has one end that is unsortable, that's
2811// not enough to mark it done. The other end may be sortable, allowing the
2812// span to be added.
2813// FIXME: if abs(start - end) > 1, mark intermediates as unsortable on both ends
2814void SkOpSegment::markUnsortable(int start, int end) {
2815 SkOpSpan* span = &fTs[start];
2816 if (start < end) {
2817#if DEBUG_UNSORTABLE
2818 debugShowNewWinding(__FUNCTION__, *span, 0);
2819#endif
2820 span->fUnsortableStart = true;
2821 } else {
2822 --span;
2823#if DEBUG_UNSORTABLE
2824 debugShowNewWinding(__FUNCTION__, *span, 0);
2825#endif
2826 span->fUnsortableEnd = true;
2827 }
2828 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
2829 return;
2830 }
2831 span->fDone = true;
2832 fDoneSpans++;
2833}
2834
2835void SkOpSegment::markWinding(int index, int winding) {
2836// SkASSERT(!done());
2837 SkASSERT(winding);
2838 double referenceT = fTs[index].fT;
2839 int lesser = index;
2840 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2841 markOneWinding(__FUNCTION__, lesser, winding);
2842 }
2843 do {
2844 markOneWinding(__FUNCTION__, index, winding);
2845 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2846}
2847
2848void SkOpSegment::markWinding(int index, int winding, int oppWinding) {
2849// SkASSERT(!done());
2850 SkASSERT(winding || oppWinding);
2851 double referenceT = fTs[index].fT;
2852 int lesser = index;
2853 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2854 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
2855 }
2856 do {
2857 markOneWinding(__FUNCTION__, index, winding, oppWinding);
2858 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2859}
2860
2861void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
2862 int nextDoorWind = SK_MaxS32;
2863 int nextOppWind = SK_MaxS32;
2864 if (tIndex > 0) {
2865 const SkOpSpan& below = fTs[tIndex - 1];
2866 if (approximately_negative(t - below.fT)) {
2867 nextDoorWind = below.fWindValue;
2868 nextOppWind = below.fOppValue;
2869 }
2870 }
2871 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2872 const SkOpSpan& above = fTs[tIndex + 1];
2873 if (approximately_negative(above.fT - t)) {
2874 nextDoorWind = above.fWindValue;
2875 nextOppWind = above.fOppValue;
2876 }
2877 }
2878 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2879 const SkOpSpan& below = fTs[tIndex - 1];
2880 nextDoorWind = below.fWindValue;
2881 nextOppWind = below.fOppValue;
2882 }
2883 if (nextDoorWind != SK_MaxS32) {
2884 SkOpSpan& newSpan = fTs[tIndex];
2885 newSpan.fWindValue = nextDoorWind;
2886 newSpan.fOppValue = nextOppWind;
2887 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
2888 newSpan.fDone = true;
2889 ++fDoneSpans;
2890 }
2891 }
2892}
2893
caryclark@google.com570863f2013-09-16 15:55:01 +00002894double SkOpSegment::missingNear(double t, const SkOpSegment* other, const SkPoint& startPt,
2895 const SkPoint& endPt) const {
2896 int count = this->count();
2897 for (int index = 0; index < count; ++index) {
2898 const SkOpSpan& span = this->span(index);
2899 if (span.fOther == other && span.fPt == startPt) {
2900 double midT = (t + span.fT) / 2;
2901 if (betweenPoints(midT, startPt, endPt)) {
2902 return span.fT;
2903 }
2904 }
2905 }
2906 return -1;
2907}
2908
caryclark@google.com07393ca2013-04-08 11:47:37 +00002909// return span if when chasing, two or more radiating spans are not done
2910// OPTIMIZATION: ? multiple spans is detected when there is only one valid
2911// candidate and the remaining spans have windValue == 0 (canceled by
2912// coincidence). The coincident edges could either be removed altogether,
2913// or this code could be more complicated in detecting this case. Worth it?
2914bool SkOpSegment::multipleSpans(int end) const {
2915 return end > 0 && end < fTs.count() - 1;
2916}
2917
2918bool SkOpSegment::nextCandidate(int* start, int* end) const {
2919 while (fTs[*end].fDone) {
2920 if (fTs[*end].fT == 1) {
2921 return false;
2922 }
2923 ++(*end);
2924 }
2925 *start = *end;
2926 *end = nextExactSpan(*start, 1);
2927 return true;
2928}
2929
2930SkOpSegment* SkOpSegment::nextChase(int* index, const int step, int* min, SkOpSpan** last) {
2931 int end = nextExactSpan(*index, step);
2932 SkASSERT(end >= 0);
caryclark@google.com570863f2013-09-16 15:55:01 +00002933 if (fTs[end].fSmall) {
2934 *last = NULL;
2935 return NULL;
2936 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002937 if (multipleSpans(end)) {
2938 *last = &fTs[end];
2939 return NULL;
2940 }
2941 const SkOpSpan& endSpan = fTs[end];
2942 SkOpSegment* other = endSpan.fOther;
2943 *index = endSpan.fOtherIndex;
fmalita@google.com22eb7942013-05-01 20:35:51 +00002944 SkASSERT(*index >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002945 int otherEnd = other->nextExactSpan(*index, step);
2946 SkASSERT(otherEnd >= 0);
2947 *min = SkMin32(*index, otherEnd);
caryclark@google.com570863f2013-09-16 15:55:01 +00002948 if (other->fTs[*min].fSmall) {
caryclark@google.coma5e55922013-05-07 18:51:31 +00002949 *last = NULL;
2950 return NULL;
2951 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002952 return other;
2953}
2954
2955// This has callers for two different situations: one establishes the end
2956// of the current span, and one establishes the beginning of the next span
2957// (thus the name). When this is looking for the end of the current span,
2958// coincidence is found when the beginning Ts contain -step and the end
2959// contains step. When it is looking for the beginning of the next, the
2960// first Ts found can be ignored and the last Ts should contain -step.
2961// OPTIMIZATION: probably should split into two functions
2962int SkOpSegment::nextSpan(int from, int step) const {
2963 const SkOpSpan& fromSpan = fTs[from];
2964 int count = fTs.count();
2965 int to = from;
2966 while (step > 0 ? ++to < count : --to >= 0) {
2967 const SkOpSpan& span = fTs[to];
2968 if (approximately_zero(span.fT - fromSpan.fT)) {
2969 continue;
2970 }
2971 return to;
2972 }
2973 return -1;
2974}
2975
2976// FIXME
2977// this returns at any difference in T, vs. a preset minimum. It may be
2978// that all callers to nextSpan should use this instead.
caryclark@google.com07393ca2013-04-08 11:47:37 +00002979int SkOpSegment::nextExactSpan(int from, int step) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002980 int to = from;
caryclark@google.com570863f2013-09-16 15:55:01 +00002981 if (step < 0) {
2982 const SkOpSpan& fromSpan = fTs[from];
2983 while (--to >= 0) {
2984 const SkOpSpan& span = fTs[to];
2985 if (precisely_negative(fromSpan.fT - span.fT) || span.fTiny) {
2986 continue;
2987 }
2988 return to;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002989 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002990 } else {
2991 while (fTs[from].fTiny) {
2992 from++;
2993 }
2994 const SkOpSpan& fromSpan = fTs[from];
2995 int count = fTs.count();
2996 while (++to < count) {
2997 const SkOpSpan& span = fTs[to];
2998 if (precisely_negative(span.fT - fromSpan.fT)) {
2999 continue;
3000 }
3001 return to;
3002 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003003 }
3004 return -1;
3005}
3006
3007void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
3008 int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
3009 int deltaSum = spanSign(index, endIndex);
3010 int oppDeltaSum = oppSign(index, endIndex);
3011 if (operand()) {
3012 *maxWinding = *sumSuWinding;
3013 *sumWinding = *sumSuWinding -= deltaSum;
3014 *oppMaxWinding = *sumMiWinding;
3015 *oppSumWinding = *sumMiWinding -= oppDeltaSum;
3016 } else {
3017 *maxWinding = *sumMiWinding;
3018 *sumWinding = *sumMiWinding -= deltaSum;
3019 *oppMaxWinding = *sumSuWinding;
3020 *oppSumWinding = *sumSuWinding -= oppDeltaSum;
3021 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003022 SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum);
3023 SkASSERT(abs(*oppSumWinding) <= SkPathOpsDebug::gMaxWindSum);
3024}
3025
3026void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
3027 int* maxWinding, int* sumWinding) {
3028 int deltaSum = spanSign(index, endIndex);
3029 *maxWinding = *sumMiWinding;
3030 *sumWinding = *sumMiWinding -= deltaSum;
3031 SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003032}
3033
3034// This marks all spans unsortable so that this info is available for early
3035// exclusion in find top and others. This could be optimized to only mark
3036// adjacent spans that unsortable. However, this makes it difficult to later
3037// determine starting points for edge detection in find top and the like.
caryclark@google.comd892bd82013-06-17 14:10:36 +00003038bool SkOpSegment::SortAngles(const SkTArray<SkOpAngle, true>& angles,
3039 SkTArray<SkOpAngle*, true>* angleList,
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003040 SortAngleKind orderKind) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003041 bool sortable = true;
3042 int angleCount = angles.count();
3043 int angleIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003044 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3045 const SkOpAngle& angle = angles[angleIndex];
caryclark@google.comd892bd82013-06-17 14:10:36 +00003046 angleList->push_back(const_cast<SkOpAngle*>(&angle));
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003047#if DEBUG_ANGLE
3048 (*(angleList->end() - 1))->setID(angleIndex);
3049#endif
3050 sortable &= !(angle.unsortable() || (orderKind == kMustBeOrdered_SortAngleKind
3051 && angle.unorderable()));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003052 }
3053 if (sortable) {
caryclark@google.com7dfbb072013-04-22 14:37:05 +00003054 SkTQSort<SkOpAngle>(angleList->begin(), angleList->end() - 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003055 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003056 if (angles[angleIndex].unsortable() || (orderKind == kMustBeOrdered_SortAngleKind
3057 && angles[angleIndex].unorderable())) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003058 sortable = false;
3059 break;
3060 }
3061 }
3062 }
3063 if (!sortable) {
3064 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3065 const SkOpAngle& angle = angles[angleIndex];
3066 angle.segment()->markUnsortable(angle.start(), angle.end());
3067 }
3068 }
3069 return sortable;
3070}
3071
caryclark@google.com570863f2013-09-16 15:55:01 +00003072// set segments to unsortable if angle is unsortable, but do not set all angles
3073// note that for a simple 4 way crossing, two of the edges may be orderable even though
3074// two edges are too short to be orderable.
3075// perhaps some classes of unsortable angles should make all shared angles unsortable, but
3076// simple lines that have tiny crossings are always sortable on the large ends
3077// OPTIMIZATION: check earlier when angles are added to input if any are unsortable
3078// may make sense then to mark all segments in angle sweep as unsortableStart/unsortableEnd
3079// solely for the purpose of short-circuiting future angle building around this center
3080bool SkOpSegment::SortAngles2(const SkTArray<SkOpAngle, true>& angles,
3081 SkTArray<SkOpAngle*, true>* angleList) {
3082 int angleCount = angles.count();
3083 int angleIndex;
3084 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3085 const SkOpAngle& angle = angles[angleIndex];
3086 if (angle.unsortable()) {
3087 return false;
3088 }
3089 angleList->push_back(const_cast<SkOpAngle*>(&angle));
3090#if DEBUG_ANGLE
3091 (*(angleList->end() - 1))->setID(angleIndex);
3092#endif
3093 }
3094 SkTQSort<SkOpAngle>(angleList->begin(), angleList->end() - 1);
3095 // at this point angles are sorted but individually may not be orderable
3096 // this means that only adjcent orderable segments may transfer winding
3097 return true;
3098}
3099
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003100// return true if midpoints were computed
3101bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
3102 SkASSERT(start != end);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003103 edge[0] = fTs[start].fPt;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003104 int points = SkPathOpsVerbToPoints(fVerb);
3105 edge[points] = fTs[end].fPt;
3106 if (fVerb == SkPath::kLine_Verb) {
3107 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003108 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003109 double startT = fTs[start].fT;
3110 double endT = fTs[end].fT;
3111 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
3112 // don't compute midpoints if we already have them
3113 if (fVerb == SkPath::kQuad_Verb) {
3114 edge[1] = fPts[1];
3115 return false;
3116 }
3117 SkASSERT(fVerb == SkPath::kCubic_Verb);
3118 if (start < end) {
3119 edge[1] = fPts[1];
3120 edge[2] = fPts[2];
3121 return false;
3122 }
3123 edge[1] = fPts[2];
3124 edge[2] = fPts[1];
3125 return false;
3126 }
3127 const SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[points].fX, edge[points].fY }};
3128 if (fVerb == SkPath::kQuad_Verb) {
3129 edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint();
3130 } else {
3131 SkASSERT(fVerb == SkPath::kCubic_Verb);
3132 SkDPoint ctrl[2];
3133 SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl);
3134 edge[1] = ctrl[0].asSkPoint();
3135 edge[2] = ctrl[1].asSkPoint();
3136 }
3137 return true;
3138}
3139
3140// return true if midpoints were computed
3141bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
3142 SkASSERT(start != end);
3143 (*result)[0].set(fTs[start].fPt);
3144 int points = SkPathOpsVerbToPoints(fVerb);
3145 (*result)[points].set(fTs[end].fPt);
3146 if (fVerb == SkPath::kLine_Verb) {
3147 return false;
3148 }
3149 double startT = fTs[start].fT;
3150 double endT = fTs[end].fT;
3151 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
3152 // don't compute midpoints if we already have them
3153 if (fVerb == SkPath::kQuad_Verb) {
3154 (*result)[1].set(fPts[1]);
3155 return false;
3156 }
3157 SkASSERT(fVerb == SkPath::kCubic_Verb);
3158 if (start < end) {
3159 (*result)[1].set(fPts[1]);
3160 (*result)[2].set(fPts[2]);
3161 return false;
3162 }
3163 (*result)[1].set(fPts[2]);
3164 (*result)[2].set(fPts[1]);
3165 return false;
3166 }
3167 if (fVerb == SkPath::kQuad_Verb) {
3168 (*result)[1] = SkDQuad::SubDivide(fPts, (*result)[0], (*result)[2], startT, endT);
3169 } else {
3170 SkASSERT(fVerb == SkPath::kCubic_Verb);
3171 SkDCubic::SubDivide(fPts, (*result)[0], (*result)[3], startT, endT, &(*result)[1]);
3172 }
3173 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003174}
3175
3176void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
3177 SkPoint edge[4];
3178 subDivide(start, end, edge);
reed@google.com277c3f82013-05-31 15:17:50 +00003179 (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003180}
3181
caryclark@google.com570863f2013-09-16 15:55:01 +00003182void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt,
3183 const SkPoint& startPt) {
3184 int outCount = outsidePts->count();
3185 if (outCount == 0 || endPt != (*outsidePts)[outCount - 2]) {
3186 outsidePts->push_back(endPt);
3187 outsidePts->push_back(startPt);
3188 }
3189}
3190
3191void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoint& startPt) {
3192 int outCount = outsidePts->count();
3193 if (outCount == 0 || startPt != (*outsidePts)[outCount - 1]) {
3194 outsidePts->push_back(startPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003195 }
3196}
3197
3198void SkOpSegment::undoneSpan(int* start, int* end) {
3199 size_t tCount = fTs.count();
3200 size_t index;
3201 for (index = 0; index < tCount; ++index) {
3202 if (!fTs[index].fDone) {
3203 break;
3204 }
3205 }
3206 SkASSERT(index < tCount - 1);
3207 *start = index;
3208 double startT = fTs[index].fT;
3209 while (approximately_negative(fTs[++index].fT - startT))
3210 SkASSERT(index < tCount);
3211 SkASSERT(index < tCount);
3212 *end = index;
3213}
3214
3215int SkOpSegment::updateOppWinding(int index, int endIndex) const {
3216 int lesser = SkMin32(index, endIndex);
3217 int oppWinding = oppSum(lesser);
3218 int oppSpanWinding = oppSign(index, endIndex);
3219 if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
3220 && oppWinding != SK_MaxS32) {
3221 oppWinding -= oppSpanWinding;
3222 }
3223 return oppWinding;
3224}
3225
3226int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
3227 int startIndex = angle->start();
3228 int endIndex = angle->end();
3229 return updateOppWinding(endIndex, startIndex);
3230}
3231
3232int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
3233 int startIndex = angle->start();
3234 int endIndex = angle->end();
3235 return updateOppWinding(startIndex, endIndex);
3236}
3237
3238int SkOpSegment::updateWinding(int index, int endIndex) const {
3239 int lesser = SkMin32(index, endIndex);
3240 int winding = windSum(lesser);
3241 int spanWinding = spanSign(index, endIndex);
caryclark@google.com570863f2013-09-16 15:55:01 +00003242 if (winding && UseInnerWinding(winding - spanWinding, winding)
3243 && winding != SK_MaxS32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003244 winding -= spanWinding;
3245 }
3246 return winding;
3247}
3248
3249int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
3250 int startIndex = angle->start();
3251 int endIndex = angle->end();
3252 return updateWinding(endIndex, startIndex);
3253}
3254
caryclark@google.com570863f2013-09-16 15:55:01 +00003255int SkOpSegment::updateWindingReverse(int index, int endIndex) const {
3256 int lesser = SkMin32(index, endIndex);
3257 int winding = windSum(lesser);
3258 int spanWinding = spanSign(endIndex, index);
3259 if (winding && UseInnerWindingReverse(winding - spanWinding, winding)
3260 && winding != SK_MaxS32) {
3261 winding -= spanWinding;
3262 }
3263 return winding;
3264}
3265
caryclark@google.com07393ca2013-04-08 11:47:37 +00003266int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
3267 int startIndex = angle->start();
3268 int endIndex = angle->end();
caryclark@google.com570863f2013-09-16 15:55:01 +00003269 return updateWindingReverse(endIndex, startIndex);
3270}
3271
3272// OPTIMIZATION: does the following also work, and is it any faster?
3273// return outerWinding * innerWinding > 0
3274// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
3275bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
3276 SkASSERT(outerWinding != SK_MaxS32);
3277 SkASSERT(innerWinding != SK_MaxS32);
3278 int absOut = abs(outerWinding);
3279 int absIn = abs(innerWinding);
3280 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
3281 return result;
3282}
3283
3284bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) {
3285 SkASSERT(outerWinding != SK_MaxS32);
3286 SkASSERT(innerWinding != SK_MaxS32);
3287 int absOut = abs(outerWinding);
3288 int absIn = abs(innerWinding);
3289 bool result = absOut == absIn ? true : absOut < absIn;
3290 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003291}
3292
3293int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const {
3294 if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
3295 return SK_MinS32;
3296 }
3297 int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
3298 SkASSERT(winding != SK_MinS32);
3299 int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
3300#if DEBUG_WINDING_AT_T
3301 SkDebugf("%s oldWinding=%d windValue=%d", __FUNCTION__, winding, windVal);
3302#endif
3303 // see if a + change in T results in a +/- change in X (compute x'(T))
reed@google.com277c3f82013-05-31 15:17:50 +00003304 *dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003305 if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
3306 *dx = fPts[2].fX - fPts[1].fX - *dx;
3307 }
3308 if (*dx == 0) {
3309#if DEBUG_WINDING_AT_T
3310 SkDebugf(" dx=0 winding=SK_MinS32\n");
3311#endif
3312 return SK_MinS32;
3313 }
skia.committer@gmail.coma4aced42013-07-09 07:00:56 +00003314 if (windVal < 0) { // reverse sign if opp contour traveled in reverse
caryclark@google.com07e97fc2013-07-08 17:17:02 +00003315 *dx = -*dx;
3316 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003317 if (winding * *dx > 0) { // if same signs, result is negative
3318 winding += *dx > 0 ? -windVal : windVal;
3319 }
3320#if DEBUG_WINDING_AT_T
3321 SkDebugf(" dx=%c winding=%d\n", *dx > 0 ? '+' : '-', winding);
3322#endif
3323 return winding;
3324}
3325
3326int SkOpSegment::windSum(const SkOpAngle* angle) const {
3327 int start = angle->start();
3328 int end = angle->end();
3329 int index = SkMin32(start, end);
3330 return windSum(index);
3331}
3332
3333int SkOpSegment::windValue(const SkOpAngle* angle) const {
3334 int start = angle->start();
3335 int end = angle->end();
3336 int index = SkMin32(start, end);
3337 return windValue(index);
3338}
3339
3340int SkOpSegment::windValueAt(double t) const {
3341 int count = fTs.count();
3342 for (int index = 0; index < count; ++index) {
3343 if (fTs[index].fT == t) {
3344 return fTs[index].fWindValue;
3345 }
3346 }
3347 SkASSERT(0);
3348 return 0;
3349}
3350
3351void SkOpSegment::zeroSpan(SkOpSpan* span) {
caryclark@google.coma5e55922013-05-07 18:51:31 +00003352 SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003353 span->fWindValue = 0;
3354 span->fOppValue = 0;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003355 if (span->fTiny || span->fSmall) {
3356 return;
3357 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003358 SkASSERT(!span->fDone);
3359 span->fDone = true;
3360 ++fDoneSpans;
3361}
skia.committer@gmail.com32840172013-04-09 07:01:27 +00003362
caryclark@google.com07393ca2013-04-08 11:47:37 +00003363#if DEBUG_SWAP_TOP
3364bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const {
3365 if (fVerb != SkPath::kCubic_Verb) {
3366 return false;
3367 }
3368 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3369 return dst.controlsContainedByEnds();
3370}
3371#endif
3372
3373#if DEBUG_CONCIDENT
3374// SkASSERT if pair has not already been added
3375void SkOpSegment::debugAddTPair(double t, const SkOpSegment& other, double otherT) const {
3376 for (int i = 0; i < fTs.count(); ++i) {
3377 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3378 return;
3379 }
3380 }
3381 SkASSERT(0);
3382}
3383#endif
3384
3385#if DEBUG_CONCIDENT
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003386void SkOpSegment::debugShowTs(const char* prefix) const {
3387 SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003388 int lastWind = -1;
3389 int lastOpp = -1;
3390 double lastT = -1;
3391 int i;
3392 for (i = 0; i < fTs.count(); ++i) {
3393 bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
3394 || lastOpp != fTs[i].fOppValue;
3395 if (change && lastWind >= 0) {
3396 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3397 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3398 }
3399 if (change) {
3400 SkDebugf(" [o=%d", fTs[i].fOther->fID);
3401 lastWind = fTs[i].fWindValue;
3402 lastOpp = fTs[i].fOppValue;
3403 lastT = fTs[i].fT;
3404 } else {
3405 SkDebugf(",%d", fTs[i].fOther->fID);
3406 }
3407 }
3408 if (i <= 0) {
3409 return;
3410 }
3411 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3412 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3413 if (fOperand) {
3414 SkDebugf(" operand");
3415 }
3416 if (done()) {
3417 SkDebugf(" done");
3418 }
3419 SkDebugf("\n");
3420}
3421#endif
3422
caryclark@google.coma5e55922013-05-07 18:51:31 +00003423#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
caryclark@google.com07393ca2013-04-08 11:47:37 +00003424void SkOpSegment::debugShowActiveSpans() const {
caryclark@google.com4fdbb222013-07-23 15:27:41 +00003425 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003426 if (done()) {
3427 return;
3428 }
3429#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3430 int lastId = -1;
3431 double lastT = -1;
3432#endif
3433 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003434 if (fTs[i].fDone) {
3435 continue;
3436 }
caryclark@google.coma5e55922013-05-07 18:51:31 +00003437 SkASSERT(i < fTs.count() - 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003438#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3439 if (lastId == fID && lastT == fTs[i].fT) {
3440 continue;
3441 }
3442 lastId = fID;
3443 lastT = fTs[i].fT;
3444#endif
3445 SkDebugf("%s id=%d", __FUNCTION__, fID);
3446 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
reed@google.com277c3f82013-05-31 15:17:50 +00003447 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003448 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3449 }
3450 const SkOpSpan* span = &fTs[i];
3451 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span));
3452 int iEnd = i + 1;
3453 while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) {
3454 ++iEnd;
3455 }
3456 SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT);
3457 const SkOpSegment* other = fTs[i].fOther;
3458 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3459 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3460 if (fTs[i].fWindSum == SK_MinS32) {
3461 SkDebugf("?");
3462 } else {
3463 SkDebugf("%d", fTs[i].fWindSum);
3464 }
3465 SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
3466 }
3467}
3468#endif
3469
3470
3471#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
3472void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) {
3473 const SkPoint& pt = xyAtT(&span);
3474 SkDebugf("%s id=%d", fun, fID);
3475 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
reed@google.com277c3f82013-05-31 15:17:50 +00003476 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003477 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3478 }
3479 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3480 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3481 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d windSum=",
3482 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
3483 (&span)[1].fT, winding);
3484 if (span.fWindSum == SK_MinS32) {
3485 SkDebugf("?");
3486 } else {
3487 SkDebugf("%d", span.fWindSum);
3488 }
3489 SkDebugf(" windValue=%d\n", span.fWindValue);
3490}
3491
3492void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding,
3493 int oppWinding) {
3494 const SkPoint& pt = xyAtT(&span);
3495 SkDebugf("%s id=%d", fun, fID);
3496 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
reed@google.com277c3f82013-05-31 15:17:50 +00003497 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003498 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3499 }
3500 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3501 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3502 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=",
3503 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
3504 (&span)[1].fT, winding, oppWinding);
3505 if (span.fOppSum == SK_MinS32) {
3506 SkDebugf("?");
3507 } else {
3508 SkDebugf("%d", span.fOppSum);
3509 }
3510 SkDebugf(" windSum=");
3511 if (span.fWindSum == SK_MinS32) {
3512 SkDebugf("?");
3513 } else {
3514 SkDebugf("%d", span.fWindSum);
3515 }
3516 SkDebugf(" windValue=%d\n", span.fWindValue);
3517}
3518#endif
3519
3520#if DEBUG_SORT || DEBUG_SWAP_TOP
caryclark@google.comd892bd82013-06-17 14:10:36 +00003521void SkOpSegment::debugShowSort(const char* fun, const SkTArray<SkOpAngle*, true>& angles,
3522 int first, const int contourWinding,
caryclark@google.com07e97fc2013-07-08 17:17:02 +00003523 const int oppContourWinding, bool sortable) const {
caryclark@google.com570863f2013-09-16 15:55:01 +00003524 if (--SkPathOpsDebug::gSortCount < 0) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003525 return;
3526 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003527 if (!sortable) {
3528 if (angles.count() == 0) {
3529 return;
3530 }
3531 if (first < 0) {
3532 first = 0;
3533 }
3534 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003535 SkASSERT(angles[first]->segment() == this);
caryclark@google.com07e97fc2013-07-08 17:17:02 +00003536 SkASSERT(!sortable || angles.count() > 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003537 int lastSum = contourWinding;
3538 int oppLastSum = oppContourWinding;
3539 const SkOpAngle* firstAngle = angles[first];
3540 int windSum = lastSum - spanSign(firstAngle);
3541 int oppoSign = oppSign(firstAngle);
3542 int oppWindSum = oppLastSum - oppoSign;
caryclark@google.com570863f2013-09-16 15:55:01 +00003543 #define WIND_AS_STRING(x) char x##Str[12]; \
3544 if (!SkPathOpsDebug::ValidWind(x)) strcpy(x##Str, "?"); \
3545 else SK_SNPRINTF(x##Str, sizeof(x##Str), "%d", x)
caryclark@google.com07393ca2013-04-08 11:47:37 +00003546 WIND_AS_STRING(contourWinding);
3547 WIND_AS_STRING(oppContourWinding);
3548 SkDebugf("%s %s contourWinding=%s oppContourWinding=%s sign=%d\n", fun, __FUNCTION__,
3549 contourWindingStr, oppContourWindingStr, spanSign(angles[first]));
3550 int index = first;
3551 bool firstTime = true;
3552 do {
3553 const SkOpAngle& angle = *angles[index];
3554 const SkOpSegment& segment = *angle.segment();
3555 int start = angle.start();
3556 int end = angle.end();
3557 const SkOpSpan& sSpan = segment.fTs[start];
3558 const SkOpSpan& eSpan = segment.fTs[end];
3559 const SkOpSpan& mSpan = segment.fTs[SkMin32(start, end)];
3560 bool opp = segment.fOperand ^ fOperand;
3561 if (!firstTime) {
3562 oppoSign = segment.oppSign(&angle);
3563 if (opp) {
3564 oppLastSum = oppWindSum;
3565 oppWindSum -= segment.spanSign(&angle);
3566 if (oppoSign) {
3567 lastSum = windSum;
3568 windSum -= oppoSign;
3569 }
3570 } else {
3571 lastSum = windSum;
3572 windSum -= segment.spanSign(&angle);
3573 if (oppoSign) {
3574 oppLastSum = oppWindSum;
3575 oppWindSum -= oppoSign;
3576 }
3577 }
3578 }
3579 SkDebugf("%s [%d] %s", __FUNCTION__, index,
3580 angle.unsortable() ? "*** UNSORTABLE *** " : "");
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003581 #if DEBUG_SORT_COMPACT
3582 SkDebugf("id=%d %s start=%d (%1.9g,%1.9g) end=%d (%1.9g,%1.9g)",
reed@google.com277c3f82013-05-31 15:17:50 +00003583 segment.fID, kLVerbStr[SkPathOpsVerbToPoints(segment.fVerb)],
caryclark@google.com07393ca2013-04-08 11:47:37 +00003584 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3585 segment.xAtT(&eSpan), segment.yAtT(&eSpan));
3586 #else
3587 switch (segment.fVerb) {
3588 case SkPath::kLine_Verb:
3589 SkDebugf(LINE_DEBUG_STR, LINE_DEBUG_DATA(segment.fPts));
3590 break;
3591 case SkPath::kQuad_Verb:
3592 SkDebugf(QUAD_DEBUG_STR, QUAD_DEBUG_DATA(segment.fPts));
3593 break;
3594 case SkPath::kCubic_Verb:
3595 SkDebugf(CUBIC_DEBUG_STR, CUBIC_DEBUG_DATA(segment.fPts));
3596 break;
3597 default:
3598 SkASSERT(0);
3599 }
3600 SkDebugf(" tStart=%1.9g tEnd=%1.9g", sSpan.fT, eSpan.fT);
3601 #endif
3602 SkDebugf(" sign=%d windValue=%d windSum=", angle.sign(), mSpan.fWindValue);
caryclark@google.com570863f2013-09-16 15:55:01 +00003603 SkPathOpsDebug::WindingPrintf(mSpan.fWindSum);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003604 int last, wind;
3605 if (opp) {
3606 last = oppLastSum;
3607 wind = oppWindSum;
3608 } else {
3609 last = lastSum;
3610 wind = windSum;
3611 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003612 bool useInner = SkPathOpsDebug::ValidWind(last) && SkPathOpsDebug::ValidWind(wind)
3613 && UseInnerWinding(last, wind);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003614 WIND_AS_STRING(last);
3615 WIND_AS_STRING(wind);
3616 WIND_AS_STRING(lastSum);
3617 WIND_AS_STRING(oppLastSum);
3618 WIND_AS_STRING(windSum);
3619 WIND_AS_STRING(oppWindSum);
3620 #undef WIND_AS_STRING
3621 if (!oppoSign) {
3622 SkDebugf(" %s->%s (max=%s)", lastStr, windStr, useInner ? windStr : lastStr);
3623 } else {
3624 SkDebugf(" %s->%s (%s->%s)", lastStr, windStr, opp ? lastSumStr : oppLastSumStr,
3625 opp ? windSumStr : oppWindSumStr);
3626 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003627 SkDebugf(" done=%d unord=%d small=%d tiny=%d opp=%d\n",
3628 mSpan.fDone, angle.unorderable(), mSpan.fSmall, mSpan.fTiny, opp);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003629 ++index;
3630 if (index == angles.count()) {
3631 index = 0;
3632 }
3633 if (firstTime) {
3634 firstTime = false;
3635 }
3636 } while (index != first);
3637}
3638
caryclark@google.comd892bd82013-06-17 14:10:36 +00003639void SkOpSegment::debugShowSort(const char* fun, const SkTArray<SkOpAngle*, true>& angles,
caryclark@google.com07e97fc2013-07-08 17:17:02 +00003640 int first, bool sortable) {
caryclark@google.com570863f2013-09-16 15:55:01 +00003641 if (!sortable) {
3642 if (angles.count() == 0) {
3643 return;
3644 }
3645 if (first < 0) {
3646 first = 0;
3647 }
3648 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003649 const SkOpAngle* firstAngle = angles[first];
3650 const SkOpSegment* segment = firstAngle->segment();
3651 int winding = segment->updateWinding(firstAngle);
3652 int oppWinding = segment->updateOppWinding(firstAngle);
caryclark@google.com07e97fc2013-07-08 17:17:02 +00003653 debugShowSort(fun, angles, first, winding, oppWinding, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003654}
3655
3656#endif
3657
3658#if DEBUG_SHOW_WINDING
3659int SkOpSegment::debugShowWindingValues(int slotCount, int ofInterest) const {
3660 if (!(1 << fID & ofInterest)) {
3661 return 0;
3662 }
3663 int sum = 0;
caryclark@google.comd892bd82013-06-17 14:10:36 +00003664 SkTArray<char, true> slots(slotCount * 2);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003665 memset(slots.begin(), ' ', slotCount * 2);
3666 for (int i = 0; i < fTs.count(); ++i) {
3667 // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
3668 // continue;
3669 // }
3670 sum += fTs[i].fWindValue;
3671 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
3672 sum += fTs[i].fOppValue;
3673 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
3674 }
3675 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
3676 slots.begin() + slotCount);
3677 return sum;
3678}
3679#endif
caryclark@google.com4fdbb222013-07-23 15:27:41 +00003680
3681void SkOpSegment::debugValidate() const {
3682#if DEBUG_VALIDATE
3683 int count = fTs.count();
3684 SkASSERT(count >= 2);
3685 SkASSERT(fTs[0].fT == 0);
3686 SkASSERT(fTs[count - 1].fT == 1);
3687 int done = 0;
3688 double t = -1;
3689 for (int i = 0; i < count; ++i) {
3690 const SkOpSpan& span = fTs[i];
3691 SkASSERT(t <= span.fT);
3692 t = span.fT;
3693 int otherIndex = span.fOtherIndex;
3694 const SkOpSegment* other = span.fOther;
3695 const SkOpSpan& otherSpan = other->fTs[otherIndex];
3696 SkASSERT(otherSpan.fPt == span.fPt);
3697 SkASSERT(otherSpan.fOtherT == t);
3698 SkASSERT(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]);
3699 done += span.fDone;
3700 }
3701 SkASSERT(done == fDoneSpans);
3702#endif
3703}
caryclark@google.com570863f2013-09-16 15:55:01 +00003704
3705#ifdef SK_DEBUG
3706void SkOpSegment::dumpPts() const {
3707 int last = SkPathOpsVerbToPoints(fVerb);
3708 SkDebugf("{{");
3709 int index = 0;
3710 do {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003711 SkDPoint::dump(fPts[index]);
caryclark@google.com570863f2013-09-16 15:55:01 +00003712 SkDebugf(", ");
3713 } while (++index < last);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003714 SkDPoint::dump(fPts[index]);
caryclark@google.com570863f2013-09-16 15:55:01 +00003715 SkDebugf("}}\n");
3716}
3717
3718void SkOpSegment::dumpDPts() const {
3719 int count = SkPathOpsVerbToPoints(fVerb);
3720 SkDebugf("{{");
3721 int index = 0;
3722 do {
3723 SkDPoint dPt = {fPts[index].fX, fPts[index].fY};
3724 dPt.dump();
3725 if (index != count) {
3726 SkDebugf(", ");
3727 }
3728 } while (++index <= count);
3729 SkDebugf("}}\n");
3730}
3731
3732void SkOpSegment::dumpSpans() const {
3733 int count = this->count();
3734 for (int index = 0; index < count; ++index) {
3735 const SkOpSpan& span = this->span(index);
3736 SkDebugf("[%d] ", index);
3737 span.dump();
3738 }
3739}
3740#endif