blob: 727da4a5f5584d9db08ce87abc04252e17a46e06 [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
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000023// miTo=0 miTo=1 miTo=0 miTo=1
24// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
caryclark@google.com07393ca2013-04-08 11:47:37 +000025// 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
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000040const SkOpAngle* SkOpSegment::activeAngle(int index, int* start, int* end, bool* done,
41 bool* sortable) const {
42 if (const SkOpAngle* result = activeAngleInner(index, start, end, done, sortable)) {
43 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +000044 }
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)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000049 if (const SkOpAngle* result = activeAngleOther(lesser, start, end, done, sortable)) {
50 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +000051 }
52 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000053 do {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000054 if (const SkOpAngle* result = activeAngleOther(index, start, end, done, sortable)) {
55 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +000056 }
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));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000065 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +000066}
67
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000068const SkOpAngle* SkOpSegment::activeAngleInner(int index, int* start, int* end, bool* done,
69 bool* sortable) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +000070 int next = nextExactSpan(index, 1);
71 if (next > 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000072 const SkOpSpan& upSpan = fTs[index];
73 if (upSpan.fUnsortableStart) {
74 *sortable = false;
75 return NULL;
76 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000077 if (upSpan.fWindValue || upSpan.fOppValue) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000078 if (*end < 0) {
79 *start = index;
80 *end = next;
caryclark@google.com07393ca2013-04-08 11:47:37 +000081 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000082 if (!upSpan.fDone && !upSpan.fUnsortableEnd) {
83 if (upSpan.fWindSum != SK_MinS32) {
84 return spanToAngle(index, next);
85 }
86 *done = false;
87 }
88 } else {
89 SkASSERT(upSpan.fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +000090 }
91 }
92 int prev = nextExactSpan(index, -1);
93 // edge leading into junction
94 if (prev >= 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000095 const SkOpSpan& downSpan = fTs[prev];
96 if (downSpan.fUnsortableEnd) {
97 *sortable = false;
98 return NULL;
99 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000100 if (downSpan.fWindValue || downSpan.fOppValue) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000101 if (*end < 0) {
102 *start = index;
103 *end = prev;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000104 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000105 if (!downSpan.fDone) {
106 if (downSpan.fWindSum != SK_MinS32) {
107 return spanToAngle(index, prev);
108 }
109 *done = false;
110 }
111 } else {
112 SkASSERT(downSpan.fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000113 }
114 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000115 return NULL;
116}
117
118const SkOpAngle* SkOpSegment::activeAngleOther(int index, int* start, int* end, bool* done,
119 bool* sortable) const {
120 const SkOpSpan* span = &fTs[index];
121 SkOpSegment* other = span->fOther;
122 int oIndex = span->fOtherIndex;
123 return other->activeAngleInner(oIndex, start, end, done, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000124}
125
126SkPoint SkOpSegment::activeLeftTop(bool onlySortable, int* firstT) const {
127 SkASSERT(!done());
128 SkPoint topPt = {SK_ScalarMax, SK_ScalarMax};
129 int count = fTs.count();
130 // see if either end is not done since we want smaller Y of the pair
131 bool lastDone = true;
132 bool lastUnsortable = false;
133 double lastT = -1;
134 for (int index = 0; index < count; ++index) {
135 const SkOpSpan& span = fTs[index];
136 if (onlySortable && (span.fUnsortableStart || lastUnsortable)) {
137 goto next;
138 }
139 if (span.fDone && lastDone) {
140 goto next;
141 }
142 if (approximately_negative(span.fT - lastT)) {
143 goto next;
144 }
145 {
146 const SkPoint& xy = xyAtT(&span);
147 if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
148 topPt = xy;
149 if (firstT) {
150 *firstT = index;
151 }
152 }
153 if (fVerb != SkPath::kLine_Verb && !lastDone) {
reed@google.com277c3f82013-05-31 15:17:50 +0000154 SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000155 if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
156 && topPt.fX > curveTop.fX)) {
157 topPt = curveTop;
158 if (firstT) {
159 *firstT = index;
160 }
161 }
162 }
163 lastT = span.fT;
164 }
165next:
166 lastDone = span.fDone;
167 lastUnsortable = span.fUnsortableEnd;
168 }
169 return topPt;
170}
171
172bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
173 int sumMiWinding = updateWinding(endIndex, index);
174 int sumSuWinding = updateOppWinding(endIndex, index);
175 if (fOperand) {
176 SkTSwap<int>(sumMiWinding, sumSuWinding);
177 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000178 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000179}
180
181bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000182 int* sumMiWinding, int* sumSuWinding) {
183 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000184 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000185 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000186 bool miFrom;
187 bool miTo;
188 bool suFrom;
189 bool suTo;
190 if (operand()) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000191 miFrom = (oppMaxWinding & xorMiMask) != 0;
192 miTo = (oppSumWinding & xorMiMask) != 0;
193 suFrom = (maxWinding & xorSuMask) != 0;
194 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000195 } else {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000196 miFrom = (maxWinding & xorMiMask) != 0;
197 miTo = (sumWinding & xorMiMask) != 0;
198 suFrom = (oppMaxWinding & xorSuMask) != 0;
199 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000200 }
201 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
202#if DEBUG_ACTIVE_OP
203 SkDebugf("%s op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n", __FUNCTION__,
caryclark@google.com570863f2013-09-16 15:55:01 +0000204 SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000205#endif
206 return result;
207}
208
209bool SkOpSegment::activeWinding(int index, int endIndex) {
210 int sumWinding = updateWinding(endIndex, index);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000211 return activeWinding(index, endIndex, &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000212}
213
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000214bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) {
215 int maxWinding;
216 setUpWinding(index, endIndex, &maxWinding, sumWinding);
217 bool from = maxWinding != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000218 bool to = *sumWinding != 0;
219 bool result = gUnaryActiveEdge[from][to];
220 return result;
221}
222
caryclark@google.com570863f2013-09-16 15:55:01 +0000223void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt,
224 SkOpSegment* other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000225 int tIndex = -1;
226 int tCount = fTs.count();
227 int oIndex = -1;
228 int oCount = other->fTs.count();
229 do {
230 ++tIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000231 } while (startPt != fTs[tIndex].fPt && tIndex < tCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000232 int tIndexStart = tIndex;
233 do {
234 ++oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000235 } while (endPt != other->fTs[oIndex].fPt && oIndex < oCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000236 int oIndexStart = oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000237 const SkPoint* nextPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000238 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000239 nextPt = &fTs[++tIndex].fPt;
240 SkASSERT(fTs[tIndex].fT < 1 || startPt != *nextPt);
241 } while (startPt == *nextPt);
242 double nextT = fTs[tIndex].fT;
243 const SkPoint* oNextPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000244 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000245 oNextPt = &other->fTs[++oIndex].fPt;
246 SkASSERT(other->fTs[oIndex].fT < 1 || endPt != *oNextPt);
247 } while (endPt == *oNextPt);
248 double oNextT = other->fTs[oIndex].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000249 // at this point, spans before and after are at:
250 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
251 // if tIndexStart == 0, no prior span
252 // if nextT == 1, no following span
253
254 // advance the span with zero winding
255 // if the following span exists (not past the end, non-zero winding)
256 // connect the two edges
257 if (!fTs[tIndexStart].fWindValue) {
258 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
259#if DEBUG_CONCIDENT
260 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
261 __FUNCTION__, fID, other->fID, tIndexStart - 1,
262 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
263 xyAtT(tIndexStart).fY);
264#endif
265 addTPair(fTs[tIndexStart].fT, other, other->fTs[oIndex].fT, false,
266 fTs[tIndexStart].fPt);
267 }
268 if (nextT < 1 && fTs[tIndex].fWindValue) {
269#if DEBUG_CONCIDENT
270 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
271 __FUNCTION__, fID, other->fID, tIndex,
272 fTs[tIndex].fT, xyAtT(tIndex).fX,
273 xyAtT(tIndex).fY);
274#endif
275 addTPair(fTs[tIndex].fT, other, other->fTs[oIndexStart].fT, false, fTs[tIndex].fPt);
276 }
277 } else {
278 SkASSERT(!other->fTs[oIndexStart].fWindValue);
279 if (oIndexStart > 0 && other->fTs[oIndexStart - 1].fWindValue) {
280#if DEBUG_CONCIDENT
281 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
282 __FUNCTION__, fID, other->fID, oIndexStart - 1,
283 other->fTs[oIndexStart].fT, other->xyAtT(oIndexStart).fX,
284 other->xyAtT(oIndexStart).fY);
285 other->debugAddTPair(other->fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
286#endif
287 }
288 if (oNextT < 1 && other->fTs[oIndex].fWindValue) {
289#if DEBUG_CONCIDENT
290 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
291 __FUNCTION__, fID, other->fID, oIndex,
292 other->fTs[oIndex].fT, other->xyAtT(oIndex).fX,
293 other->xyAtT(oIndex).fY);
294 other->debugAddTPair(other->fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
295#endif
296 }
297 }
298}
299
caryclark@google.com570863f2013-09-16 15:55:01 +0000300void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
301 SkOpSegment* other) {
302 // walk this to startPt
303 // walk other to startPt
caryclark@google.com07393ca2013-04-08 11:47:37 +0000304 // if either is > 0, add a pointer to the other, copying adjacent winding
305 int tIndex = -1;
306 int oIndex = -1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000307 do {
308 ++tIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000309 } while (startPt != fTs[tIndex].fPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000310 int ttIndex = tIndex;
311 bool checkOtherTMatch = false;
312 do {
313 const SkOpSpan& span = fTs[ttIndex];
314 if (startPt != span.fPt) {
315 break;
316 }
317 if (span.fOther == other && span.fPt == startPt) {
318 checkOtherTMatch = true;
319 break;
320 }
321 } while (++ttIndex < count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000322 do {
323 ++oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000324 } while (startPt != other->fTs[oIndex].fPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000325 bool skipAdd = false;
326 if (checkOtherTMatch) {
327 int ooIndex = oIndex;
328 do {
329 const SkOpSpan& oSpan = other->fTs[ooIndex];
330 if (startPt != oSpan.fPt) {
331 break;
332 }
333 if (oSpan.fT == fTs[ttIndex].fOtherT) {
334 skipAdd = true;
335 break;
336 }
337 } while (++ooIndex < other->count());
338 }
339 if ((tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) && !skipAdd) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000340 addTPair(fTs[tIndex].fT, other, other->fTs[oIndex].fT, false, startPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000341 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000342 SkPoint nextPt = startPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000343 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000344 const SkPoint* workPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000345 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000346 workPt = &fTs[++tIndex].fPt;
347 } while (nextPt == *workPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000348 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000349 workPt = &other->fTs[++oIndex].fPt;
350 } while (nextPt == *workPt);
351 nextPt = *workPt;
352 double tStart = fTs[tIndex].fT;
353 double oStart = other->fTs[oIndex].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000354 if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) {
355 break;
356 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000357 addTPair(tStart, other, oStart, false, nextPt);
358 } while (endPt != nextPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000359}
360
361void SkOpSegment::addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
362 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
363 fBounds.setCubicBounds(pts);
364}
365
366void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active) const {
367 SkPoint edge[4];
368 const SkPoint* ePtr;
369 int lastT = fTs.count() - 1;
370 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
371 ePtr = fPts;
372 } else {
373 // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
374 subDivide(start, end, edge);
375 ePtr = edge;
376 }
377 if (active) {
378 bool reverse = ePtr == fPts && start != 0;
379 if (reverse) {
reed@google.com277c3f82013-05-31 15:17:50 +0000380 path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000381 switch (fVerb) {
382 case SkPath::kLine_Verb:
383 path->deferredLine(ePtr[0]);
384 break;
385 case SkPath::kQuad_Verb:
386 path->quadTo(ePtr[1], ePtr[0]);
387 break;
388 case SkPath::kCubic_Verb:
389 path->cubicTo(ePtr[2], ePtr[1], ePtr[0]);
390 break;
391 default:
392 SkASSERT(0);
393 }
394 // return ePtr[0];
395 } else {
396 path->deferredMoveLine(ePtr[0]);
397 switch (fVerb) {
398 case SkPath::kLine_Verb:
399 path->deferredLine(ePtr[1]);
400 break;
401 case SkPath::kQuad_Verb:
402 path->quadTo(ePtr[1], ePtr[2]);
403 break;
404 case SkPath::kCubic_Verb:
405 path->cubicTo(ePtr[1], ePtr[2], ePtr[3]);
406 break;
407 default:
408 SkASSERT(0);
409 }
410 }
411 }
reed@google.com277c3f82013-05-31 15:17:50 +0000412 // return ePtr[SkPathOpsVerbToPoints(fVerb)];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000413}
414
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000415void SkOpSegment::addEndSpan(int endIndex) {
416 int spanCount = fTs.count();
417 int angleIndex = fAngles.count();
418 int startIndex = endIndex - 1;
419 while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) {
420 ++startIndex;
421 SkASSERT(startIndex < spanCount - 1);
422 ++endIndex;
423 }
424 fAngles.push_back().set(this, spanCount - 1, startIndex);
425#if DEBUG_ANGLE
426 fAngles.back().setID(angleIndex);
427 debugCheckPointsEqualish(endIndex, spanCount);
428#endif
429 setFromAngleIndex(endIndex, angleIndex);
430}
431
432void SkOpSegment::setFromAngleIndex(int endIndex, int angleIndex) {
433 int spanCount = fTs.count();
434 do {
435 fTs[endIndex].fFromAngleIndex = angleIndex;
436 } while (++endIndex < spanCount);
437}
438
caryclark@google.com07393ca2013-04-08 11:47:37 +0000439void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
440 init(pts, SkPath::kLine_Verb, operand, evenOdd);
441 fBounds.set(pts, 2);
442}
443
444// add 2 to edge or out of range values to get T extremes
445void SkOpSegment::addOtherT(int index, double otherT, int otherIndex) {
446 SkOpSpan& span = fTs[index];
caryclark@google.com03610322013-04-18 15:58:21 +0000447 if (precisely_zero(otherT)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000448 otherT = 0;
caryclark@google.com03610322013-04-18 15:58:21 +0000449 } else if (precisely_equal(otherT, 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000450 otherT = 1;
451 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000452 span.fOtherT = otherT;
453 span.fOtherIndex = otherIndex;
454}
455
456void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
457 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
458 fBounds.setQuadBounds(pts);
459}
460
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000461int SkOpSegment::addSingletonAngleDown(int endIndex, SkOpSegment** otherPtr) {
462 int startIndex = findEndSpan(endIndex);
463 SkASSERT(startIndex > 0);
464 int spanIndex = endIndex - 1;
465 fSingletonAngles.push_back().set(this, spanIndex, startIndex - 1);
466 setFromAngleIndex(startIndex, fAngles.count() + fSingletonAngles.count() - 1);
467 SkOpSegment* other;
468 do {
469 other = fTs[spanIndex].fOther;
470 if (other->fTs[0].fWindValue) {
471 break;
472 }
473 --spanIndex;
474 SkASSERT(fTs[spanIndex].fT == 1);
475 } while (true);
476 int oEndIndex = other->findStartSpan(0);
477 SkASSERT(oEndIndex > 0);
478 int otherIndex = other->fSingletonAngles.count();
479 other->fSingletonAngles.push_back().set(other, 0, oEndIndex);
480 other->setToAngleIndex(oEndIndex, other->fAngles.count() + otherIndex);
481 *otherPtr = other;
482 return otherIndex;
483}
484
485int SkOpSegment::addSingletonAngleUp(int start, SkOpSegment** otherPtr) {
486 int endIndex = findStartSpan(start);
487 SkASSERT(endIndex > 0);
488 int thisIndex = fSingletonAngles.count();
489 fSingletonAngles.push_back().set(this, start, endIndex);
490 setToAngleIndex(endIndex, fAngles.count() + thisIndex);
491 int spanIndex = start;
492 SkOpSegment* other;
493 int oCount, oStartIndex;
494 do {
495 other = fTs[spanIndex].fOther;
496 oCount = other->count();
497 oStartIndex = other->findEndSpan(oCount);
498 SkASSERT(oStartIndex > 0);
499 if (other->fTs[oStartIndex - 1].fWindValue) {
500 break;
501 }
502 ++spanIndex;
503 SkASSERT(fTs[spanIndex].fT == 0);
504 } while (true);
505 int otherIndex = other->fSingletonAngles.count();
506 other->fSingletonAngles.push_back().set(other, oCount - 1, oStartIndex - 1);
507 other->setFromAngleIndex(oStartIndex, other->fAngles.count() + otherIndex);
508 *otherPtr = other;
509 return otherIndex;
510}
511
512SkOpAngle* SkOpSegment::addSingletonAngles(int start, int step) {
513 int thisIndex = fSingletonAngles.count();
514 SkOpSegment* other;
515 int otherIndex;
516 if (step > 0) {
517 otherIndex = addSingletonAngleUp(start, &other);
518 } else {
519 otherIndex = addSingletonAngleDown(start + 1, &other);
520 }
521 fSingletonAngles[thisIndex].insert(&other->fSingletonAngles[otherIndex]);
522#if DEBUG_ANGLE
523 fSingletonAngles[thisIndex].setID(fAngles.count() + thisIndex);
524 other->fSingletonAngles[otherIndex].setID(other->fAngles.count() + otherIndex);
525#endif
526 return &fSingletonAngles[thisIndex];
527}
528
529void SkOpSegment::addStartSpan(int endIndex) {
530 int angleIndex = fAngles.count();
531 int index = 0;
532 fAngles.push_back().set(this, index, endIndex);
533#if DEBUG_ANGLE
534 fAngles.back().setID(angleIndex);
535 debugCheckPointsEqualish(index, endIndex);
536#endif
537 setToAngleIndex(endIndex, angleIndex);
538}
539
540void SkOpSegment::setToAngleIndex(int endIndex, int angleIndex) {
541 int index = 0;
542 do {
543 fTs[index].fToAngleIndex = angleIndex;
544 } while (++index < endIndex);
545}
546
caryclark@google.com07393ca2013-04-08 11:47:37 +0000547 // Defer all coincident edge processing until
548 // after normal intersections have been computed
549
550// no need to be tricky; insert in normal T order
551// resolve overlapping ts when considering coincidence later
552
553 // add non-coincident intersection. Resulting edges are sorted in T.
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000554int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000555 SkASSERT(this != other || fVerb == SkPath::kCubic_Verb);
556 #if 0 // this needs an even rougher association to be useful
557 SkASSERT(SkDPoint::RoughlyEqual(ptAtT(newT), pt));
558 #endif
caryclark@google.com03610322013-04-18 15:58:21 +0000559 if (precisely_zero(newT)) {
560 newT = 0;
561 } else if (precisely_equal(newT, 1)) {
562 newT = 1;
563 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000564 // FIXME: in the pathological case where there is a ton of intercepts,
565 // binary search?
566 int insertedAt = -1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000567 int tCount = fTs.count();
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000568 const SkPoint& firstPt = fPts[0];
569 const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000570 for (int index = 0; index < tCount; ++index) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000571 // OPTIMIZATION: if there are three or more identical Ts, then
572 // the fourth and following could be further insertion-sorted so
573 // that all the edges are clockwise or counterclockwise.
574 // This could later limit segment tests to the two adjacent
575 // neighbors, although it doesn't help with determining which
576 // circular direction to go in.
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000577 const SkOpSpan& span = fTs[index];
578 if (newT < span.fT) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000579 insertedAt = index;
580 break;
581 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000582 if (newT == span.fT) {
583 if (pt == span.fPt) {
584 insertedAt = index;
585 break;
586 }
587 if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
588 insertedAt = index;
589 break;
590 }
591 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000592 }
593 SkOpSpan* span;
594 if (insertedAt >= 0) {
595 span = fTs.insert(insertedAt);
596 } else {
597 insertedAt = tCount;
598 span = fTs.append();
599 }
600 span->fT = newT;
601 span->fOther = other;
602 span->fPt = pt;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000603#if 0
604 // cubics, for instance, may not be exact enough to satisfy this check (e.g., cubicOp69d)
605 SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX)
606 && approximately_equal(xyAtT(newT).fY, pt.fY));
607#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000608 span->fFromAngleIndex = -1;
609 span->fToAngleIndex = -1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000610 span->fWindSum = SK_MinS32;
611 span->fOppSum = SK_MinS32;
612 span->fWindValue = 1;
613 span->fOppValue = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000614 span->fChased = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000615 if ((span->fDone = newT == 1)) {
616 ++fDoneSpans;
617 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000618 span->fLoop = false;
619 span->fSmall = false;
620 span->fTiny = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000621 span->fUnsortableStart = false;
622 span->fUnsortableEnd = false;
623 int less = -1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000624// find range of spans with nearly the same point as this one
625 while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, pt)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000626 if (fVerb == SkPath::kCubic_Verb) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000627 double tInterval = newT - span[less].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000628 double tMid = newT - tInterval / 2;
629 SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
630 if (!midPt.approximatelyEqual(xyAtT(span))) {
631 break;
632 }
633 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000634 --less;
635 }
636 int more = 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000637 while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].fPt, pt)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000638 if (fVerb == SkPath::kCubic_Verb) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000639 double tEndInterval = span[more].fT - newT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000640 double tMid = newT - tEndInterval / 2;
641 SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
642 if (!midEndPt.approximatelyEqual(xyAtT(span))) {
643 break;
644 }
645 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000646 ++more;
647 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000648 ++less;
649 --more;
650 while (more - 1 > less && span[more].fPt == span[more - 1].fPt
651 && span[more].fT == span[more - 1].fT) {
652 --more;
653 }
654 if (less == more) {
655 return insertedAt;
656 }
657 if (precisely_negative(span[more].fT - span[less].fT)) {
658 return insertedAt;
659 }
660// if the total range of t values is big enough, mark all tiny
661 bool tiny = span[less].fPt == span[more].fPt;
662 int index = less;
663 do {
664 fSmall = span[index].fSmall = true;
665 fTiny |= span[index].fTiny = tiny;
666 if (!span[index].fDone) {
667 span[index].fDone = true;
668 ++fDoneSpans;
669 }
670 } while (++index < more);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000671 return insertedAt;
672}
673
674// set spans from start to end to decrement by one
675// note this walks other backwards
caryclark@google.coma5e55922013-05-07 18:51:31 +0000676// FIXME: there's probably an edge case that can be constructed where
caryclark@google.com07393ca2013-04-08 11:47:37 +0000677// two span in one segment are separated by float epsilon on one span but
678// not the other, if one segment is very small. For this
679// case the counts asserted below may or may not be enough to separate the
680// spans. Even if the counts work out, what if the spans aren't correctly
681// sorted? It feels better in such a case to match the span's other span
682// pointer since both coincident segments must contain the same spans.
caryclark@google.coma5e55922013-05-07 18:51:31 +0000683// FIXME? It seems that decrementing by one will fail for complex paths that
684// have three or more coincident edges. Shouldn't this subtract the difference
685// between the winding values?
caryclark@google.com570863f2013-09-16 15:55:01 +0000686/* |--> |-->
687this 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4
688other 2<<<<1<<<<0 2<<<<1<<<<0 2<<<<1<<<<0
689 ^ ^ <--| <--|
690 startPt endPt test/oTest first pos test/oTest final pos
691*/
692void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000693 bool binary = fOperand != other->fOperand;
694 int index = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000695 while (startPt != fTs[index].fPt) {
696 SkASSERT(index < fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000697 ++index;
698 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000699 while (index > 0 && precisely_equal(fTs[index].fT, fTs[index - 1].fT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000700 --index;
701 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000702 int oIndex = other->fTs.count();
caryclark@google.com570863f2013-09-16 15:55:01 +0000703 while (startPt != other->fTs[--oIndex].fPt) { // look for startPt match
704 SkASSERT(oIndex > 0);
705 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000706 double oStartT = other->fTs[oIndex].fT;
707 // look for first point beyond match
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000708 while (startPt == other->fTs[--oIndex].fPt || precisely_equal(oStartT, other->fTs[oIndex].fT)) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000709 SkASSERT(oIndex > 0);
710 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000711 SkOpSpan* test = &fTs[index];
712 SkOpSpan* oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +0000713 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
714 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000715 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000716 SkASSERT(test->fT < 1);
717 SkASSERT(oTest->fT < 1);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000718 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000719 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000720 bool bigger = test->fWindValue >= oTest->fWindValue;
caryclark@google.com570863f2013-09-16 15:55:01 +0000721 const SkPoint& testPt = test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000722 double testT = test->fT;
caryclark@google.com570863f2013-09-16 15:55:01 +0000723 const SkPoint& oTestPt = oTest->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000724 double oTestT = oTest->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000725 do {
726 if (decrement) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000727 if (binary && bigger) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000728 test->fOppValue--;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000729 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +0000730 decrementSpan(test);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000731 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000732 } else if (track) {
733 TrackOutsidePair(&outsidePts, testPt, oTestPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000734 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000735 SkASSERT(index < fTs.count() - 1);
736 test = &fTs[++index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000737 } while (testPt == test->fPt || precisely_equal(testT, test->fT));
caryclark@google.com570863f2013-09-16 15:55:01 +0000738 SkDEBUGCODE(int originalWindValue = oTest->fWindValue);
739 do {
740 SkASSERT(oTest->fT < 1);
741 SkASSERT(originalWindValue == oTest->fWindValue);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000742 if (decrement) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000743 if (binary && !bigger) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000744 oTest->fOppValue--;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000745 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +0000746 other->decrementSpan(oTest);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000747 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000748 } else if (track) {
749 TrackOutsidePair(&oOutsidePts, oTestPt, testPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000750 }
751 if (!oIndex) {
752 break;
753 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000754 oTest = &other->fTs[--oIndex];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000755 } while (oTestPt == oTest->fPt || precisely_equal(oTestT, oTest->fT));
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000756 } while (endPt != test->fPt && test->fT < 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000757 // FIXME: determine if canceled edges need outside ts added
caryclark@google.com570863f2013-09-16 15:55:01 +0000758 int outCount = outsidePts.count();
759 if (!done() && outCount) {
760 addCancelOutsides(outsidePts[0], outsidePts[1], other);
761 if (outCount > 2) {
762 addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000763 }
764 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000765 if (!other->done() && oOutsidePts.count()) {
766 other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000767 }
768}
769
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000770int SkOpSegment::addSelfT(const SkPoint& pt, double newT) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000771 // if the tail nearly intersects itself but not quite, the caller records this separately
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000772 int result = addT(this, pt, newT);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000773 SkOpSpan* span = &fTs[result];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000774 fLoop = span->fLoop = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000775 return result;
776}
777
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000778void SkOpSegment::addSimpleAngle(int index) {
779 if (index == 0) {
780 SkASSERT(!fTs[index].fTiny && fTs[index + 1].fT > 0);
781 addStartSpan(1);
782 } else {
783 SkASSERT(!fTs[index - 1].fTiny && fTs[index - 1].fT < 1);
784 addEndSpan(index);
785 }
786 SkOpSpan& span = fTs[index];
787 SkOpSegment* other = span.fOther;
788 SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
789 SkOpAngle* angle, * oAngle;
790 if (index == 0) {
791 SkASSERT(span.fOtherIndex - 1 >= 0);
792 SkASSERT(span.fOtherT == 1);
793 SkDEBUGCODE(SkOpSpan& oPrior = other->fTs[span.fOtherIndex - 1]);
794 SkASSERT(!oPrior.fTiny && oPrior.fT < 1);
795 other->addEndSpan(span.fOtherIndex);
796 angle = &this->angle(span.fToAngleIndex);
797 oAngle = &other->angle(oSpan.fFromAngleIndex);
798 } else {
799 SkASSERT(span.fOtherIndex + 1 < other->count());
800 SkASSERT(span.fOtherT == 0);
801 SkASSERT(!oSpan.fTiny && (other->fTs[span.fOtherIndex + 1].fT > 0
802 || (other->fTs[span.fOtherIndex + 1].fFromAngleIndex < 0
803 && other->fTs[span.fOtherIndex + 1].fToAngleIndex < 0)));
804 int oIndex = 1;
805 do {
806 const SkOpSpan& osSpan = other->span(oIndex);
807 if (osSpan.fFromAngleIndex >= 0 || osSpan.fT > 0) {
808 break;
809 }
810 ++oIndex;
811 SkASSERT(oIndex < other->count());
812 } while (true);
813 other->addStartSpan(oIndex);
814 angle = &this->angle(span.fFromAngleIndex);
815 oAngle = &other->angle(oSpan.fToAngleIndex);
816 }
817 angle->insert(oAngle);
818}
819
820bool SkOpSegment::alignSpan(int index, double thisT, const SkPoint& thisPt) {
821 bool aligned = false;
822 SkOpSpan* span = &fTs[index];
823 SkOpSegment* other = span->fOther;
824 int oIndex = span->fOtherIndex;
825 SkOpSpan* oSpan = &other->fTs[oIndex];
826 if (span->fT != thisT) {
827 span->fT = thisT;
828 oSpan->fOtherT = thisT;
829 aligned = true;
830 }
831 if (span->fPt != thisPt) {
832 span->fPt = thisPt;
833 oSpan->fPt = thisPt;
834 aligned = true;
835 }
836 double oT = oSpan->fT;
837 if (oT == 0 || oT == 1) {
838 return aligned;
839 }
840 int oStart = other->nextSpan(oIndex, -1) + 1;
841 int oEnd = other->nextSpan(oIndex, 1);
842 oSpan = &other->fTs[oStart];
843 oT = oSpan->fT;
844 bool oAligned = false;
845 if (oSpan->fPt != thisPt) {
846 oAligned |= other->alignSpan(oStart, oT, thisPt);
847 }
848 int otherIndex = oStart;
849 while (++otherIndex < oEnd) {
850 SkOpSpan* oNextSpan = &other->fTs[otherIndex];
851 if (oNextSpan->fT != oT || oNextSpan->fPt != thisPt) {
852 oAligned |= other->alignSpan(otherIndex, oT, thisPt);
853 }
854 }
855 if (oAligned) {
856 other->alignSpanState(oStart, oEnd);
857 }
858 return aligned;
859}
860
861void SkOpSegment::alignSpanState(int start, int end) {
862 SkOpSpan* lastSpan = &fTs[--end];
863 bool allSmall = lastSpan->fSmall;
864 bool allTiny = lastSpan->fTiny;
865 bool allDone = lastSpan->fDone;
866 SkDEBUGCODE(int winding = lastSpan->fWindValue);
867 SkDEBUGCODE(int oppWinding = lastSpan->fOppValue);
868 int index = start;
869 while (index < end) {
870 SkOpSpan* span = &fTs[index];
871 span->fSmall = allSmall;
872 span->fTiny = allTiny;
873 if (span->fDone != allDone) {
874 span->fDone = allDone;
875 fDoneSpans += allDone ? 1 : -1;
876 }
877 SkASSERT(span->fWindValue == winding);
878 SkASSERT(span->fOppValue == oppWinding);
879 ++index;
880 }
881}
882
caryclark@google.com570863f2013-09-16 15:55:01 +0000883void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
884 SkTArray<SkPoint, true>* outsideTs) {
885 int index = *indexPtr;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000886 int oWindValue = oTest.fWindValue;
887 int oOppValue = oTest.fOppValue;
caryclark@google.com570863f2013-09-16 15:55:01 +0000888 if (binary) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000889 SkTSwap<int>(oWindValue, oOppValue);
890 }
891 SkOpSpan* const test = &fTs[index];
892 SkOpSpan* end = test;
caryclark@google.com570863f2013-09-16 15:55:01 +0000893 const SkPoint& oStartPt = oTest.fPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000894 do {
895 if (bumpSpan(end, oWindValue, oOppValue)) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000896 TrackOutside(outsideTs, oStartPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000897 }
898 end = &fTs[++index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000899 } while ((end->fPt == test->fPt || precisely_equal(end->fT, test->fT)) && end->fT < 1);
caryclark@google.com570863f2013-09-16 15:55:01 +0000900 *indexPtr = index;
901}
902
caryclark@google.com07393ca2013-04-08 11:47:37 +0000903// because of the order in which coincidences are resolved, this and other
904// may not have the same intermediate points. Compute the corresponding
905// intermediate T values (using this as the master, other as the follower)
906// and walk other conditionally -- hoping that it catches up in the end
caryclark@google.com570863f2013-09-16 15:55:01 +0000907void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
908 SkTArray<SkPoint, true>* oOutsidePts) {
909 int oIndex = *oIndexPtr;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000910 SkOpSpan* const oTest = &fTs[oIndex];
911 SkOpSpan* oEnd = oTest;
caryclark@google.com570863f2013-09-16 15:55:01 +0000912 const SkPoint& oStartPt = oTest->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000913 double oStartT = oTest->fT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000914#if 0 // FIXME : figure out what disabling this breaks
915 const SkPoint& startPt = test.fPt;
916 // this is always true since oEnd == oTest && oStartPt == oTest->fPt -- find proper condition
917 if (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000918 TrackOutside(oOutsidePts, startPt);
919 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000920#endif
921 while (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000922 zeroSpan(oEnd);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000923 oEnd = &fTs[++oIndex];
924 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000925 *oIndexPtr = oIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000926}
927
928// FIXME: need to test this case:
929// contourA has two segments that are coincident
930// contourB has two segments that are coincident in the same place
931// each ends up with +2/0 pairs for winding count
932// since logic below doesn't transfer count (only increments/decrements) can this be
933// resolved to +4/0 ?
934
935// set spans from start to end to increment the greater by one and decrement
936// the lesser
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000937void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
caryclark@google.com570863f2013-09-16 15:55:01 +0000938 SkOpSegment* other) {
939 bool binary = fOperand != other->fOperand;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000940 int index = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000941 while (startPt != fTs[index].fPt) {
942 SkASSERT(index < fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000943 ++index;
944 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000945 double startT = fTs[index].fT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000946 while (index > 0 && precisely_equal(fTs[index - 1].fT, startT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000947 --index;
948 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000949 int oIndex = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000950 while (startPt != other->fTs[oIndex].fPt) {
951 SkASSERT(oIndex < other->fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000952 ++oIndex;
953 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000954 double oStartT = other->fTs[oIndex].fT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000955 while (oIndex > 0 && precisely_equal(other->fTs[oIndex - 1].fT, oStartT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000956 --oIndex;
957 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000958 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
959 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000960 SkOpSpan* test = &fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +0000961 const SkPoint* testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000962 double testT = test->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000963 SkOpSpan* oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +0000964 const SkPoint* oTestPt = &oTest->fPt;
965 SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000966 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000967 SkASSERT(test->fT < 1);
968 SkASSERT(oTest->fT < 1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000969
970 // consolidate the winding count even if done
caryclark@google.com570863f2013-09-16 15:55:01 +0000971 if ((test->fWindValue == 0 && test->fOppValue == 0)
972 || (oTest->fWindValue == 0 && oTest->fOppValue == 0)) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000973 SkDEBUGCODE(int firstWind = test->fWindValue);
974 SkDEBUGCODE(int firstOpp = test->fOppValue);
975 do {
976 SkASSERT(firstWind == fTs[index].fWindValue);
977 SkASSERT(firstOpp == fTs[index].fOppValue);
978 ++index;
979 SkASSERT(index < fTs.count());
980 } while (*testPt == fTs[index].fPt);
981 SkDEBUGCODE(firstWind = oTest->fWindValue);
982 SkDEBUGCODE(firstOpp = oTest->fOppValue);
983 do {
984 SkASSERT(firstWind == other->fTs[oIndex].fWindValue);
985 SkASSERT(firstOpp == other->fTs[oIndex].fOppValue);
986 ++oIndex;
987 SkASSERT(oIndex < other->fTs.count());
988 } while (*oTestPt == other->fTs[oIndex].fPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000989 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +0000990 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
991 bumpCoincidentThis(*oTest, binary, &index, &outsidePts);
992 other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts);
993 } else {
994 other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts);
995 bumpCoincidentOther(*oTest, &index, &outsidePts);
996 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000997 }
998 test = &fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +0000999 testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001000 testT = test->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001001 oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +00001002 oTestPt = &oTest->fPt;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001003 if (endPt == *testPt || precisely_equal(endT, testT)) {
1004 break;
1005 }
1006// SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
caryclark@google.com570863f2013-09-16 15:55:01 +00001007 } while (endPt != *oTestPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001008 // in rare cases, one may have ended before the other
1009 if (endPt != *testPt && !precisely_equal(endT, testT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001010 int lastWind = test[-1].fWindValue;
1011 int lastOpp = test[-1].fOppValue;
1012 bool zero = lastWind == 0 && lastOpp == 0;
1013 do {
1014 if (test->fWindValue || test->fOppValue) {
1015 test->fWindValue = lastWind;
1016 test->fOppValue = lastOpp;
1017 if (zero) {
1018 test->fDone = true;
1019 ++fDoneSpans;
1020 }
1021 }
1022 test = &fTs[++index];
1023 testPt = &test->fPt;
1024 } while (endPt != *testPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001025 }
1026 if (endPt != *oTestPt) {
1027 // look ahead to see if zeroing more spans will allows us to catch up
1028 int oPeekIndex = oIndex;
1029 bool success = true;
1030 SkOpSpan* oPeek;
1031 do {
1032 oPeek = &other->fTs[oPeekIndex];
1033 if (oPeek->fT == 1) {
1034 success = false;
1035 break;
1036 }
1037 ++oPeekIndex;
1038 } while (endPt != oPeek->fPt);
1039 if (success) {
1040 // make sure the matching point completes the coincidence span
1041 success = false;
1042 do {
1043 if (oPeek->fOther == this) {
1044 success = true;
1045 break;
1046 }
1047 oPeek = &other->fTs[++oPeekIndex];
1048 } while (endPt == oPeek->fPt);
1049 }
1050 if (success) {
1051 do {
1052 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
1053 other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts);
1054 } else {
1055 other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts);
1056 }
1057 oTest = &other->fTs[oIndex];
1058 oTestPt = &oTest->fPt;
1059 } while (endPt != *oTestPt);
1060 }
1061 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001062 int outCount = outsidePts.count();
1063 if (!done() && outCount) {
1064 addCoinOutsides(outsidePts[0], endPt, other);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001065 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001066 if (!other->done() && oOutsidePts.count()) {
1067 other->addCoinOutsides(oOutsidePts[0], endPt, this);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001068 }
1069}
1070
1071// FIXME: this doesn't prevent the same span from being added twice
1072// fix in caller, SkASSERT here?
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001073const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
caryclark@google.com07393ca2013-04-08 11:47:37 +00001074 const SkPoint& pt) {
1075 int tCount = fTs.count();
1076 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1077 const SkOpSpan& span = fTs[tIndex];
1078 if (!approximately_negative(span.fT - t)) {
1079 break;
1080 }
1081 if (approximately_negative(span.fT - t) && span.fOther == other
1082 && approximately_equal(span.fOtherT, otherT)) {
1083#if DEBUG_ADD_T_PAIR
1084 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1085 __FUNCTION__, fID, t, other->fID, otherT);
1086#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001087 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001088 }
1089 }
1090#if DEBUG_ADD_T_PAIR
1091 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1092 __FUNCTION__, fID, t, other->fID, otherT);
1093#endif
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001094 int insertedAt = addT(other, pt, t);
1095 int otherInsertedAt = other->addT(this, pt, otherT);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001096 addOtherT(insertedAt, otherT, otherInsertedAt);
1097 other->addOtherT(otherInsertedAt, t, insertedAt);
1098 matchWindingValue(insertedAt, t, borrowWind);
1099 other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001100 return &span(insertedAt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001101}
1102
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001103const SkOpAngle& SkOpSegment::angle(int index) const {
1104 SkASSERT(index >= 0);
1105 int count = fAngles.count();
1106 if (index < count) {
1107 return fAngles[index];
caryclark@google.com07393ca2013-04-08 11:47:37 +00001108 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001109 index -= count;
1110 count = fSingletonAngles.count();
1111 SkASSERT(index < count);
1112 return fSingletonAngles[index];
caryclark@google.com07393ca2013-04-08 11:47:37 +00001113}
1114
caryclark@google.com570863f2013-09-16 15:55:01 +00001115bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
1116 const SkPoint midPt = ptAtT(midT);
1117 SkPathOpsBounds bounds;
1118 bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1119 bounds.sort();
1120 return bounds.almostContains(midPt);
1121}
1122
caryclark@google.com07393ca2013-04-08 11:47:37 +00001123bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const {
1124 if (lesser > greater) {
1125 SkTSwap<int>(lesser, greater);
1126 }
1127 return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
1128}
1129
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001130// in extreme cases (like the buffer overflow test) return false to abort
1131// for now, if one t value represents two different points, then the values are too extreme
1132// to generate meaningful results
1133bool SkOpSegment::calcAngles() {
1134 int spanCount = fTs.count();
1135 if (spanCount <= 2) {
1136 return spanCount == 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001137 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001138 int index = 1;
1139 const SkOpSpan* firstSpan = &fTs[index];
1140 int activePrior = checkSetAngle(0);
1141 const SkOpSpan* span = &fTs[0];
1142 if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) {
1143 index = findStartSpan(0); // curve start intersects
1144 if (index < 0) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001145 return false;
1146 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001147 if (activePrior >= 0) {
1148 addStartSpan(index);
1149 }
1150 }
1151 bool addEnd;
1152 int endIndex = spanCount - 1;
1153 span = &fTs[endIndex - 1];
1154 if ((addEnd = span->fT == 1 || span->fTiny)) { // if curve end intersects
1155 endIndex = findEndSpan(endIndex);
1156 if (endIndex < 0) {
1157 return false;
1158 }
1159 } else {
1160 addEnd = fTs[endIndex].fOtherT != 0 || fTs[endIndex].fOther->multipleStarts();
1161 }
1162 SkASSERT(endIndex >= index);
1163 int prior = 0;
1164 while (index < endIndex) {
1165 const SkOpSpan& fromSpan = fTs[index]; // for each intermediate intersection
1166 const SkOpSpan* lastSpan;
1167 span = &fromSpan;
1168 int start = index;
1169 do {
1170 lastSpan = span;
1171 span = &fTs[++index];
1172 SkASSERT(index < spanCount);
1173 if (!precisely_negative(span->fT - lastSpan->fT) && !lastSpan->fTiny) {
1174 break;
1175 }
1176 if (!SkDPoint::ApproximatelyEqual(lastSpan->fPt, span->fPt)) {
1177 return false;
1178 }
1179 } while (true);
1180 int angleIndex = fAngles.count();
1181 int priorAngleIndex;
1182 if (activePrior >= 0) {
1183 int pActive = firstActive(prior);
1184 SkASSERT(pActive < start);
1185 fAngles.push_back().set(this, start, pActive);
1186 priorAngleIndex = angleIndex++;
1187 #if DEBUG_ANGLE
1188 fAngles.back().setID(priorAngleIndex);
1189 #endif
1190 }
1191 int active = checkSetAngle(start);
1192 if (active >= 0) {
1193 SkASSERT(active < index);
1194 fAngles.push_back().set(this, active, index);
1195 #if DEBUG_ANGLE
1196 fAngles.back().setID(angleIndex);
1197 #endif
1198 }
1199 #if DEBUG_ANGLE
1200 debugCheckPointsEqualish(start, index);
1201 #endif
1202 prior = start;
1203 do {
1204 const SkOpSpan* startSpan = &fTs[start - 1];
1205 if (!startSpan->fSmall || startSpan->fFromAngleIndex >= 0
1206 || startSpan->fToAngleIndex >= 0) {
1207 break;
1208 }
1209 --start;
1210 } while (start > 0);
1211 do {
1212 if (activePrior >= 0) {
1213 SkASSERT(fTs[start].fFromAngleIndex == -1);
1214 fTs[start].fFromAngleIndex = priorAngleIndex;
1215 }
1216 if (active >= 0) {
1217 SkASSERT(fTs[start].fToAngleIndex == -1);
1218 fTs[start].fToAngleIndex = angleIndex;
1219 }
1220 } while (++start < index);
1221 activePrior = active;
1222 }
1223 if (addEnd && activePrior >= 0) {
1224 addEndSpan(endIndex);
1225 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001226 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001227}
1228
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001229int SkOpSegment::checkSetAngle(int tIndex) const {
1230 const SkOpSpan* span = &fTs[tIndex];
1231 while (span->fTiny /* || span->fSmall */) {
1232 span = &fTs[++tIndex];
caryclark@google.com07393ca2013-04-08 11:47:37 +00001233 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001234 return isCanceled(tIndex) ? -1 : tIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001235}
1236
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001237// at this point, the span is already ordered, or unorderable, or unsortable
1238int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
1239 SkASSERT(includeType != SkOpAngle::kUnaryXor);
1240 SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
1241 if (NULL == firstAngle) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001242 return SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001243 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001244 // if all angles have a computed winding,
1245 // or if no adjacent angles are orderable,
1246 // or if adjacent orderable angles have no computed winding,
1247 // there's nothing to do
1248 // if two orderable angles are adjacent, and one has winding computed, transfer to the other
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001249 SkOpAngle* baseAngle = NULL;
caryclark@google.com570863f2013-09-16 15:55:01 +00001250 bool tryReverse = false;
1251 // look for counterclockwise transfers
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001252 SkOpAngle* angle = firstAngle;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001253 do {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001254 int testWinding = angle->segment()->windSum(angle);
1255 if (SK_MinS32 != testWinding && !angle->unorderable()) {
1256 baseAngle = angle;
1257 continue;
1258 }
1259 if (angle->unorderable()) {
1260 baseAngle = NULL;
1261 tryReverse = true;
1262 continue;
1263 }
1264 if (baseAngle) {
1265 ComputeOneSum(baseAngle, angle, includeType);
1266 baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
1267 tryReverse |= !baseAngle;
1268 }
1269 } while ((angle = angle->next()) != firstAngle);
1270 if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(angle)) {
1271 firstAngle = baseAngle;
1272 tryReverse = true;
1273 }
1274 if (tryReverse) {
1275 baseAngle = NULL;
1276 angle = firstAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +00001277 do {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001278 int testWinding = angle->segment()->windSum(angle);
1279 if (SK_MinS32 != testWinding) {
1280 baseAngle = angle;
caryclark@google.com570863f2013-09-16 15:55:01 +00001281 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001282 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001283 if (angle->unorderable()) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001284 baseAngle = NULL;
caryclark@google.com570863f2013-09-16 15:55:01 +00001285 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001286 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001287 if (baseAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001288 ComputeOneSumReverse(baseAngle, angle, includeType);
1289 baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001290 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001291 } while ((angle = angle->previous()) != firstAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001292 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001293 int minIndex = SkMin32(startIndex, endIndex);
1294 return windSum(minIndex);
1295}
1296
caryclark@google.com570863f2013-09-16 15:55:01 +00001297void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
1298 SkOpAngle::IncludeType includeType) {
1299 const SkOpSegment* baseSegment = baseAngle->segment();
1300 int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
1301 int sumSuWinding;
1302 bool binary = includeType >= SkOpAngle::kBinarySingle;
1303 if (binary) {
1304 sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
1305 if (baseSegment->operand()) {
1306 SkTSwap<int>(sumMiWinding, sumSuWinding);
1307 }
1308 }
1309 SkOpSegment* nextSegment = nextAngle->segment();
1310 int maxWinding, sumWinding;
1311 SkOpSpan* last;
1312 if (binary) {
1313 int oppMaxWinding, oppSumWinding;
1314 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
1315 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1316 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001317 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001318 } else {
1319 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
1320 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001321 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001322 }
1323 nextAngle->setLastMarked(last);
1324}
1325
1326void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
1327 SkOpAngle::IncludeType includeType) {
1328 const SkOpSegment* baseSegment = baseAngle->segment();
1329 int sumMiWinding = baseSegment->updateWinding(baseAngle);
1330 int sumSuWinding;
1331 bool binary = includeType >= SkOpAngle::kBinarySingle;
1332 if (binary) {
1333 sumSuWinding = baseSegment->updateOppWinding(baseAngle);
1334 if (baseSegment->operand()) {
1335 SkTSwap<int>(sumMiWinding, sumSuWinding);
1336 }
1337 }
1338 SkOpSegment* nextSegment = nextAngle->segment();
1339 int maxWinding, sumWinding;
1340 SkOpSpan* last;
1341 if (binary) {
1342 int oppMaxWinding, oppSumWinding;
1343 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
1344 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1345 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001346 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001347 } else {
1348 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
1349 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001350 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001351 }
1352 nextAngle->setLastMarked(last);
1353}
1354
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001355void SkOpSegment::constructLine(SkPoint shortLine[2]) {
1356 addLine(shortLine, false, false);
1357 addT(NULL, shortLine[0], 0);
1358 addT(NULL, shortLine[1], 1);
1359 addStartSpan(1);
1360 addEndSpan(1);
1361 SkOpAngle& angle = fAngles.push_back();
1362 angle.set(this, 0, 1);
1363}
1364
caryclark@google.com07393ca2013-04-08 11:47:37 +00001365int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
1366 bool* hitSomething, double mid, bool opp, bool current) const {
1367 SkScalar bottom = fBounds.fBottom;
1368 int bestTIndex = -1;
1369 if (bottom <= *bestY) {
1370 return bestTIndex;
1371 }
1372 SkScalar top = fBounds.fTop;
1373 if (top >= basePt.fY) {
1374 return bestTIndex;
1375 }
1376 if (fBounds.fLeft > basePt.fX) {
1377 return bestTIndex;
1378 }
1379 if (fBounds.fRight < basePt.fX) {
1380 return bestTIndex;
1381 }
1382 if (fBounds.fLeft == fBounds.fRight) {
1383 // if vertical, and directly above test point, wait for another one
1384 return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
1385 }
1386 // intersect ray starting at basePt with edge
1387 SkIntersections intersections;
1388 // OPTIMIZE: use specialty function that intersects ray with curve,
1389 // returning t values only for curve (we don't care about t on ray)
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00001390 intersections.allowNear(false);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001391 int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
1392 (fPts, top, bottom, basePt.fX, false);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001393 if (pts == 0 || (current && pts == 1)) {
1394 return bestTIndex;
1395 }
1396 if (current) {
1397 SkASSERT(pts > 1);
1398 int closestIdx = 0;
1399 double closest = fabs(intersections[0][0] - mid);
1400 for (int idx = 1; idx < pts; ++idx) {
1401 double test = fabs(intersections[0][idx] - mid);
1402 if (closest > test) {
1403 closestIdx = idx;
1404 closest = test;
1405 }
1406 }
1407 intersections.quickRemoveOne(closestIdx, --pts);
1408 }
1409 double bestT = -1;
1410 for (int index = 0; index < pts; ++index) {
1411 double foundT = intersections[0][index];
1412 if (approximately_less_than_zero(foundT)
1413 || approximately_greater_than_one(foundT)) {
1414 continue;
1415 }
reed@google.com277c3f82013-05-31 15:17:50 +00001416 SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001417 if (approximately_negative(testY - *bestY)
1418 || approximately_negative(basePt.fY - testY)) {
1419 continue;
1420 }
1421 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1422 return SK_MinS32; // if the intersection is edge on, wait for another one
1423 }
1424 if (fVerb > SkPath::kLine_Verb) {
reed@google.com277c3f82013-05-31 15:17:50 +00001425 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001426 if (approximately_zero(dx)) {
1427 return SK_MinS32; // hit vertical, wait for another one
1428 }
1429 }
1430 *bestY = testY;
1431 bestT = foundT;
1432 }
1433 if (bestT < 0) {
1434 return bestTIndex;
1435 }
1436 SkASSERT(bestT >= 0);
1437 SkASSERT(bestT <= 1);
1438 int start;
1439 int end = 0;
1440 do {
1441 start = end;
1442 end = nextSpan(start, 1);
1443 } while (fTs[end].fT < bestT);
1444 // FIXME: see next candidate for a better pattern to find the next start/end pair
1445 while (start + 1 < end && fTs[start].fDone) {
1446 ++start;
1447 }
1448 if (!isCanceled(start)) {
1449 *hitT = bestT;
1450 bestTIndex = start;
1451 *hitSomething = true;
1452 }
1453 return bestTIndex;
1454}
1455
caryclark@google.com570863f2013-09-16 15:55:01 +00001456bool SkOpSegment::decrementSpan(SkOpSpan* span) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001457 SkASSERT(span->fWindValue > 0);
1458 if (--(span->fWindValue) == 0) {
1459 if (!span->fOppValue && !span->fDone) {
1460 span->fDone = true;
1461 ++fDoneSpans;
caryclark@google.com570863f2013-09-16 15:55:01 +00001462 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001463 }
1464 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001465 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001466}
1467
1468bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001469 SkASSERT(!span->fDone || span->fTiny || span->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001470 span->fWindValue += windDelta;
1471 SkASSERT(span->fWindValue >= 0);
1472 span->fOppValue += oppDelta;
1473 SkASSERT(span->fOppValue >= 0);
1474 if (fXor) {
1475 span->fWindValue &= 1;
1476 }
1477 if (fOppXor) {
1478 span->fOppValue &= 1;
1479 }
1480 if (!span->fWindValue && !span->fOppValue) {
1481 span->fDone = true;
1482 ++fDoneSpans;
1483 return true;
1484 }
1485 return false;
1486}
1487
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001488const SkOpSpan& SkOpSegment::firstSpan(const SkOpSpan& thisSpan) const {
1489 const SkOpSpan* firstSpan = &thisSpan; // rewind to the start
1490 const SkOpSpan* beginSpan = fTs.begin();
1491 const SkPoint& testPt = thisSpan.fPt;
1492 while (firstSpan > beginSpan && firstSpan[-1].fPt == testPt) {
1493 --firstSpan;
1494 }
1495 return *firstSpan;
1496}
1497
1498const SkOpSpan& SkOpSegment::lastSpan(const SkOpSpan& thisSpan) const {
1499 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
1500 const SkOpSpan* lastSpan = &thisSpan; // find the end
1501 const SkPoint& testPt = thisSpan.fPt;
1502 while (lastSpan < endSpan && lastSpan[1].fPt == testPt) {
1503 ++lastSpan;
1504 }
1505 return *lastSpan;
1506}
1507
1508// with a loop, the comparison is move involved
1509// scan backwards and forwards to count all matching points
1510// (verify that there are twp scans marked as loops)
1511// compare that against 2 matching scans for loop plus other results
1512bool SkOpSegment::calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts) {
1513 const SkOpSpan& firstSpan = this->firstSpan(thisSpan); // rewind to the start
1514 const SkOpSpan& lastSpan = this->lastSpan(thisSpan); // find the end
1515 double firstLoopT = -1, lastLoopT = -1;
1516 const SkOpSpan* testSpan = &firstSpan - 1;
1517 while (++testSpan <= &lastSpan) {
1518 if (testSpan->fLoop) {
1519 firstLoopT = testSpan->fT;
1520 break;
1521 }
1522 }
1523 testSpan = &lastSpan + 1;
1524 while (--testSpan >= &firstSpan) {
1525 if (testSpan->fLoop) {
1526 lastLoopT = testSpan->fT;
1527 break;
1528 }
1529 }
1530 SkASSERT((firstLoopT == -1) == (lastLoopT == -1));
1531 if (firstLoopT == -1) {
1532 return false;
1533 }
1534 SkASSERT(firstLoopT < lastLoopT);
1535 testSpan = &firstSpan - 1;
1536 smallCounts[0] = smallCounts[1] = 0;
1537 while (++testSpan <= &lastSpan) {
1538 SkASSERT(approximately_equal(testSpan->fT, firstLoopT) +
1539 approximately_equal(testSpan->fT, lastLoopT) == 1);
1540 smallCounts[approximately_equal(testSpan->fT, lastLoopT)]++;
1541 }
1542 return true;
1543}
1544
1545// see if spans with two or more intersections have the same number on the other end
1546void SkOpSegment::checkDuplicates() {
1547 debugValidate();
1548 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
1549 int index;
1550 int endIndex = 0;
1551 bool endFound;
1552 do {
1553 index = endIndex;
1554 endIndex = nextExactSpan(index, 1);
1555 if ((endFound = endIndex < 0)) {
1556 endIndex = count();
1557 }
1558 int dupCount = endIndex - index;
1559 if (dupCount < 2) {
1560 continue;
1561 }
1562 do {
1563 const SkOpSpan* thisSpan = &fTs[index];
1564 SkOpSegment* other = thisSpan->fOther;
1565 int oIndex = thisSpan->fOtherIndex;
1566 int oStart = other->nextExactSpan(oIndex, -1) + 1;
1567 int oEnd = other->nextExactSpan(oIndex, 1);
1568 if (oEnd < 0) {
1569 oEnd = other->count();
1570 }
1571 int oCount = oEnd - oStart;
1572 // force the other to match its t and this pt if not on an end point
1573 if (oCount != dupCount) {
1574 MissingSpan& missing = missingSpans.push_back();
1575 missing.fOther = NULL;
1576 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
1577 missing.fPt = thisSpan->fPt;
1578 const SkOpSpan& oSpan = other->span(oIndex);
1579 if (oCount > dupCount) {
1580 missing.fSegment = this;
1581 missing.fT = thisSpan->fT;
1582 other->checkLinks(&oSpan, &missingSpans);
1583 } else {
1584 missing.fSegment = other;
1585 missing.fT = oSpan.fT;
1586 checkLinks(thisSpan, &missingSpans);
1587 }
1588 if (!missingSpans.back().fOther) {
1589 missingSpans.pop_back();
1590 }
1591 }
1592 } while (++index < endIndex);
1593 } while (!endFound);
1594 int missingCount = missingSpans.count();
1595 if (missingCount == 0) {
1596 return;
1597 }
1598 SkSTArray<kMissingSpanCount, MissingSpan, true> missingCoincidence;
1599 for (index = 0; index < missingCount; ++index) {
1600 MissingSpan& missing = missingSpans[index];
1601 SkOpSegment* missingOther = missing.fOther;
1602 if (missing.fSegment == missing.fOther) {
1603 continue;
1604 }
1605 const SkOpSpan* added = missing.fSegment->addTPair(missing.fT, missingOther,
1606 missing.fOtherT, false, missing.fPt);
1607 if (added && added->fSmall) {
1608 missing.fSegment->checkSmallCoincidence(*added, &missingCoincidence);
1609 }
1610 }
1611 for (index = 0; index < missingCount; ++index) {
1612 MissingSpan& missing = missingSpans[index];
1613 missing.fSegment->fixOtherTIndex();
1614 missing.fOther->fixOtherTIndex();
1615 }
1616 for (index = 0; index < missingCoincidence.count(); ++index) {
1617 MissingSpan& missing = missingCoincidence[index];
1618 missing.fSegment->fixOtherTIndex();
1619 }
1620 debugValidate();
1621}
1622
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001623// look to see if the curve end intersects an intermediary that intersects the other
1624void SkOpSegment::checkEnds() {
caryclark@google.com4fdbb222013-07-23 15:27:41 +00001625 debugValidate();
caryclark@google.com570863f2013-09-16 15:55:01 +00001626 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001627 int count = fTs.count();
1628 for (int index = 0; index < count; ++index) {
1629 const SkOpSpan& span = fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +00001630 double otherT = span.fOtherT;
1631 if (otherT != 0 && otherT != 1) { // only check ends
1632 continue;
1633 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001634 const SkOpSegment* other = span.fOther;
caryclark@google.com570863f2013-09-16 15:55:01 +00001635 // peek start/last describe the range of spans that match the other t of this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001636 int peekStart = span.fOtherIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00001637 while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT)
1638 ;
1639 int otherCount = other->fTs.count();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001640 int peekLast = span.fOtherIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00001641 while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT)
1642 ;
1643 if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001644 continue;
1645 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001646 // t start/last describe the range of spans that match the t of this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001647 double t = span.fT;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00001648 double tBottom = -1;
1649 int tStart = -1;
1650 int tLast = count;
1651 bool lastSmall = false;
1652 double afterT = t;
1653 for (int inner = 0; inner < count; ++inner) {
1654 double innerT = fTs[inner].fT;
1655 if (innerT <= t && innerT > tBottom) {
1656 if (innerT < t || !lastSmall) {
1657 tStart = inner - 1;
1658 }
1659 tBottom = innerT;
1660 }
1661 if (innerT > afterT) {
1662 if (t == afterT && lastSmall) {
1663 afterT = innerT;
1664 } else {
1665 tLast = inner;
1666 break;
1667 }
1668 }
1669 lastSmall = innerT <= t ? fTs[inner].fSmall : false;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001670 }
1671 for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001672 if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001673 continue;
1674 }
1675 const SkOpSpan& peekSpan = other->fTs[peekIndex];
1676 SkOpSegment* match = peekSpan.fOther;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001677 if (match->done()) {
1678 continue; // if the edge has already been eaten (likely coincidence), ignore it
1679 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001680 const double matchT = peekSpan.fOtherT;
caryclark@google.com570863f2013-09-16 15:55:01 +00001681 // see if any of the spans match the other spans
1682 for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001683 const SkOpSpan& tSpan = fTs[tIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +00001684 if (tSpan.fOther == match) {
1685 if (tSpan.fOtherT == matchT) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001686 goto nextPeekIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00001687 }
1688 double midT = (tSpan.fOtherT + matchT) / 2;
1689 if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001690 goto nextPeekIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00001691 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001692 }
1693 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001694 if (missingSpans.count() > 0) {
1695 const MissingSpan& lastMissing = missingSpans.back();
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001696 if (lastMissing.fT == t
caryclark@google.com570863f2013-09-16 15:55:01 +00001697 && lastMissing.fOther == match
1698 && lastMissing.fOtherT == matchT) {
1699 SkASSERT(lastMissing.fPt == peekSpan.fPt);
1700 continue;
1701 }
1702 }
1703#if DEBUG_CHECK_ENDS
1704 SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n",
1705 __FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY);
1706#endif
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001707 // this segment is missing a entry that the other contains
1708 // remember so we can add the missing one and recompute the indices
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001709 {
1710 MissingSpan& missing = missingSpans.push_back();
1711 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001712 missing.fT = t;
1713 missing.fOther = match;
1714 missing.fOtherT = matchT;
1715 missing.fPt = peekSpan.fPt;
1716 }
1717 break;
1718nextPeekIndex:
1719 ;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001720 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001721 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001722 if (missingSpans.count() == 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001723 debugValidate();
caryclark@google.com4fdbb222013-07-23 15:27:41 +00001724 return;
1725 }
1726 debugValidate();
caryclark@google.com570863f2013-09-16 15:55:01 +00001727 int missingCount = missingSpans.count();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001728 for (int index = 0; index < missingCount; ++index) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001729 MissingSpan& missing = missingSpans[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001730 if (this != missing.fOther) {
1731 addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
1732 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001733 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00001734 fixOtherTIndex();
caryclark@google.com570863f2013-09-16 15:55:01 +00001735 // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
1736 // avoid this
caryclark@google.com4fdbb222013-07-23 15:27:41 +00001737 for (int index = 0; index < missingCount; ++index) {
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001738 missingSpans[index].fOther->fixOtherTIndex();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001739 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00001740 debugValidate();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00001741}
1742
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001743void SkOpSegment::checkLinks(const SkOpSpan* base,
1744 SkTArray<MissingSpan, true>* missingSpans) const {
1745 const SkOpSpan* first = fTs.begin();
1746 const SkOpSpan* last = fTs.end() - 1;
1747 SkASSERT(base >= first && last >= base);
1748 const SkOpSegment* other = base->fOther;
1749 const SkOpSpan* oFirst = other->fTs.begin();
1750 const SkOpSpan* oLast = other->fTs.end() - 1;
1751 const SkOpSpan* oSpan = &other->fTs[base->fOtherIndex];
1752 const SkOpSpan* test = base;
1753 const SkOpSpan* missing = NULL;
1754 while (test > first && (--test)->fPt == base->fPt) {
1755 CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
1756 }
1757 test = base;
1758 while (test < last && (++test)->fPt == base->fPt) {
1759 CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
1760 }
1761}
1762
1763// see if spans with two or more intersections all agree on common t and point values
1764void SkOpSegment::checkMultiples() {
1765 debugValidate();
1766 int index;
1767 int end = 0;
1768 while (fTs[++end].fT == 0)
1769 ;
1770 while (fTs[end].fT < 1) {
1771 int start = index = end;
1772 end = nextExactSpan(index, 1);
1773 if (end <= index) {
1774 return; // buffer overflow example triggers this
1775 }
1776 if (index + 1 == end) {
1777 continue;
1778 }
1779 // force the duplicates to agree on t and pt if not on the end
1780 double thisT = fTs[index].fT;
1781 const SkPoint& thisPt = fTs[index].fPt;
1782 bool aligned = false;
1783 while (++index < end) {
1784 aligned |= alignSpan(index, thisT, thisPt);
1785 }
1786 if (aligned) {
1787 alignSpanState(start, end);
1788 }
1789 }
1790 debugValidate();
1791}
1792
1793void SkOpSegment::CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
1794 const SkOpSpan* oFirst, const SkOpSpan* oLast, const SkOpSpan** missingPtr,
1795 SkTArray<MissingSpan, true>* missingSpans) {
1796 SkASSERT(oSpan->fPt == test->fPt);
1797 const SkOpSpan* oTest = oSpan;
1798 while (oTest > oFirst && (--oTest)->fPt == test->fPt) {
1799 if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
1800 return;
1801 }
1802 }
1803 oTest = oSpan;
1804 while (oTest < oLast && (++oTest)->fPt == test->fPt) {
1805 if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
1806 return;
1807 }
1808 }
1809 if (*missingPtr) {
1810 missingSpans->push_back();
1811 }
1812 MissingSpan& lastMissing = missingSpans->back();
1813 if (*missingPtr) {
1814 lastMissing = missingSpans->end()[-2];
1815 }
1816 *missingPtr = test;
1817 lastMissing.fOther = test->fOther;
1818 lastMissing.fOtherT = test->fOtherT;
1819}
1820
caryclark@google.com570863f2013-09-16 15:55:01 +00001821bool SkOpSegment::checkSmall(int index) const {
1822 if (fTs[index].fSmall) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001823 return true;
1824 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001825 double tBase = fTs[index].fT;
1826 while (index > 0 && precisely_negative(tBase - fTs[--index].fT))
1827 ;
1828 return fTs[index].fSmall;
1829}
1830
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001831// a pair of curves may turn into coincident lines -- small may be a hint that that happened
1832// if a cubic contains a loop, the counts must be adjusted
1833void SkOpSegment::checkSmall() {
1834 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
1835 const SkOpSpan* beginSpan = fTs.begin();
1836 const SkOpSpan* thisSpan = beginSpan - 1;
1837 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
1838 while (++thisSpan < endSpan) {
1839 if (!thisSpan->fSmall) {
1840 continue;
1841 }
1842 if (!thisSpan->fWindValue) {
1843 continue;
1844 }
1845 const SkOpSpan& firstSpan = this->firstSpan(*thisSpan);
1846 const SkOpSpan& lastSpan = this->lastSpan(*thisSpan);
1847 ptrdiff_t smallCount = &lastSpan - &firstSpan + 1;
1848 SkASSERT(1 <= smallCount && smallCount < count());
1849 if (smallCount <= 1) {
1850 SkASSERT(1 == smallCount);
1851 checkSmallCoincidence(firstSpan, NULL);
1852 continue;
1853 }
1854 // at this point, check for missing computed intersections
1855 const SkPoint& testPt = firstSpan.fPt;
1856 thisSpan = &firstSpan - 1;
1857 SkOpSegment* other = NULL;
1858 while (++thisSpan <= &lastSpan) {
1859 other = thisSpan->fOther;
1860 if (other != this) {
1861 break;
1862 }
1863 }
1864 SkASSERT(other != this);
1865 int oIndex = thisSpan->fOtherIndex;
1866 const SkOpSpan& oSpan = other->span(oIndex);
1867 const SkOpSpan& oFirstSpan = other->firstSpan(oSpan);
1868 const SkOpSpan& oLastSpan = other->lastSpan(oSpan);
1869 ptrdiff_t oCount = &oLastSpan - &oFirstSpan + 1;
1870 if (fLoop) {
1871 int smallCounts[2];
1872 SkASSERT(!other->fLoop); // FIXME: we need more complicated logic for pair of loops
1873 if (calcLoopSpanCount(*thisSpan, smallCounts)) {
1874 if (smallCounts[0] && oCount != smallCounts[0]) {
1875 SkASSERT(0); // FIXME: need a working test case to properly code & debug
1876 }
1877 if (smallCounts[1] && oCount != smallCounts[1]) {
1878 SkASSERT(0); // FIXME: need a working test case to properly code & debug
1879 }
1880 goto nextSmallCheck;
1881 }
1882 }
1883 if (other->fLoop) {
1884 int otherCounts[2];
1885 if (other->calcLoopSpanCount(other->span(oIndex), otherCounts)) {
1886 if (otherCounts[0] && otherCounts[0] != smallCount) {
1887 SkASSERT(0); // FIXME: need a working test case to properly code & debug
1888 }
1889 if (otherCounts[1] && otherCounts[1] != smallCount) {
1890 SkASSERT(0); // FIXME: need a working test case to properly code & debug
1891 }
1892 goto nextSmallCheck;
1893 }
1894 }
1895 if (oCount != smallCount) { // check if number of pts in this match other
1896 MissingSpan& missing = missingSpans.push_back();
1897 missing.fOther = NULL;
1898 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
1899 missing.fPt = testPt;
1900 const SkOpSpan& oSpan = other->span(oIndex);
1901 if (oCount > smallCount) {
1902 missing.fSegment = this;
1903 missing.fT = thisSpan->fT;
1904 other->checkLinks(&oSpan, &missingSpans);
1905 } else {
1906 missing.fSegment = other;
1907 missing.fT = oSpan.fT;
1908 checkLinks(thisSpan, &missingSpans);
1909 }
1910 if (!missingSpans.back().fOther || missing.fSegment->done()) {
1911 missingSpans.pop_back();
1912 }
1913 }
1914nextSmallCheck:
1915 thisSpan = &lastSpan;
1916 }
1917 int missingCount = missingSpans.count();
1918 for (int index = 0; index < missingCount; ++index) {
1919 MissingSpan& missing = missingSpans[index];
1920 SkOpSegment* missingOther = missing.fOther;
1921 // note that add t pair may edit span arrays, so prior pointers to spans are no longer valid
1922 if (!missing.fSegment->addTPair(missing.fT, missingOther, missing.fOtherT, false,
1923 missing.fPt)) {
1924 continue;
1925 }
1926 int otherTIndex = missingOther->findT(missing.fOtherT, missing.fSegment);
1927 const SkOpSpan& otherSpan = missingOther->span(otherTIndex);
1928 if (otherSpan.fSmall) {
1929 const SkOpSpan* nextSpan = &otherSpan;
1930 do {
1931 ++nextSpan;
1932 } while (nextSpan->fSmall);
1933 missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt, nextSpan->fT,
1934 missingOther);
1935 } else if (otherSpan.fT > 0) {
1936 const SkOpSpan* priorSpan = &otherSpan;
1937 do {
1938 --priorSpan;
1939 } while (priorSpan->fT == otherSpan.fT);
1940 if (priorSpan->fSmall) {
1941 missing.fSegment->addTCancel(missing.fPt, priorSpan->fPt, missingOther);
1942 }
1943 }
1944 }
1945 // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
1946 // avoid this
1947 for (int index = 0; index < missingCount; ++index) {
1948 MissingSpan& missing = missingSpans[index];
1949 missing.fSegment->fixOtherTIndex();
1950 missing.fOther->fixOtherTIndex();
1951 }
1952 debugValidate();
1953}
1954
1955void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span,
1956 SkTArray<MissingSpan, true>* checkMultiple) {
1957 SkASSERT(span.fSmall);
1958 SkASSERT(span.fWindValue);
1959 SkASSERT(&span < fTs.end() - 1);
1960 const SkOpSpan* next = &span + 1;
1961 SkASSERT(!next->fSmall || checkMultiple);
1962 if (checkMultiple) {
1963 while (next->fSmall) {
1964 ++next;
1965 SkASSERT(next < fTs.end());
1966 }
1967 }
1968 SkOpSegment* other = span.fOther;
1969 while (other != next->fOther) {
1970 if (!checkMultiple) {
1971 return;
1972 }
1973 const SkOpSpan* test = next + 1;
1974 if (test == fTs.end()) {
1975 return;
1976 }
1977 if (test->fPt != next->fPt || !precisely_equal(test->fT, next->fT)) {
1978 return;
1979 }
1980 next = test;
1981 }
1982 SkASSERT(span.fT < next->fT);
1983 int oStartIndex = other->findExactT(span.fOtherT, this);
1984 int oEndIndex = other->findExactT(next->fOtherT, this);
1985 // FIXME: be overly conservative by limiting this to the caller that allows multiple smalls
1986 if (!checkMultiple || fVerb != SkPath::kLine_Verb || other->fVerb != SkPath::kLine_Verb) {
1987 SkPoint mid = ptAtT((span.fT + next->fT) / 2);
1988 const SkOpSpan& oSpanStart = other->fTs[oStartIndex];
1989 const SkOpSpan& oSpanEnd = other->fTs[oEndIndex];
1990 SkPoint oMid = other->ptAtT((oSpanStart.fT + oSpanEnd.fT) / 2);
1991 if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
1992 return;
1993 }
1994 }
1995 // FIXME: again, be overly conservative to avoid breaking existing tests
1996 const SkOpSpan& oSpan = oStartIndex < oEndIndex ? other->fTs[oStartIndex]
1997 : other->fTs[oEndIndex];
1998 if (checkMultiple && !oSpan.fSmall) {
1999 return;
2000 }
2001 SkASSERT(oSpan.fSmall);
2002 if (oStartIndex < oEndIndex) {
2003 addTCoincident(span.fPt, next->fPt, next->fT, other);
2004 } else {
2005 addTCancel(span.fPt, next->fPt, other);
2006 }
2007 if (!checkMultiple) {
2008 return;
2009 }
2010 // check to see if either segment is coincident with a third segment -- if it is, and if
2011 // the opposite segment is not already coincident with the third, make it so
2012 // OPTIMIZE: to make this check easier, add coincident and cancel could set a coincident bit
2013 if (span.fWindValue != 1 || span.fOppValue != 0) {
2014// start here;
2015 // iterate through the spans, looking for the third coincident case
2016 // if we find one, we need to return state to the caller so that the indices can be fixed
2017 // this also suggests that all of this function is fragile since it relies on a valid index
2018 }
2019 // probably should make this a common function rather than copy/paste code
2020 if (oSpan.fWindValue != 1 || oSpan.fOppValue != 0) {
2021 const SkOpSpan* oTest = &oSpan;
2022 while (--oTest >= other->fTs.begin()) {
2023 if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
2024 break;
2025 }
2026 SkOpSegment* testOther = oTest->fOther;
2027 SkASSERT(testOther != this);
2028 // look in both directions to see if there is a coincident span
2029 const SkOpSpan* tTest = testOther->fTs.begin();
2030 for (int testIndex = 0; testIndex < testOther->count(); ++testIndex) {
2031 if (tTest->fPt != span.fPt) {
2032 ++tTest;
2033 continue;
2034 }
2035 if (testOther->verb() != SkPath::kLine_Verb
2036 || other->verb() != SkPath::kLine_Verb) {
2037 SkPoint mid = ptAtT((span.fT + next->fT) / 2);
2038 SkPoint oMid = other->ptAtT((oTest->fOtherT + tTest->fT) / 2);
2039 if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
2040 continue;
2041 }
2042 }
2043#if DEBUG_CONCIDENT
2044 SkDebugf("%s coincident found=%d %1.9g %1.9g\n", __FUNCTION__, testOther->fID,
2045 oTest->fOtherT, tTest->fT);
2046#endif
2047 if (tTest->fT < oTest->fOtherT) {
2048 addTCoincident(span.fPt, next->fPt, next->fT, testOther);
2049 } else {
2050 addTCancel(span.fPt, next->fPt, testOther);
2051 }
2052 MissingSpan missing;
2053 missing.fSegment = testOther;
2054 checkMultiple->push_back(missing);
2055 break;
2056 }
2057 }
2058 oTest = &oSpan;
2059 while (++oTest < other->fTs.end()) {
2060 if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
2061 break;
2062 }
2063
2064 }
2065 }
2066}
2067
caryclark@google.com570863f2013-09-16 15:55:01 +00002068// if pair of spans on either side of tiny have the same end point and mid point, mark
2069// them as parallel
caryclark@google.com570863f2013-09-16 15:55:01 +00002070void SkOpSegment::checkTiny() {
2071 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
2072 SkOpSpan* thisSpan = fTs.begin() - 1;
2073 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be tiny
2074 while (++thisSpan < endSpan) {
2075 if (!thisSpan->fTiny) {
2076 continue;
2077 }
2078 SkOpSpan* nextSpan = thisSpan + 1;
2079 double thisT = thisSpan->fT;
2080 double nextT = nextSpan->fT;
2081 if (thisT == nextT) {
2082 continue;
2083 }
2084 SkASSERT(thisT < nextT);
2085 SkASSERT(thisSpan->fPt == nextSpan->fPt);
2086 SkOpSegment* thisOther = thisSpan->fOther;
2087 SkOpSegment* nextOther = nextSpan->fOther;
2088 int oIndex = thisSpan->fOtherIndex;
2089 for (int oStep = -1; oStep <= 1; oStep += 2) {
2090 int oEnd = thisOther->nextExactSpan(oIndex, oStep);
2091 if (oEnd < 0) {
2092 continue;
2093 }
2094 const SkOpSpan& oSpan = thisOther->span(oEnd);
2095 int nIndex = nextSpan->fOtherIndex;
2096 for (int nStep = -1; nStep <= 1; nStep += 2) {
2097 int nEnd = nextOther->nextExactSpan(nIndex, nStep);
2098 if (nEnd < 0) {
2099 continue;
2100 }
2101 const SkOpSpan& nSpan = nextOther->span(nEnd);
2102 if (oSpan.fPt != nSpan.fPt) {
2103 continue;
2104 }
2105 double oMidT = (thisSpan->fOtherT + oSpan.fT) / 2;
2106 const SkPoint& oPt = thisOther->ptAtT(oMidT);
2107 double nMidT = (nextSpan->fOtherT + nSpan.fT) / 2;
2108 const SkPoint& nPt = nextOther->ptAtT(nMidT);
2109 if (!AlmostEqualUlps(oPt, nPt)) {
2110 continue;
2111 }
2112#if DEBUG_CHECK_TINY
2113 SkDebugf("%s [%d] add coincidence [%d] [%d]\n", __FUNCTION__, fID,
2114 thisOther->fID, nextOther->fID);
2115#endif
2116 // this segment is missing a entry that the other contains
2117 // remember so we can add the missing one and recompute the indices
2118 MissingSpan& missing = missingSpans.push_back();
2119 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
caryclark@google.com570863f2013-09-16 15:55:01 +00002120 missing.fSegment = thisOther;
2121 missing.fT = thisSpan->fOtherT;
2122 missing.fOther = nextOther;
2123 missing.fOtherT = nextSpan->fOtherT;
2124 missing.fPt = thisSpan->fPt;
2125 }
2126 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002127 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002128 int missingCount = missingSpans.count();
2129 if (!missingCount) {
2130 return;
2131 }
2132 for (int index = 0; index < missingCount; ++index) {
2133 MissingSpan& missing = missingSpans[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002134 if (missing.fSegment != missing.fOther) {
2135 missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false,
2136 missing.fPt);
2137 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002138 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002139 // OPTIMIZE: consolidate to avoid multiple calls to fix index
caryclark@google.com570863f2013-09-16 15:55:01 +00002140 for (int index = 0; index < missingCount; ++index) {
2141 MissingSpan& missing = missingSpans[index];
2142 missing.fSegment->fixOtherTIndex();
2143 missing.fOther->fixOtherTIndex();
2144 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002145}
2146
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002147bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart,
2148 int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const {
2149 SkASSERT(span->fT == 0 || span->fT == 1);
2150 SkASSERT(span->fOtherT == 0 || span->fOtherT == 1);
2151 const SkOpSpan* otherSpan = &other->span(oEnd);
2152 double refT = otherSpan->fT;
2153 const SkPoint& refPt = otherSpan->fPt;
2154 const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0);
2155 do {
2156 const SkOpSegment* match = span->fOther;
2157 if (match == otherSpan->fOther) {
2158 // find start of respective spans and see if both have winding
2159 int startIndex, endIndex;
2160 if (span->fOtherT == 1) {
2161 endIndex = span->fOtherIndex;
2162 startIndex = match->nextExactSpan(endIndex, -1);
2163 } else {
2164 startIndex = span->fOtherIndex;
2165 endIndex = match->nextExactSpan(startIndex, 1);
2166 }
2167 const SkOpSpan& startSpan = match->span(startIndex);
2168 if (startSpan.fWindValue != 0) {
2169 // draw ray from endSpan.fPt perpendicular to end tangent and measure distance
2170 // to other segment.
2171 const SkOpSpan& endSpan = match->span(endIndex);
2172 SkDLine ray;
2173 SkVector dxdy;
2174 if (span->fOtherT == 1) {
2175 ray.fPts[0].set(startSpan.fPt);
2176 dxdy = match->dxdy(startIndex);
2177 } else {
2178 ray.fPts[0].set(endSpan.fPt);
2179 dxdy = match->dxdy(endIndex);
2180 }
2181 ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY;
2182 ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX;
2183 SkIntersections i;
2184 int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray);
2185 for (int index = 0; index < roots; ++index) {
2186 if (ray.fPts[0].approximatelyEqual(i.pt(index))) {
2187 double matchMidT = (match->span(startIndex).fT
2188 + match->span(endIndex).fT) / 2;
2189 SkPoint matchMidPt = match->ptAtT(matchMidT);
2190 double otherMidT = (i[0][index] + other->span(oStart).fT) / 2;
2191 SkPoint otherMidPt = other->ptAtT(otherMidT);
2192 if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) {
2193 *startPt = startSpan.fPt;
2194 *endPt = endSpan.fPt;
2195 *endT = endSpan.fT;
2196 return true;
2197 }
2198 }
2199 }
2200 }
2201 return false;
2202 }
2203 if (otherSpan == lastSpan) {
2204 break;
2205 }
2206 otherSpan += step;
2207 } while (otherSpan->fT == refT || otherSpan->fPt == refPt);
2208 return false;
2209}
2210
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002211int SkOpSegment::findEndSpan(int endIndex) const {
2212 const SkOpSpan* span = &fTs[--endIndex];
2213 const SkPoint& lastPt = span->fPt;
2214 double endT = span->fT;
2215 do {
2216 span = &fTs[--endIndex];
2217 } while (SkDPoint::ApproximatelyEqual(span->fPt, lastPt) && (span->fT == endT || span->fTiny));
2218 return endIndex + 1;
2219}
2220
caryclark@google.com07393ca2013-04-08 11:47:37 +00002221/*
2222 The M and S variable name parts stand for the operators.
2223 Mi stands for Minuend (see wiki subtraction, analogous to difference)
2224 Su stands for Subtrahend
2225 The Opp variable name part designates that the value is for the Opposite operator.
2226 Opposite values result from combining coincident spans.
2227 */
2228SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
2229 bool* unsortable, SkPathOp op, const int xorMiMask,
2230 const int xorSuMask) {
2231 const int startIndex = *nextStart;
2232 const int endIndex = *nextEnd;
2233 SkASSERT(startIndex != endIndex);
2234 SkDEBUGCODE(const int count = fTs.count());
2235 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
2236 const int step = SkSign32(endIndex - startIndex);
2237 const int end = nextExactSpan(startIndex, step);
2238 SkASSERT(end >= 0);
2239 SkOpSpan* endSpan = &fTs[end];
2240 SkOpSegment* other;
2241 if (isSimple(end)) {
2242 // mark the smaller of startIndex, endIndex done, and all adjacent
2243 // spans with the same T value (but not 'other' spans)
2244#if DEBUG_WINDING
2245 SkDebugf("%s simple\n", __FUNCTION__);
2246#endif
2247 int min = SkMin32(startIndex, endIndex);
2248 if (fTs[min].fDone) {
2249 return NULL;
2250 }
2251 markDoneBinary(min);
2252 other = endSpan->fOther;
2253 *nextStart = endSpan->fOtherIndex;
2254 double startT = other->fTs[*nextStart].fT;
2255 *nextEnd = *nextStart;
2256 do {
2257 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00002258 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002259 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002260 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +00002261 *unsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002262 return NULL;
2263 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002264 return other;
2265 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002266 SkASSERT(startIndex - endIndex != 0);
2267 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002268 // more than one viable candidate -- measure angles to find best
2269
2270 int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp);
caryclark@google.com570863f2013-09-16 15:55:01 +00002271 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002272 if (!sortable) {
2273 *unsortable = true;
2274 return NULL;
2275 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002276 SkOpAngle* angle = spanToAngle(end, startIndex);
2277 if (angle->unorderable()) {
2278 *unsortable = true;
2279 return NULL;
2280 }
2281#if DEBUG_SORT
2282 SkDebugf("%s\n", __FUNCTION__);
2283 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002284#endif
2285 int sumMiWinding = updateWinding(endIndex, startIndex);
2286 int sumSuWinding = updateOppWinding(endIndex, startIndex);
2287 if (operand()) {
2288 SkTSwap<int>(sumMiWinding, sumSuWinding);
2289 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002290 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002291 const SkOpAngle* foundAngle = NULL;
2292 bool foundDone = false;
2293 // iterate through the angle, and compute everyone's winding
2294 SkOpSegment* nextSegment;
2295 int activeCount = 0;
2296 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002297 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002298 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002299 nextAngle->end(), op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002300 if (activeAngle) {
2301 ++activeCount;
2302 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002303 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002304 *unsortable = true;
2305 return NULL;
2306 }
2307 foundAngle = nextAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +00002308 foundDone = nextSegment->done(nextAngle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002309 }
2310 }
2311 if (nextSegment->done()) {
2312 continue;
2313 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002314 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002315 continue;
2316 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002317 if (!activeAngle) {
2318 nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
2319 }
2320 SkOpSpan* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002321 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002322 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002323 *chase->append() = last;
2324#if DEBUG_WINDING
caryclark@google.com570863f2013-09-16 15:55:01 +00002325 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
2326 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
2327 last->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002328#endif
2329 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002330 } while ((nextAngle = nextAngle->next()) != angle);
2331#if DEBUG_ANGLE
2332 if (foundAngle) {
2333 foundAngle->debugSameAs(foundAngle);
2334 }
2335#endif
2336
caryclark@google.com07393ca2013-04-08 11:47:37 +00002337 markDoneBinary(SkMin32(startIndex, endIndex));
2338 if (!foundAngle) {
2339 return NULL;
2340 }
2341 *nextStart = foundAngle->start();
2342 *nextEnd = foundAngle->end();
2343 nextSegment = foundAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002344#if DEBUG_WINDING
2345 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2346 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
2347 #endif
2348 return nextSegment;
2349}
2350
2351SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart,
2352 int* nextEnd, bool* unsortable) {
2353 const int startIndex = *nextStart;
2354 const int endIndex = *nextEnd;
2355 SkASSERT(startIndex != endIndex);
2356 SkDEBUGCODE(const int count = fTs.count());
2357 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
2358 const int step = SkSign32(endIndex - startIndex);
2359 const int end = nextExactSpan(startIndex, step);
2360 SkASSERT(end >= 0);
2361 SkOpSpan* endSpan = &fTs[end];
2362 SkOpSegment* other;
2363 if (isSimple(end)) {
2364 // mark the smaller of startIndex, endIndex done, and all adjacent
2365 // spans with the same T value (but not 'other' spans)
2366#if DEBUG_WINDING
2367 SkDebugf("%s simple\n", __FUNCTION__);
2368#endif
2369 int min = SkMin32(startIndex, endIndex);
2370 if (fTs[min].fDone) {
2371 return NULL;
2372 }
2373 markDoneUnary(min);
2374 other = endSpan->fOther;
2375 *nextStart = endSpan->fOtherIndex;
2376 double startT = other->fTs[*nextStart].fT;
2377 *nextEnd = *nextStart;
2378 do {
2379 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00002380 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002381 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
caryclark@google.com570863f2013-09-16 15:55:01 +00002382 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
2383 *unsortable = true;
2384 return NULL;
2385 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002386 return other;
2387 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002388 SkASSERT(startIndex - endIndex != 0);
2389 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002390 // more than one viable candidate -- measure angles to find best
2391
2392 int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00002393 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002394 if (!sortable) {
2395 *unsortable = true;
2396 return NULL;
2397 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002398 SkOpAngle* angle = spanToAngle(end, startIndex);
2399#if DEBUG_SORT
2400 SkDebugf("%s\n", __FUNCTION__);
2401 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002402#endif
2403 int sumWinding = updateWinding(endIndex, startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002404 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002405 const SkOpAngle* foundAngle = NULL;
2406 bool foundDone = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002407 SkOpSegment* nextSegment;
2408 int activeCount = 0;
2409 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002410 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002411 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002412 &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002413 if (activeAngle) {
2414 ++activeCount;
2415 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002416 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002417 *unsortable = true;
2418 return NULL;
2419 }
2420 foundAngle = nextAngle;
2421 foundDone = nextSegment->done(nextAngle);
2422 }
2423 }
2424 if (nextSegment->done()) {
2425 continue;
2426 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002427 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002428 continue;
2429 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002430 if (!activeAngle) {
2431 nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
2432 }
2433 SkOpSpan* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002434 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002435 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
2436 // assert here that span isn't already in array
caryclark@google.com07393ca2013-04-08 11:47:37 +00002437 *chase->append() = last;
2438#if DEBUG_WINDING
caryclark@google.com570863f2013-09-16 15:55:01 +00002439 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
2440 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
2441 last->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002442#endif
2443 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002444 } while ((nextAngle = nextAngle->next()) != angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002445 markDoneUnary(SkMin32(startIndex, endIndex));
2446 if (!foundAngle) {
2447 return NULL;
2448 }
2449 *nextStart = foundAngle->start();
2450 *nextEnd = foundAngle->end();
2451 nextSegment = foundAngle->segment();
2452#if DEBUG_WINDING
2453 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2454 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
2455 #endif
2456 return nextSegment;
2457}
2458
2459SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsortable) {
2460 const int startIndex = *nextStart;
2461 const int endIndex = *nextEnd;
2462 SkASSERT(startIndex != endIndex);
2463 SkDEBUGCODE(int count = fTs.count());
caryclark@google.com570863f2013-09-16 15:55:01 +00002464 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002465 int step = SkSign32(endIndex - startIndex);
2466 int end = nextExactSpan(startIndex, step);
2467 SkASSERT(end >= 0);
2468 SkOpSpan* endSpan = &fTs[end];
2469 SkOpSegment* other;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002470// Detect cases where all the ends canceled out (e.g.,
2471// there is no angle) and therefore there's only one valid connection
2472 if (isSimple(end) || !spanToAngle(end, startIndex)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002473#if DEBUG_WINDING
2474 SkDebugf("%s simple\n", __FUNCTION__);
2475#endif
2476 int min = SkMin32(startIndex, endIndex);
2477 if (fTs[min].fDone) {
2478 return NULL;
2479 }
2480 markDone(min, 1);
2481 other = endSpan->fOther;
2482 *nextStart = endSpan->fOtherIndex;
2483 double startT = other->fTs[*nextStart].fT;
2484 // FIXME: I don't know why the logic here is difference from the winding case
2485 SkDEBUGCODE(bool firstLoop = true;)
2486 if ((approximately_less_than_zero(startT) && step < 0)
2487 || (approximately_greater_than_one(startT) && step > 0)) {
2488 step = -step;
2489 SkDEBUGCODE(firstLoop = false;)
2490 }
2491 do {
2492 *nextEnd = *nextStart;
2493 do {
2494 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00002495 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002496 if (other->fTs[SkMin32(*nextStart, *nextEnd)].fWindValue) {
2497 break;
2498 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002499 SkASSERT(firstLoop);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002500 SkDEBUGCODE(firstLoop = false;)
2501 step = -step;
2502 } while (true);
2503 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
2504 return other;
2505 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002506 SkASSERT(startIndex - endIndex != 0);
2507 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002508 // parallel block above with presorted version
2509 SkOpAngle* angle = spanToAngle(end, startIndex);
2510 SkASSERT(angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002511#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002512 SkDebugf("%s\n", __FUNCTION__);
2513 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002514#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002515 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002516 const SkOpAngle* foundAngle = NULL;
2517 bool foundDone = false;
2518 SkOpSegment* nextSegment;
2519 int activeCount = 0;
2520 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002521 nextSegment = nextAngle->segment();
2522 ++activeCount;
2523 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002524 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002525 *unsortable = true;
2526 return NULL;
2527 }
2528 foundAngle = nextAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002529 if (!(foundDone = nextSegment->done(nextAngle))) {
2530 break;
2531 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002532 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002533 nextAngle = nextAngle->next();
2534 } while (nextAngle != angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002535 markDone(SkMin32(startIndex, endIndex), 1);
2536 if (!foundAngle) {
2537 return NULL;
2538 }
2539 *nextStart = foundAngle->start();
2540 *nextEnd = foundAngle->end();
2541 nextSegment = foundAngle->segment();
2542#if DEBUG_WINDING
2543 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2544 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
2545 #endif
2546 return nextSegment;
2547}
2548
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002549int SkOpSegment::findStartSpan(int startIndex) const {
2550 int index = startIndex;
2551 const SkOpSpan* span = &fTs[index];
2552 const SkPoint& firstPt = span->fPt;
2553 double firstT = span->fT;
2554 do {
2555 if (index > 0) {
2556 if (span->fT != firstT) {
2557 break;
2558 }
2559 if (!SkDPoint::ApproximatelyEqual(firstPt, fTs[index].fPt)) {
2560 return -1;
2561 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002562 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002563 ++index;
2564 if (span->fTiny) {
2565 if (!SkDPoint::ApproximatelyEqual(firstPt, fTs[index].fPt)) {
2566 return -1;
2567 }
2568 firstT = fTs[index++].fT;
2569 }
2570 span = &fTs[index];
2571 } while (true);
2572 return index;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002573}
2574
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002575int SkOpSegment::findExactT(double t, const SkOpSegment* match) const {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002576 int count = this->count();
2577 for (int index = 0; index < count; ++index) {
2578 const SkOpSpan& span = fTs[index];
2579 if (span.fT == t && span.fOther == match) {
2580 return index;
2581 }
2582 }
2583 SkASSERT(0);
2584 return -1;
2585}
2586
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002587int SkOpSegment::findT(double t, const SkOpSegment* match) const {
2588 int count = this->count();
2589 for (int index = 0; index < count; ++index) {
2590 const SkOpSpan& span = fTs[index];
2591 if (approximately_equal_orderable(span.fT, t) && span.fOther == match) {
2592 return index;
2593 }
2594 }
2595 SkASSERT(0);
2596 return -1;
2597}
caryclark@google.com07393ca2013-04-08 11:47:37 +00002598
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002599SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002600 // iterate through T intersections and return topmost
2601 // topmost tangent from y-min to first pt is closer to horizontal
2602 SkASSERT(!done());
2603 int firstT = -1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002604 /* SkPoint topPt = */ activeLeftTop(true, &firstT);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002605 if (firstT < 0) {
2606 *unsortable = true;
2607 firstT = 0;
2608 while (fTs[firstT].fDone) {
2609 SkASSERT(firstT < fTs.count());
2610 ++firstT;
2611 }
2612 *tIndexPtr = firstT;
2613 *endIndexPtr = nextExactSpan(firstT, 1);
2614 return this;
2615 }
2616 // sort the edges to find the leftmost
2617 int step = 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002618 int end;
2619 if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002620 step = -1;
2621 end = nextSpan(firstT, step);
2622 SkASSERT(end != -1);
2623 }
2624 // if the topmost T is not on end, or is three-way or more, find left
2625 // look for left-ness from tLeft to firstT (matching y of other)
caryclark@google.com07393ca2013-04-08 11:47:37 +00002626 SkASSERT(firstT - end != 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002627 SkOpAngle* markAngle = spanToAngle(firstT, end);
2628 if (!markAngle) {
2629 markAngle = addSingletonAngles(firstT, step);
caryclark@google.com570863f2013-09-16 15:55:01 +00002630 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002631 markAngle->markStops();
2632 const SkOpAngle* baseAngle = markAngle->findFirst();
2633 if (!baseAngle) {
2634 return NULL; // nothing to do
caryclark@google.com570863f2013-09-16 15:55:01 +00002635 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002636 SkScalar top = SK_ScalarMax;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002637 const SkOpAngle* firstAngle = NULL;
2638 const SkOpAngle* angle = baseAngle;
2639 do {
2640 if (!angle->unorderable()) {
2641 SkOpSegment* next = angle->segment();
2642 SkPathOpsBounds bounds;
2643 next->subDivideBounds(angle->end(), angle->start(), &bounds);
2644 if (approximately_greater(top, bounds.fTop)) {
2645 top = bounds.fTop;
2646 firstAngle = angle;
2647 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002648 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002649 angle = angle->next();
2650 } while (angle != baseAngle);
2651 SkASSERT(firstAngle);
2652#if DEBUG_SORT
2653 SkDebugf("%s\n", __FUNCTION__);
2654 firstAngle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002655#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00002656 // skip edges that have already been processed
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002657 angle = firstAngle;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002658 SkOpSegment* leftSegment;
2659 do {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002660// SkASSERT(!angle->unsortable());
caryclark@google.com07393ca2013-04-08 11:47:37 +00002661 leftSegment = angle->segment();
2662 *tIndexPtr = angle->end();
2663 *endIndexPtr = angle->start();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002664 angle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002665 } while (leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone);
2666 if (leftSegment->verb() >= SkPath::kQuad_Verb) {
2667 const int tIndex = *tIndexPtr;
2668 const int endIndex = *endIndexPtr;
2669 if (!leftSegment->clockwise(tIndex, endIndex)) {
2670 bool swap = !leftSegment->monotonicInY(tIndex, endIndex)
2671 && !leftSegment->serpentine(tIndex, endIndex);
2672 #if DEBUG_SWAP_TOP
2673 SkDebugf("%s swap=%d serpentine=%d containedByEnds=%d monotonic=%d\n", __FUNCTION__,
2674 swap,
2675 leftSegment->serpentine(tIndex, endIndex),
2676 leftSegment->controlsContainedByEnds(tIndex, endIndex),
2677 leftSegment->monotonicInY(tIndex, endIndex));
2678 #endif
2679 if (swap) {
2680 // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
2681 // sorted but merely the first not already processed (i.e., not done)
2682 SkTSwap(*tIndexPtr, *endIndexPtr);
2683 }
2684 }
2685 }
2686 SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
2687 return leftSegment;
2688}
2689
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002690int SkOpSegment::firstActive(int tIndex) const {
2691 while (fTs[tIndex].fTiny) {
2692 SkASSERT(!isCanceled(tIndex));
2693 ++tIndex;
2694 }
2695 return tIndex;
2696}
2697
caryclark@google.com07393ca2013-04-08 11:47:37 +00002698// FIXME: not crazy about this
2699// when the intersections are performed, the other index is into an
2700// incomplete array. As the array grows, the indices become incorrect
2701// while the following fixes the indices up again, it isn't smart about
2702// skipping segments whose indices are already correct
2703// assuming we leave the code that wrote the index in the first place
caryclark@google.com570863f2013-09-16 15:55:01 +00002704// FIXME: if called after remove, this needs to correct tiny
caryclark@google.com07393ca2013-04-08 11:47:37 +00002705void SkOpSegment::fixOtherTIndex() {
2706 int iCount = fTs.count();
2707 for (int i = 0; i < iCount; ++i) {
2708 SkOpSpan& iSpan = fTs[i];
2709 double oT = iSpan.fOtherT;
2710 SkOpSegment* other = iSpan.fOther;
2711 int oCount = other->fTs.count();
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002712 SkDEBUGCODE(iSpan.fOtherIndex = -1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002713 for (int o = 0; o < oCount; ++o) {
2714 SkOpSpan& oSpan = other->fTs[o];
2715 if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) {
2716 iSpan.fOtherIndex = o;
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002717 oSpan.fOtherIndex = i;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002718 break;
2719 }
2720 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002721 SkASSERT(iSpan.fOtherIndex >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002722 }
2723}
2724
2725void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
2726 fDoneSpans = 0;
2727 fOperand = operand;
2728 fXor = evenOdd;
2729 fPts = pts;
2730 fVerb = verb;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002731 fLoop = fSmall = fTiny = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002732}
2733
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002734void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002735 int local = spanSign(start, end);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002736 if (angleIncludeType == SkOpAngle::kBinarySingle) {
2737 int oppLocal = oppSign(start, end);
2738 (void) markAndChaseWinding(start, end, local, oppLocal);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002739 // OPTIMIZATION: the reverse mark and chase could skip the first marking
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002740 (void) markAndChaseWinding(end, start, local, oppLocal);
2741 } else {
2742 (void) markAndChaseWinding(start, end, local);
2743 // OPTIMIZATION: the reverse mark and chase could skip the first marking
2744 (void) markAndChaseWinding(end, start, local);
2745 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002746}
2747
2748/*
2749when we start with a vertical intersect, we try to use the dx to determine if the edge is to
2750the left or the right of vertical. This determines if we need to add the span's
2751sign or not. However, this isn't enough.
2752If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
2753If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
2754from has the same x direction as this span, the winding should change. If the dx is opposite, then
2755the same winding is shared by both.
2756*/
2757void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
2758 int oppWind, SkScalar hitOppDx) {
2759 SkASSERT(hitDx || !winding);
reed@google.com277c3f82013-05-31 15:17:50 +00002760 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002761 SkASSERT(dx);
2762 int windVal = windValue(SkMin32(start, end));
2763#if DEBUG_WINDING_AT_T
2764 SkDebugf("%s oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, winding,
2765 hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
2766#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002767 int sideWind = winding + (dx < 0 ? windVal : -windVal);
2768 if (abs(winding) < abs(sideWind)) {
2769 winding = sideWind;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002770 }
2771#if DEBUG_WINDING_AT_T
2772 SkDebugf(" winding=%d\n", winding);
2773#endif
2774 SkDEBUGCODE(int oppLocal = oppSign(start, end));
2775 SkASSERT(hitOppDx || !oppWind || !oppLocal);
2776 int oppWindVal = oppValue(SkMin32(start, end));
2777 if (!oppWind) {
2778 oppWind = dx < 0 ? oppWindVal : -oppWindVal;
2779 } else if (hitOppDx * dx >= 0) {
2780 int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
2781 if (abs(oppWind) < abs(oppSideWind)) {
2782 oppWind = oppSideWind;
2783 }
2784 }
2785 (void) markAndChaseWinding(start, end, winding, oppWind);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002786 // OPTIMIZATION: the reverse mark and chase could skip the first marking
2787 (void) markAndChaseWinding(end, start, winding, oppWind);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002788}
2789
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002790bool SkOpSegment::inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const {
2791 if (!baseAngle->inLoop()) {
2792 return false;
2793 }
2794 int index = *indexPtr;
2795 int froIndex = fTs[index].fFromAngleIndex;
2796 int toIndex = fTs[index].fToAngleIndex;
2797 while (++index < spanCount) {
2798 int nextFro = fTs[index].fFromAngleIndex;
2799 int nextTo = fTs[index].fToAngleIndex;
2800 if (froIndex != nextFro || toIndex != nextTo) {
2801 break;
2802 }
2803 }
2804 *indexPtr = index;
2805 return true;
2806}
2807
caryclark@google.com07393ca2013-04-08 11:47:37 +00002808// OPTIMIZE: successive calls could start were the last leaves off
2809// or calls could specialize to walk forwards or backwards
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002810bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002811 int tCount = fTs.count();
2812 for (int index = 0; index < tCount; ++index) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002813 const SkOpSpan& span = fTs[index];
2814 if (approximately_zero(startT - span.fT) && pt == span.fPt) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002815 return false;
2816 }
2817 }
2818 return true;
2819}
2820
2821bool SkOpSegment::isSimple(int end) const {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002822#if 1
caryclark@google.com07393ca2013-04-08 11:47:37 +00002823 int count = fTs.count();
2824 if (count == 2) {
2825 return true;
2826 }
2827 double t = fTs[end].fT;
2828 if (approximately_less_than_zero(t)) {
2829 return !approximately_less_than_zero(fTs[1].fT);
2830 }
2831 if (approximately_greater_than_one(t)) {
2832 return !approximately_greater_than_one(fTs[count - 2].fT);
2833 }
2834 return false;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002835#else
2836 // OPTIMIZE: code could be rejiggered to use this simpler test. To make this work, a little
2837 // more logic is required to ignore the dangling canceled out spans
2838 const SkOpSpan& span = fTs[end];
2839 return span.fFromAngleIndex < 0 && span.fToAngleIndex < 0;
2840#endif
2841}
2842
2843bool SkOpSegment::isSmall(const SkOpAngle* angle) const {
2844 int start = angle->start();
2845 int end = angle->end();
2846 const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
2847 return mSpan.fSmall;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002848}
2849
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002850bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
2851 int start = angle->start();
2852 int end = angle->end();
2853 const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
2854 return mSpan.fTiny;
2855}
2856
2857bool SkOpSegment::isTiny(int index) const {
2858 return fTs[index].fTiny;
2859}
2860
2861// look pair of active edges going away from coincident edge
2862// one of them should be the continuation of other
2863// if both are active, look to see if they both the connect to another coincident pair
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002864// if at least one is a line, then make the pair coincident
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002865// if neither is a line, test for coincidence
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002866bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, int step, bool cancel) {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002867 int otherTIndex = other->findT(otherT, this);
2868 int next = other->nextExactSpan(otherTIndex, step);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002869 int otherMin = SkMin32(otherTIndex, next);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002870 int otherWind = other->span(otherMin).fWindValue;
2871 if (otherWind == 0) {
2872 return false;
2873 }
2874 SkASSERT(next >= 0);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00002875 int tIndex = 0;
2876 do {
2877 SkOpSpan* test = &fTs[tIndex];
2878 SkASSERT(test->fT == 0);
2879 if (test->fOther == other || test->fOtherT != 1) {
2880 continue;
2881 }
2882 SkPoint startPt, endPt;
2883 double endT;
2884 if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) {
2885 SkOpSegment* match = test->fOther;
2886 if (cancel) {
2887 match->addTCancel(startPt, endPt, other);
2888 } else {
2889 match->addTCoincident(startPt, endPt, endT, other);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002890 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00002891 return true;
2892 }
2893 } while (fTs[++tIndex].fT == 0);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002894 return false;
2895}
2896
caryclark@google.com07393ca2013-04-08 11:47:37 +00002897// this span is excluded by the winding rule -- chase the ends
2898// as long as they are unambiguous to mark connections as done
2899// and give them the same winding value
caryclark@google.com07393ca2013-04-08 11:47:37 +00002900
2901SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
2902 int step = SkSign32(endIndex - index);
2903 int min = SkMin32(index, endIndex);
2904 markDoneBinary(min);
2905 SkOpSpan* last;
2906 SkOpSegment* other = this;
2907 while ((other = other->nextChase(&index, step, &min, &last))) {
2908 if (other->done()) {
2909 return NULL;
2910 }
2911 other->markDoneBinary(min);
2912 }
2913 return last;
2914}
2915
2916SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
2917 int step = SkSign32(endIndex - index);
2918 int min = SkMin32(index, endIndex);
2919 markDoneUnary(min);
2920 SkOpSpan* last;
2921 SkOpSegment* other = this;
2922 while ((other = other->nextChase(&index, step, &min, &last))) {
2923 if (other->done()) {
2924 return NULL;
2925 }
2926 other->markDoneUnary(min);
2927 }
2928 return last;
2929}
2930
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002931SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002932 int index = angle->start();
2933 int endIndex = angle->end();
2934 int step = SkSign32(endIndex - index);
2935 int min = SkMin32(index, endIndex);
2936 markWinding(min, winding);
2937 SkOpSpan* last;
2938 SkOpSegment* other = this;
2939 while ((other = other->nextChase(&index, step, &min, &last))) {
2940 if (other->fTs[min].fWindSum != SK_MinS32) {
2941 SkASSERT(other->fTs[min].fWindSum == winding);
2942 return NULL;
2943 }
2944 other->markWinding(min, winding);
2945 }
2946 return last;
2947}
2948
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002949SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding) {
2950 int min = SkMin32(index, endIndex);
2951 int step = SkSign32(endIndex - index);
2952 markWinding(min, winding);
2953 SkOpSpan* last;
2954 SkOpSegment* other = this;
2955 while ((other = other->nextChase(&index, step, &min, &last))) {
2956 if (other->fTs[min].fWindSum != SK_MinS32) {
2957 SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
2958 return NULL;
2959 }
2960 other->markWinding(min, winding);
2961 }
2962 return last;
2963}
2964
caryclark@google.com07393ca2013-04-08 11:47:37 +00002965SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
2966 int min = SkMin32(index, endIndex);
2967 int step = SkSign32(endIndex - index);
2968 markWinding(min, winding, oppWinding);
2969 SkOpSpan* last;
2970 SkOpSegment* other = this;
2971 while ((other = other->nextChase(&index, step, &min, &last))) {
2972 if (other->fTs[min].fWindSum != SK_MinS32) {
2973 SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
2974 return NULL;
2975 }
2976 other->markWinding(min, winding, oppWinding);
2977 }
2978 return last;
2979}
2980
2981SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding) {
2982 int start = angle->start();
2983 int end = angle->end();
2984 return markAndChaseWinding(start, end, winding, oppWinding);
2985}
2986
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00002987SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002988 SkASSERT(angle->segment() == this);
2989 if (UseInnerWinding(maxWinding, sumWinding)) {
2990 maxWinding = sumWinding;
2991 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00002992 SkOpSpan* last = markAndChaseWinding(angle, maxWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00002993#if DEBUG_WINDING
2994 if (last) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002995 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
2996 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2997 SkPathOpsDebug::WindingPrintf(last->fWindSum);
2998 SkDebugf(" small=%d\n", last->fSmall);
caryclark@google.com570863f2013-09-16 15:55:01 +00002999 }
3000#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003001 return last;
3002}
3003
3004SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003005 int oppSumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003006 SkASSERT(angle->segment() == this);
3007 if (UseInnerWinding(maxWinding, sumWinding)) {
3008 maxWinding = sumWinding;
3009 }
3010 if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
3011 oppMaxWinding = oppSumWinding;
3012 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003013 SkOpSpan* last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00003014#if DEBUG_WINDING
3015 if (last) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003016 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
3017 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
3018 SkPathOpsDebug::WindingPrintf(last->fWindSum);
3019 SkDebugf(" small=%d\n", last->fSmall);
caryclark@google.com570863f2013-09-16 15:55:01 +00003020 }
3021#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003022 return last;
3023}
3024
3025// FIXME: this should also mark spans with equal (x,y)
3026// This may be called when the segment is already marked done. While this
3027// wastes time, it shouldn't do any more than spin through the T spans.
3028// OPTIMIZATION: abort on first done found (assuming that this code is
3029// always called to mark segments done).
3030void SkOpSegment::markDone(int index, int winding) {
3031 // SkASSERT(!done());
3032 SkASSERT(winding);
3033 double referenceT = fTs[index].fT;
3034 int lesser = index;
3035 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3036 markOneDone(__FUNCTION__, lesser, winding);
3037 }
3038 do {
3039 markOneDone(__FUNCTION__, index, winding);
3040 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003041 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003042}
3043
caryclark@google.com07393ca2013-04-08 11:47:37 +00003044void SkOpSegment::markDoneBinary(int index) {
3045 double referenceT = fTs[index].fT;
3046 int lesser = index;
3047 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3048 markOneDoneBinary(__FUNCTION__, lesser);
3049 }
3050 do {
3051 markOneDoneBinary(__FUNCTION__, index);
3052 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003053 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003054}
3055
3056void SkOpSegment::markDoneUnary(int index) {
3057 double referenceT = fTs[index].fT;
3058 int lesser = index;
3059 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3060 markOneDoneUnary(__FUNCTION__, lesser);
3061 }
3062 do {
3063 markOneDoneUnary(__FUNCTION__, index);
3064 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003065 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003066}
3067
3068void SkOpSegment::markOneDone(const char* funName, int tIndex, int winding) {
3069 SkOpSpan* span = markOneWinding(funName, tIndex, winding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003070 if (!span || span->fDone) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003071 return;
3072 }
3073 span->fDone = true;
3074 fDoneSpans++;
3075}
3076
3077void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
3078 SkOpSpan* span = verifyOneWinding(funName, tIndex);
3079 if (!span) {
3080 return;
3081 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003082 SkASSERT(!span->fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003083 span->fDone = true;
3084 fDoneSpans++;
3085}
3086
caryclark@google.com07393ca2013-04-08 11:47:37 +00003087void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
3088 SkOpSpan* span = verifyOneWindingU(funName, tIndex);
3089 if (!span) {
3090 return;
3091 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003092 if (span->fWindSum == SK_MinS32) {
3093 SkDebugf("%s uncomputed\n", __FUNCTION__);
3094 }
3095 SkASSERT(!span->fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003096 span->fDone = true;
3097 fDoneSpans++;
3098}
3099
3100SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding) {
3101 SkOpSpan& span = fTs[tIndex];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003102 if (span.fDone && !span.fSmall) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003103 return NULL;
3104 }
3105#if DEBUG_MARK_DONE
3106 debugShowNewWinding(funName, span, winding);
3107#endif
3108 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com570863f2013-09-16 15:55:01 +00003109 SkASSERT(abs(winding) <= SkPathOpsDebug::gMaxWindSum);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003110 span.fWindSum = winding;
3111 return &span;
3112}
3113
3114SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
3115 int oppWinding) {
3116 SkOpSpan& span = fTs[tIndex];
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003117 if (span.fDone && !span.fSmall) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003118 return NULL;
3119 }
3120#if DEBUG_MARK_DONE
3121 debugShowNewWinding(funName, span, winding, oppWinding);
3122#endif
3123 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com570863f2013-09-16 15:55:01 +00003124 SkASSERT(abs(winding) <= SkPathOpsDebug::gMaxWindSum);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003125 span.fWindSum = winding;
3126 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00003127 SkASSERT(abs(oppWinding) <= SkPathOpsDebug::gMaxWindSum);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003128 span.fOppSum = oppWinding;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003129 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003130 return &span;
3131}
3132
3133// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
3134bool SkOpSegment::clockwise(int tStart, int tEnd) const {
3135 SkASSERT(fVerb != SkPath::kLine_Verb);
3136 SkPoint edge[4];
3137 subDivide(tStart, tEnd, edge);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003138 int points = SkPathOpsVerbToPoints(fVerb);
3139 double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003140 if (fVerb == SkPath::kCubic_Verb) {
3141 SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
3142 if (edge[1].fY < lesser && edge[2].fY < lesser) {
3143 SkDLine tangent1 = {{ {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} }};
3144 SkDLine tangent2 = {{ {edge[2].fX, edge[2].fY}, {edge[3].fX, edge[3].fY} }};
3145 if (SkIntersections::Test(tangent1, tangent2)) {
3146 SkPoint topPt = cubic_top(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3147 sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
3148 sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
3149 return sum <= 0;
3150 }
3151 }
3152 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003153 for (int idx = 0; idx < points; ++idx){
caryclark@google.com07393ca2013-04-08 11:47:37 +00003154 sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
3155 }
3156 return sum <= 0;
3157}
3158
3159bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
3160 if (fVerb == SkPath::kLine_Verb) {
3161 return false;
3162 }
3163 if (fVerb == SkPath::kQuad_Verb) {
3164 SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3165 return dst.monotonicInY();
3166 }
3167 SkASSERT(fVerb == SkPath::kCubic_Verb);
3168 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3169 return dst.monotonicInY();
3170}
3171
3172bool SkOpSegment::serpentine(int tStart, int tEnd) const {
3173 if (fVerb != SkPath::kCubic_Verb) {
3174 return false;
3175 }
3176 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3177 return dst.serpentine();
3178}
3179
3180SkOpSpan* SkOpSegment::verifyOneWinding(const char* funName, int tIndex) {
3181 SkOpSpan& span = fTs[tIndex];
3182 if (span.fDone) {
3183 return NULL;
3184 }
3185#if DEBUG_MARK_DONE
3186 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
3187#endif
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003188// If the prior angle in the sort is unorderable, the winding sum may not be computable.
3189// To enable the assert, the 'prior is unorderable' state could be
3190// piped down to this test, but not sure it's worth it.
3191// (Once the sort order is stored in the span, this test may be feasible.)
3192// SkASSERT(span.fWindSum != SK_MinS32);
3193// SkASSERT(span.fOppSum != SK_MinS32);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003194 return &span;
3195}
3196
3197SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
3198 SkOpSpan& span = fTs[tIndex];
3199 if (span.fDone) {
3200 return NULL;
3201 }
3202#if DEBUG_MARK_DONE
3203 debugShowNewWinding(funName, span, span.fWindSum);
3204#endif
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003205// If the prior angle in the sort is unorderable, the winding sum may not be computable.
3206// To enable the assert, the 'prior is unorderable' state could be
3207// piped down to this test, but not sure it's worth it.
3208// (Once the sort order is stored in the span, this test may be feasible.)
3209// SkASSERT(span.fWindSum != SK_MinS32);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003210 return &span;
3211}
3212
3213// note that just because a span has one end that is unsortable, that's
3214// not enough to mark it done. The other end may be sortable, allowing the
3215// span to be added.
3216// FIXME: if abs(start - end) > 1, mark intermediates as unsortable on both ends
3217void SkOpSegment::markUnsortable(int start, int end) {
3218 SkOpSpan* span = &fTs[start];
3219 if (start < end) {
3220#if DEBUG_UNSORTABLE
3221 debugShowNewWinding(__FUNCTION__, *span, 0);
3222#endif
3223 span->fUnsortableStart = true;
3224 } else {
3225 --span;
3226#if DEBUG_UNSORTABLE
3227 debugShowNewWinding(__FUNCTION__, *span, 0);
3228#endif
3229 span->fUnsortableEnd = true;
3230 }
3231 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003232 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003233 return;
3234 }
3235 span->fDone = true;
3236 fDoneSpans++;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003237 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003238}
3239
3240void SkOpSegment::markWinding(int index, int winding) {
3241// SkASSERT(!done());
3242 SkASSERT(winding);
3243 double referenceT = fTs[index].fT;
3244 int lesser = index;
3245 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3246 markOneWinding(__FUNCTION__, lesser, winding);
3247 }
3248 do {
3249 markOneWinding(__FUNCTION__, index, winding);
3250 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003251 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003252}
3253
3254void SkOpSegment::markWinding(int index, int winding, int oppWinding) {
3255// SkASSERT(!done());
3256 SkASSERT(winding || oppWinding);
3257 double referenceT = fTs[index].fT;
3258 int lesser = index;
3259 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3260 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
3261 }
3262 do {
3263 markOneWinding(__FUNCTION__, index, winding, oppWinding);
3264 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003265 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003266}
3267
3268void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
3269 int nextDoorWind = SK_MaxS32;
3270 int nextOppWind = SK_MaxS32;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003271 // prefer exact matches
caryclark@google.com07393ca2013-04-08 11:47:37 +00003272 if (tIndex > 0) {
3273 const SkOpSpan& below = fTs[tIndex - 1];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003274 if (below.fT == t) {
3275 nextDoorWind = below.fWindValue;
3276 nextOppWind = below.fOppValue;
3277 }
3278 }
3279 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3280 const SkOpSpan& above = fTs[tIndex + 1];
3281 if (above.fT == t) {
3282 nextDoorWind = above.fWindValue;
3283 nextOppWind = above.fOppValue;
3284 }
3285 }
3286 if (nextDoorWind == SK_MaxS32 && tIndex > 0) {
3287 const SkOpSpan& below = fTs[tIndex - 1];
caryclark@google.com07393ca2013-04-08 11:47:37 +00003288 if (approximately_negative(t - below.fT)) {
3289 nextDoorWind = below.fWindValue;
3290 nextOppWind = below.fOppValue;
3291 }
3292 }
3293 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3294 const SkOpSpan& above = fTs[tIndex + 1];
3295 if (approximately_negative(above.fT - t)) {
3296 nextDoorWind = above.fWindValue;
3297 nextOppWind = above.fOppValue;
3298 }
3299 }
3300 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
3301 const SkOpSpan& below = fTs[tIndex - 1];
3302 nextDoorWind = below.fWindValue;
3303 nextOppWind = below.fOppValue;
3304 }
3305 if (nextDoorWind != SK_MaxS32) {
3306 SkOpSpan& newSpan = fTs[tIndex];
3307 newSpan.fWindValue = nextDoorWind;
3308 newSpan.fOppValue = nextOppWind;
3309 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
3310 newSpan.fDone = true;
3311 ++fDoneSpans;
3312 }
3313 }
3314}
3315
3316// return span if when chasing, two or more radiating spans are not done
3317// OPTIMIZATION: ? multiple spans is detected when there is only one valid
3318// candidate and the remaining spans have windValue == 0 (canceled by
3319// coincidence). The coincident edges could either be removed altogether,
3320// or this code could be more complicated in detecting this case. Worth it?
3321bool SkOpSegment::multipleSpans(int end) const {
3322 return end > 0 && end < fTs.count() - 1;
3323}
3324
3325bool SkOpSegment::nextCandidate(int* start, int* end) const {
3326 while (fTs[*end].fDone) {
3327 if (fTs[*end].fT == 1) {
3328 return false;
3329 }
3330 ++(*end);
3331 }
3332 *start = *end;
3333 *end = nextExactSpan(*start, 1);
3334 return true;
3335}
3336
3337SkOpSegment* SkOpSegment::nextChase(int* index, const int step, int* min, SkOpSpan** last) {
3338 int end = nextExactSpan(*index, step);
3339 SkASSERT(end >= 0);
caryclark@google.com570863f2013-09-16 15:55:01 +00003340 if (fTs[end].fSmall) {
3341 *last = NULL;
3342 return NULL;
3343 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003344 if (multipleSpans(end)) {
3345 *last = &fTs[end];
3346 return NULL;
3347 }
3348 const SkOpSpan& endSpan = fTs[end];
3349 SkOpSegment* other = endSpan.fOther;
3350 *index = endSpan.fOtherIndex;
fmalita@google.com22eb7942013-05-01 20:35:51 +00003351 SkASSERT(*index >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003352 int otherEnd = other->nextExactSpan(*index, step);
3353 SkASSERT(otherEnd >= 0);
3354 *min = SkMin32(*index, otherEnd);
caryclark@google.com570863f2013-09-16 15:55:01 +00003355 if (other->fTs[*min].fSmall) {
caryclark@google.coma5e55922013-05-07 18:51:31 +00003356 *last = NULL;
3357 return NULL;
3358 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003359 return other;
3360}
3361
3362// This has callers for two different situations: one establishes the end
3363// of the current span, and one establishes the beginning of the next span
3364// (thus the name). When this is looking for the end of the current span,
3365// coincidence is found when the beginning Ts contain -step and the end
3366// contains step. When it is looking for the beginning of the next, the
3367// first Ts found can be ignored and the last Ts should contain -step.
3368// OPTIMIZATION: probably should split into two functions
3369int SkOpSegment::nextSpan(int from, int step) const {
3370 const SkOpSpan& fromSpan = fTs[from];
3371 int count = fTs.count();
3372 int to = from;
3373 while (step > 0 ? ++to < count : --to >= 0) {
3374 const SkOpSpan& span = fTs[to];
3375 if (approximately_zero(span.fT - fromSpan.fT)) {
3376 continue;
3377 }
3378 return to;
3379 }
3380 return -1;
3381}
3382
3383// FIXME
3384// this returns at any difference in T, vs. a preset minimum. It may be
3385// that all callers to nextSpan should use this instead.
caryclark@google.com07393ca2013-04-08 11:47:37 +00003386int SkOpSegment::nextExactSpan(int from, int step) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003387 int to = from;
caryclark@google.com570863f2013-09-16 15:55:01 +00003388 if (step < 0) {
3389 const SkOpSpan& fromSpan = fTs[from];
3390 while (--to >= 0) {
3391 const SkOpSpan& span = fTs[to];
3392 if (precisely_negative(fromSpan.fT - span.fT) || span.fTiny) {
3393 continue;
3394 }
3395 return to;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003396 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003397 } else {
3398 while (fTs[from].fTiny) {
3399 from++;
3400 }
3401 const SkOpSpan& fromSpan = fTs[from];
3402 int count = fTs.count();
3403 while (++to < count) {
3404 const SkOpSpan& span = fTs[to];
3405 if (precisely_negative(span.fT - fromSpan.fT)) {
3406 continue;
3407 }
3408 return to;
3409 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003410 }
3411 return -1;
3412}
3413
3414void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
3415 int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
3416 int deltaSum = spanSign(index, endIndex);
3417 int oppDeltaSum = oppSign(index, endIndex);
3418 if (operand()) {
3419 *maxWinding = *sumSuWinding;
3420 *sumWinding = *sumSuWinding -= deltaSum;
3421 *oppMaxWinding = *sumMiWinding;
3422 *oppSumWinding = *sumMiWinding -= oppDeltaSum;
3423 } else {
3424 *maxWinding = *sumMiWinding;
3425 *sumWinding = *sumMiWinding -= deltaSum;
3426 *oppMaxWinding = *sumSuWinding;
3427 *oppSumWinding = *sumSuWinding -= oppDeltaSum;
3428 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003429 SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum);
3430 SkASSERT(abs(*oppSumWinding) <= SkPathOpsDebug::gMaxWindSum);
3431}
3432
3433void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
3434 int* maxWinding, int* sumWinding) {
3435 int deltaSum = spanSign(index, endIndex);
3436 *maxWinding = *sumMiWinding;
3437 *sumWinding = *sumMiWinding -= deltaSum;
3438 SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003439}
3440
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003441void SkOpSegment::sortAngles() {
3442 int spanCount = fTs.count();
3443 if (spanCount <= 2) {
3444 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003445 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003446 int index = 0;
3447 do {
3448 int fromIndex = fTs[index].fFromAngleIndex;
3449 int toIndex = fTs[index].fToAngleIndex;
3450 if (fromIndex < 0 && toIndex < 0) {
3451 index += 1;
3452 continue;
3453 }
3454 SkOpAngle* baseAngle = NULL;
3455 if (fromIndex >= 0) {
3456 baseAngle = &this->angle(fromIndex);
3457 if (inLoop(baseAngle, spanCount, &index)) {
3458 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003459 }
3460 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003461#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003462 bool wroteAfterHeader = false;
caryclark@google.com570863f2013-09-16 15:55:01 +00003463#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003464 if (toIndex >= 0) {
3465 SkOpAngle* toAngle = &this->angle(toIndex);
3466 if (!baseAngle) {
3467 baseAngle = toAngle;
3468 if (inLoop(baseAngle, spanCount, &index)) {
3469 continue;
3470 }
3471 } else {
3472 SkDEBUGCODE(int newIndex = index);
3473 SkASSERT(!inLoop(baseAngle, spanCount, &newIndex) && newIndex == index);
3474#if DEBUG_ANGLE
3475 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
3476 index);
3477 wroteAfterHeader = true;
3478#endif
3479 baseAngle->insert(toAngle);
3480 }
3481 }
3482 int nextFrom, nextTo;
3483 int firstIndex = index;
3484 do {
3485 SkOpSpan& span = fTs[index];
3486 SkOpSegment* other = span.fOther;
3487 SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
3488 int otherAngleIndex = oSpan.fFromAngleIndex;
3489 if (otherAngleIndex >= 0) {
3490#if DEBUG_ANGLE
3491 if (!wroteAfterHeader) {
3492 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
3493 index);
3494 wroteAfterHeader = true;
3495 }
3496#endif
3497 baseAngle->insert(&other->angle(otherAngleIndex));
3498 }
3499 otherAngleIndex = oSpan.fToAngleIndex;
3500 if (otherAngleIndex >= 0) {
3501#if DEBUG_ANGLE
3502 if (!wroteAfterHeader) {
3503 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
3504 index);
3505 wroteAfterHeader = true;
3506 }
3507#endif
3508 baseAngle->insert(&other->angle(otherAngleIndex));
3509 }
3510 if (++index == spanCount) {
3511 break;
3512 }
3513 nextFrom = fTs[index].fFromAngleIndex;
3514 nextTo = fTs[index].fToAngleIndex;
3515 } while (fromIndex == nextFrom && toIndex == nextTo);
3516 if (baseAngle && baseAngle->loopCount() == 1) {
3517 index = firstIndex;
3518 do {
3519 SkOpSpan& span = fTs[index];
3520 span.fFromAngleIndex = span.fToAngleIndex = -1;
3521 if (++index == spanCount) {
3522 break;
3523 }
3524 nextFrom = fTs[index].fFromAngleIndex;
3525 nextTo = fTs[index].fToAngleIndex;
3526 } while (fromIndex == nextFrom && toIndex == nextTo);
3527 baseAngle = NULL;
3528 }
3529#if DEBUG_SORT
3530 SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
3531#endif
3532 } while (index < spanCount);
caryclark@google.com570863f2013-09-16 15:55:01 +00003533}
3534
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003535// return true if midpoints were computed
3536bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
3537 SkASSERT(start != end);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003538 edge[0] = fTs[start].fPt;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003539 int points = SkPathOpsVerbToPoints(fVerb);
3540 edge[points] = fTs[end].fPt;
3541 if (fVerb == SkPath::kLine_Verb) {
3542 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003543 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003544 double startT = fTs[start].fT;
3545 double endT = fTs[end].fT;
3546 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
3547 // don't compute midpoints if we already have them
3548 if (fVerb == SkPath::kQuad_Verb) {
3549 edge[1] = fPts[1];
3550 return false;
3551 }
3552 SkASSERT(fVerb == SkPath::kCubic_Verb);
3553 if (start < end) {
3554 edge[1] = fPts[1];
3555 edge[2] = fPts[2];
3556 return false;
3557 }
3558 edge[1] = fPts[2];
3559 edge[2] = fPts[1];
3560 return false;
3561 }
3562 const SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[points].fX, edge[points].fY }};
3563 if (fVerb == SkPath::kQuad_Verb) {
3564 edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint();
3565 } else {
3566 SkASSERT(fVerb == SkPath::kCubic_Verb);
3567 SkDPoint ctrl[2];
3568 SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl);
3569 edge[1] = ctrl[0].asSkPoint();
3570 edge[2] = ctrl[1].asSkPoint();
3571 }
3572 return true;
3573}
3574
3575// return true if midpoints were computed
3576bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
3577 SkASSERT(start != end);
3578 (*result)[0].set(fTs[start].fPt);
3579 int points = SkPathOpsVerbToPoints(fVerb);
3580 (*result)[points].set(fTs[end].fPt);
3581 if (fVerb == SkPath::kLine_Verb) {
3582 return false;
3583 }
3584 double startT = fTs[start].fT;
3585 double endT = fTs[end].fT;
3586 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
3587 // don't compute midpoints if we already have them
3588 if (fVerb == SkPath::kQuad_Verb) {
3589 (*result)[1].set(fPts[1]);
3590 return false;
3591 }
3592 SkASSERT(fVerb == SkPath::kCubic_Verb);
3593 if (start < end) {
3594 (*result)[1].set(fPts[1]);
3595 (*result)[2].set(fPts[2]);
3596 return false;
3597 }
3598 (*result)[1].set(fPts[2]);
3599 (*result)[2].set(fPts[1]);
3600 return false;
3601 }
3602 if (fVerb == SkPath::kQuad_Verb) {
3603 (*result)[1] = SkDQuad::SubDivide(fPts, (*result)[0], (*result)[2], startT, endT);
3604 } else {
3605 SkASSERT(fVerb == SkPath::kCubic_Verb);
3606 SkDCubic::SubDivide(fPts, (*result)[0], (*result)[3], startT, endT, &(*result)[1]);
3607 }
3608 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003609}
3610
3611void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
3612 SkPoint edge[4];
3613 subDivide(start, end, edge);
reed@google.com277c3f82013-05-31 15:17:50 +00003614 (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003615}
3616
caryclark@google.com570863f2013-09-16 15:55:01 +00003617void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt,
3618 const SkPoint& startPt) {
3619 int outCount = outsidePts->count();
3620 if (outCount == 0 || endPt != (*outsidePts)[outCount - 2]) {
3621 outsidePts->push_back(endPt);
3622 outsidePts->push_back(startPt);
3623 }
3624}
3625
3626void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoint& startPt) {
3627 int outCount = outsidePts->count();
3628 if (outCount == 0 || startPt != (*outsidePts)[outCount - 1]) {
3629 outsidePts->push_back(startPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003630 }
3631}
3632
3633void SkOpSegment::undoneSpan(int* start, int* end) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003634 int tCount = fTs.count();
3635 int index;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003636 for (index = 0; index < tCount; ++index) {
3637 if (!fTs[index].fDone) {
3638 break;
3639 }
3640 }
3641 SkASSERT(index < tCount - 1);
3642 *start = index;
3643 double startT = fTs[index].fT;
3644 while (approximately_negative(fTs[++index].fT - startT))
3645 SkASSERT(index < tCount);
3646 SkASSERT(index < tCount);
3647 *end = index;
3648}
3649
3650int SkOpSegment::updateOppWinding(int index, int endIndex) const {
3651 int lesser = SkMin32(index, endIndex);
3652 int oppWinding = oppSum(lesser);
3653 int oppSpanWinding = oppSign(index, endIndex);
3654 if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
3655 && oppWinding != SK_MaxS32) {
3656 oppWinding -= oppSpanWinding;
3657 }
3658 return oppWinding;
3659}
3660
3661int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
3662 int startIndex = angle->start();
3663 int endIndex = angle->end();
3664 return updateOppWinding(endIndex, startIndex);
3665}
3666
3667int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
3668 int startIndex = angle->start();
3669 int endIndex = angle->end();
3670 return updateOppWinding(startIndex, endIndex);
3671}
3672
3673int SkOpSegment::updateWinding(int index, int endIndex) const {
3674 int lesser = SkMin32(index, endIndex);
3675 int winding = windSum(lesser);
3676 int spanWinding = spanSign(index, endIndex);
caryclark@google.com570863f2013-09-16 15:55:01 +00003677 if (winding && UseInnerWinding(winding - spanWinding, winding)
3678 && winding != SK_MaxS32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003679 winding -= spanWinding;
3680 }
3681 return winding;
3682}
3683
3684int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
3685 int startIndex = angle->start();
3686 int endIndex = angle->end();
3687 return updateWinding(endIndex, startIndex);
3688}
3689
caryclark@google.com570863f2013-09-16 15:55:01 +00003690int SkOpSegment::updateWindingReverse(int index, int endIndex) const {
3691 int lesser = SkMin32(index, endIndex);
3692 int winding = windSum(lesser);
3693 int spanWinding = spanSign(endIndex, index);
3694 if (winding && UseInnerWindingReverse(winding - spanWinding, winding)
3695 && winding != SK_MaxS32) {
3696 winding -= spanWinding;
3697 }
3698 return winding;
3699}
3700
caryclark@google.com07393ca2013-04-08 11:47:37 +00003701int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
3702 int startIndex = angle->start();
3703 int endIndex = angle->end();
caryclark@google.com570863f2013-09-16 15:55:01 +00003704 return updateWindingReverse(endIndex, startIndex);
3705}
3706
3707// OPTIMIZATION: does the following also work, and is it any faster?
3708// return outerWinding * innerWinding > 0
3709// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
3710bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
3711 SkASSERT(outerWinding != SK_MaxS32);
3712 SkASSERT(innerWinding != SK_MaxS32);
3713 int absOut = abs(outerWinding);
3714 int absIn = abs(innerWinding);
3715 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
3716 return result;
3717}
3718
3719bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) {
3720 SkASSERT(outerWinding != SK_MaxS32);
3721 SkASSERT(innerWinding != SK_MaxS32);
3722 int absOut = abs(outerWinding);
3723 int absIn = abs(innerWinding);
3724 bool result = absOut == absIn ? true : absOut < absIn;
3725 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003726}
3727
3728int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const {
3729 if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
3730 return SK_MinS32;
3731 }
3732 int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
3733 SkASSERT(winding != SK_MinS32);
3734 int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
3735#if DEBUG_WINDING_AT_T
3736 SkDebugf("%s oldWinding=%d windValue=%d", __FUNCTION__, winding, windVal);
3737#endif
3738 // see if a + change in T results in a +/- change in X (compute x'(T))
reed@google.com277c3f82013-05-31 15:17:50 +00003739 *dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003740 if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
3741 *dx = fPts[2].fX - fPts[1].fX - *dx;
3742 }
3743 if (*dx == 0) {
3744#if DEBUG_WINDING_AT_T
3745 SkDebugf(" dx=0 winding=SK_MinS32\n");
3746#endif
3747 return SK_MinS32;
3748 }
skia.committer@gmail.coma4aced42013-07-09 07:00:56 +00003749 if (windVal < 0) { // reverse sign if opp contour traveled in reverse
caryclark@google.com07e97fc2013-07-08 17:17:02 +00003750 *dx = -*dx;
3751 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003752 if (winding * *dx > 0) { // if same signs, result is negative
3753 winding += *dx > 0 ? -windVal : windVal;
3754 }
3755#if DEBUG_WINDING_AT_T
3756 SkDebugf(" dx=%c winding=%d\n", *dx > 0 ? '+' : '-', winding);
3757#endif
3758 return winding;
3759}
3760
3761int SkOpSegment::windSum(const SkOpAngle* angle) const {
3762 int start = angle->start();
3763 int end = angle->end();
3764 int index = SkMin32(start, end);
3765 return windSum(index);
3766}
3767
caryclark@google.com07393ca2013-04-08 11:47:37 +00003768void SkOpSegment::zeroSpan(SkOpSpan* span) {
caryclark@google.coma5e55922013-05-07 18:51:31 +00003769 SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003770 span->fWindValue = 0;
3771 span->fOppValue = 0;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003772 if (span->fTiny || span->fSmall) {
3773 return;
3774 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003775 SkASSERT(!span->fDone);
3776 span->fDone = true;
3777 ++fDoneSpans;
3778}