blob: 1fb5afa028af33e894a82fe890e8b03af185f52a [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 */
reed0dc4dd62015-03-24 13:55:33 -07007#include "SkIntersections.h"
caryclarkdac1d172014-06-17 05:15:38 -07008#include "SkOpContour.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +00009#include "SkOpSegment.h"
10#include "SkPathWriter.h"
reed0dc4dd62015-03-24 13:55:33 -070011#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000012
13#define F (false) // discard the edge
14#define T (true) // keep the edge
15
16static const bool gUnaryActiveEdge[2][2] = {
17// from=0 from=1
18// to=0,1 to=0,1
19 {F, T}, {T, F},
20};
21
caryclark@google.com07393ca2013-04-08 11:47:37 +000022static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
23// miFrom=0 miFrom=1
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000024// miTo=0 miTo=1 miTo=0 miTo=1
25// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
caryclark@google.com07393ca2013-04-08 11:47:37 +000026// suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1
27 {{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su
28 {{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su
29 {{{{F, T}, {T, F}}, {{T, T}, {F, F}}}, {{{T, F}, {T, F}}, {{F, F}, {F, F}}}}, // mi | su
30 {{{{F, T}, {T, F}}, {{T, F}, {F, T}}}, {{{T, F}, {F, T}}, {{F, T}, {T, F}}}}, // mi ^ su
31};
32
33#undef F
34#undef T
35
reed0dc4dd62015-03-24 13:55:33 -070036enum {
37 kOutsideTrackedTCount = 16, // FIXME: determine what this should be
38 kMissingSpanCount = 4, // FIXME: determine what this should be
39};
40
41const SkOpAngle* SkOpSegment::activeAngle(int index, int* start, int* end, bool* done,
42 bool* sortable) const {
43 if (const SkOpAngle* result = activeAngleInner(index, start, end, done, sortable)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000044 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +000045 }
reed0dc4dd62015-03-24 13:55:33 -070046 double referenceT = fTs[index].fT;
47 int lesser = index;
48 while (--lesser >= 0
49 && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) {
50 if (const SkOpAngle* result = activeAngleOther(lesser, start, end, done, sortable)) {
51 return result;
52 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000053 }
reed0dc4dd62015-03-24 13:55:33 -070054 do {
55 if (const SkOpAngle* result = activeAngleOther(index, start, end, done, sortable)) {
56 return result;
57 }
58 if (++index == fTs.count()) {
59 break;
60 }
61 if (fTs[index - 1].fTiny) {
62 referenceT = fTs[index].fT;
63 continue;
64 }
65 } while (precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000066 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +000067}
68
reed0dc4dd62015-03-24 13:55:33 -070069const SkOpAngle* SkOpSegment::activeAngleInner(int index, int* start, int* end, bool* done,
70 bool* sortable) const {
71 int next = nextExactSpan(index, 1);
72 if (next > 0) {
73 const SkOpSpan& upSpan = fTs[index];
74 if (upSpan.fWindValue || upSpan.fOppValue) {
75 if (*end < 0) {
76 *start = index;
77 *end = next;
caryclark@google.com07393ca2013-04-08 11:47:37 +000078 }
reed0dc4dd62015-03-24 13:55:33 -070079 if (!upSpan.fDone) {
80 if (upSpan.fWindSum != SK_MinS32) {
81 return spanToAngle(index, next);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000082 }
83 *done = false;
84 }
85 } else {
reed0dc4dd62015-03-24 13:55:33 -070086 SkASSERT(upSpan.fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +000087 }
88 }
reed0dc4dd62015-03-24 13:55:33 -070089 int prev = nextExactSpan(index, -1);
caryclark@google.com07393ca2013-04-08 11:47:37 +000090 // edge leading into junction
reed0dc4dd62015-03-24 13:55:33 -070091 if (prev >= 0) {
92 const SkOpSpan& downSpan = fTs[prev];
93 if (downSpan.fWindValue || downSpan.fOppValue) {
94 if (*end < 0) {
95 *start = index;
96 *end = prev;
caryclark@google.com07393ca2013-04-08 11:47:37 +000097 }
reed0dc4dd62015-03-24 13:55:33 -070098 if (!downSpan.fDone) {
99 if (downSpan.fWindSum != SK_MinS32) {
100 return spanToAngle(index, prev);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000101 }
102 *done = false;
103 }
104 } else {
reed0dc4dd62015-03-24 13:55:33 -0700105 SkASSERT(downSpan.fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000106 }
107 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000108 return NULL;
109}
110
reed0dc4dd62015-03-24 13:55:33 -0700111const SkOpAngle* SkOpSegment::activeAngleOther(int index, int* start, int* end, bool* done,
112 bool* sortable) const {
113 const SkOpSpan* span = &fTs[index];
114 SkOpSegment* other = span->fOther;
115 int oIndex = span->fOtherIndex;
116 return other->activeAngleInner(oIndex, start, end, done, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000117}
118
reed0dc4dd62015-03-24 13:55:33 -0700119SkPoint SkOpSegment::activeLeftTop(int* firstT) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000120 SkASSERT(!done());
121 SkPoint topPt = {SK_ScalarMax, SK_ScalarMax};
reed0dc4dd62015-03-24 13:55:33 -0700122 int count = fTs.count();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000123 // see if either end is not done since we want smaller Y of the pair
124 bool lastDone = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000125 double lastT = -1;
reed0dc4dd62015-03-24 13:55:33 -0700126 for (int index = 0; index < count; ++index) {
127 const SkOpSpan& span = fTs[index];
128 if (span.fDone && lastDone) {
129 goto next;
130 }
131 if (approximately_negative(span.fT - lastT)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000132 goto next;
133 }
134 {
reed0dc4dd62015-03-24 13:55:33 -0700135 const SkPoint& xy = xyAtT(&span);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000136 if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
137 topPt = xy;
reed0dc4dd62015-03-24 13:55:33 -0700138 if (firstT) {
139 *firstT = index;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000140 }
141 }
142 if (fVerb != SkPath::kLine_Verb && !lastDone) {
reed0dc4dd62015-03-24 13:55:33 -0700143 SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000144 if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
145 && topPt.fX > curveTop.fX)) {
146 topPt = curveTop;
reed0dc4dd62015-03-24 13:55:33 -0700147 if (firstT) {
148 *firstT = index;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000149 }
150 }
151 }
reed0dc4dd62015-03-24 13:55:33 -0700152 lastT = span.fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000153 }
154next:
reed0dc4dd62015-03-24 13:55:33 -0700155 lastDone = span.fDone;
156 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000157 return topPt;
158}
159
reed0dc4dd62015-03-24 13:55:33 -0700160bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
161 int sumMiWinding = updateWinding(endIndex, index);
162 int sumSuWinding = updateOppWinding(endIndex, index);
caryclark65f55312014-11-13 06:58:52 -0800163#if DEBUG_LIMIT_WIND_SUM
164 SkASSERT(abs(sumMiWinding) <= DEBUG_LIMIT_WIND_SUM);
165 SkASSERT(abs(sumSuWinding) <= DEBUG_LIMIT_WIND_SUM);
166#endif
reed0dc4dd62015-03-24 13:55:33 -0700167 if (fOperand) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000168 SkTSwap<int>(sumMiWinding, sumSuWinding);
169 }
reed0dc4dd62015-03-24 13:55:33 -0700170 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000171}
172
reed0dc4dd62015-03-24 13:55:33 -0700173bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
174 int* sumMiWinding, int* sumSuWinding) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000175 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
reed0dc4dd62015-03-24 13:55:33 -0700176 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000177 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000178 bool miFrom;
179 bool miTo;
180 bool suFrom;
181 bool suTo;
182 if (operand()) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000183 miFrom = (oppMaxWinding & xorMiMask) != 0;
184 miTo = (oppSumWinding & xorMiMask) != 0;
185 suFrom = (maxWinding & xorSuMask) != 0;
186 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000187 } else {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000188 miFrom = (maxWinding & xorMiMask) != 0;
189 miTo = (sumWinding & xorMiMask) != 0;
190 suFrom = (oppMaxWinding & xorSuMask) != 0;
191 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000192 }
193 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
194#if DEBUG_ACTIVE_OP
caryclarkdac1d172014-06-17 05:15:38 -0700195 SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n",
reed0dc4dd62015-03-24 13:55:33 -0700196 __FUNCTION__, debugID(), span(index).fT, span(endIndex).fT,
caryclark@google.com570863f2013-09-16 15:55:01 +0000197 SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000198#endif
199 return result;
200}
201
reed0dc4dd62015-03-24 13:55:33 -0700202bool SkOpSegment::activeWinding(int index, int endIndex) {
203 int sumWinding = updateWinding(endIndex, index);
204 return activeWinding(index, endIndex, &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000205}
206
reed0dc4dd62015-03-24 13:55:33 -0700207bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000208 int maxWinding;
reed0dc4dd62015-03-24 13:55:33 -0700209 setUpWinding(index, endIndex, &maxWinding, sumWinding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000210 bool from = maxWinding != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000211 bool to = *sumWinding != 0;
212 bool result = gUnaryActiveEdge[from][to];
213 return result;
214}
215
reed0dc4dd62015-03-24 13:55:33 -0700216void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt,
217 SkOpSegment* other) {
218 int tIndex = -1;
219 int tCount = fTs.count();
220 int oIndex = -1;
221 int oCount = other->fTs.count();
222 do {
223 ++tIndex;
224 } while (startPt != fTs[tIndex].fPt && tIndex < tCount);
225 int tIndexStart = tIndex;
226 do {
227 ++oIndex;
228 } while (endPt != other->fTs[oIndex].fPt && oIndex < oCount);
229 int oIndexStart = oIndex;
230 const SkPoint* nextPt;
231 do {
232 nextPt = &fTs[++tIndex].fPt;
233 SkASSERT(fTs[tIndex].fT < 1 || startPt != *nextPt);
234 } while (startPt == *nextPt);
235 double nextT = fTs[tIndex].fT;
236 const SkPoint* oNextPt;
237 do {
238 oNextPt = &other->fTs[++oIndex].fPt;
239 SkASSERT(other->fTs[oIndex].fT < 1 || endPt != *oNextPt);
240 } while (endPt == *oNextPt);
241 double oNextT = other->fTs[oIndex].fT;
242 // at this point, spans before and after are at:
243 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
244 // if tIndexStart == 0, no prior span
245 // if nextT == 1, no following span
246
247 // advance the span with zero winding
248 // if the following span exists (not past the end, non-zero winding)
249 // connect the two edges
250 if (!fTs[tIndexStart].fWindValue) {
251 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
252#if DEBUG_CONCIDENT
253 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
254 __FUNCTION__, fID, other->fID, tIndexStart - 1,
255 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
256 xyAtT(tIndexStart).fY);
257#endif
258 SkPoint copy = fTs[tIndexStart].fPt; // add t pair may move the point array
259 addTPair(fTs[tIndexStart].fT, other, other->fTs[oIndex].fT, false, copy);
260 }
261 if (nextT < 1 && fTs[tIndex].fWindValue) {
262#if DEBUG_CONCIDENT
263 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
264 __FUNCTION__, fID, other->fID, tIndex,
265 fTs[tIndex].fT, xyAtT(tIndex).fX,
266 xyAtT(tIndex).fY);
267#endif
268 SkPoint copy = fTs[tIndex].fPt; // add t pair may move the point array
269 addTPair(fTs[tIndex].fT, other, other->fTs[oIndexStart].fT, false, copy);
270 }
271 } else {
272 SkASSERT(!other->fTs[oIndexStart].fWindValue);
273 if (oIndexStart > 0 && other->fTs[oIndexStart - 1].fWindValue) {
274#if DEBUG_CONCIDENT
275 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
276 __FUNCTION__, fID, other->fID, oIndexStart - 1,
277 other->fTs[oIndexStart].fT, other->xyAtT(oIndexStart).fX,
278 other->xyAtT(oIndexStart).fY);
279 other->debugAddTPair(other->fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
280#endif
281 }
282 if (oNextT < 1 && other->fTs[oIndex].fWindValue) {
283#if DEBUG_CONCIDENT
284 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
285 __FUNCTION__, fID, other->fID, oIndex,
286 other->fTs[oIndex].fT, other->xyAtT(oIndex).fX,
287 other->xyAtT(oIndex).fY);
288 other->debugAddTPair(other->fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
289#endif
290 }
291 }
292}
293
294void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
295 SkOpSegment* other) {
296 // walk this to startPt
297 // walk other to startPt
298 // if either is > 0, add a pointer to the other, copying adjacent winding
299 int tIndex = -1;
300 int oIndex = -1;
301 do {
302 ++tIndex;
303 } while (startPt != fTs[tIndex].fPt);
304 int ttIndex = tIndex;
305 bool checkOtherTMatch = false;
306 do {
307 const SkOpSpan& span = fTs[ttIndex];
308 if (startPt != span.fPt) {
309 break;
310 }
311 if (span.fOther == other && span.fPt == startPt) {
312 checkOtherTMatch = true;
313 break;
314 }
315 } while (++ttIndex < count());
316 do {
317 ++oIndex;
318 } while (startPt != other->fTs[oIndex].fPt);
319 bool skipAdd = false;
320 if (checkOtherTMatch) {
321 int ooIndex = oIndex;
322 do {
323 const SkOpSpan& oSpan = other->fTs[ooIndex];
324 if (startPt != oSpan.fPt) {
325 break;
326 }
327 if (oSpan.fT == fTs[ttIndex].fOtherT) {
328 skipAdd = true;
329 break;
330 }
331 } while (++ooIndex < other->count());
332 }
333 if ((tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) && !skipAdd) {
334 addTPair(fTs[tIndex].fT, other, other->fTs[oIndex].fT, false, startPt);
335 }
336 SkPoint nextPt = startPt;
337 do {
338 const SkPoint* workPt;
339 do {
340 workPt = &fTs[++tIndex].fPt;
341 } while (nextPt == *workPt);
342 const SkPoint* oWorkPt;
343 do {
344 oWorkPt = &other->fTs[++oIndex].fPt;
345 } while (nextPt == *oWorkPt);
346 nextPt = *workPt;
347 double tStart = fTs[tIndex].fT;
348 double oStart = other->fTs[oIndex].fT;
349 if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) {
350 break;
351 }
352 if (*workPt == *oWorkPt) {
353 addTPair(tStart, other, oStart, false, nextPt);
354 }
355 } while (endPt != nextPt);
356}
357
358void SkOpSegment::addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
359 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
360 fBounds.setCubicBounds(pts);
361}
362
363void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000364 SkPoint edge[4];
365 const SkPoint* ePtr;
reed0dc4dd62015-03-24 13:55:33 -0700366 int lastT = fTs.count() - 1;
367 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000368 ePtr = fPts;
369 } else {
370 // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
371 subDivide(start, end, edge);
372 ePtr = edge;
373 }
374 if (active) {
reed0dc4dd62015-03-24 13:55:33 -0700375 bool reverse = ePtr == fPts && start != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000376 if (reverse) {
reed@google.com277c3f82013-05-31 15:17:50 +0000377 path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000378 switch (fVerb) {
379 case SkPath::kLine_Verb:
380 path->deferredLine(ePtr[0]);
381 break;
382 case SkPath::kQuad_Verb:
383 path->quadTo(ePtr[1], ePtr[0]);
384 break;
385 case SkPath::kCubic_Verb:
386 path->cubicTo(ePtr[2], ePtr[1], ePtr[0]);
387 break;
388 default:
389 SkASSERT(0);
390 }
reed0dc4dd62015-03-24 13:55:33 -0700391 // return ePtr[0];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000392 } else {
393 path->deferredMoveLine(ePtr[0]);
394 switch (fVerb) {
395 case SkPath::kLine_Verb:
396 path->deferredLine(ePtr[1]);
397 break;
398 case SkPath::kQuad_Verb:
399 path->quadTo(ePtr[1], ePtr[2]);
400 break;
401 case SkPath::kCubic_Verb:
402 path->cubicTo(ePtr[1], ePtr[2], ePtr[3]);
403 break;
404 default:
405 SkASSERT(0);
406 }
407 }
408 }
reed0dc4dd62015-03-24 13:55:33 -0700409 // return ePtr[SkPathOpsVerbToPoints(fVerb)];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000410}
411
reed0dc4dd62015-03-24 13:55:33 -0700412void SkOpSegment::addEndSpan(int endIndex) {
413 SkASSERT(span(endIndex).fT == 1 || (span(endIndex).fTiny
414// && approximately_greater_than_one(span(endIndex).fT)
415 ));
416 int spanCount = fTs.count();
417 int startIndex = endIndex - 1;
418 while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) {
419 --startIndex;
420 SkASSERT(startIndex > 0);
421 --endIndex;
422 }
423 SkOpAngle& angle = fAngles.push_back();
424 angle.set(this, spanCount - 1, startIndex);
425#if DEBUG_ANGLE
426 debugCheckPointsEqualish(endIndex, spanCount);
427#endif
428 setFromAngle(endIndex, &angle);
429}
430
431void SkOpSegment::setFromAngle(int endIndex, SkOpAngle* angle) {
432 int spanCount = fTs.count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000433 do {
reed0dc4dd62015-03-24 13:55:33 -0700434 fTs[endIndex].fFromAngle = angle;
435 } while (++endIndex < spanCount);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000436}
437
reed0dc4dd62015-03-24 13:55:33 -0700438void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
439 init(pts, SkPath::kLine_Verb, operand, evenOdd);
440 fBounds.set(pts, 2);
441}
442
443// add 2 to edge or out of range values to get T extremes
444void SkOpSegment::addOtherT(int index, double otherT, int otherIndex) {
445 SkOpSpan& span = fTs[index];
446 if (precisely_zero(otherT)) {
447 otherT = 0;
448 } else if (precisely_equal(otherT, 1)) {
449 otherT = 1;
caryclarkccec0f92015-03-24 07:28:17 -0700450 }
reed0dc4dd62015-03-24 13:55:33 -0700451 span.fOtherT = otherT;
452 span.fOtherIndex = otherIndex;
453}
454
455void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
456 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
457 fBounds.setQuadBounds(pts);
458}
459
460SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
461 int spanIndex = count() - 1;
462 int startIndex = nextExactSpan(spanIndex, -1);
463 SkASSERT(startIndex >= 0);
464 SkOpAngle& angle = fAngles.push_back();
465 *anglePtr = &angle;
466 angle.set(this, spanIndex, startIndex);
467 setFromAngle(spanIndex, &angle);
468 SkOpSegment* other;
469 int oStartIndex, oEndIndex;
470 do {
471 const SkOpSpan& span = fTs[spanIndex];
472 SkASSERT(span.fT > 0);
473 other = span.fOther;
474 oStartIndex = span.fOtherIndex;
475 oEndIndex = other->nextExactSpan(oStartIndex, 1);
476 if (oEndIndex > 0 && other->span(oStartIndex).fWindValue) {
477 break;
478 }
479 oEndIndex = oStartIndex;
480 oStartIndex = other->nextExactSpan(oEndIndex, -1);
481 --spanIndex;
482 } while (oStartIndex < 0 || !other->span(oStartIndex).fWindSum);
483 SkOpAngle& oAngle = other->fAngles.push_back();
484 oAngle.set(other, oStartIndex, oEndIndex);
485 other->setToAngle(oEndIndex, &oAngle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000486 *otherPtr = other;
reed0dc4dd62015-03-24 13:55:33 -0700487 return &oAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000488}
489
reed0dc4dd62015-03-24 13:55:33 -0700490SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
491 int endIndex = nextExactSpan(0, 1);
492 SkASSERT(endIndex > 0);
493 SkOpAngle& angle = fAngles.push_back();
494 *anglePtr = &angle;
495 angle.set(this, 0, endIndex);
496 setToAngle(endIndex, &angle);
497 int spanIndex = 0;
498 SkOpSegment* other;
499 int oStartIndex, oEndIndex;
500 do {
501 const SkOpSpan& span = fTs[spanIndex];
502 SkASSERT(span.fT < 1);
503 other = span.fOther;
504 oEndIndex = span.fOtherIndex;
505 oStartIndex = other->nextExactSpan(oEndIndex, -1);
506 if (oStartIndex >= 0 && other->span(oStartIndex).fWindValue) {
507 break;
508 }
509 oStartIndex = oEndIndex;
510 oEndIndex = other->nextExactSpan(oStartIndex, 1);
511 ++spanIndex;
512 } while (oEndIndex < 0 || !other->span(oStartIndex).fWindValue);
513 SkOpAngle& oAngle = other->fAngles.push_back();
514 oAngle.set(other, oEndIndex, oStartIndex);
515 other->setFromAngle(oEndIndex, &oAngle);
516 *otherPtr = other;
517 return &oAngle;
518}
519
520SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000521 SkOpSegment* other;
caryclarkdac1d172014-06-17 05:15:38 -0700522 SkOpAngle* angle, * otherAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000523 if (step > 0) {
reed0dc4dd62015-03-24 13:55:33 -0700524 otherAngle = addSingletonAngleUp(&other, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000525 } else {
reed0dc4dd62015-03-24 13:55:33 -0700526 otherAngle = addSingletonAngleDown(&other, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000527 }
caryclarkdac1d172014-06-17 05:15:38 -0700528 angle->insert(otherAngle);
529 return angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000530}
531
reed0dc4dd62015-03-24 13:55:33 -0700532void SkOpSegment::addStartSpan(int endIndex) {
533 int index = 0;
534 SkOpAngle& angle = fAngles.push_back();
535 angle.set(this, index, endIndex);
536#if DEBUG_ANGLE
537 debugCheckPointsEqualish(index, endIndex);
538#endif
539 setToAngle(endIndex, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000540}
541
reed0dc4dd62015-03-24 13:55:33 -0700542void SkOpSegment::setToAngle(int endIndex, SkOpAngle* angle) {
543 int index = 0;
caryclarkccec0f92015-03-24 07:28:17 -0700544 do {
reed0dc4dd62015-03-24 13:55:33 -0700545 fTs[index].fToAngle = angle;
546 } while (++index < endIndex);
caryclarkccec0f92015-03-24 07:28:17 -0700547}
548
reed0dc4dd62015-03-24 13:55:33 -0700549 // Defer all coincident edge processing until
550 // after normal intersections have been computed
551
552// no need to be tricky; insert in normal T order
553// resolve overlapping ts when considering coincidence later
554
555 // add non-coincident intersection. Resulting edges are sorted in T.
556int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
557 SkASSERT(this != other || fVerb == SkPath::kCubic_Verb);
558 #if 0 // this needs an even rougher association to be useful
559 SkASSERT(SkDPoint::RoughlyEqual(ptAtT(newT), pt));
560 #endif
561 const SkPoint& firstPt = fPts[0];
562 const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
563 SkASSERT(newT == 0 || !precisely_zero(newT));
564 SkASSERT(newT == 1 || !precisely_equal(newT, 1));
565 // FIXME: in the pathological case where there is a ton of intercepts,
566 // binary search?
567 int insertedAt = -1;
568 int tCount = fTs.count();
569 for (int index = 0; index < tCount; ++index) {
570 // OPTIMIZATION: if there are three or more identical Ts, then
571 // the fourth and following could be further insertion-sorted so
572 // that all the edges are clockwise or counterclockwise.
573 // This could later limit segment tests to the two adjacent
574 // neighbors, although it doesn't help with determining which
575 // circular direction to go in.
576 const SkOpSpan& span = fTs[index];
577 if (newT < span.fT) {
578 insertedAt = index;
caryclarkccec0f92015-03-24 07:28:17 -0700579 break;
580 }
reed0dc4dd62015-03-24 13:55:33 -0700581 if (newT == span.fT) {
582 if (pt == span.fPt) {
583 insertedAt = index;
584 break;
585 }
586 if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
587 insertedAt = index;
588 break;
589 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000590 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000591 }
caryclarkccec0f92015-03-24 07:28:17 -0700592 SkOpSpan* span;
reed0dc4dd62015-03-24 13:55:33 -0700593 if (insertedAt >= 0) {
594 span = fTs.insert(insertedAt);
595 } else {
596 insertedAt = tCount;
597 span = fTs.append();
598 }
599 span->fT = newT;
600 span->fOtherT = -1;
601 span->fOther = other;
602 span->fPt = pt;
603#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
608 span->fFromAngle = NULL;
609 span->fToAngle = NULL;
610 span->fWindSum = SK_MinS32;
611 span->fOppSum = SK_MinS32;
612 span->fWindValue = 1;
613 span->fOppValue = 0;
614 span->fChased = false;
615 span->fCoincident = false;
616 span->fLoop = false;
617 span->fNear = false;
618 span->fMultiple = false;
619 span->fSmall = false;
620 span->fTiny = false;
621 if ((span->fDone = newT == 1)) {
622 ++fDoneSpans;
623 }
624 setSpanFlags(pt, newT, span);
625 return insertedAt;
caryclarkccec0f92015-03-24 07:28:17 -0700626}
627
reed0dc4dd62015-03-24 13:55:33 -0700628void SkOpSegment::setSpanFlags(const SkPoint& pt, double newT, SkOpSpan* span) {
629 int less = -1;
630// FIXME: note that this relies on spans being a continguous array
631// find range of spans with nearly the same point as this one
632 // FIXME: SkDPoint::ApproximatelyEqual is better but breaks tests at the moment
633 while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, pt)) {
634 if (fVerb == SkPath::kCubic_Verb) {
635 double tInterval = newT - span[less].fT;
636 double tMid = newT - tInterval / 2;
637 SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
638 if (!midPt.approximatelyEqual(xyAtT(span))) {
639 break;
640 }
641 }
642 --less;
643 }
644 int more = 1;
645 // FIXME: SkDPoint::ApproximatelyEqual is better but breaks tests at the moment
646 while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].fPt, pt)) {
647 if (fVerb == SkPath::kCubic_Verb) {
648 double tEndInterval = span[more].fT - newT;
649 double tMid = newT - tEndInterval / 2;
650 SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
651 if (!midEndPt.approximatelyEqual(xyAtT(span))) {
652 break;
653 }
654 }
655 ++more;
656 }
657 ++less;
658 --more;
659 while (more - 1 > less && span[more].fPt == span[more - 1].fPt
660 && span[more].fT == span[more - 1].fT) {
661 --more;
662 }
663 if (less == more) {
664 return;
665 }
666 if (precisely_negative(span[more].fT - span[less].fT)) {
667 return;
668 }
669// if the total range of t values is big enough, mark all tiny
670 bool tiny = span[less].fPt == span[more].fPt;
671 int index = less;
672 do {
673 fSmall = span[index].fSmall = true;
674 fTiny |= span[index].fTiny = tiny;
675 if (!span[index].fDone) {
676 span[index].fDone = true;
677 ++fDoneSpans;
678 }
679 } while (++index < more);
680 return;
681}
682
683void SkOpSegment::resetSpanFlags() {
684 fSmall = fTiny = false;
685 fDoneSpans = 0;
686 int start = 0;
687 int last = this->count() - 1;
688 do {
689 SkOpSpan* startSpan = &this->fTs[start];
690 double startT = startSpan->fT;
691 startSpan->fSmall = startSpan->fTiny = false; // sets range initial
692 bool terminus = startT == 1;
693 if ((startSpan->fDone = !startSpan->fWindValue | terminus)) {
694 ++fDoneSpans;
695 }
696 ++start; // range initial + 1
697 if (terminus) {
698 continue;
699 }
700 const SkPoint& pt = startSpan->fPt;
701 int end = start; // range initial + 1
702 while (end <= last) {
703 const SkOpSpan& endSpan = this->span(end);
704 if (!AlmostEqualUlps(endSpan.fPt, pt)) {
705 break;
706 }
707 if (fVerb == SkPath::kCubic_Verb) {
708 double tMid = (startSpan->fT + endSpan.fT) / 2;
709 SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
710 if (!midEndPt.approximatelyEqual(xyAtT(startSpan))) {
711 break;
712 }
713 }
714 ++end;
715 }
716 if (start == end) { // end == range final + 1
717 continue;
718 }
719 while (--end >= start) { // end == range final
720 const SkOpSpan& endSpan = this->span(end);
721 const SkOpSpan& priorSpan = this->span(end - 1);
722 if (endSpan.fPt != priorSpan.fPt || endSpan.fT != priorSpan.fT) {
723 break; // end == range final + 1
724 }
725 }
726 if (end < start) { // end == range final + 1
727 continue;
728 }
729 int index = start - 1; // index == range initial
730 start = end; // start = range final + 1
731 const SkOpSpan& nextSpan = this->span(end);
732 if (precisely_negative(nextSpan.fT - startSpan->fT)) {
733 while (++index < end) {
734 startSpan = &this->fTs[index];
735 startSpan->fSmall = startSpan->fTiny = false; // sets range initial + 1
736 if ((startSpan->fDone = !startSpan->fWindValue)) {
737 ++fDoneSpans;
738 }
739 }
740 continue;
741 }
742 if (!startSpan->fWindValue) {
743 --fDoneSpans; // added back below
744 }
745 bool tiny = nextSpan.fPt == startSpan->fPt;
746 do {
747 fSmall = startSpan->fSmall = true; // sets range initial
748 fTiny |= startSpan->fTiny = tiny;
749 startSpan->fDone = true;
750 ++fDoneSpans;
751 startSpan = &this->fTs[++index];
752 } while (index < end); // loop through tiny small range end (last)
753 } while (start <= last);
754}
755
756// set spans from start to end to decrement by one
757// note this walks other backwards
758// FIXME: there's probably an edge case that can be constructed where
759// two span in one segment are separated by float epsilon on one span but
760// not the other, if one segment is very small. For this
761// case the counts asserted below may or may not be enough to separate the
762// spans. Even if the counts work out, what if the spans aren't correctly
763// sorted? It feels better in such a case to match the span's other span
764// pointer since both coincident segments must contain the same spans.
765// FIXME? It seems that decrementing by one will fail for complex paths that
766// have three or more coincident edges. Shouldn't this subtract the difference
767// between the winding values?
768/* |--> |-->
769this 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4
770other 2<<<<1<<<<0 2<<<<1<<<<0 2<<<<1<<<<0
771 ^ ^ <--| <--|
772 startPt endPt test/oTest first pos test/oTest final pos
773*/
774void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) {
775 bool binary = fOperand != other->fOperand;
776 int index = 0;
777 while (startPt != fTs[index].fPt) {
778 SkASSERT(index < fTs.count());
779 ++index;
780 }
781 while (index > 0 && precisely_equal(fTs[index].fT, fTs[index - 1].fT)) {
782 --index;
783 }
784 bool oFoundEnd = false;
785 int oIndex = other->fTs.count();
786 while (startPt != other->fTs[--oIndex].fPt) { // look for startPt match
787 SkASSERT(oIndex > 0);
788 }
789 double oStartT = other->fTs[oIndex].fT;
790 // look for first point beyond match
791 while (startPt == other->fTs[--oIndex].fPt || precisely_equal(oStartT, other->fTs[oIndex].fT)) {
792 if (!oIndex) {
793 return; // tiny spans may move in the wrong direction
794 }
795 }
796 SkOpSpan* test = &fTs[index];
797 SkOpSpan* oTest = &other->fTs[oIndex];
798 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
799 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
800 bool decrement, track, bigger;
801 int originalWindValue;
802 const SkPoint* testPt;
803 const SkPoint* oTestPt;
804 do {
805 SkASSERT(test->fT < 1);
806 SkASSERT(oTest->fT < 1);
807 decrement = test->fWindValue && oTest->fWindValue;
808 track = test->fWindValue || oTest->fWindValue;
809 bigger = test->fWindValue >= oTest->fWindValue;
810 testPt = &test->fPt;
811 double testT = test->fT;
812 oTestPt = &oTest->fPt;
813 double oTestT = oTest->fT;
814 do {
815 if (decrement) {
816 if (binary && bigger) {
817 test->fOppValue--;
818 } else {
819 decrementSpan(test);
820 }
821 } else if (track) {
822 TrackOutsidePair(&outsidePts, *testPt, *oTestPt);
823 }
824 SkASSERT(index < fTs.count() - 1);
825 test = &fTs[++index];
826 } while (*testPt == test->fPt || precisely_equal(testT, test->fT));
827 originalWindValue = oTest->fWindValue;
828 do {
829 SkASSERT(oTest->fT < 1);
830 SkASSERT(originalWindValue == oTest->fWindValue);
831 if (decrement) {
832 if (binary && !bigger) {
833 oTest->fOppValue--;
834 } else {
835 other->decrementSpan(oTest);
836 }
837 } else if (track) {
838 TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
839 }
840 if (!oIndex) {
841 break;
842 }
843 oFoundEnd |= endPt == oTest->fPt;
844 oTest = &other->fTs[--oIndex];
845 } while (*oTestPt == oTest->fPt || precisely_equal(oTestT, oTest->fT));
846 } while (endPt != test->fPt && test->fT < 1);
847 // FIXME: determine if canceled edges need outside ts added
848 if (!oFoundEnd) {
849 for (int oIdx2 = oIndex; oIdx2 >= 0; --oIdx2) {
850 SkOpSpan* oTst2 = &other->fTs[oIdx2];
851 if (originalWindValue != oTst2->fWindValue) {
852 goto skipAdvanceOtherCancel;
853 }
854 if (!oTst2->fWindValue) {
855 goto skipAdvanceOtherCancel;
856 }
857 if (endPt == other->fTs[oIdx2].fPt) {
858 break;
859 }
860 }
861 oFoundEnd = endPt == oTest->fPt;
862 do {
863 SkASSERT(originalWindValue == oTest->fWindValue);
864 if (decrement) {
865 if (binary && !bigger) {
866 oTest->fOppValue--;
867 } else {
868 other->decrementSpan(oTest);
869 }
870 } else if (track) {
871 TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
872 }
873 if (!oIndex) {
874 break;
875 }
876 oTest = &other->fTs[--oIndex];
877 oFoundEnd |= endPt == oTest->fPt;
878 } while (!oFoundEnd || endPt == oTest->fPt);
879 }
880skipAdvanceOtherCancel:
881 int outCount = outsidePts.count();
882 if (!done() && outCount) {
883 addCancelOutsides(outsidePts[0], outsidePts[1], other);
884 if (outCount > 2) {
885 addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other);
886 }
887 }
888 if (!other->done() && oOutsidePts.count()) {
889 other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
890 }
891 setCoincidentRange(startPt, endPt, other);
892 other->setCoincidentRange(startPt, endPt, this);
893}
894
895int SkOpSegment::addSelfT(const SkPoint& pt, double newT) {
896 // if the tail nearly intersects itself but not quite, the caller records this separately
897 int result = addT(this, pt, newT);
898 SkOpSpan* span = &fTs[result];
899 fLoop = span->fLoop = true;
900 return result;
901}
902
903// find the starting or ending span with an existing loop of angles
904// FIXME? replicate for all identical starting/ending spans?
905// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
906// FIXME? assert that only one other span has a valid windValue or oppValue
907void SkOpSegment::addSimpleAngle(int index) {
908 SkOpSpan* span = &fTs[index];
909 int idx;
910 int start, end;
911 if (span->fT == 0) {
912 idx = 0;
913 span = &fTs[0];
914 do {
915 if (span->fToAngle) {
916 SkASSERT(span->fToAngle->loopCount() == 2);
917 SkASSERT(!span->fFromAngle);
918 span->fFromAngle = span->fToAngle->next();
919 return;
920 }
921 span = &fTs[++idx];
922 } while (span->fT == 0);
923 SkASSERT(!fTs[0].fTiny && fTs[idx].fT > 0);
924 addStartSpan(idx);
925 start = 0;
926 end = idx;
927 } else {
928 idx = count() - 1;
929 span = &fTs[idx];
930 do {
931 if (span->fFromAngle) {
932 SkASSERT(span->fFromAngle->loopCount() == 2);
933 SkASSERT(!span->fToAngle);
934 span->fToAngle = span->fFromAngle->next();
935 return;
936 }
937 span = &fTs[--idx];
938 } while (span->fT == 1);
939 SkASSERT(!fTs[idx].fTiny && fTs[idx].fT < 1);
940 addEndSpan(++idx);
941 start = idx;
942 end = count();
943 }
944 SkOpSegment* other;
945 SkOpSpan* oSpan;
946 index = start;
947 do {
948 span = &fTs[index];
949 other = span->fOther;
950 int oFrom = span->fOtherIndex;
951 oSpan = &other->fTs[oFrom];
952 if (oSpan->fT < 1 && oSpan->fWindValue) {
953 break;
954 }
955 if (oSpan->fT == 0) {
956 continue;
957 }
958 oFrom = other->nextExactSpan(oFrom, -1);
959 SkOpSpan* oFromSpan = &other->fTs[oFrom];
960 SkASSERT(oFromSpan->fT < 1);
961 if (oFromSpan->fWindValue) {
962 break;
963 }
964 } while (++index < end);
965 SkOpAngle* angle, * oAngle;
966 if (span->fT == 0) {
967 SkASSERT(span->fOtherIndex - 1 >= 0);
968 SkASSERT(span->fOtherT == 1);
969 SkDEBUGCODE(int oPriorIndex = other->nextExactSpan(span->fOtherIndex, -1));
970 SkDEBUGCODE(const SkOpSpan& oPrior = other->span(oPriorIndex));
971 SkASSERT(!oPrior.fTiny && oPrior.fT < 1);
972 other->addEndSpan(span->fOtherIndex);
973 angle = span->fToAngle;
974 oAngle = oSpan->fFromAngle;
975 } else {
976 SkASSERT(span->fOtherIndex + 1 < other->count());
977 SkASSERT(span->fOtherT == 0);
978 SkASSERT(!oSpan->fTiny && (other->fTs[span->fOtherIndex + 1].fT > 0
979 || (other->fTs[span->fOtherIndex + 1].fFromAngle == NULL
980 && other->fTs[span->fOtherIndex + 1].fToAngle == NULL)));
981 int oIndex = 1;
982 do {
983 const SkOpSpan& osSpan = other->span(oIndex);
984 if (osSpan.fFromAngle || osSpan.fT > 0) {
985 break;
986 }
987 ++oIndex;
988 SkASSERT(oIndex < other->count());
989 } while (true);
990 other->addStartSpan(oIndex);
991 angle = span->fFromAngle;
992 oAngle = oSpan->fToAngle;
993 }
994 angle->insert(oAngle);
995}
996
997void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
998 debugValidate();
999 int count = this->count();
1000 for (int index = 0; index < count; ++index) {
1001 SkOpSpan& span = fTs[index];
1002 if (!span.fMultiple) {
1003 continue;
1004 }
1005 int end = nextExactSpan(index, 1);
1006 SkASSERT(end > index + 1);
1007 const SkPoint& thisPt = span.fPt;
1008 while (index < end - 1) {
1009 SkOpSegment* other1 = span.fOther;
1010 int oCnt = other1->count();
1011 for (int idx2 = index + 1; idx2 < end; ++idx2) {
1012 SkOpSpan& span2 = fTs[idx2];
1013 SkOpSegment* other2 = span2.fOther;
1014 for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
1015 SkOpSpan& oSpan = other1->fTs[oIdx];
1016 if (oSpan.fOther != other2) {
1017 continue;
1018 }
1019 if (oSpan.fPt == thisPt) {
1020 goto skipExactMatches;
1021 }
1022 }
1023 for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
1024 SkOpSpan& oSpan = other1->fTs[oIdx];
1025 if (oSpan.fOther != other2) {
1026 continue;
1027 }
1028 if (SkDPoint::RoughlyEqual(oSpan.fPt, thisPt)) {
1029 SkOpSpan& oSpan2 = other2->fTs[oSpan.fOtherIndex];
1030 if (zero_or_one(span.fOtherT) || zero_or_one(oSpan.fT)
1031 || zero_or_one(span2.fOtherT) || zero_or_one(oSpan2.fT)) {
1032 return;
1033 }
1034 if (!way_roughly_equal(span.fOtherT, oSpan.fT)
1035 || !way_roughly_equal(span2.fOtherT, oSpan2.fT)
1036 || !way_roughly_equal(span2.fOtherT, oSpan.fOtherT)
1037 || !way_roughly_equal(span.fOtherT, oSpan2.fOtherT)) {
1038 return;
1039 }
1040 alignSpan(thisPt, span.fOtherT, other1, span2.fOtherT,
1041 other2, &oSpan, alignedArray);
1042 alignSpan(thisPt, span2.fOtherT, other2, span.fOtherT,
1043 other1, &oSpan2, alignedArray);
1044 break;
1045 }
1046 }
1047 skipExactMatches:
1048 ;
1049 }
1050 ++index;
1051 }
1052 }
1053 debugValidate();
1054}
1055
1056void SkOpSegment::alignRange(int lower, int upper,
1057 const SkOpSegment* other, int oLower, int oUpper) {
1058 for (int oIndex = oLower; oIndex <= oUpper; ++oIndex) {
1059 const SkOpSpan& oSpan = other->span(oIndex);
1060 const SkOpSegment* oOther = oSpan.fOther;
1061 if (oOther == this) {
1062 continue;
1063 }
1064 SkOpSpan* matchSpan;
1065 int matchIndex;
1066 const SkOpSpan* refSpan;
1067 for (int iIndex = lower; iIndex <= upper; ++iIndex) {
1068 const SkOpSpan& iSpan = this->span(iIndex);
1069 const SkOpSegment* iOther = iSpan.fOther;
1070 if (iOther == other) {
1071 continue;
1072 }
1073 if (iOther == oOther) {
1074 goto nextI;
1075 }
1076 }
1077 {
1078 // oSpan does not have a match in this
1079 int iCount = this->count();
1080 const SkOpSpan* iMatch = NULL;
1081 double iMatchTDiff;
1082 matchIndex = -1;
1083 for (int iIndex = 0; iIndex < iCount; ++iIndex) {
1084 const SkOpSpan& iSpan = this->span(iIndex);
1085 const SkOpSegment* iOther = iSpan.fOther;
1086 if (iOther != oOther) {
1087 continue;
1088 }
1089 double testTDiff = fabs(iSpan.fOtherT - oSpan.fOtherT);
1090 if (!iMatch || testTDiff < iMatchTDiff) {
1091 matchIndex = iIndex;
1092 iMatch = &iSpan;
1093 iMatchTDiff = testTDiff;
1094 }
1095 }
1096 if (matchIndex < 0) {
1097 continue; // the entry is missing, & will be picked up later (FIXME: fix it here?)
1098 }
1099 matchSpan = &this->fTs[matchIndex];
1100 refSpan = &this->span(lower);
1101 if (!SkDPoint::ApproximatelyEqual(matchSpan->fPt, refSpan->fPt)) {
1102 goto nextI;
1103 }
1104 if (matchIndex != lower - 1 && matchIndex != upper + 1) {
1105 // the consecutive spans need to be rearranged to get the missing one close
1106 continue; // FIXME: more work to do
1107 }
1108 }
1109 {
1110 this->fixOtherTIndex();
1111 SkScalar newT;
1112 if (matchSpan->fT != 0 && matchSpan->fT != 1) {
1113 newT = matchSpan->fT = refSpan->fT;
1114 matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = refSpan->fT;
1115 } else { // leave span at the start or end there and adjust the neighbors
1116 newT = matchSpan->fT;
1117 for (int iIndex = lower; iIndex <= upper; ++iIndex) {
1118 matchSpan = &this->fTs[iIndex];
1119 matchSpan->fT = newT;
1120 matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = newT;
1121 }
1122 }
1123 this->resetSpanFlags(); // fix up small / tiny / done
1124 // align ts of other ranges with adjacent spans that match the aligned points
1125 lower = SkTMin(lower, matchIndex);
1126 while (lower > 0) {
1127 const SkOpSpan& span = this->span(lower - 1);
1128 if (span.fT != newT) {
1129 break;
1130 }
1131 --lower;
1132 }
1133 upper = SkTMax(upper, matchIndex);
1134 int last = this->count() - 1;
1135 while (upper < last) {
1136 const SkOpSpan& span = this->span(upper + 1);
1137 if (span.fT != newT) {
1138 break;
1139 }
1140 ++upper;
1141 }
1142 for (int iIndex = lower; iIndex <= upper; ++iIndex) {
1143 const SkOpSpan& span = this->span(iIndex);
1144 SkOpSegment* aOther = span.fOther;
1145 int aLower = span.fOtherIndex;
1146 SkScalar aT = span.fOtherT;
1147 bool aResetFlags = false;
1148 while (aLower > 0) {
1149 SkOpSpan* aSpan = &aOther->fTs[aLower - 1];
1150 for (int iIndex = lower; iIndex <= upper; ++iIndex) {
1151 if (aSpan->fPt == this->fTs[iIndex].fPt) {
1152 goto matchFound;
1153 }
1154 }
1155 break;
1156 matchFound:
1157 --aLower;
1158 }
1159 int aUpper = span.fOtherIndex;
1160 int aLast = aOther->count() - 1;
1161 while (aUpper < aLast) {
1162 SkOpSpan* aSpan = &aOther->fTs[aUpper + 1];
1163 for (int iIndex = lower; iIndex <= upper; ++iIndex) {
1164 if (aSpan->fPt == this->fTs[iIndex].fPt) {
1165 goto matchFound2;
1166 }
1167 }
1168 break;
1169 matchFound2:
1170 ++aUpper;
1171 }
1172 if (aOther->fTs[aLower].fT == 0) {
1173 aT = 0;
1174 } else if (aOther->fTs[aUpper].fT == 1) {
1175 aT = 1;
1176 }
1177 bool aFixed = false;
1178 for (int aIndex = aLower; aIndex <= aUpper; ++aIndex) {
1179 SkOpSpan* aSpan = &aOther->fTs[aIndex];
1180 if (aSpan->fT == aT) {
1181 continue;
1182 }
1183 SkASSERT(way_roughly_equal(aSpan->fT, aT));
1184 if (!aFixed) {
1185 aOther->fixOtherTIndex();
1186 aFixed = true;
1187 }
1188 aSpan->fT = aT;
1189 aSpan->fOther->fTs[aSpan->fOtherIndex].fOtherT = aT;
1190 aResetFlags = true;
1191 }
1192 if (aResetFlags) {
1193 aOther->resetSpanFlags();
1194 }
1195 }
1196 }
1197nextI: ;
1198 }
1199}
1200
1201void SkOpSegment::alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other,
1202 double otherT, const SkOpSegment* other2, SkOpSpan* oSpan,
1203 SkTDArray<AlignedSpan>* alignedArray) {
1204 AlignedSpan* aligned = alignedArray->append();
1205 aligned->fOldPt = oSpan->fPt;
1206 aligned->fPt = newPt;
1207 aligned->fOldT = oSpan->fT;
1208 aligned->fT = newT;
1209 aligned->fSegment = this; // OPTIMIZE: may be unused, can remove
1210 aligned->fOther1 = other;
1211 aligned->fOther2 = other2;
1212 SkASSERT(SkDPoint::RoughlyEqual(oSpan->fPt, newPt));
1213 oSpan->fPt = newPt;
1214// SkASSERT(way_roughly_equal(oSpan->fT, newT));
1215 oSpan->fT = newT;
1216// SkASSERT(way_roughly_equal(oSpan->fOtherT, otherT));
1217 oSpan->fOtherT = otherT;
1218}
1219
1220bool SkOpSegment::alignSpan(int index, double thisT, const SkPoint& thisPt) {
1221 bool aligned = false;
1222 SkOpSpan* span = &fTs[index];
1223 SkOpSegment* other = span->fOther;
1224 int oIndex = span->fOtherIndex;
1225 SkOpSpan* oSpan = &other->fTs[oIndex];
1226 if (span->fT != thisT) {
1227 span->fT = thisT;
1228 oSpan->fOtherT = thisT;
1229 aligned = true;
1230 }
1231 if (span->fPt != thisPt) {
1232 span->fPt = thisPt;
1233 oSpan->fPt = thisPt;
1234 aligned = true;
1235 }
1236 double oT = oSpan->fT;
1237 if (oT == 0) {
1238 return aligned;
1239 }
1240 int oStart = other->nextSpan(oIndex, -1) + 1;
1241 oSpan = &other->fTs[oStart];
1242 int otherIndex = oStart;
1243 if (oT == 1) {
1244 if (aligned) {
1245 while (oSpan->fPt == thisPt && oSpan->fT != 1) {
1246 oSpan->fTiny = true;
1247 ++oSpan;
1248 }
1249 }
1250 return aligned;
1251 }
1252 oT = oSpan->fT;
1253 int oEnd = other->nextSpan(oIndex, 1);
1254 bool oAligned = false;
1255 if (oSpan->fPt != thisPt) {
1256 oAligned |= other->alignSpan(oStart, oT, thisPt);
1257 }
1258 while (++otherIndex < oEnd) {
1259 SkOpSpan* oNextSpan = &other->fTs[otherIndex];
1260 if (oNextSpan->fT != oT || oNextSpan->fPt != thisPt) {
1261 oAligned |= other->alignSpan(otherIndex, oT, thisPt);
1262 }
1263 }
1264 if (oAligned) {
1265 other->alignSpanState(oStart, oEnd);
1266 }
1267 return aligned;
1268}
1269
1270void SkOpSegment::alignSpanState(int start, int end) {
1271 SkOpSpan* lastSpan = &fTs[--end];
1272 bool allSmall = lastSpan->fSmall;
1273 bool allTiny = lastSpan->fTiny;
1274 bool allDone = lastSpan->fDone;
1275 SkDEBUGCODE(int winding = lastSpan->fWindValue);
1276 SkDEBUGCODE(int oppWinding = lastSpan->fOppValue);
1277 int index = start;
1278 while (index < end) {
1279 SkOpSpan* span = &fTs[index];
1280 span->fSmall = allSmall;
1281 span->fTiny = allTiny;
1282 if (span->fDone != allDone) {
1283 span->fDone = allDone;
1284 fDoneSpans += allDone ? 1 : -1;
1285 }
1286 SkASSERT(span->fWindValue == winding);
1287 SkASSERT(span->fOppValue == oppWinding);
1288 ++index;
1289 }
1290}
1291
1292void SkOpSegment::blindCancel(const SkCoincidence& coincidence, SkOpSegment* other) {
1293 bool binary = fOperand != other->fOperand;
1294 int index = 0;
1295 int last = this->count();
1296 do {
1297 SkOpSpan& span = this->fTs[--last];
1298 if (span.fT != 1 && !span.fSmall) {
1299 break;
1300 }
1301 span.fCoincident = true;
1302 } while (true);
1303 int oIndex = other->count();
1304 do {
1305 SkOpSpan& oSpan = other->fTs[--oIndex];
1306 if (oSpan.fT != 1 && !oSpan.fSmall) {
1307 break;
1308 }
1309 oSpan.fCoincident = true;
1310 } while (true);
1311 do {
1312 SkOpSpan* test = &this->fTs[index];
1313 int baseWind = test->fWindValue;
1314 int baseOpp = test->fOppValue;
1315 int endIndex = index;
1316 while (++endIndex <= last) {
1317 SkOpSpan* endSpan = &this->fTs[endIndex];
1318 SkASSERT(endSpan->fT < 1);
1319 if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
1320 break;
1321 }
1322 endSpan->fCoincident = true;
1323 }
1324 SkOpSpan* oTest = &other->fTs[oIndex];
1325 int oBaseWind = oTest->fWindValue;
1326 int oBaseOpp = oTest->fOppValue;
1327 int oStartIndex = oIndex;
1328 while (--oStartIndex >= 0) {
1329 SkOpSpan* oStartSpan = &other->fTs[oStartIndex];
1330 if (oStartSpan->fWindValue != oBaseWind || oStartSpan->fOppValue != oBaseOpp) {
1331 break;
1332 }
1333 oStartSpan->fCoincident = true;
1334 }
1335 bool decrement = baseWind && oBaseWind;
1336 bool bigger = baseWind >= oBaseWind;
1337 do {
1338 SkASSERT(test->fT < 1);
1339 if (decrement) {
1340 if (binary && bigger) {
1341 test->fOppValue--;
1342 } else {
1343 decrementSpan(test);
1344 }
1345 }
1346 test->fCoincident = true;
1347 test = &fTs[++index];
1348 } while (index < endIndex);
1349 do {
1350 SkASSERT(oTest->fT < 1);
1351 if (decrement) {
1352 if (binary && !bigger) {
1353 oTest->fOppValue--;
1354 } else {
1355 other->decrementSpan(oTest);
1356 }
1357 }
1358 oTest->fCoincident = true;
1359 oTest = &other->fTs[--oIndex];
1360 } while (oIndex > oStartIndex);
1361 } while (index <= last && oIndex >= 0);
1362 SkASSERT(index > last);
1363 SkASSERT(oIndex < 0);
1364}
1365
1366void SkOpSegment::blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other) {
1367 bool binary = fOperand != other->fOperand;
1368 int index = 0;
1369 int last = this->count();
1370 do {
1371 SkOpSpan& span = this->fTs[--last];
1372 if (span.fT != 1 && !span.fSmall) {
1373 break;
1374 }
1375 span.fCoincident = true;
1376 } while (true);
1377 int oIndex = 0;
1378 int oLast = other->count();
1379 do {
1380 SkOpSpan& oSpan = other->fTs[--oLast];
1381 if (oSpan.fT != 1 && !oSpan.fSmall) {
1382 break;
1383 }
1384 oSpan.fCoincident = true;
1385 } while (true);
1386 do {
1387 SkOpSpan* test = &this->fTs[index];
1388 int baseWind = test->fWindValue;
1389 int baseOpp = test->fOppValue;
1390 int endIndex = index;
1391 SkOpSpan* endSpan;
1392 while (++endIndex <= last) {
1393 endSpan = &this->fTs[endIndex];
1394 SkASSERT(endSpan->fT < 1);
1395 if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
1396 break;
1397 }
1398 endSpan->fCoincident = true;
1399 }
1400 SkOpSpan* oTest = &other->fTs[oIndex];
1401 int oBaseWind = oTest->fWindValue;
1402 int oBaseOpp = oTest->fOppValue;
1403 int oEndIndex = oIndex;
1404 SkOpSpan* oEndSpan;
1405 while (++oEndIndex <= oLast) {
1406 oEndSpan = &this->fTs[oEndIndex];
1407 SkASSERT(oEndSpan->fT < 1);
1408 if (oEndSpan->fWindValue != oBaseWind || oEndSpan->fOppValue != oBaseOpp) {
1409 break;
1410 }
1411 oEndSpan->fCoincident = true;
1412 }
1413 // consolidate the winding count even if done
1414 if ((test->fWindValue || test->fOppValue) && (oTest->fWindValue || oTest->fOppValue)) {
1415 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
1416 bumpCoincidentBlind(binary, index, endIndex);
1417 other->bumpCoincidentOBlind(oIndex, oEndIndex);
1418 } else {
1419 other->bumpCoincidentBlind(binary, oIndex, oEndIndex);
1420 bumpCoincidentOBlind(index, endIndex);
1421 }
1422 }
1423 index = endIndex;
1424 oIndex = oEndIndex;
1425 } while (index <= last && oIndex <= oLast);
1426 SkASSERT(index > last);
1427 SkASSERT(oIndex > oLast);
1428}
1429
1430void SkOpSegment::bumpCoincidentBlind(bool binary, int index, int endIndex) {
1431 const SkOpSpan& oTest = fTs[index];
1432 int oWindValue = oTest.fWindValue;
1433 int oOppValue = oTest.fOppValue;
1434 if (binary) {
1435 SkTSwap<int>(oWindValue, oOppValue);
1436 }
1437 do {
1438 (void) bumpSpan(&fTs[index], oWindValue, oOppValue);
1439 } while (++index < endIndex);
1440}
1441
1442bool SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
1443 SkTArray<SkPoint, true>* outsideTs) {
1444 int index = *indexPtr;
1445 int oWindValue = oTest.fWindValue;
1446 int oOppValue = oTest.fOppValue;
1447 if (binary) {
1448 SkTSwap<int>(oWindValue, oOppValue);
1449 }
1450 SkOpSpan* const test = &fTs[index];
1451 SkOpSpan* end = test;
1452 const SkPoint& oStartPt = oTest.fPt;
1453 do {
1454 if (end->fDone && !end->fTiny && !end->fSmall) { // extremely large paths trigger this
1455 return false;
1456 }
1457 if (bumpSpan(end, oWindValue, oOppValue)) {
1458 TrackOutside(outsideTs, oStartPt);
1459 }
1460 end = &fTs[++index];
1461 } while ((end->fPt == test->fPt || precisely_equal(end->fT, test->fT)) && end->fT < 1);
1462 *indexPtr = index;
1463 return true;
1464}
1465
1466void SkOpSegment::bumpCoincidentOBlind(int index, int endIndex) {
1467 do {
1468 zeroSpan(&fTs[index]);
1469 } while (++index < endIndex);
1470}
1471
1472// because of the order in which coincidences are resolved, this and other
1473// may not have the same intermediate points. Compute the corresponding
1474// intermediate T values (using this as the master, other as the follower)
1475// and walk other conditionally -- hoping that it catches up in the end
1476bool SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
1477 SkTArray<SkPoint, true>* oOutsidePts, const SkPoint& oEndPt) {
1478 int oIndex = *oIndexPtr;
1479 SkOpSpan* const oTest = &fTs[oIndex];
1480 SkOpSpan* oEnd = oTest;
1481 const SkPoint& oStartPt = oTest->fPt;
1482 double oStartT = oTest->fT;
1483#if 0 // FIXME : figure out what disabling this breaks
1484 const SkPoint& startPt = test.fPt;
1485 // this is always true since oEnd == oTest && oStartPt == oTest->fPt -- find proper condition
1486 if (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
1487 TrackOutside(oOutsidePts, startPt);
1488 }
1489#endif
1490 bool foundEnd = false;
1491 while (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
1492 foundEnd |= oEndPt == oEnd->fPt;
1493 zeroSpan(oEnd);
1494 oEnd = &fTs[++oIndex];
1495 }
1496 *oIndexPtr = oIndex;
1497 return foundEnd;
1498}
1499
1500// FIXME: need to test this case:
1501// contourA has two segments that are coincident
1502// contourB has two segments that are coincident in the same place
1503// each ends up with +2/0 pairs for winding count
1504// since logic below doesn't transfer count (only increments/decrements) can this be
1505// resolved to +4/0 ?
1506
1507// set spans from start to end to increment the greater by one and decrement
1508// the lesser
1509bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
1510 SkOpSegment* other) {
1511 bool binary = fOperand != other->fOperand;
1512 int index = 0;
1513 while (startPt != fTs[index].fPt) {
1514 SkASSERT(index < fTs.count());
1515 ++index;
1516 }
1517 double startT = fTs[index].fT;
1518 while (index > 0 && precisely_equal(fTs[index - 1].fT, startT)) {
1519 --index;
1520 }
1521 int oIndex = 0;
1522 while (startPt != other->fTs[oIndex].fPt) {
1523 SkASSERT(oIndex < other->fTs.count());
1524 ++oIndex;
1525 }
1526 double oStartT = other->fTs[oIndex].fT;
1527 while (oIndex > 0 && precisely_equal(other->fTs[oIndex - 1].fT, oStartT)) {
1528 --oIndex;
1529 }
1530 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
1531 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
1532 SkOpSpan* test = &fTs[index];
1533 const SkPoint* testPt = &test->fPt;
1534 double testT = test->fT;
1535 SkOpSpan* oTest = &other->fTs[oIndex];
1536 const SkPoint* oTestPt = &oTest->fPt;
1537 // paths with extreme data will fail this test and eject out of pathops altogether later on
1538 // SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
1539 do {
1540 SkASSERT(test->fT < 1);
1541 if (oTest->fT == 1) {
1542 // paths with extreme data may be so mismatched that we fail here
1543 return false;
1544 }
1545
1546 // consolidate the winding count even if done
1547 bool foundEnd = false;
1548 if ((test->fWindValue == 0 && test->fOppValue == 0)
1549 || (oTest->fWindValue == 0 && oTest->fOppValue == 0)) {
1550 SkDEBUGCODE(int firstWind = test->fWindValue);
1551 SkDEBUGCODE(int firstOpp = test->fOppValue);
1552 do {
1553 SkASSERT(firstWind == fTs[index].fWindValue);
1554 SkASSERT(firstOpp == fTs[index].fOppValue);
1555 ++index;
1556 SkASSERT(index < fTs.count());
1557 } while (*testPt == fTs[index].fPt);
1558 SkDEBUGCODE(firstWind = oTest->fWindValue);
1559 SkDEBUGCODE(firstOpp = oTest->fOppValue);
1560 do {
1561 SkASSERT(firstWind == other->fTs[oIndex].fWindValue);
1562 SkASSERT(firstOpp == other->fTs[oIndex].fOppValue);
1563 ++oIndex;
1564 SkASSERT(oIndex < other->fTs.count());
1565 } while (*oTestPt == other->fTs[oIndex].fPt);
1566 } else {
1567 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
1568 if (!bumpCoincidentThis(*oTest, binary, &index, &outsidePts)) {
1569 return false;
1570 }
1571 foundEnd = other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt);
1572 } else {
1573 if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
1574 return false;
1575 }
1576 foundEnd = bumpCoincidentOther(*oTest, &index, &outsidePts, endPt);
1577 }
1578 }
1579 test = &fTs[index];
1580 testPt = &test->fPt;
1581 testT = test->fT;
1582 oTest = &other->fTs[oIndex];
1583 oTestPt = &oTest->fPt;
1584 if (endPt == *testPt || precisely_equal(endT, testT)) {
1585 break;
1586 }
1587 if (0 && foundEnd) { // FIXME: this is likely needed but wait until a test case triggers it
1588 break;
1589 }
1590// SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
1591 } while (endPt != *oTestPt);
1592 // in rare cases, one may have ended before the other
1593 if (endPt != *testPt && !precisely_equal(endT, testT)) {
1594 int lastWind = test[-1].fWindValue;
1595 int lastOpp = test[-1].fOppValue;
1596 bool zero = lastWind == 0 && lastOpp == 0;
1597 do {
1598 if (test->fWindValue || test->fOppValue) {
1599 test->fWindValue = lastWind;
1600 test->fOppValue = lastOpp;
1601 if (zero) {
1602 SkASSERT(!test->fDone);
1603 test->fDone = true;
1604 ++fDoneSpans;
1605 }
1606 }
1607 test = &fTs[++index];
1608 testPt = &test->fPt;
1609 } while (endPt != *testPt);
1610 }
1611 if (endPt != *oTestPt) {
1612 // look ahead to see if zeroing more spans will allows us to catch up
1613 int oPeekIndex = oIndex;
1614 bool success = true;
1615 SkOpSpan* oPeek;
1616 int oCount = other->count();
1617 do {
1618 oPeek = &other->fTs[oPeekIndex];
1619 if (++oPeekIndex == oCount) {
1620 success = false;
1621 break;
1622 }
1623 } while (endPt != oPeek->fPt);
1624 if (success) {
1625 // make sure the matching point completes the coincidence span
1626 success = false;
1627 do {
1628 if (oPeek->fOther == this) {
1629 success = true;
1630 break;
1631 }
1632 if (++oPeekIndex == oCount) {
1633 break;
1634 }
1635 oPeek = &other->fTs[oPeekIndex];
1636 } while (endPt == oPeek->fPt);
1637 }
1638 if (success) {
1639 do {
1640 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
1641 if (other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt)) {
1642 break;
caryclarkccec0f92015-03-24 07:28:17 -07001643 }
1644 } else {
reed0dc4dd62015-03-24 13:55:33 -07001645 if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
1646 return false;
caryclarkccec0f92015-03-24 07:28:17 -07001647 }
1648 }
reed0dc4dd62015-03-24 13:55:33 -07001649 oTest = &other->fTs[oIndex];
1650 oTestPt = &oTest->fPt;
1651 } while (endPt != *oTestPt);
1652 }
1653 }
1654 int outCount = outsidePts.count();
1655 if (!done() && outCount) {
1656 addCoinOutsides(outsidePts[0], endPt, other);
1657 }
1658 if (!other->done() && oOutsidePts.count()) {
1659 other->addCoinOutsides(oOutsidePts[0], endPt, this);
1660 }
1661 setCoincidentRange(startPt, endPt, other);
1662 other->setCoincidentRange(startPt, endPt, this);
1663 return true;
1664}
1665
1666// FIXME: this doesn't prevent the same span from being added twice
1667// fix in caller, SkASSERT here?
1668// FIXME: this may erroneously reject adds for cubic loops
1669const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
1670 const SkPoint& pt, const SkPoint& pt2) {
1671 int tCount = fTs.count();
1672 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1673 const SkOpSpan& span = fTs[tIndex];
1674 if (!approximately_negative(span.fT - t)) {
1675 break;
1676 }
1677 if (span.fOther == other) {
1678 bool tsMatch = approximately_equal(span.fT, t);
1679 bool otherTsMatch = approximately_equal(span.fOtherT, otherT);
1680 // FIXME: add cubic loop detecting logic here
1681 // if fLoop bit is set on span, that could be enough if addOtherT copies the bit
1682 // or if a new bit is added ala fOtherLoop
1683 if (tsMatch || otherTsMatch) {
1684#if DEBUG_ADD_T_PAIR
1685 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1686 __FUNCTION__, fID, t, other->fID, otherT);
1687#endif
1688 return NULL;
caryclarkccec0f92015-03-24 07:28:17 -07001689 }
1690 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001691 }
reed0dc4dd62015-03-24 13:55:33 -07001692 int oCount = other->count();
1693 for (int oIndex = 0; oIndex < oCount; ++oIndex) {
1694 const SkOpSpan& oSpan = other->span(oIndex);
1695 if (!approximately_negative(oSpan.fT - otherT)) {
1696 break;
1697 }
1698 if (oSpan.fOther == this) {
1699 bool otherTsMatch = approximately_equal(oSpan.fT, otherT);
1700 bool tsMatch = approximately_equal(oSpan.fOtherT, t);
1701 if (otherTsMatch || tsMatch) {
1702#if DEBUG_ADD_T_PAIR
1703 SkDebugf("%s addTPair other duplicate this=%d %1.9g other=%d %1.9g\n",
1704 __FUNCTION__, fID, t, other->fID, otherT);
1705#endif
1706 return NULL;
1707 }
caryclarkccec0f92015-03-24 07:28:17 -07001708 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001709 }
reed0dc4dd62015-03-24 13:55:33 -07001710#if DEBUG_ADD_T_PAIR
1711 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1712 __FUNCTION__, fID, t, other->fID, otherT);
1713#endif
1714 SkASSERT(other != this);
1715 int insertedAt = addT(other, pt, t);
1716 int otherInsertedAt = other->addT(this, pt2, otherT);
1717 this->addOtherT(insertedAt, otherT, otherInsertedAt);
1718 other->addOtherT(otherInsertedAt, t, insertedAt);
1719 this->matchWindingValue(insertedAt, t, borrowWind);
1720 other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
1721 SkOpSpan& span = this->fTs[insertedAt];
1722 if (pt != pt2) {
1723 span.fNear = true;
1724 SkOpSpan& oSpan = other->fTs[otherInsertedAt];
1725 oSpan.fNear = true;
caryclarkccec0f92015-03-24 07:28:17 -07001726 }
reed0dc4dd62015-03-24 13:55:33 -07001727 // if the newly inserted spans match a neighbor on one but not the other, make them agree
1728 int lower = this->nextExactSpan(insertedAt, -1) + 1;
1729 int upper = this->nextExactSpan(insertedAt, 1) - 1;
1730 if (upper < 0) {
1731 upper = this->count() - 1;
1732 }
1733 int oLower = other->nextExactSpan(otherInsertedAt, -1) + 1;
1734 int oUpper = other->nextExactSpan(otherInsertedAt, 1) - 1;
1735 if (oUpper < 0) {
1736 oUpper = other->count() - 1;
1737 }
1738 if (lower == upper && oLower == oUpper) {
1739 return &span;
1740 }
1741#if DEBUG_CONCIDENT
1742 SkDebugf("%s id=%d lower=%d upper=%d other=%d oLower=%d oUpper=%d\n", __FUNCTION__,
1743 debugID(), lower, upper, other->debugID(), oLower, oUpper);
1744#endif
1745 // find the nearby spans in one range missing in the other
1746 this->alignRange(lower, upper, other, oLower, oUpper);
1747 other->alignRange(oLower, oUpper, this, lower, upper);
1748 return &span;
1749}
1750
1751const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
1752 const SkPoint& pt) {
1753 return addTPair(t, other, otherT, borrowWind, pt, pt);
1754}
1755
1756bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
1757 const SkPoint midPt = ptAtT(midT);
1758 SkPathOpsBounds bounds;
1759 bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1760 bounds.sort();
1761 return bounds.almostContains(midPt);
1762}
1763
1764bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const {
1765 if (lesser > greater) {
1766 SkTSwap<int>(lesser, greater);
1767 }
1768 return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
1769}
1770
1771// in extreme cases (like the buffer overflow test) return false to abort
1772// for now, if one t value represents two different points, then the values are too extreme
1773// to generate meaningful results
1774bool SkOpSegment::calcAngles() {
1775 int spanCount = fTs.count();
1776 if (spanCount <= 2) {
1777 return spanCount == 2;
1778 }
1779 int index = 1;
1780 const SkOpSpan* firstSpan = &fTs[index];
1781 int activePrior = checkSetAngle(0);
1782 const SkOpSpan* span = &fTs[0];
1783 if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) {
1784 index = findStartSpan(0); // curve start intersects
1785 if (fTs[index].fT == 0) {
1786 return false;
1787 }
1788 SkASSERT(index > 0);
1789 if (activePrior >= 0) {
1790 addStartSpan(index);
1791 }
1792 }
1793 bool addEnd;
1794 int endIndex = spanCount - 1;
1795 span = &fTs[endIndex - 1];
1796 if ((addEnd = span->fT == 1 || span->fTiny)) { // if curve end intersects
1797 endIndex = findEndSpan(endIndex);
1798 SkASSERT(endIndex > 0);
1799 } else {
1800 addEnd = fTs[endIndex].fOtherT != 0 || fTs[endIndex].fOther->multipleStarts();
1801 }
1802 SkASSERT(endIndex >= index);
1803 int prior = 0;
1804 while (index < endIndex) {
1805 const SkOpSpan& fromSpan = fTs[index]; // for each intermediate intersection
1806 const SkOpSpan* lastSpan;
1807 span = &fromSpan;
1808 int start = index;
1809 do {
1810 lastSpan = span;
1811 span = &fTs[++index];
1812 SkASSERT(index < spanCount);
1813 if (!precisely_negative(span->fT - lastSpan->fT) && !lastSpan->fTiny) {
1814 break;
1815 }
1816 if (!SkDPoint::ApproximatelyEqual(lastSpan->fPt, span->fPt)) {
1817 return false;
1818 }
1819 } while (true);
1820 SkOpAngle* angle = NULL;
1821 SkOpAngle* priorAngle;
1822 if (activePrior >= 0) {
1823 int pActive = firstActive(prior);
1824 SkASSERT(pActive < start);
1825 priorAngle = &fAngles.push_back();
1826 priorAngle->set(this, start, pActive);
1827 }
1828 int active = checkSetAngle(start);
1829 if (active >= 0) {
1830 SkASSERT(active < index);
1831 angle = &fAngles.push_back();
1832 angle->set(this, active, index);
1833 }
1834 #if DEBUG_ANGLE
1835 debugCheckPointsEqualish(start, index);
1836 #endif
1837 prior = start;
1838 do {
1839 const SkOpSpan* startSpan = &fTs[start - 1];
1840 if (!startSpan->fSmall || isCanceled(start - 1) || startSpan->fFromAngle
1841 || startSpan->fToAngle) {
1842 break;
1843 }
1844 --start;
1845 } while (start > 0);
1846 do {
1847 if (activePrior >= 0) {
1848 SkASSERT(fTs[start].fFromAngle == NULL);
1849 fTs[start].fFromAngle = priorAngle;
1850 }
1851 if (active >= 0) {
1852 SkASSERT(fTs[start].fToAngle == NULL);
1853 fTs[start].fToAngle = angle;
1854 }
1855 } while (++start < index);
1856 activePrior = active;
1857 }
1858 if (addEnd && activePrior >= 0) {
1859 addEndSpan(endIndex);
1860 }
1861 return true;
1862}
1863
1864int SkOpSegment::checkSetAngle(int tIndex) const {
1865 const SkOpSpan* span = &fTs[tIndex];
1866 while (span->fTiny /* || span->fSmall */) {
1867 span = &fTs[++tIndex];
1868 }
1869 return isCanceled(tIndex) ? -1 : tIndex;
1870}
1871
1872// at this point, the span is already ordered, or unorderable
1873int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
1874 SkASSERT(includeType != SkOpAngle::kUnaryXor);
1875 SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
1876 if (NULL == firstAngle || NULL == firstAngle->next()) {
1877 return SK_NaN32;
1878 }
1879 // if all angles have a computed winding,
1880 // or if no adjacent angles are orderable,
1881 // or if adjacent orderable angles have no computed winding,
1882 // there's nothing to do
1883 // if two orderable angles are adjacent, and both are next to orderable angles,
1884 // and one has winding computed, transfer to the other
1885 SkOpAngle* baseAngle = NULL;
1886 bool tryReverse = false;
1887 // look for counterclockwise transfers
1888 SkOpAngle* angle = firstAngle->previous();
1889 SkOpAngle* next = angle->next();
1890 firstAngle = next;
1891 do {
1892 SkOpAngle* prior = angle;
1893 angle = next;
1894 next = angle->next();
1895 SkASSERT(prior->next() == angle);
1896 SkASSERT(angle->next() == next);
1897 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
1898 baseAngle = NULL;
1899 continue;
1900 }
1901 int testWinding = angle->segment()->windSum(angle);
1902 if (SK_MinS32 != testWinding) {
1903 baseAngle = angle;
1904 tryReverse = true;
1905 continue;
1906 }
1907 if (baseAngle) {
1908 ComputeOneSum(baseAngle, angle, includeType);
1909 baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
1910 }
1911 } while (next != firstAngle);
1912 if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
1913 firstAngle = baseAngle;
1914 tryReverse = true;
1915 }
1916 if (tryReverse) {
1917 baseAngle = NULL;
1918 SkOpAngle* prior = firstAngle;
1919 do {
1920 angle = prior;
1921 prior = angle->previous();
1922 SkASSERT(prior->next() == angle);
1923 next = angle->next();
1924 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
1925 baseAngle = NULL;
1926 continue;
1927 }
1928 int testWinding = angle->segment()->windSum(angle);
1929 if (SK_MinS32 != testWinding) {
1930 baseAngle = angle;
1931 continue;
1932 }
1933 if (baseAngle) {
1934 ComputeOneSumReverse(baseAngle, angle, includeType);
1935 baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
1936 }
1937 } while (prior != firstAngle);
1938 }
1939 int minIndex = SkMin32(startIndex, endIndex);
1940 return windSum(minIndex);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001941}
1942
caryclark@google.com570863f2013-09-16 15:55:01 +00001943void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
1944 SkOpAngle::IncludeType includeType) {
1945 const SkOpSegment* baseSegment = baseAngle->segment();
1946 int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
1947 int sumSuWinding;
1948 bool binary = includeType >= SkOpAngle::kBinarySingle;
1949 if (binary) {
1950 sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
1951 if (baseSegment->operand()) {
1952 SkTSwap<int>(sumMiWinding, sumSuWinding);
1953 }
1954 }
1955 SkOpSegment* nextSegment = nextAngle->segment();
1956 int maxWinding, sumWinding;
reed0dc4dd62015-03-24 13:55:33 -07001957 SkOpSpan* last;
caryclark@google.com570863f2013-09-16 15:55:01 +00001958 if (binary) {
1959 int oppMaxWinding, oppSumWinding;
1960 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
1961 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1962 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001963 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001964 } else {
1965 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
1966 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001967 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001968 }
1969 nextAngle->setLastMarked(last);
1970}
1971
1972void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
1973 SkOpAngle::IncludeType includeType) {
1974 const SkOpSegment* baseSegment = baseAngle->segment();
1975 int sumMiWinding = baseSegment->updateWinding(baseAngle);
1976 int sumSuWinding;
1977 bool binary = includeType >= SkOpAngle::kBinarySingle;
1978 if (binary) {
1979 sumSuWinding = baseSegment->updateOppWinding(baseAngle);
1980 if (baseSegment->operand()) {
1981 SkTSwap<int>(sumMiWinding, sumSuWinding);
1982 }
1983 }
1984 SkOpSegment* nextSegment = nextAngle->segment();
1985 int maxWinding, sumWinding;
reed0dc4dd62015-03-24 13:55:33 -07001986 SkOpSpan* last;
caryclark@google.com570863f2013-09-16 15:55:01 +00001987 if (binary) {
1988 int oppMaxWinding, oppSumWinding;
1989 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
1990 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1991 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001992 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001993 } else {
1994 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
1995 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001996 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001997 }
1998 nextAngle->setLastMarked(last);
1999}
2000
reed0dc4dd62015-03-24 13:55:33 -07002001bool SkOpSegment::containsPt(const SkPoint& pt, int index, int endIndex) const {
2002 int step = index < endIndex ? 1 : -1;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002003 do {
reed0dc4dd62015-03-24 13:55:33 -07002004 const SkOpSpan& span = this->span(index);
2005 if (span.fPt == pt) {
2006 const SkOpSpan& endSpan = this->span(endIndex);
2007 return span.fT == endSpan.fT && pt != endSpan.fPt;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002008 }
reed0dc4dd62015-03-24 13:55:33 -07002009 index += step;
2010 } while (index != endIndex);
2011 return false;
caryclarkdac1d172014-06-17 05:15:38 -07002012}
2013
reed0dc4dd62015-03-24 13:55:33 -07002014bool SkOpSegment::containsT(double t, const SkOpSegment* other, double otherT) const {
2015 int count = this->count();
2016 for (int index = 0; index < count; ++index) {
2017 const SkOpSpan& span = fTs[index];
2018 if (t < span.fT) {
2019 return false;
2020 }
2021 if (t == span.fT) {
2022 if (other != span.fOther) {
2023 continue;
2024 }
2025 if (other->fVerb != SkPath::kCubic_Verb) {
2026 return true;
2027 }
2028 if (!other->fLoop) {
2029 return true;
2030 }
2031 double otherMidT = (otherT + span.fOtherT) / 2;
2032 SkPoint otherPt = other->ptAtT(otherMidT);
2033 return SkDPoint::ApproximatelyEqual(span.fPt, otherPt);
2034 }
2035 }
2036 return false;
2037}
2038
2039int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
2040 bool* hitSomething, double mid, bool opp, bool current) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002041 SkScalar bottom = fBounds.fBottom;
reed0dc4dd62015-03-24 13:55:33 -07002042 int bestTIndex = -1;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002043 if (bottom <= *bestY) {
reed0dc4dd62015-03-24 13:55:33 -07002044 return bestTIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002045 }
2046 SkScalar top = fBounds.fTop;
2047 if (top >= basePt.fY) {
reed0dc4dd62015-03-24 13:55:33 -07002048 return bestTIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002049 }
2050 if (fBounds.fLeft > basePt.fX) {
reed0dc4dd62015-03-24 13:55:33 -07002051 return bestTIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002052 }
2053 if (fBounds.fRight < basePt.fX) {
reed0dc4dd62015-03-24 13:55:33 -07002054 return bestTIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002055 }
2056 if (fBounds.fLeft == fBounds.fRight) {
2057 // if vertical, and directly above test point, wait for another one
reed0dc4dd62015-03-24 13:55:33 -07002058 return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002059 }
2060 // intersect ray starting at basePt with edge
2061 SkIntersections intersections;
2062 // OPTIMIZE: use specialty function that intersects ray with curve,
2063 // returning t values only for curve (we don't care about t on ray)
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002064 intersections.allowNear(false);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002065 int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
2066 (fPts, top, bottom, basePt.fX, false);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002067 if (pts == 0 || (current && pts == 1)) {
reed0dc4dd62015-03-24 13:55:33 -07002068 return bestTIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002069 }
2070 if (current) {
2071 SkASSERT(pts > 1);
2072 int closestIdx = 0;
2073 double closest = fabs(intersections[0][0] - mid);
2074 for (int idx = 1; idx < pts; ++idx) {
2075 double test = fabs(intersections[0][idx] - mid);
2076 if (closest > test) {
2077 closestIdx = idx;
2078 closest = test;
2079 }
2080 }
2081 intersections.quickRemoveOne(closestIdx, --pts);
2082 }
2083 double bestT = -1;
2084 for (int index = 0; index < pts; ++index) {
2085 double foundT = intersections[0][index];
2086 if (approximately_less_than_zero(foundT)
2087 || approximately_greater_than_one(foundT)) {
2088 continue;
2089 }
reed@google.com277c3f82013-05-31 15:17:50 +00002090 SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002091 if (approximately_negative(testY - *bestY)
2092 || approximately_negative(basePt.fY - testY)) {
2093 continue;
2094 }
2095 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
reed0dc4dd62015-03-24 13:55:33 -07002096 return SK_MinS32; // if the intersection is edge on, wait for another one
caryclark@google.com07393ca2013-04-08 11:47:37 +00002097 }
2098 if (fVerb > SkPath::kLine_Verb) {
reed@google.com277c3f82013-05-31 15:17:50 +00002099 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002100 if (approximately_zero(dx)) {
reed0dc4dd62015-03-24 13:55:33 -07002101 return SK_MinS32; // hit vertical, wait for another one
caryclark@google.com07393ca2013-04-08 11:47:37 +00002102 }
2103 }
2104 *bestY = testY;
2105 bestT = foundT;
2106 }
2107 if (bestT < 0) {
reed0dc4dd62015-03-24 13:55:33 -07002108 return bestTIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002109 }
2110 SkASSERT(bestT >= 0);
reed0dc4dd62015-03-24 13:55:33 -07002111 SkASSERT(bestT <= 1);
2112 int start;
2113 int end = 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002114 do {
reed0dc4dd62015-03-24 13:55:33 -07002115 start = end;
2116 end = nextSpan(start, 1);
2117 } while (fTs[end].fT < bestT);
2118 // FIXME: see next candidate for a better pattern to find the next start/end pair
2119 while (start + 1 < end && fTs[start].fDone) {
2120 ++start;
2121 }
2122 if (!isCanceled(start)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002123 *hitT = bestT;
reed0dc4dd62015-03-24 13:55:33 -07002124 bestTIndex = start;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002125 *hitSomething = true;
2126 }
reed0dc4dd62015-03-24 13:55:33 -07002127 return bestTIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002128}
2129
reed0dc4dd62015-03-24 13:55:33 -07002130bool SkOpSegment::decrementSpan(SkOpSpan* span) {
2131 SkASSERT(span->fWindValue > 0);
2132 if (--(span->fWindValue) == 0) {
2133 if (!span->fOppValue && !span->fDone) {
2134 span->fDone = true;
2135 ++fDoneSpans;
2136 return true;
2137 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002138 }
reed0dc4dd62015-03-24 13:55:33 -07002139 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002140}
2141
reed0dc4dd62015-03-24 13:55:33 -07002142bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
2143 SkASSERT(!span->fDone || span->fTiny || span->fSmall);
2144 span->fWindValue += windDelta;
2145 SkASSERT(span->fWindValue >= 0);
2146 span->fOppValue += oppDelta;
2147 SkASSERT(span->fOppValue >= 0);
2148 if (fXor) {
2149 span->fWindValue &= 1;
2150 }
2151 if (fOppXor) {
2152 span->fOppValue &= 1;
2153 }
2154 if (!span->fWindValue && !span->fOppValue) {
2155 if (!span->fDone) {
2156 span->fDone = true;
2157 ++fDoneSpans;
2158 }
2159 return true;
2160 }
2161 return false;
2162}
2163
2164const SkOpSpan& SkOpSegment::firstSpan(const SkOpSpan& thisSpan) const {
2165 const SkOpSpan* firstSpan = &thisSpan; // rewind to the start
2166 const SkOpSpan* beginSpan = fTs.begin();
2167 const SkPoint& testPt = thisSpan.fPt;
2168 while (firstSpan > beginSpan && firstSpan[-1].fPt == testPt) {
2169 --firstSpan;
2170 }
2171 return *firstSpan;
2172}
2173
2174const SkOpSpan& SkOpSegment::lastSpan(const SkOpSpan& thisSpan) const {
2175 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
2176 const SkOpSpan* lastSpan = &thisSpan; // find the end
2177 const SkPoint& testPt = thisSpan.fPt;
2178 while (lastSpan < endSpan && lastSpan[1].fPt == testPt) {
2179 ++lastSpan;
2180 }
2181 return *lastSpan;
2182}
2183
2184// with a loop, the comparison is move involved
2185// scan backwards and forwards to count all matching points
2186// (verify that there are twp scans marked as loops)
2187// compare that against 2 matching scans for loop plus other results
2188bool SkOpSegment::calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts) {
2189 const SkOpSpan& firstSpan = this->firstSpan(thisSpan); // rewind to the start
2190 const SkOpSpan& lastSpan = this->lastSpan(thisSpan); // find the end
2191 double firstLoopT = -1, lastLoopT = -1;
2192 const SkOpSpan* testSpan = &firstSpan - 1;
2193 while (++testSpan <= &lastSpan) {
2194 if (testSpan->fLoop) {
2195 firstLoopT = testSpan->fT;
2196 break;
2197 }
2198 }
2199 testSpan = &lastSpan + 1;
2200 while (--testSpan >= &firstSpan) {
2201 if (testSpan->fLoop) {
2202 lastLoopT = testSpan->fT;
2203 break;
2204 }
2205 }
2206 SkASSERT((firstLoopT == -1) == (lastLoopT == -1));
2207 if (firstLoopT == -1) {
2208 return false;
2209 }
2210 SkASSERT(firstLoopT < lastLoopT);
2211 testSpan = &firstSpan - 1;
2212 smallCounts[0] = smallCounts[1] = 0;
2213 while (++testSpan <= &lastSpan) {
2214 SkASSERT(approximately_equal(testSpan->fT, firstLoopT) +
2215 approximately_equal(testSpan->fT, lastLoopT) == 1);
2216 smallCounts[approximately_equal(testSpan->fT, lastLoopT)]++;
2217 }
2218 return true;
2219}
2220
2221double SkOpSegment::calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
2222 double hiEnd, const SkOpSegment* other, int thisStart) {
2223 if (max >= hiEnd) {
2224 return -1;
2225 }
2226 int end = findOtherT(hiEnd, ref);
2227 if (end < 0) {
2228 return -1;
2229 }
2230 double tHi = span(end).fT;
2231 double tLo, refLo;
2232 if (thisStart >= 0) {
2233 tLo = span(thisStart).fT;
2234 refLo = min;
2235 } else {
2236 int start1 = findOtherT(loEnd, ref);
2237 SkASSERT(start1 >= 0);
2238 tLo = span(start1).fT;
2239 refLo = loEnd;
2240 }
2241 double missingT = (max - refLo) / (hiEnd - refLo);
2242 missingT = tLo + missingT * (tHi - tLo);
2243 return missingT;
2244}
2245
2246double SkOpSegment::calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
2247 double hiEnd, const SkOpSegment* other, int thisEnd) {
2248 if (min <= loEnd) {
2249 return -1;
2250 }
2251 int start = findOtherT(loEnd, ref);
2252 if (start < 0) {
2253 return -1;
2254 }
2255 double tLo = span(start).fT;
2256 double tHi, refHi;
2257 if (thisEnd >= 0) {
2258 tHi = span(thisEnd).fT;
2259 refHi = max;
2260 } else {
2261 int end1 = findOtherT(hiEnd, ref);
2262 if (end1 < 0) {
2263 return -1;
2264 }
2265 tHi = span(end1).fT;
2266 refHi = hiEnd;
2267 }
2268 double missingT = (min - loEnd) / (refHi - loEnd);
2269 missingT = tLo + missingT * (tHi - tLo);
2270 return missingT;
2271}
2272
2273// see if spans with two or more intersections have the same number on the other end
2274void SkOpSegment::checkDuplicates() {
2275 debugValidate();
2276 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
2277 int index;
2278 int endIndex = 0;
2279 bool endFound;
2280 do {
2281 index = endIndex;
2282 endIndex = nextExactSpan(index, 1);
2283 if ((endFound = endIndex < 0)) {
2284 endIndex = count();
2285 }
2286 int dupCount = endIndex - index;
2287 if (dupCount < 2) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002288 continue;
2289 }
reed0dc4dd62015-03-24 13:55:33 -07002290 do {
2291 const SkOpSpan* thisSpan = &fTs[index];
2292 if (thisSpan->fNear) {
2293 continue;
2294 }
2295 SkOpSegment* other = thisSpan->fOther;
2296 int oIndex = thisSpan->fOtherIndex;
2297 int oStart = other->nextExactSpan(oIndex, -1) + 1;
2298 int oEnd = other->nextExactSpan(oIndex, 1);
2299 if (oEnd < 0) {
2300 oEnd = other->count();
2301 }
2302 int oCount = oEnd - oStart;
2303 // force the other to match its t and this pt if not on an end point
2304 if (oCount != dupCount) {
2305 MissingSpan& missing = missingSpans.push_back();
2306 missing.fOther = NULL;
2307 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
2308 missing.fPt = thisSpan->fPt;
2309 const SkOpSpan& oSpan = other->span(oIndex);
2310 if (oCount > dupCount) {
2311 missing.fSegment = this;
2312 missing.fT = thisSpan->fT;
2313 other->checkLinks(&oSpan, &missingSpans);
2314 } else {
2315 missing.fSegment = other;
2316 missing.fT = oSpan.fT;
2317 checkLinks(thisSpan, &missingSpans);
2318 }
2319 if (!missingSpans.back().fOther) {
2320 missingSpans.pop_back();
2321 }
2322 }
2323 } while (++index < endIndex);
2324 } while (!endFound);
2325 int missingCount = missingSpans.count();
2326 if (missingCount == 0) {
2327 return;
2328 }
2329 SkSTArray<kMissingSpanCount, MissingSpan, true> missingCoincidence;
2330 for (index = 0; index < missingCount; ++index) {
2331 MissingSpan& missing = missingSpans[index];
2332 SkOpSegment* missingOther = missing.fOther;
2333 if (missing.fSegment == missing.fOther) {
2334 continue;
2335 }
2336#if 0 // FIXME: this eliminates spurious data from skpwww_argus_presse_fr_41 but breaks
2337 // skpwww_fashionscandal_com_94 -- calcAngles complains, but I don't understand why
2338 if (missing.fSegment->containsT(missing.fT, missing.fOther, missing.fOtherT)) {
2339#if DEBUG_DUPLICATES
2340 SkDebugf("skip 1 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fSegment->fID,
2341 missing.fT, missing.fOther->fID, missing.fOtherT);
2342#endif
2343 continue;
2344 }
2345 if (missing.fOther->containsT(missing.fOtherT, missing.fSegment, missing.fT)) {
2346#if DEBUG_DUPLICATES
2347 SkDebugf("skip 2 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fOther->fID,
2348 missing.fOtherT, missing.fSegment->fID, missing.fT);
2349#endif
2350 continue;
2351 }
2352#endif
2353 // skip if adding would insert point into an existing coincindent span
2354 if (missing.fSegment->inCoincidentSpan(missing.fT, missingOther)
2355 && missingOther->inCoincidentSpan(missing.fOtherT, this)) {
2356 continue;
2357 }
2358 // skip if the created coincident spans are small
2359 if (missing.fSegment->coincidentSmall(missing.fPt, missing.fT, missingOther)
2360 && missingOther->coincidentSmall(missing.fPt, missing.fOtherT, missing.fSegment)) {
2361 continue;
2362 }
2363 const SkOpSpan* added = missing.fSegment->addTPair(missing.fT, missingOther,
2364 missing.fOtherT, false, missing.fPt);
2365 if (added && added->fSmall) {
2366 missing.fSegment->checkSmallCoincidence(*added, &missingCoincidence);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002367 }
2368 }
reed0dc4dd62015-03-24 13:55:33 -07002369 for (index = 0; index < missingCount; ++index) {
2370 MissingSpan& missing = missingSpans[index];
2371 missing.fSegment->fixOtherTIndex();
2372 missing.fOther->fixOtherTIndex();
2373 }
2374 for (index = 0; index < missingCoincidence.count(); ++index) {
2375 MissingSpan& missing = missingCoincidence[index];
2376 missing.fSegment->fixOtherTIndex();
2377 }
2378 debugValidate();
2379}
2380
2381// look to see if the curve end intersects an intermediary that intersects the other
2382bool SkOpSegment::checkEnds() {
2383 debugValidate();
2384 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
2385 int count = fTs.count();
2386 for (int index = 0; index < count; ++index) {
2387 const SkOpSpan& span = fTs[index];
2388 double otherT = span.fOtherT;
2389 if (otherT != 0 && otherT != 1) { // only check ends
2390 continue;
2391 }
2392 const SkOpSegment* other = span.fOther;
2393 // peek start/last describe the range of spans that match the other t of this span
2394 int peekStart = span.fOtherIndex;
2395 while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT)
2396 ;
2397 int otherCount = other->fTs.count();
2398 int peekLast = span.fOtherIndex;
2399 while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT)
2400 ;
2401 if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do
2402 continue;
2403 }
2404 // t start/last describe the range of spans that match the t of this span
2405 double t = span.fT;
2406 double tBottom = -1;
2407 int tStart = -1;
2408 int tLast = count;
2409 bool lastSmall = false;
2410 double afterT = t;
2411 for (int inner = 0; inner < count; ++inner) {
2412 double innerT = fTs[inner].fT;
2413 if (innerT <= t && innerT > tBottom) {
2414 if (innerT < t || !lastSmall) {
2415 tStart = inner - 1;
2416 }
2417 tBottom = innerT;
2418 }
2419 if (innerT > afterT) {
2420 if (t == afterT && lastSmall) {
2421 afterT = innerT;
2422 } else {
2423 tLast = inner;
2424 break;
2425 }
2426 }
2427 lastSmall = innerT <= t ? fTs[inner].fSmall : false;
2428 }
2429 for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) {
2430 if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span
2431 continue;
2432 }
2433 const SkOpSpan& peekSpan = other->fTs[peekIndex];
2434 SkOpSegment* match = peekSpan.fOther;
2435 if (match->done()) {
2436 continue; // if the edge has already been eaten (likely coincidence), ignore it
2437 }
2438 const double matchT = peekSpan.fOtherT;
2439 // see if any of the spans match the other spans
2440 for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
2441 const SkOpSpan& tSpan = fTs[tIndex];
2442 if (tSpan.fOther == match) {
2443 if (tSpan.fOtherT == matchT) {
2444 goto nextPeekIndex;
2445 }
2446 double midT = (tSpan.fOtherT + matchT) / 2;
2447 if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
2448 goto nextPeekIndex;
2449 }
2450 }
2451 }
2452 if (missingSpans.count() > 0) {
2453 const MissingSpan& lastMissing = missingSpans.back();
2454 if (lastMissing.fT == t
2455 && lastMissing.fOther == match
2456 && lastMissing.fOtherT == matchT) {
2457 SkASSERT(SkDPoint::ApproximatelyEqual(lastMissing.fPt, peekSpan.fPt));
2458 continue;
2459 }
2460 }
2461 if (this == match) {
2462 return false; // extremely large paths can trigger this
2463 }
2464#if DEBUG_CHECK_ALIGN
2465 SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n",
2466 __FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY);
2467#endif
2468 // this segment is missing a entry that the other contains
2469 // remember so we can add the missing one and recompute the indices
2470 {
2471 MissingSpan& missing = missingSpans.push_back();
2472 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
2473 missing.fT = t;
2474 SkASSERT(this != match);
2475 missing.fOther = match;
2476 missing.fOtherT = matchT;
2477 missing.fPt = peekSpan.fPt;
2478 }
2479 break;
2480nextPeekIndex:
2481 ;
2482 }
2483 }
2484 if (missingSpans.count() == 0) {
2485 debugValidate();
2486 return true;
2487 }
2488 debugValidate();
2489 int missingCount = missingSpans.count();
2490 for (int index = 0; index < missingCount; ++index) {
2491 MissingSpan& missing = missingSpans[index];
2492 if (this != missing.fOther) {
2493 addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
2494 }
2495 }
2496 fixOtherTIndex();
2497 // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
2498 // avoid this
2499 for (int index = 0; index < missingCount; ++index) {
2500 missingSpans[index].fOther->fixOtherTIndex();
2501 }
2502 debugValidate();
2503 return true;
2504}
2505
2506void SkOpSegment::checkLinks(const SkOpSpan* base,
2507 SkTArray<MissingSpan, true>* missingSpans) const {
2508 const SkOpSpan* first = fTs.begin();
2509 const SkOpSpan* last = fTs.end() - 1;
2510 SkASSERT(base >= first && last >= base);
2511 const SkOpSegment* other = base->fOther;
2512 const SkOpSpan* oFirst = other->fTs.begin();
2513 const SkOpSpan* oLast = other->fTs.end() - 1;
2514 const SkOpSpan* oSpan = &other->fTs[base->fOtherIndex];
2515 const SkOpSpan* test = base;
2516 const SkOpSpan* missing = NULL;
2517 while (test > first && (--test)->fPt == base->fPt) {
2518 if (this == test->fOther) {
2519 continue;
2520 }
2521 CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
2522 }
2523 test = base;
2524 while (test < last && (++test)->fPt == base->fPt) {
2525 SkASSERT(this != test->fOther || test->fLoop);
2526 CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
2527 }
2528}
2529
2530// see if spans with two or more intersections all agree on common t and point values
2531void SkOpSegment::checkMultiples() {
2532 debugValidate();
2533 int index;
2534 int end = 0;
2535 while (fTs[++end].fT == 0)
2536 ;
2537 while (fTs[end].fT < 1) {
2538 int start = index = end;
2539 end = nextExactSpan(index, 1);
2540 if (end <= index) {
2541 return; // buffer overflow example triggers this
2542 }
2543 if (index + 1 == end) {
2544 continue;
2545 }
2546 // force the duplicates to agree on t and pt if not on the end
2547 SkOpSpan& span = fTs[index];
2548 double thisT = span.fT;
2549 const SkPoint& thisPt = span.fPt;
2550 span.fMultiple = true;
2551 bool aligned = false;
2552 while (++index < end) {
2553 aligned |= alignSpan(index, thisT, thisPt);
2554 }
2555 if (aligned) {
2556 alignSpanState(start, end);
2557 }
2558 fMultiples = true;
2559 }
2560 debugValidate();
2561}
2562
2563void SkOpSegment::CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
2564 const SkOpSpan* oFirst, const SkOpSpan* oLast, const SkOpSpan** missingPtr,
2565 SkTArray<MissingSpan, true>* missingSpans) {
2566 SkASSERT(oSpan->fPt == test->fPt);
2567 const SkOpSpan* oTest = oSpan;
2568 while (oTest > oFirst && (--oTest)->fPt == test->fPt) {
2569 if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
2570 return;
2571 }
2572 }
2573 oTest = oSpan;
2574 while (oTest < oLast && (++oTest)->fPt == test->fPt) {
2575 if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
2576 return;
2577 }
2578 }
2579 if (*missingPtr) {
2580 missingSpans->push_back();
2581 }
2582 MissingSpan& lastMissing = missingSpans->back();
2583 if (*missingPtr) {
2584 lastMissing = missingSpans->end()[-2];
2585 }
2586 *missingPtr = test;
2587 lastMissing.fOther = test->fOther;
2588 lastMissing.fOtherT = test->fOtherT;
2589}
2590
2591bool SkOpSegment::checkSmall(int index) const {
2592 if (fTs[index].fSmall) {
2593 return true;
2594 }
2595 double tBase = fTs[index].fT;
2596 while (index > 0 && precisely_negative(tBase - fTs[--index].fT))
2597 ;
2598 return fTs[index].fSmall;
2599}
2600
2601// a pair of curves may turn into coincident lines -- small may be a hint that that happened
2602// if a cubic contains a loop, the counts must be adjusted
2603void SkOpSegment::checkSmall() {
2604 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
2605 const SkOpSpan* beginSpan = fTs.begin();
2606 const SkOpSpan* thisSpan = beginSpan - 1;
2607 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
2608 while (++thisSpan < endSpan) {
2609 if (!thisSpan->fSmall) {
2610 continue;
2611 }
2612 if (!thisSpan->fWindValue) {
2613 continue;
2614 }
2615 const SkOpSpan& firstSpan = this->firstSpan(*thisSpan);
2616 const SkOpSpan& lastSpan = this->lastSpan(*thisSpan);
2617 const SkOpSpan* nextSpan = &firstSpan + 1;
2618 ptrdiff_t smallCount = &lastSpan - &firstSpan + 1;
2619 SkASSERT(1 <= smallCount && smallCount < count());
2620 if (smallCount <= 1 && !nextSpan->fSmall) {
2621 SkASSERT(1 == smallCount);
2622 checkSmallCoincidence(firstSpan, NULL);
2623 continue;
2624 }
2625 // at this point, check for missing computed intersections
2626 const SkPoint& testPt = firstSpan.fPt;
2627 thisSpan = &firstSpan - 1;
2628 SkOpSegment* other = NULL;
2629 while (++thisSpan <= &lastSpan) {
2630 other = thisSpan->fOther;
2631 if (other != this) {
2632 break;
2633 }
2634 }
2635 SkASSERT(other != this);
2636 int oIndex = thisSpan->fOtherIndex;
2637 const SkOpSpan& oSpan = other->span(oIndex);
2638 const SkOpSpan& oFirstSpan = other->firstSpan(oSpan);
2639 const SkOpSpan& oLastSpan = other->lastSpan(oSpan);
2640 ptrdiff_t oCount = &oLastSpan - &oFirstSpan + 1;
2641 if (fLoop) {
2642 int smallCounts[2];
2643 SkASSERT(!other->fLoop); // FIXME: we need more complicated logic for pair of loops
2644 if (calcLoopSpanCount(*thisSpan, smallCounts)) {
2645 if (smallCounts[0] && oCount != smallCounts[0]) {
2646 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2647 }
2648 if (smallCounts[1] && oCount != smallCounts[1]) {
2649 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2650 }
2651 goto nextSmallCheck;
2652 }
2653 }
2654 if (other->fLoop) {
2655 int otherCounts[2];
2656 if (other->calcLoopSpanCount(other->span(oIndex), otherCounts)) {
2657 if (otherCounts[0] && otherCounts[0] != smallCount) {
2658 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2659 }
2660 if (otherCounts[1] && otherCounts[1] != smallCount) {
2661 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2662 }
2663 goto nextSmallCheck;
2664 }
2665 }
2666 if (oCount != smallCount) { // check if number of pts in this match other
2667 MissingSpan& missing = missingSpans.push_back();
2668 missing.fOther = NULL;
2669 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
2670 missing.fPt = testPt;
2671 const SkOpSpan& oSpan = other->span(oIndex);
2672 if (oCount > smallCount) {
2673 missing.fSegment = this;
2674 missing.fT = thisSpan->fT;
2675 other->checkLinks(&oSpan, &missingSpans);
2676 } else {
2677 missing.fSegment = other;
2678 missing.fT = oSpan.fT;
2679 checkLinks(thisSpan, &missingSpans);
2680 }
2681 if (!missingSpans.back().fOther || missing.fSegment->done()) {
2682 missingSpans.pop_back();
2683 }
2684 }
2685nextSmallCheck:
2686 thisSpan = &lastSpan;
2687 }
2688 int missingCount = missingSpans.count();
2689 for (int index = 0; index < missingCount; ++index) {
2690 MissingSpan& missing = missingSpans[index];
2691 SkOpSegment* missingOther = missing.fOther;
2692 // note that add t pair may edit span arrays, so prior pointers to spans are no longer valid
2693 if (!missing.fSegment->addTPair(missing.fT, missingOther, missing.fOtherT, false,
2694 missing.fPt)) {
2695 continue;
2696 }
2697 int otherTIndex = missingOther->findT(missing.fOtherT, missing.fPt, missing.fSegment);
2698 const SkOpSpan& otherSpan = missingOther->span(otherTIndex);
2699 if (otherSpan.fSmall) {
2700 const SkOpSpan* nextSpan = &otherSpan;
2701 if (nextSpan->fPt == missing.fPt) {
2702 continue;
2703 }
2704 do {
2705 ++nextSpan;
2706 } while (nextSpan->fSmall);
2707 if (nextSpan->fT == 1) {
2708 continue;
2709 }
2710 SkAssertResult(missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt,
2711 nextSpan->fT, missingOther));
2712 } else if (otherSpan.fT > 0) {
2713 const SkOpSpan* priorSpan = &otherSpan;
2714 do {
2715 --priorSpan;
2716 } while (priorSpan->fT == otherSpan.fT);
2717 if (priorSpan->fSmall) {
2718 missing.fSegment->addTCancel(missing.fPt, priorSpan->fPt, missingOther);
2719 }
2720 }
2721 }
2722 // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
2723 // avoid this
2724 for (int index = 0; index < missingCount; ++index) {
2725 MissingSpan& missing = missingSpans[index];
2726 missing.fSegment->fixOtherTIndex();
2727 missing.fOther->fixOtherTIndex();
2728 }
2729 debugValidate();
2730}
2731
2732void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span,
2733 SkTArray<MissingSpan, true>* checkMultiple) {
2734 SkASSERT(span.fSmall);
2735 if (0 && !span.fWindValue) {
2736 return;
2737 }
2738 SkASSERT(&span < fTs.end() - 1);
2739 const SkOpSpan* next = &span + 1;
2740 SkASSERT(!next->fSmall || checkMultiple);
2741 if (checkMultiple) {
2742 while (next->fSmall) {
2743 ++next;
2744 SkASSERT(next < fTs.end());
2745 }
2746 }
2747 SkOpSegment* other = span.fOther;
2748 while (other != next->fOther) {
2749 if (!checkMultiple) {
2750 return;
2751 }
2752 const SkOpSpan* test = next + 1;
2753 if (test == fTs.end()) {
2754 return;
2755 }
2756 if (test->fPt != next->fPt || !precisely_equal(test->fT, next->fT)) {
2757 return;
2758 }
2759 next = test;
2760 }
2761 SkASSERT(span.fT < next->fT);
2762 int oStartIndex = other->findExactT(span.fOtherT, this);
2763 int oEndIndex = other->findExactT(next->fOtherT, this);
2764 // FIXME: be overly conservative by limiting this to the caller that allows multiple smalls
2765 if (!checkMultiple || fVerb != SkPath::kLine_Verb || other->fVerb != SkPath::kLine_Verb) {
2766 SkPoint mid = ptAtT((span.fT + next->fT) / 2);
2767 const SkOpSpan& oSpanStart = other->fTs[oStartIndex];
2768 const SkOpSpan& oSpanEnd = other->fTs[oEndIndex];
2769 SkPoint oMid = other->ptAtT((oSpanStart.fT + oSpanEnd.fT) / 2);
2770 if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
2771 return;
2772 }
2773 }
2774 // FIXME: again, be overly conservative to avoid breaking existing tests
2775 const SkOpSpan& oSpan = oStartIndex < oEndIndex ? other->fTs[oStartIndex]
2776 : other->fTs[oEndIndex];
2777 if (checkMultiple && !oSpan.fSmall) {
2778 return;
2779 }
2780// SkASSERT(oSpan.fSmall);
2781 if (oStartIndex < oEndIndex) {
2782 SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, other));
2783 } else {
2784 addTCancel(span.fPt, next->fPt, other);
2785 }
2786 if (!checkMultiple) {
2787 return;
2788 }
2789 // check to see if either segment is coincident with a third segment -- if it is, and if
2790 // the opposite segment is not already coincident with the third, make it so
2791 // OPTIMIZE: to make this check easier, add coincident and cancel could set a coincident bit
2792 if (span.fWindValue != 1 || span.fOppValue != 0) {
2793// start here;
2794 // iterate through the spans, looking for the third coincident case
2795 // if we find one, we need to return state to the caller so that the indices can be fixed
2796 // this also suggests that all of this function is fragile since it relies on a valid index
2797 }
2798 // probably should make this a common function rather than copy/paste code
2799 if (oSpan.fWindValue != 1 || oSpan.fOppValue != 0) {
2800 const SkOpSpan* oTest = &oSpan;
2801 while (--oTest >= other->fTs.begin()) {
2802 if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
2803 break;
2804 }
2805 SkOpSegment* testOther = oTest->fOther;
2806 SkASSERT(testOther != this);
2807 // look in both directions to see if there is a coincident span
2808 const SkOpSpan* tTest = testOther->fTs.begin();
2809 for (int testIndex = 0; testIndex < testOther->count(); ++testIndex) {
2810 if (tTest->fPt != span.fPt) {
2811 ++tTest;
2812 continue;
2813 }
2814 if (testOther->verb() != SkPath::kLine_Verb
2815 || other->verb() != SkPath::kLine_Verb) {
2816 SkPoint mid = ptAtT((span.fT + next->fT) / 2);
2817 SkPoint oMid = other->ptAtT((oTest->fOtherT + tTest->fT) / 2);
2818 if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
2819 continue;
2820 }
2821 }
2822#if DEBUG_CONCIDENT
2823 SkDebugf("%s coincident found=%d %1.9g %1.9g\n", __FUNCTION__, testOther->fID,
2824 oTest->fOtherT, tTest->fT);
2825#endif
2826 if (tTest->fT < oTest->fOtherT) {
2827 SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, testOther));
2828 } else {
2829 addTCancel(span.fPt, next->fPt, testOther);
2830 }
2831 MissingSpan missing;
2832 missing.fSegment = testOther;
2833 checkMultiple->push_back(missing);
2834 break;
2835 }
2836 }
2837 oTest = &oSpan;
2838 while (++oTest < other->fTs.end()) {
2839 if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
2840 break;
2841 }
2842
2843 }
2844 }
2845}
2846
2847// if pair of spans on either side of tiny have the same end point and mid point, mark
2848// them as parallel
2849void SkOpSegment::checkTiny() {
2850 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
2851 SkOpSpan* thisSpan = fTs.begin() - 1;
2852 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be tiny
2853 while (++thisSpan < endSpan) {
2854 if (!thisSpan->fTiny) {
2855 continue;
2856 }
2857 SkOpSpan* nextSpan = thisSpan + 1;
2858 double thisT = thisSpan->fT;
2859 double nextT = nextSpan->fT;
2860 if (thisT == nextT) {
2861 continue;
2862 }
2863 SkASSERT(thisT < nextT);
2864 SkASSERT(thisSpan->fPt == nextSpan->fPt);
2865 SkOpSegment* thisOther = thisSpan->fOther;
2866 SkOpSegment* nextOther = nextSpan->fOther;
2867 int oIndex = thisSpan->fOtherIndex;
2868 for (int oStep = -1; oStep <= 1; oStep += 2) {
2869 int oEnd = thisOther->nextExactSpan(oIndex, oStep);
2870 if (oEnd < 0) {
2871 continue;
2872 }
2873 const SkOpSpan& oSpan = thisOther->span(oEnd);
2874 int nIndex = nextSpan->fOtherIndex;
2875 for (int nStep = -1; nStep <= 1; nStep += 2) {
2876 int nEnd = nextOther->nextExactSpan(nIndex, nStep);
2877 if (nEnd < 0) {
2878 continue;
2879 }
2880 const SkOpSpan& nSpan = nextOther->span(nEnd);
2881 if (oSpan.fPt != nSpan.fPt) {
2882 continue;
2883 }
2884 double oMidT = (thisSpan->fOtherT + oSpan.fT) / 2;
2885 const SkPoint& oPt = thisOther->ptAtT(oMidT);
2886 double nMidT = (nextSpan->fOtherT + nSpan.fT) / 2;
2887 const SkPoint& nPt = nextOther->ptAtT(nMidT);
2888 if (!AlmostEqualUlps(oPt, nPt)) {
2889 continue;
2890 }
2891#if DEBUG_CHECK_TINY
2892 SkDebugf("%s [%d] add coincidence [%d] [%d]\n", __FUNCTION__, fID,
2893 thisOther->fID, nextOther->fID);
2894#endif
2895 // this segment is missing a entry that the other contains
2896 // remember so we can add the missing one and recompute the indices
2897 MissingSpan& missing = missingSpans.push_back();
2898 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
2899 missing.fSegment = thisOther;
2900 missing.fT = thisSpan->fOtherT;
2901 SkASSERT(this != nextOther);
2902 missing.fOther = nextOther;
2903 missing.fOtherT = nextSpan->fOtherT;
2904 missing.fPt = thisSpan->fPt;
2905 }
2906 }
2907 }
2908 int missingCount = missingSpans.count();
2909 if (!missingCount) {
2910 return;
2911 }
2912 for (int index = 0; index < missingCount; ++index) {
2913 MissingSpan& missing = missingSpans[index];
2914 if (missing.fSegment != missing.fOther) {
2915 missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false,
2916 missing.fPt);
2917 }
2918 }
2919 // OPTIMIZE: consolidate to avoid multiple calls to fix index
2920 for (int index = 0; index < missingCount; ++index) {
2921 MissingSpan& missing = missingSpans[index];
2922 missing.fSegment->fixOtherTIndex();
2923 missing.fOther->fixOtherTIndex();
2924 }
2925}
2926
2927bool SkOpSegment::coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const {
2928 int count = this->count();
2929 for (int index = 0; index < count; ++index) {
2930 const SkOpSpan& span = this->span(index);
2931 if (span.fOther != other) {
2932 continue;
2933 }
2934 if (span.fPt == pt) {
2935 continue;
2936 }
2937 if (!AlmostEqualUlps(span.fPt, pt)) {
2938 continue;
2939 }
2940 if (fVerb != SkPath::kCubic_Verb) {
2941 return true;
2942 }
2943 double tInterval = t - span.fT;
2944 double tMid = t - tInterval / 2;
2945 SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
2946 return midPt.approximatelyEqual(xyAtT(t));
2947 }
2948 return false;
2949}
2950
2951bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart,
2952 int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const {
2953 SkASSERT(span->fT == 0 || span->fT == 1);
2954 SkASSERT(span->fOtherT == 0 || span->fOtherT == 1);
2955 const SkOpSpan* otherSpan = &other->span(oEnd);
2956 double refT = otherSpan->fT;
2957 const SkPoint& refPt = otherSpan->fPt;
2958 const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0);
2959 do {
2960 const SkOpSegment* match = span->fOther;
2961 if (match == otherSpan->fOther) {
2962 // find start of respective spans and see if both have winding
2963 int startIndex, endIndex;
2964 if (span->fOtherT == 1) {
2965 endIndex = span->fOtherIndex;
2966 startIndex = match->nextExactSpan(endIndex, -1);
2967 } else {
2968 startIndex = span->fOtherIndex;
2969 endIndex = match->nextExactSpan(startIndex, 1);
2970 }
2971 const SkOpSpan& startSpan = match->span(startIndex);
2972 if (startSpan.fWindValue != 0) {
2973 // draw ray from endSpan.fPt perpendicular to end tangent and measure distance
2974 // to other segment.
2975 const SkOpSpan& endSpan = match->span(endIndex);
2976 SkDLine ray;
2977 SkVector dxdy;
2978 if (span->fOtherT == 1) {
2979 ray.fPts[0].set(startSpan.fPt);
2980 dxdy = match->dxdy(startIndex);
2981 } else {
2982 ray.fPts[0].set(endSpan.fPt);
2983 dxdy = match->dxdy(endIndex);
2984 }
2985 ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY;
2986 ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX;
2987 SkIntersections i;
2988 int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray);
2989 for (int index = 0; index < roots; ++index) {
2990 if (ray.fPts[0].approximatelyEqual(i.pt(index))) {
2991 double matchMidT = (match->span(startIndex).fT
2992 + match->span(endIndex).fT) / 2;
2993 SkPoint matchMidPt = match->ptAtT(matchMidT);
2994 double otherMidT = (i[0][index] + other->span(oStart).fT) / 2;
2995 SkPoint otherMidPt = other->ptAtT(otherMidT);
2996 if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) {
2997 *startPt = startSpan.fPt;
2998 *endPt = endSpan.fPt;
2999 *endT = endSpan.fT;
3000 return true;
3001 }
3002 }
3003 }
3004 }
3005 return false;
3006 }
3007 if (otherSpan == lastSpan) {
3008 break;
3009 }
3010 otherSpan += step;
3011 } while (otherSpan->fT == refT || otherSpan->fPt == refPt);
3012 return false;
3013}
3014
3015int SkOpSegment::findEndSpan(int endIndex) const {
3016 const SkOpSpan* span = &fTs[--endIndex];
3017 const SkPoint& lastPt = span->fPt;
3018 double endT = span->fT;
3019 do {
3020 span = &fTs[--endIndex];
3021 } while (SkDPoint::ApproximatelyEqual(span->fPt, lastPt) && (span->fT == endT || span->fTiny));
3022 return endIndex + 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003023}
3024
caryclark@google.com07393ca2013-04-08 11:47:37 +00003025/*
3026 The M and S variable name parts stand for the operators.
3027 Mi stands for Minuend (see wiki subtraction, analogous to difference)
3028 Su stands for Subtrahend
3029 The Opp variable name part designates that the value is for the Opposite operator.
3030 Opposite values result from combining coincident spans.
3031 */
reed0dc4dd62015-03-24 13:55:33 -07003032SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
3033 bool* unsortable, SkPathOp op, const int xorMiMask,
3034 const int xorSuMask) {
3035 const int startIndex = *nextStart;
3036 const int endIndex = *nextEnd;
3037 SkASSERT(startIndex != endIndex);
3038 SkDEBUGCODE(const int count = fTs.count());
3039 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
3040 int step = SkSign32(endIndex - startIndex);
3041 *nextStart = startIndex;
3042 SkOpSegment* other = isSimple(nextStart, &step);
3043 if (other)
3044 {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003045 // mark the smaller of startIndex, endIndex done, and all adjacent
3046 // spans with the same T value (but not 'other' spans)
3047#if DEBUG_WINDING
3048 SkDebugf("%s simple\n", __FUNCTION__);
3049#endif
reed0dc4dd62015-03-24 13:55:33 -07003050 int min = SkMin32(startIndex, endIndex);
3051 if (fTs[min].fDone) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003052 return NULL;
3053 }
reed0dc4dd62015-03-24 13:55:33 -07003054 markDoneBinary(min);
3055 double startT = other->fTs[*nextStart].fT;
3056 *nextEnd = *nextStart;
3057 do {
3058 *nextEnd += step;
3059 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
3060 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
3061 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
3062 *unsortable = true;
3063 return NULL;
3064 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003065 return other;
3066 }
reed0dc4dd62015-03-24 13:55:33 -07003067 const int end = nextExactSpan(startIndex, step);
3068 SkASSERT(end >= 0);
3069 SkASSERT(startIndex - endIndex != 0);
3070 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003071 // more than one viable candidate -- measure angles to find best
reed0dc4dd62015-03-24 13:55:33 -07003072
3073 int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp);
caryclark@google.com570863f2013-09-16 15:55:01 +00003074 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003075 if (!sortable) {
3076 *unsortable = true;
reed0dc4dd62015-03-24 13:55:33 -07003077 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003078 return NULL;
3079 }
reed0dc4dd62015-03-24 13:55:33 -07003080 SkOpAngle* angle = spanToAngle(end, startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003081 if (angle->unorderable()) {
3082 *unsortable = true;
reed0dc4dd62015-03-24 13:55:33 -07003083 markDoneBinary(SkMin32(startIndex, endIndex));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003084 return NULL;
3085 }
3086#if DEBUG_SORT
3087 SkDebugf("%s\n", __FUNCTION__);
3088 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003089#endif
reed0dc4dd62015-03-24 13:55:33 -07003090 int sumMiWinding = updateWinding(endIndex, startIndex);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003091 if (sumMiWinding == SK_MinS32) {
3092 *unsortable = true;
reed0dc4dd62015-03-24 13:55:33 -07003093 markDoneBinary(SkMin32(startIndex, endIndex));
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003094 return NULL;
3095 }
reed0dc4dd62015-03-24 13:55:33 -07003096 int sumSuWinding = updateOppWinding(endIndex, startIndex);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003097 if (operand()) {
3098 SkTSwap<int>(sumMiWinding, sumSuWinding);
3099 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003100 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003101 const SkOpAngle* foundAngle = NULL;
3102 bool foundDone = false;
3103 // iterate through the angle, and compute everyone's winding
3104 SkOpSegment* nextSegment;
3105 int activeCount = 0;
3106 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003107 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003108 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003109 nextAngle->end(), op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003110 if (activeAngle) {
3111 ++activeCount;
3112 if (!foundAngle || (foundDone && activeCount & 1)) {
reed0dc4dd62015-03-24 13:55:33 -07003113 if (nextSegment->isTiny(nextAngle)) {
3114 *unsortable = true;
3115 markDoneBinary(SkMin32(startIndex, endIndex));
3116 return NULL;
3117 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003118 foundAngle = nextAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +00003119 foundDone = nextSegment->done(nextAngle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003120 }
3121 }
3122 if (nextSegment->done()) {
3123 continue;
3124 }
reed0dc4dd62015-03-24 13:55:33 -07003125 if (nextSegment->isTiny(nextAngle)) {
3126 continue;
caryclark@google.com570863f2013-09-16 15:55:01 +00003127 }
reed0dc4dd62015-03-24 13:55:33 -07003128 if (!activeAngle) {
3129 (void) nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
3130 }
3131 SkOpSpan* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003132 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003133 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003134 *chase->append() = last;
3135#if DEBUG_WINDING
reed0dc4dd62015-03-24 13:55:33 -07003136 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
3137 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
3138 last->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003139#endif
3140 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003141 } while ((nextAngle = nextAngle->next()) != angle);
reed0dc4dd62015-03-24 13:55:33 -07003142#if DEBUG_ANGLE
3143 if (foundAngle) {
3144 foundAngle->debugSameAs(foundAngle);
3145 }
3146#endif
3147
3148 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003149 if (!foundAngle) {
3150 return NULL;
3151 }
3152 *nextStart = foundAngle->start();
3153 *nextEnd = foundAngle->end();
3154 nextSegment = foundAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003155#if DEBUG_WINDING
3156 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
3157 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
3158 #endif
3159 return nextSegment;
3160}
3161
reed0dc4dd62015-03-24 13:55:33 -07003162SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart,
3163 int* nextEnd, bool* unsortable) {
3164 const int startIndex = *nextStart;
3165 const int endIndex = *nextEnd;
3166 SkASSERT(startIndex != endIndex);
3167 SkDEBUGCODE(const int count = fTs.count());
3168 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
3169 int step = SkSign32(endIndex - startIndex);
3170 *nextStart = startIndex;
3171 SkOpSegment* other = isSimple(nextStart, &step);
3172 if (other)
3173 {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003174 // mark the smaller of startIndex, endIndex done, and all adjacent
3175 // spans with the same T value (but not 'other' spans)
3176#if DEBUG_WINDING
3177 SkDebugf("%s simple\n", __FUNCTION__);
3178#endif
reed0dc4dd62015-03-24 13:55:33 -07003179 int min = SkMin32(startIndex, endIndex);
3180 if (fTs[min].fDone) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003181 return NULL;
3182 }
reed0dc4dd62015-03-24 13:55:33 -07003183 markDoneUnary(min);
3184 double startT = other->fTs[*nextStart].fT;
3185 *nextEnd = *nextStart;
3186 do {
3187 *nextEnd += step;
3188 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
3189 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
3190 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
3191 *unsortable = true;
3192 return NULL;
3193 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003194 return other;
3195 }
reed0dc4dd62015-03-24 13:55:33 -07003196 const int end = nextExactSpan(startIndex, step);
3197 SkASSERT(end >= 0);
3198 SkASSERT(startIndex - endIndex != 0);
3199 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003200 // more than one viable candidate -- measure angles to find best
reed0dc4dd62015-03-24 13:55:33 -07003201
3202 int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00003203 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003204 if (!sortable) {
3205 *unsortable = true;
reed0dc4dd62015-03-24 13:55:33 -07003206 markDoneUnary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003207 return NULL;
3208 }
reed0dc4dd62015-03-24 13:55:33 -07003209 SkOpAngle* angle = spanToAngle(end, startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003210#if DEBUG_SORT
3211 SkDebugf("%s\n", __FUNCTION__);
3212 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003213#endif
reed0dc4dd62015-03-24 13:55:33 -07003214 int sumWinding = updateWinding(endIndex, startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003215 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003216 const SkOpAngle* foundAngle = NULL;
3217 bool foundDone = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003218 SkOpSegment* nextSegment;
3219 int activeCount = 0;
3220 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003221 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003222 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003223 &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003224 if (activeAngle) {
3225 ++activeCount;
3226 if (!foundAngle || (foundDone && activeCount & 1)) {
reed0dc4dd62015-03-24 13:55:33 -07003227 if (nextSegment->isTiny(nextAngle)) {
3228 *unsortable = true;
3229 markDoneUnary(SkMin32(startIndex, endIndex));
3230 return NULL;
3231 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003232 foundAngle = nextAngle;
3233 foundDone = nextSegment->done(nextAngle);
3234 }
3235 }
3236 if (nextSegment->done()) {
3237 continue;
3238 }
reed0dc4dd62015-03-24 13:55:33 -07003239 if (nextSegment->isTiny(nextAngle)) {
3240 continue;
caryclark@google.com570863f2013-09-16 15:55:01 +00003241 }
reed0dc4dd62015-03-24 13:55:33 -07003242 if (!activeAngle) {
3243 nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
3244 }
3245 SkOpSpan* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003246 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003247 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003248 *chase->append() = last;
3249#if DEBUG_WINDING
reed0dc4dd62015-03-24 13:55:33 -07003250 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
3251 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
3252 last->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003253#endif
3254 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003255 } while ((nextAngle = nextAngle->next()) != angle);
reed0dc4dd62015-03-24 13:55:33 -07003256 markDoneUnary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003257 if (!foundAngle) {
3258 return NULL;
3259 }
3260 *nextStart = foundAngle->start();
3261 *nextEnd = foundAngle->end();
3262 nextSegment = foundAngle->segment();
3263#if DEBUG_WINDING
3264 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
3265 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
3266 #endif
3267 return nextSegment;
3268}
3269
reed0dc4dd62015-03-24 13:55:33 -07003270SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsortable) {
3271 const int startIndex = *nextStart;
3272 const int endIndex = *nextEnd;
3273 SkASSERT(startIndex != endIndex);
3274 SkDEBUGCODE(int count = fTs.count());
3275 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
3276 int step = SkSign32(endIndex - startIndex);
3277// Detect cases where all the ends canceled out (e.g.,
3278// there is no angle) and therefore there's only one valid connection
3279 *nextStart = startIndex;
3280 SkOpSegment* other = isSimple(nextStart, &step);
3281 if (other)
3282 {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003283#if DEBUG_WINDING
3284 SkDebugf("%s simple\n", __FUNCTION__);
3285#endif
reed0dc4dd62015-03-24 13:55:33 -07003286 int min = SkMin32(startIndex, endIndex);
3287 if (fTs[min].fDone) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003288 return NULL;
3289 }
reed0dc4dd62015-03-24 13:55:33 -07003290 markDone(min, 1);
3291 double startT = other->fTs[*nextStart].fT;
3292 // FIXME: I don't know why the logic here is difference from the winding case
3293 SkDEBUGCODE(bool firstLoop = true;)
3294 if ((approximately_less_than_zero(startT) && step < 0)
3295 || (approximately_greater_than_one(startT) && step > 0)) {
3296 step = -step;
3297 SkDEBUGCODE(firstLoop = false;)
3298 }
3299 do {
3300 *nextEnd = *nextStart;
3301 do {
3302 *nextEnd += step;
3303 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
3304 if (other->fTs[SkMin32(*nextStart, *nextEnd)].fWindValue) {
3305 break;
3306 }
3307 SkASSERT(firstLoop);
3308 SkDEBUGCODE(firstLoop = false;)
3309 step = -step;
3310 } while (true);
3311 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +00003312 return other;
3313 }
reed0dc4dd62015-03-24 13:55:33 -07003314 SkASSERT(startIndex - endIndex != 0);
3315 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
3316 // parallel block above with presorted version
3317 int end = nextExactSpan(startIndex, step);
3318 SkASSERT(end >= 0);
3319 SkOpAngle* angle = spanToAngle(end, startIndex);
3320 SkASSERT(angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003321#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003322 SkDebugf("%s\n", __FUNCTION__);
3323 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003324#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003325 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003326 const SkOpAngle* foundAngle = NULL;
3327 bool foundDone = false;
3328 SkOpSegment* nextSegment;
3329 int activeCount = 0;
3330 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003331 nextSegment = nextAngle->segment();
3332 ++activeCount;
3333 if (!foundAngle || (foundDone && activeCount & 1)) {
reed0dc4dd62015-03-24 13:55:33 -07003334 if (nextSegment->isTiny(nextAngle)) {
3335 *unsortable = true;
3336 return NULL;
3337 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003338 foundAngle = nextAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003339 if (!(foundDone = nextSegment->done(nextAngle))) {
3340 break;
3341 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003342 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003343 nextAngle = nextAngle->next();
3344 } while (nextAngle != angle);
reed0dc4dd62015-03-24 13:55:33 -07003345 markDone(SkMin32(startIndex, endIndex), 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003346 if (!foundAngle) {
3347 return NULL;
3348 }
3349 *nextStart = foundAngle->start();
3350 *nextEnd = foundAngle->end();
3351 nextSegment = foundAngle->segment();
3352#if DEBUG_WINDING
3353 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
3354 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
3355 #endif
3356 return nextSegment;
3357}
3358
reed0dc4dd62015-03-24 13:55:33 -07003359int SkOpSegment::findStartSpan(int startIndex) const {
3360 int index = startIndex;
3361 const SkOpSpan* span = &fTs[index];
3362 const SkPoint& firstPt = span->fPt;
3363 double firstT = span->fT;
3364 const SkOpSpan* prior;
3365 do {
3366 prior = span;
3367 span = &fTs[++index];
3368 } while (SkDPoint::ApproximatelyEqual(span->fPt, firstPt)
3369 && (span->fT == firstT || prior->fTiny));
3370 return index;
3371}
3372
3373int SkOpSegment::findExactT(double t, const SkOpSegment* match) const {
3374 int count = this->count();
3375 for (int index = 0; index < count; ++index) {
3376 const SkOpSpan& span = fTs[index];
3377 if (span.fT == t && span.fOther == match) {
3378 return index;
3379 }
3380 }
3381 SkASSERT(0);
3382 return -1;
3383}
3384
3385
3386
3387int SkOpSegment::findOtherT(double t, const SkOpSegment* match) const {
3388 int count = this->count();
3389 for (int index = 0; index < count; ++index) {
3390 const SkOpSpan& span = fTs[index];
3391 if (span.fOtherT == t && span.fOther == match) {
3392 return index;
3393 }
3394 }
3395 return -1;
3396}
3397
3398int SkOpSegment::findT(double t, const SkPoint& pt, const SkOpSegment* match) const {
3399 int count = this->count();
3400 // prefer exact matches over approximate matches
3401 for (int index = 0; index < count; ++index) {
3402 const SkOpSpan& span = fTs[index];
3403 if (span.fT == t && span.fOther == match) {
3404 return index;
3405 }
3406 }
3407 for (int index = 0; index < count; ++index) {
3408 const SkOpSpan& span = fTs[index];
3409 if (approximately_equal_orderable(span.fT, t) && span.fOther == match) {
3410 return index;
3411 }
3412 }
3413 // Usually, the pair of ts are an exact match. It's possible that the t values have
3414 // been adjusted to make multiple intersections align. In this rare case, look for a
3415 // matching point / match pair instead.
3416 for (int index = 0; index < count; ++index) {
3417 const SkOpSpan& span = fTs[index];
3418 if (span.fPt == pt && span.fOther == match) {
3419 return index;
3420 }
3421 }
3422 SkASSERT(0);
3423 return -1;
3424}
3425
3426SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable,
3427 bool firstPass) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003428 // iterate through T intersections and return topmost
3429 // topmost tangent from y-min to first pt is closer to horizontal
3430 SkASSERT(!done());
reed0dc4dd62015-03-24 13:55:33 -07003431 int firstT = -1;
3432 /* SkPoint topPt = */ activeLeftTop(&firstT);
3433 if (firstT < 0) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003434 *unsortable = !firstPass;
reed0dc4dd62015-03-24 13:55:33 -07003435 firstT = 0;
3436 while (fTs[firstT].fDone) {
3437 SkASSERT(firstT < fTs.count());
3438 ++firstT;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003439 }
reed0dc4dd62015-03-24 13:55:33 -07003440 *tIndexPtr = firstT;
3441 *endIndexPtr = nextExactSpan(firstT, 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003442 return this;
3443 }
3444 // sort the edges to find the leftmost
3445 int step = 1;
reed0dc4dd62015-03-24 13:55:33 -07003446 int end;
3447 if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003448 step = -1;
reed0dc4dd62015-03-24 13:55:33 -07003449 end = nextSpan(firstT, step);
3450 SkASSERT(end != -1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003451 }
3452 // if the topmost T is not on end, or is three-way or more, find left
3453 // look for left-ness from tLeft to firstT (matching y of other)
reed0dc4dd62015-03-24 13:55:33 -07003454 SkASSERT(firstT - end != 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003455 SkOpAngle* markAngle = spanToAngle(firstT, end);
3456 if (!markAngle) {
reed0dc4dd62015-03-24 13:55:33 -07003457 markAngle = addSingletonAngles(step);
caryclark@google.com570863f2013-09-16 15:55:01 +00003458 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003459 markAngle->markStops();
caryclarke4097e32014-06-18 07:24:19 -07003460 const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
3461 : markAngle->findFirst();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003462 if (!baseAngle) {
3463 return NULL; // nothing to do
caryclark@google.com570863f2013-09-16 15:55:01 +00003464 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003465 SkScalar top = SK_ScalarMax;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003466 const SkOpAngle* firstAngle = NULL;
3467 const SkOpAngle* angle = baseAngle;
3468 do {
3469 if (!angle->unorderable()) {
reed0dc4dd62015-03-24 13:55:33 -07003470 SkOpSegment* next = angle->segment();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003471 SkPathOpsBounds bounds;
3472 next->subDivideBounds(angle->end(), angle->start(), &bounds);
caryclark65f55312014-11-13 06:58:52 -08003473 bool nearSame = AlmostEqualUlps(top, bounds.top());
3474 bool lowerSector = !firstAngle || angle->sectorEnd() < firstAngle->sectorStart();
3475 bool lesserSector = top > bounds.fTop;
3476 if (lesserSector && (!nearSame || lowerSector)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003477 top = bounds.fTop;
3478 firstAngle = angle;
3479 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003480 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003481 angle = angle->next();
3482 } while (angle != baseAngle);
caryclark65f55312014-11-13 06:58:52 -08003483 if (!firstAngle) {
3484 return NULL; // if all are unorderable, give up
3485 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003486#if DEBUG_SORT
3487 SkDebugf("%s\n", __FUNCTION__);
3488 firstAngle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003489#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003490 // skip edges that have already been processed
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003491 angle = firstAngle;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003492 SkOpSegment* leftSegment = NULL;
3493 bool looped = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003494 do {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003495 *unsortable = angle->unorderable();
3496 if (firstPass || !*unsortable) {
3497 leftSegment = angle->segment();
reed0dc4dd62015-03-24 13:55:33 -07003498 *tIndexPtr = angle->end();
3499 *endIndexPtr = angle->start();
3500 if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003501 break;
3502 }
3503 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003504 angle = angle->next();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003505 looped = true;
3506 } while (angle != firstAngle);
3507 if (angle == firstAngle && looped) {
3508 return NULL;
3509 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003510 if (leftSegment->verb() >= SkPath::kQuad_Verb) {
reed0dc4dd62015-03-24 13:55:33 -07003511 const int tIndex = *tIndexPtr;
3512 const int endIndex = *endIndexPtr;
caryclarke4097e32014-06-18 07:24:19 -07003513 bool swap;
reed0dc4dd62015-03-24 13:55:33 -07003514 if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003515 #if DEBUG_SWAP_TOP
reed0dc4dd62015-03-24 13:55:33 -07003516 SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003517 __FUNCTION__,
reed0dc4dd62015-03-24 13:55:33 -07003518 swap, leftSegment->debugInflections(tIndex, endIndex),
3519 leftSegment->serpentine(tIndex, endIndex),
3520 leftSegment->controlsContainedByEnds(tIndex, endIndex),
3521 leftSegment->monotonicInY(tIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003522 #endif
3523 if (swap) {
3524 // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
3525 // sorted but merely the first not already processed (i.e., not done)
reed0dc4dd62015-03-24 13:55:33 -07003526 SkTSwap(*tIndexPtr, *endIndexPtr);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003527 }
3528 }
3529 }
reed0dc4dd62015-03-24 13:55:33 -07003530 SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003531 return leftSegment;
3532}
3533
reed0dc4dd62015-03-24 13:55:33 -07003534int SkOpSegment::firstActive(int tIndex) const {
3535 while (fTs[tIndex].fTiny) {
3536 SkASSERT(!isCanceled(tIndex));
3537 ++tIndex;
3538 }
3539 return tIndex;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003540}
3541
reed0dc4dd62015-03-24 13:55:33 -07003542// FIXME: not crazy about this
3543// when the intersections are performed, the other index is into an
3544// incomplete array. As the array grows, the indices become incorrect
3545// while the following fixes the indices up again, it isn't smart about
3546// skipping segments whose indices are already correct
3547// assuming we leave the code that wrote the index in the first place
3548// FIXME: if called after remove, this needs to correct tiny
3549void SkOpSegment::fixOtherTIndex() {
3550 int iCount = fTs.count();
3551 for (int i = 0; i < iCount; ++i) {
3552 SkOpSpan& iSpan = fTs[i];
3553 double oT = iSpan.fOtherT;
3554 SkOpSegment* other = iSpan.fOther;
3555 int oCount = other->fTs.count();
3556 SkDEBUGCODE(iSpan.fOtherIndex = -1);
3557 for (int o = 0; o < oCount; ++o) {
3558 SkOpSpan& oSpan = other->fTs[o];
3559 if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) {
3560 iSpan.fOtherIndex = o;
3561 oSpan.fOtherIndex = i;
3562 break;
3563 }
3564 }
3565 SkASSERT(iSpan.fOtherIndex >= 0);
3566 }
3567}
3568
3569bool SkOpSegment::inCoincidentSpan(double t, const SkOpSegment* other) const {
3570 int foundEnds = 0;
3571 int count = this->count();
3572 for (int index = 0; index < count; ++index) {
3573 const SkOpSpan& span = this->span(index);
3574 if (span.fCoincident) {
3575 foundEnds |= (span.fOther == other) << ((t > span.fT) + (t >= span.fT));
3576 }
3577 }
3578 SkASSERT(foundEnds != 7);
3579 return foundEnds == 0x3 || foundEnds == 0x5 || foundEnds == 0x6; // two bits set
3580}
3581
3582bool SkOpSegment::inconsistentAngle(int maxWinding, int sumWinding, int oppMaxWinding,
3583 int oppSumWinding, const SkOpAngle* angle) const {
3584 SkASSERT(angle->segment() == this);
3585 if (UseInnerWinding(maxWinding, sumWinding)) {
3586 maxWinding = sumWinding;
3587 }
3588 if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
3589 oppMaxWinding = oppSumWinding;
3590 }
3591 return inconsistentWinding(angle, maxWinding, oppMaxWinding);
3592}
3593
3594bool SkOpSegment::inconsistentWinding(const SkOpAngle* angle, int winding,
3595 int oppWinding) const {
3596 int index = angle->start();
3597 int endIndex = angle->end();
3598 int min = SkMin32(index, endIndex);
3599 int step = SkSign32(endIndex - index);
3600 if (inconsistentWinding(min, winding, oppWinding)) {
3601 return true;
3602 }
3603 const SkOpSegment* other = this;
3604 while ((other = other->nextChase(&index, &step, &min, NULL))) {
3605 if (other->fTs[min].fWindSum != SK_MinS32) {
3606 break;
3607 }
3608 if (fOperand == other->fOperand) {
3609 if (other->inconsistentWinding(min, winding, oppWinding)) {
3610 return true;
3611 }
3612 } else {
3613 if (other->inconsistentWinding(min, oppWinding, winding)) {
3614 return true;
3615 }
3616 }
3617 }
3618 return false;
3619}
3620
3621bool SkOpSegment::inconsistentWinding(int index, int winding, int oppWinding) const {
3622 SkASSERT(winding || oppWinding);
3623 double referenceT = this->span(index).fT;
3624 int lesser = index;
3625 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3626 if (inconsistentWinding(__FUNCTION__, lesser, winding, oppWinding)) {
3627 return true;
3628 }
3629 }
3630 do {
3631 if (inconsistentWinding(__FUNCTION__, index, winding, oppWinding)) {
3632 return true;
3633 }
3634 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3635 return false;
3636}
3637
3638bool SkOpSegment::inconsistentWinding(const char* funName, int tIndex, int winding,
3639 int oppWinding) const {
3640 const SkOpSpan& span = this->span(tIndex);
3641 if (span.fDone && !span.fSmall) {
3642 return false;
3643 }
3644 return (span.fWindSum != SK_MinS32 && span.fWindSum != winding)
3645 || (span.fOppSum != SK_MinS32 && span.fOppSum != oppWinding);
3646}
3647
3648void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
3649 fDoneSpans = 0;
3650 fOperand = operand;
3651 fXor = evenOdd;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003652 fPts = pts;
3653 fVerb = verb;
reed0dc4dd62015-03-24 13:55:33 -07003654 fLoop = fMultiples = fSmall = fTiny = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003655}
3656
reed0dc4dd62015-03-24 13:55:33 -07003657void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
3658 int local = spanSign(start, end);
caryclark65f55312014-11-13 06:58:52 -08003659 SkDEBUGCODE(bool success);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003660 if (angleIncludeType == SkOpAngle::kBinarySingle) {
reed0dc4dd62015-03-24 13:55:33 -07003661 int oppLocal = oppSign(start, end);
caryclark65f55312014-11-13 06:58:52 -08003662 SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, oppLocal, NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003663 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08003664 SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, oppLocal, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003665 } else {
caryclark65f55312014-11-13 06:58:52 -08003666 SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003667 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08003668 SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003669 }
caryclark65f55312014-11-13 06:58:52 -08003670 SkASSERT(success);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003671}
3672
3673/*
3674when we start with a vertical intersect, we try to use the dx to determine if the edge is to
3675the left or the right of vertical. This determines if we need to add the span's
3676sign or not. However, this isn't enough.
3677If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
3678If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
3679from has the same x direction as this span, the winding should change. If the dx is opposite, then
3680the same winding is shared by both.
3681*/
reed0dc4dd62015-03-24 13:55:33 -07003682bool SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
3683 int oppWind, SkScalar hitOppDx) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003684 SkASSERT(hitDx || !winding);
reed@google.com277c3f82013-05-31 15:17:50 +00003685 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
reed0dc4dd62015-03-24 13:55:33 -07003686 SkASSERT(dx);
3687 int windVal = windValue(SkMin32(start, end));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003688#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07003689 SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), winding,
caryclark@google.com07393ca2013-04-08 11:47:37 +00003690 hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
3691#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003692 int sideWind = winding + (dx < 0 ? windVal : -windVal);
3693 if (abs(winding) < abs(sideWind)) {
3694 winding = sideWind;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003695 }
reed0dc4dd62015-03-24 13:55:33 -07003696 SkDEBUGCODE(int oppLocal = oppSign(start, end));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003697 SkASSERT(hitOppDx || !oppWind || !oppLocal);
reed0dc4dd62015-03-24 13:55:33 -07003698 int oppWindVal = oppValue(SkMin32(start, end));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003699 if (!oppWind) {
3700 oppWind = dx < 0 ? oppWindVal : -oppWindVal;
3701 } else if (hitOppDx * dx >= 0) {
3702 int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
3703 if (abs(oppWind) < abs(oppSideWind)) {
3704 oppWind = oppSideWind;
3705 }
3706 }
caryclarkdac1d172014-06-17 05:15:38 -07003707#if DEBUG_WINDING_AT_T
3708 SkDebugf(" winding=%d oppWind=%d\n", winding, oppWind);
3709#endif
caryclark65f55312014-11-13 06:58:52 -08003710 // if this fails to mark (because the edges are too small) inform caller to try again
3711 bool success = markAndChaseWinding(start, end, winding, oppWind, NULL);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003712 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08003713 success |= markAndChaseWinding(end, start, winding, oppWind, NULL);
3714 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003715}
3716
reed0dc4dd62015-03-24 13:55:33 -07003717bool SkOpSegment::inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const {
3718 if (!baseAngle->inLoop()) {
3719 return false;
3720 }
3721 int index = *indexPtr;
3722 SkOpAngle* from = fTs[index].fFromAngle;
3723 SkOpAngle* to = fTs[index].fToAngle;
3724 while (++index < spanCount) {
3725 SkOpAngle* nextFrom = fTs[index].fFromAngle;
3726 SkOpAngle* nextTo = fTs[index].fToAngle;
3727 if (from != nextFrom || to != nextTo) {
3728 break;
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003729 }
caryclarkccec0f92015-03-24 07:28:17 -07003730 }
reed0dc4dd62015-03-24 13:55:33 -07003731 *indexPtr = index;
3732 return true;
3733}
3734
3735// OPTIMIZE: successive calls could start were the last leaves off
3736// or calls could specialize to walk forwards or backwards
3737bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
3738 int tCount = fTs.count();
3739 for (int index = 0; index < tCount; ++index) {
3740 const SkOpSpan& span = fTs[index];
3741 if (approximately_zero(startT - span.fT) && pt == span.fPt) {
3742 return false;
3743 }
3744 }
3745 return true;
3746}
3747
3748
3749SkOpSegment* SkOpSegment::isSimple(int* end, int* step) {
3750 return nextChase(end, step, NULL, NULL);
3751}
3752
3753bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
3754 int start = angle->start();
3755 int end = angle->end();
3756 const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
3757 return mSpan.fTiny;
3758}
3759
3760bool SkOpSegment::isTiny(int index) const {
3761 return fTs[index].fTiny;
3762}
3763
3764// look pair of active edges going away from coincident edge
3765// one of them should be the continuation of other
3766// if both are active, look to see if they both the connect to another coincident pair
3767// if at least one is a line, then make the pair coincident
3768// if neither is a line, test for coincidence
3769bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt,
3770 int step, bool cancel) {
3771 int otherTIndex = other->findT(otherT, otherPt, this);
3772 int next = other->nextExactSpan(otherTIndex, step);
3773 int otherMin = SkMin32(otherTIndex, next);
3774 int otherWind = other->span(otherMin).fWindValue;
3775 if (otherWind == 0) {
3776 return false;
3777 }
3778 if (next < 0) {
3779 return false; // can happen if t values were adjusted but coincident ts were not
3780 }
3781 int tIndex = 0;
3782 do {
3783 SkOpSpan* test = &fTs[tIndex];
3784 SkASSERT(test->fT == 0);
3785 if (test->fOther == other || test->fOtherT != 1) {
3786 continue;
3787 }
3788 SkPoint startPt, endPt;
3789 double endT;
3790 if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) {
3791 SkOpSegment* match = test->fOther;
3792 if (cancel) {
3793 match->addTCancel(startPt, endPt, other);
3794 } else {
3795 if (!match->addTCoincident(startPt, endPt, endT, other)) {
3796 return false;
3797 }
3798 }
3799 return true;
3800 }
3801 } while (fTs[++tIndex].fT == 0);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003802 return false;
3803}
3804
reed0dc4dd62015-03-24 13:55:33 -07003805// this span is excluded by the winding rule -- chase the ends
3806// as long as they are unambiguous to mark connections as done
3807// and give them the same winding value
caryclark@google.com07393ca2013-04-08 11:47:37 +00003808
reed0dc4dd62015-03-24 13:55:33 -07003809SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
3810 int step = SkSign32(endIndex - index);
3811 int min = SkMin32(index, endIndex);
3812 markDoneBinary(min);
3813 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003814 SkOpSegment* other = this;
reed0dc4dd62015-03-24 13:55:33 -07003815 while ((other = other->nextChase(&index, &step, &min, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003816 if (other->done()) {
caryclarkdac1d172014-06-17 05:15:38 -07003817 SkASSERT(!last);
3818 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003819 }
reed0dc4dd62015-03-24 13:55:33 -07003820 other->markDoneBinary(min);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003821 }
3822 return last;
3823}
3824
reed0dc4dd62015-03-24 13:55:33 -07003825SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
3826 int step = SkSign32(endIndex - index);
3827 int min = SkMin32(index, endIndex);
3828 markDoneUnary(min);
3829 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003830 SkOpSegment* other = this;
reed0dc4dd62015-03-24 13:55:33 -07003831 while ((other = other->nextChase(&index, &step, &min, &last))) {
3832 if (other->done()) {
caryclarkdac1d172014-06-17 05:15:38 -07003833 SkASSERT(!last);
3834 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003835 }
reed0dc4dd62015-03-24 13:55:33 -07003836 other->markDoneUnary(min);
3837 }
3838 return last;
3839}
3840
3841bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, SkOpSpan** lastPtr) {
3842 int index = angle->start();
3843 int endIndex = angle->end();
3844 return markAndChaseWinding(index, endIndex, winding, lastPtr);
3845}
3846
3847bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, SkOpSpan** lastPtr) {
3848 int min = SkMin32(index, endIndex);
3849 int step = SkSign32(endIndex - index);
3850 bool success = markWinding(min, winding);
3851 SkOpSpan* last = NULL;
3852 SkOpSegment* other = this;
3853 while ((other = other->nextChase(&index, &step, &min, &last))) {
3854 if (other->fTs[min].fWindSum != SK_MinS32) {
3855 SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
3856 SkASSERT(!last);
3857 break;
3858 }
3859 (void) other->markWinding(min, winding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003860 }
caryclark65f55312014-11-13 06:58:52 -08003861 if (lastPtr) {
3862 *lastPtr = last;
3863 }
3864 return success;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003865}
3866
reed0dc4dd62015-03-24 13:55:33 -07003867bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding,
3868 SkOpSpan** lastPtr) {
3869 int min = SkMin32(index, endIndex);
3870 int step = SkSign32(endIndex - index);
3871 bool success = markWinding(min, winding, oppWinding);
3872 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003873 SkOpSegment* other = this;
reed0dc4dd62015-03-24 13:55:33 -07003874 while ((other = other->nextChase(&index, &step, &min, &last))) {
3875 if (other->fTs[min].fWindSum != SK_MinS32) {
3876#ifdef SK_DEBUG
3877 if (!other->fTs[min].fLoop) {
3878 if (fOperand == other->fOperand) {
3879// FIXME: this is probably a bug -- rects4 asserts here
3880// SkASSERT(other->fTs[min].fWindSum == winding);
3881// FIXME: this is probably a bug -- rects3 asserts here
3882// SkASSERT(other->fTs[min].fOppSum == oppWinding);
3883 } else {
3884// FIXME: this is probably a bug -- issue414409b asserts here
3885// SkASSERT(other->fTs[min].fWindSum == oppWinding);
3886// FIXME: this is probably a bug -- skpwww_joomla_org_23 asserts here
3887// SkASSERT(other->fTs[min].fOppSum == winding);
caryclarkdac1d172014-06-17 05:15:38 -07003888 }
3889 }
3890 SkASSERT(!last);
reed0dc4dd62015-03-24 13:55:33 -07003891#endif
caryclarkdac1d172014-06-17 05:15:38 -07003892 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003893 }
reed0dc4dd62015-03-24 13:55:33 -07003894 if (fOperand == other->fOperand) {
3895 (void) other->markWinding(min, winding, oppWinding);
caryclarkdac1d172014-06-17 05:15:38 -07003896 } else {
reed0dc4dd62015-03-24 13:55:33 -07003897 (void) other->markWinding(min, oppWinding, winding);
caryclarkdac1d172014-06-17 05:15:38 -07003898 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003899 }
caryclark65f55312014-11-13 06:58:52 -08003900 if (lastPtr) {
3901 *lastPtr = last;
3902 }
3903 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003904}
3905
reed0dc4dd62015-03-24 13:55:33 -07003906bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding,
3907 SkOpSpan** lastPtr) {
3908 int start = angle->start();
3909 int end = angle->end();
3910 return markAndChaseWinding(start, end, winding, oppWinding, lastPtr);
3911}
3912
3913SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003914 SkASSERT(angle->segment() == this);
3915 if (UseInnerWinding(maxWinding, sumWinding)) {
3916 maxWinding = sumWinding;
3917 }
reed0dc4dd62015-03-24 13:55:33 -07003918 SkOpSpan* last;
3919 SkAssertResult(markAndChaseWinding(angle, maxWinding, &last));
caryclark@google.com570863f2013-09-16 15:55:01 +00003920#if DEBUG_WINDING
3921 if (last) {
reed0dc4dd62015-03-24 13:55:33 -07003922 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
3923 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
3924 SkPathOpsDebug::WindingPrintf(last->fWindSum);
3925 SkDebugf(" small=%d\n", last->fSmall);
caryclark@google.com570863f2013-09-16 15:55:01 +00003926 }
3927#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003928 return last;
3929}
3930
reed0dc4dd62015-03-24 13:55:33 -07003931SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
3932 int oppSumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003933 SkASSERT(angle->segment() == this);
3934 if (UseInnerWinding(maxWinding, sumWinding)) {
3935 maxWinding = sumWinding;
3936 }
3937 if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
3938 oppMaxWinding = oppSumWinding;
3939 }
reed0dc4dd62015-03-24 13:55:33 -07003940 SkOpSpan* last;
caryclark65f55312014-11-13 06:58:52 -08003941 // caller doesn't require that this marks anything
reed0dc4dd62015-03-24 13:55:33 -07003942 (void) markAndChaseWinding(angle, maxWinding, oppMaxWinding, &last);
caryclark@google.com570863f2013-09-16 15:55:01 +00003943#if DEBUG_WINDING
3944 if (last) {
reed0dc4dd62015-03-24 13:55:33 -07003945 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
3946 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
3947 SkPathOpsDebug::WindingPrintf(last->fWindSum);
3948 SkDebugf(" small=%d\n", last->fSmall);
caryclark@google.com570863f2013-09-16 15:55:01 +00003949 }
3950#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003951 return last;
3952}
3953
reed0dc4dd62015-03-24 13:55:33 -07003954// FIXME: this should also mark spans with equal (x,y)
3955// This may be called when the segment is already marked done. While this
3956// wastes time, it shouldn't do any more than spin through the T spans.
3957// OPTIMIZATION: abort on first done found (assuming that this code is
3958// always called to mark segments done).
3959void SkOpSegment::markDone(int index, int winding) {
3960 // SkASSERT(!done());
3961 SkASSERT(winding);
3962 double referenceT = fTs[index].fT;
3963 int lesser = index;
3964 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3965 markOneDone(__FUNCTION__, lesser, winding);
3966 }
3967 do {
3968 markOneDone(__FUNCTION__, index, winding);
3969 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3970 debugValidate();
3971}
3972
3973void SkOpSegment::markDoneBinary(int index) {
3974 double referenceT = fTs[index].fT;
3975 int lesser = index;
3976 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3977 markOneDoneBinary(__FUNCTION__, lesser);
3978 }
3979 do {
3980 markOneDoneBinary(__FUNCTION__, index);
3981 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3982 debugValidate();
3983}
3984
3985void SkOpSegment::markDoneFinal(int index) {
3986 double referenceT = fTs[index].fT;
3987 int lesser = index;
3988 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3989 markOneDoneFinal(__FUNCTION__, lesser);
3990 }
3991 do {
3992 markOneDoneFinal(__FUNCTION__, index);
3993 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3994 debugValidate();
3995}
3996
3997void SkOpSegment::markDoneUnary(int index) {
3998 double referenceT = fTs[index].fT;
3999 int lesser = index;
4000 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
4001 markOneDoneUnary(__FUNCTION__, lesser);
4002 }
4003 do {
4004 markOneDoneUnary(__FUNCTION__, index);
4005 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
4006 debugValidate();
4007}
4008
4009void SkOpSegment::markOneDone(const char* funName, int tIndex, int winding) {
4010 SkOpSpan* span;
4011 (void) markOneWinding(funName, tIndex, winding, &span); // allowed to do nothing
4012 if (span->fDone) {
caryclarkccec0f92015-03-24 07:28:17 -07004013 return;
4014 }
reed0dc4dd62015-03-24 13:55:33 -07004015 span->fDone = true;
4016 ++fDoneSpans;
caryclarkccec0f92015-03-24 07:28:17 -07004017}
4018
reed0dc4dd62015-03-24 13:55:33 -07004019void SkOpSegment::markOneDoneFinal(const char* funName, int tIndex) {
4020 SkOpSpan* span = &fTs[tIndex];
4021 if (span->fDone) {
4022 return;
4023 }
4024 span->fDone = true;
4025 ++fDoneSpans;
4026}
4027
4028void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
4029 SkOpSpan* span = verifyOneWinding(funName, tIndex);
4030 if (!span) {
4031 return;
4032 }
4033 SkASSERT(!span->fDone);
4034 span->fDone = true;
4035 ++fDoneSpans;
4036}
4037
4038void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
4039 SkOpSpan* span = verifyOneWindingU(funName, tIndex);
4040 if (!span) {
4041 return;
4042 }
4043 if (span->fWindSum == SK_MinS32) {
4044 SkDebugf("%s uncomputed\n", __FUNCTION__);
4045 }
4046 SkASSERT(!span->fDone);
4047 span->fDone = true;
4048 ++fDoneSpans;
4049}
4050
4051bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding, SkOpSpan** lastPtr) {
4052 SkOpSpan* span = &fTs[tIndex];
4053 if (lastPtr) {
4054 *lastPtr = span;
4055 }
4056 if (span->fDone && !span->fSmall) {
4057 return false;
4058 }
4059#if DEBUG_MARK_DONE
4060 debugShowNewWinding(funName, *span, winding);
4061#endif
4062 SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
4063#if DEBUG_LIMIT_WIND_SUM
4064 SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
4065#endif
4066 span->fWindSum = winding;
4067 return true;
4068}
4069
4070bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
4071 int oppWinding, SkOpSpan** lastPtr) {
4072 SkOpSpan* span = &fTs[tIndex];
4073 if (span->fDone && !span->fSmall) {
4074 return false;
4075 }
4076#if DEBUG_MARK_DONE
4077 debugShowNewWinding(funName, *span, winding, oppWinding);
4078#endif
4079 SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
4080#if DEBUG_LIMIT_WIND_SUM
4081 SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
4082#endif
4083 span->fWindSum = winding;
4084 SkASSERT(span->fOppSum == SK_MinS32 || span->fOppSum == oppWinding);
4085#if DEBUG_LIMIT_WIND_SUM
4086 SkASSERT(abs(oppWinding) <= DEBUG_LIMIT_WIND_SUM);
4087#endif
4088 span->fOppSum = oppWinding;
4089 debugValidate();
4090 if (lastPtr) {
4091 *lastPtr = span;
4092 }
4093 return true;
4094}
4095
4096// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
4097bool SkOpSegment::clockwise(int tStart, int tEnd, bool* swap) const {
4098 SkASSERT(fVerb != SkPath::kLine_Verb);
4099 SkPoint edge[4];
4100 subDivide(tStart, tEnd, edge);
4101 int points = SkPathOpsVerbToPoints(fVerb);
4102 double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
4103 bool sumSet = false;
4104 if (fVerb == SkPath::kCubic_Verb) {
4105 SkDCubic cubic;
4106 cubic.set(edge);
4107 double inflectionTs[2];
4108 int inflections = cubic.findInflections(inflectionTs);
4109 // FIXME: this fixes cubicOp114 and breaks cubicOp58d
4110 // the trouble is that cubics with inflections confuse whether the curve breaks towards
4111 // or away, which in turn is used to determine if it is on the far right or left.
4112 // Probably a totally different approach is in order. At one time I tried to project a
4113 // horizontal ray to determine winding, but was confused by how to map the vertically
4114 // oriented winding computation over.
4115 if (0 && inflections) {
4116 double tLo = this->span(tStart).fT;
4117 double tHi = this->span(tEnd).fT;
4118 double tLoStart = tLo;
4119 for (int index = 0; index < inflections; ++index) {
4120 if (between(tLo, inflectionTs[index], tHi)) {
4121 tLo = inflectionTs[index];
4122 }
4123 }
4124 if (tLo != tLoStart && tLo != tHi) {
4125 SkDPoint sub[2];
4126 sub[0] = cubic.ptAtT(tLo);
4127 sub[1].set(edge[3]);
4128 SkDPoint ctrl[2];
4129 SkDCubic::SubDivide(fPts, sub[0], sub[1], tLo, tHi, ctrl);
4130 edge[0] = sub[0].asSkPoint();
4131 edge[1] = ctrl[0].asSkPoint();
4132 edge[2] = ctrl[1].asSkPoint();
4133 sum = (edge[0].fX - edge[3].fX) * (edge[0].fY + edge[3].fY);
4134 }
4135 }
4136 SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
4137 if (edge[1].fY < lesser && edge[2].fY < lesser) {
4138 SkDLine tangent1 = {{ {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} }};
4139 SkDLine tangent2 = {{ {edge[2].fX, edge[2].fY}, {edge[3].fX, edge[3].fY} }};
4140 if (SkIntersections::Test(tangent1, tangent2)) {
4141 SkPoint topPt = cubic_top(fPts, fTs[tStart].fT, fTs[tEnd].fT);
4142 sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
4143 sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
4144 sumSet = true;
4145 }
4146 }
4147 }
4148 if (!sumSet) {
4149 for (int idx = 0; idx < points; ++idx){
4150 sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
4151 }
4152 }
4153 if (fVerb == SkPath::kCubic_Verb) {
4154 SkDCubic cubic;
4155 cubic.set(edge);
4156 *swap = sum > 0 && !cubic.monotonicInY() && !cubic.serpentine();
4157 } else {
4158 SkDQuad quad;
4159 quad.set(edge);
4160 *swap = sum > 0 && !quad.monotonicInY();
4161 }
4162 return sum <= 0;
4163}
4164
4165bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
4166 SkASSERT(fVerb != SkPath::kLine_Verb);
4167 if (fVerb == SkPath::kQuad_Verb) {
4168 SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
4169 return dst.monotonicInY();
4170 }
4171 SkASSERT(fVerb == SkPath::kCubic_Verb);
4172 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
4173 return dst.monotonicInY();
4174}
4175
4176bool SkOpSegment::serpentine(int tStart, int tEnd) const {
4177 if (fVerb != SkPath::kCubic_Verb) {
4178 return false;
4179 }
4180 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
4181 return dst.serpentine();
4182}
4183
4184SkOpSpan* SkOpSegment::verifyOneWinding(const char* funName, int tIndex) {
4185 SkOpSpan& span = fTs[tIndex];
4186 if (span.fDone) {
4187 return NULL;
4188 }
4189#if DEBUG_MARK_DONE
4190 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
4191#endif
4192// If the prior angle in the sort is unorderable, the winding sum may not be computable.
4193// To enable the assert, the 'prior is unorderable' state could be
4194// piped down to this test, but not sure it's worth it.
4195// (Once the sort order is stored in the span, this test may be feasible.)
4196// SkASSERT(span.fWindSum != SK_MinS32);
4197// SkASSERT(span.fOppSum != SK_MinS32);
4198 return &span;
4199}
4200
4201SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
4202 SkOpSpan& span = fTs[tIndex];
4203 if (span.fDone) {
4204 return NULL;
4205 }
4206#if DEBUG_MARK_DONE
4207 debugShowNewWinding(funName, span, span.fWindSum);
4208#endif
4209// If the prior angle in the sort is unorderable, the winding sum may not be computable.
4210// To enable the assert, the 'prior is unorderable' state could be
4211// piped down to this test, but not sure it's worth it.
4212// (Once the sort order is stored in the span, this test may be feasible.)
4213// SkASSERT(span.fWindSum != SK_MinS32);
4214 return &span;
4215}
4216
4217bool SkOpSegment::markWinding(int index, int winding) {
4218// SkASSERT(!done());
caryclark@google.com07393ca2013-04-08 11:47:37 +00004219 SkASSERT(winding);
reed0dc4dd62015-03-24 13:55:33 -07004220 double referenceT = fTs[index].fT;
4221 int lesser = index;
4222 bool success = false;
4223 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
4224 success |= markOneWinding(__FUNCTION__, lesser, winding, NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004225 }
reed0dc4dd62015-03-24 13:55:33 -07004226 do {
4227 success |= markOneWinding(__FUNCTION__, index, winding, NULL);
4228 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclarkccec0f92015-03-24 07:28:17 -07004229 debugValidate();
reed0dc4dd62015-03-24 13:55:33 -07004230 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004231}
4232
reed0dc4dd62015-03-24 13:55:33 -07004233bool SkOpSegment::markWinding(int index, int winding, int oppWinding) {
4234// SkASSERT(!done());
caryclark@google.com07393ca2013-04-08 11:47:37 +00004235 SkASSERT(winding || oppWinding);
reed0dc4dd62015-03-24 13:55:33 -07004236 double referenceT = fTs[index].fT;
4237 int lesser = index;
4238 bool success = false;
4239 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
4240 success |= markOneWinding(__FUNCTION__, lesser, winding, oppWinding, NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004241 }
reed0dc4dd62015-03-24 13:55:33 -07004242 do {
4243 success |= markOneWinding(__FUNCTION__, index, winding, oppWinding, NULL);
4244 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004245 debugValidate();
reed0dc4dd62015-03-24 13:55:33 -07004246 return success;
4247}
4248
4249void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
4250 int nextDoorWind = SK_MaxS32;
4251 int nextOppWind = SK_MaxS32;
4252 // prefer exact matches
4253 if (tIndex > 0) {
4254 const SkOpSpan& below = fTs[tIndex - 1];
4255 if (below.fT == t) {
4256 nextDoorWind = below.fWindValue;
4257 nextOppWind = below.fOppValue;
4258 }
4259 }
4260 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
4261 const SkOpSpan& above = fTs[tIndex + 1];
4262 if (above.fT == t) {
4263 nextDoorWind = above.fWindValue;
4264 nextOppWind = above.fOppValue;
4265 }
4266 }
4267 if (nextDoorWind == SK_MaxS32 && tIndex > 0) {
4268 const SkOpSpan& below = fTs[tIndex - 1];
4269 if (approximately_negative(t - below.fT)) {
4270 nextDoorWind = below.fWindValue;
4271 nextOppWind = below.fOppValue;
4272 }
4273 }
4274 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
4275 const SkOpSpan& above = fTs[tIndex + 1];
4276 if (approximately_negative(above.fT - t)) {
4277 nextDoorWind = above.fWindValue;
4278 nextOppWind = above.fOppValue;
4279 }
4280 }
4281 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
4282 const SkOpSpan& below = fTs[tIndex - 1];
4283 nextDoorWind = below.fWindValue;
4284 nextOppWind = below.fOppValue;
4285 }
4286 if (nextDoorWind != SK_MaxS32) {
4287 SkOpSpan& newSpan = fTs[tIndex];
4288 newSpan.fWindValue = nextDoorWind;
4289 newSpan.fOppValue = nextOppWind;
4290 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
4291 newSpan.fDone = true;
4292 ++fDoneSpans;
4293 }
4294 }
4295}
4296
4297bool SkOpSegment::nextCandidate(int* start, int* end) const {
4298 while (fTs[*end].fDone) {
4299 if (fTs[*end].fT == 1) {
4300 return false;
4301 }
4302 ++(*end);
4303 }
4304 *start = *end;
4305 *end = nextExactSpan(*start, 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004306 return true;
4307}
4308
reed0dc4dd62015-03-24 13:55:33 -07004309static SkOpSegment* set_last(SkOpSpan** last, const SkOpSpan* endSpan) {
4310 if (last && !endSpan->fSmall) {
4311 *last = const_cast<SkOpSpan*>(endSpan); // FIXME: get rid of cast
caryclarkdac1d172014-06-17 05:15:38 -07004312 }
4313 return NULL;
4314}
4315
reed0dc4dd62015-03-24 13:55:33 -07004316SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr,
4317 SkOpSpan** last) const {
4318 int origIndex = *indexPtr;
caryclarkdac1d172014-06-17 05:15:38 -07004319 int step = *stepPtr;
reed0dc4dd62015-03-24 13:55:33 -07004320 int end = nextExactSpan(origIndex, step);
4321 SkASSERT(end >= 0);
4322 const SkOpSpan& endSpan = this->span(end);
4323 SkOpAngle* angle = step > 0 ? endSpan.fFromAngle : endSpan.fToAngle;
4324 int foundIndex;
4325 int otherEnd;
caryclarkdac1d172014-06-17 05:15:38 -07004326 SkOpSegment* other;
4327 if (angle == NULL) {
reed0dc4dd62015-03-24 13:55:33 -07004328 if (endSpan.fT != 0 && endSpan.fT != 1) {
caryclarkdac1d172014-06-17 05:15:38 -07004329 return NULL;
4330 }
reed0dc4dd62015-03-24 13:55:33 -07004331 other = endSpan.fOther;
4332 foundIndex = endSpan.fOtherIndex;
4333 otherEnd = other->nextExactSpan(foundIndex, step);
caryclarkdac1d172014-06-17 05:15:38 -07004334 } else {
4335 int loopCount = angle->loopCount();
4336 if (loopCount > 2) {
reed0dc4dd62015-03-24 13:55:33 -07004337 return set_last(last, &endSpan);
caryclarkdac1d172014-06-17 05:15:38 -07004338 }
4339 const SkOpAngle* next = angle->next();
caryclark65b427c2014-09-18 10:32:57 -07004340 if (NULL == next) {
4341 return NULL;
4342 }
reed0dc4dd62015-03-24 13:55:33 -07004343 if (angle->sign() != next->sign()) {
caryclarkdac1d172014-06-17 05:15:38 -07004344#if DEBUG_WINDING
4345 SkDebugf("%s mismatched signs\n", __FUNCTION__);
caryclarkccec0f92015-03-24 07:28:17 -07004346#endif
reed0dc4dd62015-03-24 13:55:33 -07004347 // return set_last(last, &endSpan);
4348 }
caryclarkdac1d172014-06-17 05:15:38 -07004349 other = next->segment();
reed0dc4dd62015-03-24 13:55:33 -07004350 foundIndex = end = next->start();
caryclarkdac1d172014-06-17 05:15:38 -07004351 otherEnd = next->end();
caryclark@google.com570863f2013-09-16 15:55:01 +00004352 }
reed0dc4dd62015-03-24 13:55:33 -07004353 int foundStep = foundIndex < otherEnd ? 1 : -1;
caryclarkdac1d172014-06-17 05:15:38 -07004354 if (*stepPtr != foundStep) {
reed0dc4dd62015-03-24 13:55:33 -07004355 return set_last(last, &endSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004356 }
reed0dc4dd62015-03-24 13:55:33 -07004357 SkASSERT(*indexPtr >= 0);
4358 if (otherEnd < 0) {
caryclark65b427c2014-09-18 10:32:57 -07004359 return NULL;
4360 }
4361// SkASSERT(otherEnd >= 0);
reed0dc4dd62015-03-24 13:55:33 -07004362#if 1
4363 int origMin = origIndex + (step < 0 ? step : 0);
4364 const SkOpSpan& orig = this->span(origMin);
4365#endif
4366 int foundMin = SkMin32(foundIndex, otherEnd);
4367#if 1
4368 const SkOpSpan& found = other->span(foundMin);
4369 if (found.fWindValue != orig.fWindValue || found.fOppValue != orig.fOppValue) {
4370 return set_last(last, &endSpan);
caryclarkdac1d172014-06-17 05:15:38 -07004371 }
reed0dc4dd62015-03-24 13:55:33 -07004372#endif
4373 *indexPtr = foundIndex;
caryclarkdac1d172014-06-17 05:15:38 -07004374 *stepPtr = foundStep;
4375 if (minPtr) {
4376 *minPtr = foundMin;
caryclark@google.coma5e55922013-05-07 18:51:31 +00004377 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004378 return other;
4379}
4380
reed0dc4dd62015-03-24 13:55:33 -07004381// This has callers for two different situations: one establishes the end
4382// of the current span, and one establishes the beginning of the next span
4383// (thus the name). When this is looking for the end of the current span,
4384// coincidence is found when the beginning Ts contain -step and the end
4385// contains step. When it is looking for the beginning of the next, the
4386// first Ts found can be ignored and the last Ts should contain -step.
4387// OPTIMIZATION: probably should split into two functions
4388int SkOpSegment::nextSpan(int from, int step) const {
4389 const SkOpSpan& fromSpan = fTs[from];
4390 int count = fTs.count();
4391 int to = from;
4392 while (step > 0 ? ++to < count : --to >= 0) {
4393 const SkOpSpan& span = fTs[to];
4394 if (approximately_zero(span.fT - fromSpan.fT)) {
4395 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004396 }
reed0dc4dd62015-03-24 13:55:33 -07004397 return to;
caryclarkccec0f92015-03-24 07:28:17 -07004398 }
reed0dc4dd62015-03-24 13:55:33 -07004399 return -1;
4400}
4401
4402// FIXME
4403// this returns at any difference in T, vs. a preset minimum. It may be
4404// that all callers to nextSpan should use this instead.
4405int SkOpSegment::nextExactSpan(int from, int step) const {
4406 int to = from;
4407 if (step < 0) {
4408 const SkOpSpan& fromSpan = fTs[from];
4409 while (--to >= 0) {
4410 const SkOpSpan& span = fTs[to];
4411 if (precisely_negative(fromSpan.fT - span.fT) || span.fTiny) {
caryclark@google.com570863f2013-09-16 15:55:01 +00004412 continue;
4413 }
reed0dc4dd62015-03-24 13:55:33 -07004414 return to;
caryclark5e27e0e2014-08-12 07:46:33 -07004415 }
reed0dc4dd62015-03-24 13:55:33 -07004416 } else {
4417 while (fTs[from].fTiny) {
4418 from++;
4419 }
4420 const SkOpSpan& fromSpan = fTs[from];
4421 int count = fTs.count();
4422 while (++to < count) {
4423 const SkOpSpan& span = fTs[to];
4424 if (precisely_negative(span.fT - fromSpan.fT)) {
caryclarkccec0f92015-03-24 07:28:17 -07004425 continue;
4426 }
reed0dc4dd62015-03-24 13:55:33 -07004427 return to;
caryclarkdac1d172014-06-17 05:15:38 -07004428 }
caryclarkccec0f92015-03-24 07:28:17 -07004429 }
reed0dc4dd62015-03-24 13:55:33 -07004430 return -1;
caryclarkccec0f92015-03-24 07:28:17 -07004431}
4432
reed0dc4dd62015-03-24 13:55:33 -07004433void SkOpSegment::pinT(const SkPoint& pt, double* t) {
4434 if (pt == fPts[0]) {
4435 *t = 0;
4436 }
4437 int count = SkPathOpsVerbToPoints(fVerb);
4438 if (pt == fPts[count]) {
4439 *t = 1;
4440 }
caryclarkccec0f92015-03-24 07:28:17 -07004441}
4442
reed0dc4dd62015-03-24 13:55:33 -07004443bool SkOpSegment::reversePoints(const SkPoint& p1, const SkPoint& p2) const {
4444 SkASSERT(p1 != p2);
4445 int spanCount = count();
4446 int p1IndexMin = -1;
4447 int p2IndexMax = spanCount;
4448 for (int index = 0; index < spanCount; ++index) {
4449 const SkOpSpan& span = fTs[index];
4450 if (span.fPt == p1) {
4451 if (p1IndexMin < 0) {
4452 p1IndexMin = index;
4453 }
4454 } else if (span.fPt == p2) {
4455 p2IndexMax = index;
4456 }
4457 }
4458 return p1IndexMin > p2IndexMax;
4459}
4460
4461void SkOpSegment::setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt,
4462 SkOpSegment* other) {
4463 int count = this->count();
4464 for (int index = 0; index < count; ++index) {
4465 SkOpSpan &span = fTs[index];
4466 if ((startPt == span.fPt || endPt == span.fPt) && other == span.fOther) {
4467 span.fCoincident = true;
4468 }
4469 }
4470}
4471
4472void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
4473 int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
4474 int deltaSum = spanSign(index, endIndex);
4475 int oppDeltaSum = oppSign(index, endIndex);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004476 if (operand()) {
4477 *maxWinding = *sumSuWinding;
4478 *sumWinding = *sumSuWinding -= deltaSum;
4479 *oppMaxWinding = *sumMiWinding;
4480 *oppSumWinding = *sumMiWinding -= oppDeltaSum;
4481 } else {
4482 *maxWinding = *sumMiWinding;
4483 *sumWinding = *sumMiWinding -= deltaSum;
4484 *oppMaxWinding = *sumSuWinding;
4485 *oppSumWinding = *sumSuWinding -= oppDeltaSum;
4486 }
reed0dc4dd62015-03-24 13:55:33 -07004487#if DEBUG_LIMIT_WIND_SUM
4488 SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
4489 SkASSERT(abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
4490#endif
4491}
4492
4493void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
4494 int* maxWinding, int* sumWinding) {
4495 int deltaSum = spanSign(index, endIndex);
4496 *maxWinding = *sumMiWinding;
4497 *sumWinding = *sumMiWinding -= deltaSum;
4498#if DEBUG_LIMIT_WIND_SUM
4499 SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
4500#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00004501}
4502
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004503void SkOpSegment::sortAngles() {
reed0dc4dd62015-03-24 13:55:33 -07004504 int spanCount = fTs.count();
4505 if (spanCount <= 2) {
4506 return;
4507 }
4508 int index = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004509 do {
reed0dc4dd62015-03-24 13:55:33 -07004510 SkOpAngle* fromAngle = fTs[index].fFromAngle;
4511 SkOpAngle* toAngle = fTs[index].fToAngle;
caryclarkdac1d172014-06-17 05:15:38 -07004512 if (!fromAngle && !toAngle) {
reed0dc4dd62015-03-24 13:55:33 -07004513 index += 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004514 continue;
4515 }
reed0dc4dd62015-03-24 13:55:33 -07004516 SkOpAngle* baseAngle = NULL;
4517 if (fromAngle) {
4518 baseAngle = fromAngle;
4519 if (inLoop(baseAngle, spanCount, &index)) {
4520 continue;
4521 }
4522 }
caryclark@google.com570863f2013-09-16 15:55:01 +00004523#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004524 bool wroteAfterHeader = false;
caryclark@google.com570863f2013-09-16 15:55:01 +00004525#endif
reed0dc4dd62015-03-24 13:55:33 -07004526 if (toAngle) {
4527 if (!baseAngle) {
4528 baseAngle = toAngle;
4529 if (inLoop(baseAngle, spanCount, &index)) {
4530 continue;
4531 }
4532 } else {
4533 SkDEBUGCODE(int newIndex = index);
4534 SkASSERT(!inLoop(baseAngle, spanCount, &newIndex) && newIndex == index);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004535#if DEBUG_ANGLE
reed0dc4dd62015-03-24 13:55:33 -07004536 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
4537 index);
4538 wroteAfterHeader = true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004539#endif
reed0dc4dd62015-03-24 13:55:33 -07004540 baseAngle->insert(toAngle);
caryclarkccec0f92015-03-24 07:28:17 -07004541 }
reed0dc4dd62015-03-24 13:55:33 -07004542 }
4543 SkOpAngle* nextFrom, * nextTo;
4544 int firstIndex = index;
4545 do {
4546 SkOpSpan& span = fTs[index];
4547 SkOpSegment* other = span.fOther;
4548 SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
4549 SkOpAngle* oAngle = oSpan.fFromAngle;
caryclarkdac1d172014-06-17 05:15:38 -07004550 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004551#if DEBUG_ANGLE
4552 if (!wroteAfterHeader) {
reed0dc4dd62015-03-24 13:55:33 -07004553 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
4554 index);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004555 wroteAfterHeader = true;
4556 }
4557#endif
reed0dc4dd62015-03-24 13:55:33 -07004558 if (!oAngle->loopContains(*baseAngle)) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004559 baseAngle->insert(oAngle);
4560 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004561 }
reed0dc4dd62015-03-24 13:55:33 -07004562 oAngle = oSpan.fToAngle;
4563 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004564#if DEBUG_ANGLE
reed0dc4dd62015-03-24 13:55:33 -07004565 if (!wroteAfterHeader) {
4566 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
4567 index);
4568 wroteAfterHeader = true;
4569 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004570#endif
reed0dc4dd62015-03-24 13:55:33 -07004571 if (!oAngle->loopContains(*baseAngle)) {
4572 baseAngle->insert(oAngle);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004573 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004574 }
reed0dc4dd62015-03-24 13:55:33 -07004575 if (++index == spanCount) {
4576 break;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004577 }
reed0dc4dd62015-03-24 13:55:33 -07004578 nextFrom = fTs[index].fFromAngle;
4579 nextTo = fTs[index].fToAngle;
4580 } while (fromAngle == nextFrom && toAngle == nextTo);
4581 if (baseAngle && baseAngle->loopCount() == 1) {
4582 index = firstIndex;
4583 do {
4584 SkOpSpan& span = fTs[index];
4585 span.fFromAngle = span.fToAngle = NULL;
4586 if (++index == spanCount) {
4587 break;
4588 }
4589 nextFrom = fTs[index].fFromAngle;
4590 nextTo = fTs[index].fToAngle;
4591 } while (fromAngle == nextFrom && toAngle == nextTo);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004592 baseAngle = NULL;
4593 }
4594#if DEBUG_SORT
4595 SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
4596#endif
reed0dc4dd62015-03-24 13:55:33 -07004597 } while (index < spanCount);
caryclark@google.com570863f2013-09-16 15:55:01 +00004598}
4599
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004600// return true if midpoints were computed
reed0dc4dd62015-03-24 13:55:33 -07004601bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004602 SkASSERT(start != end);
reed0dc4dd62015-03-24 13:55:33 -07004603 edge[0] = fTs[start].fPt;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004604 int points = SkPathOpsVerbToPoints(fVerb);
reed0dc4dd62015-03-24 13:55:33 -07004605 edge[points] = fTs[end].fPt;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004606 if (fVerb == SkPath::kLine_Verb) {
4607 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004608 }
reed0dc4dd62015-03-24 13:55:33 -07004609 double startT = fTs[start].fT;
4610 double endT = fTs[end].fT;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004611 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
4612 // don't compute midpoints if we already have them
4613 if (fVerb == SkPath::kQuad_Verb) {
4614 edge[1] = fPts[1];
4615 return false;
4616 }
4617 SkASSERT(fVerb == SkPath::kCubic_Verb);
4618 if (start < end) {
4619 edge[1] = fPts[1];
4620 edge[2] = fPts[2];
4621 return false;
4622 }
4623 edge[1] = fPts[2];
4624 edge[2] = fPts[1];
4625 return false;
4626 }
4627 const SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[points].fX, edge[points].fY }};
4628 if (fVerb == SkPath::kQuad_Verb) {
4629 edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint();
4630 } else {
4631 SkASSERT(fVerb == SkPath::kCubic_Verb);
4632 SkDPoint ctrl[2];
4633 SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl);
4634 edge[1] = ctrl[0].asSkPoint();
4635 edge[2] = ctrl[1].asSkPoint();
4636 }
4637 return true;
4638}
4639
reed0dc4dd62015-03-24 13:55:33 -07004640// return true if midpoints were computed
4641bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004642 SkASSERT(start != end);
reed0dc4dd62015-03-24 13:55:33 -07004643 (*result)[0].set(fTs[start].fPt);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004644 int points = SkPathOpsVerbToPoints(fVerb);
reed0dc4dd62015-03-24 13:55:33 -07004645 (*result)[points].set(fTs[end].fPt);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004646 if (fVerb == SkPath::kLine_Verb) {
4647 return false;
4648 }
reed0dc4dd62015-03-24 13:55:33 -07004649 double startT = fTs[start].fT;
4650 double endT = fTs[end].fT;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004651 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
4652 // don't compute midpoints if we already have them
4653 if (fVerb == SkPath::kQuad_Verb) {
4654 (*result)[1].set(fPts[1]);
4655 return false;
4656 }
4657 SkASSERT(fVerb == SkPath::kCubic_Verb);
reed0dc4dd62015-03-24 13:55:33 -07004658 if (start < end) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004659 (*result)[1].set(fPts[1]);
4660 (*result)[2].set(fPts[2]);
4661 return false;
4662 }
4663 (*result)[1].set(fPts[2]);
4664 (*result)[2].set(fPts[1]);
4665 return false;
4666 }
4667 if (fVerb == SkPath::kQuad_Verb) {
4668 (*result)[1] = SkDQuad::SubDivide(fPts, (*result)[0], (*result)[2], startT, endT);
4669 } else {
4670 SkASSERT(fVerb == SkPath::kCubic_Verb);
4671 SkDCubic::SubDivide(fPts, (*result)[0], (*result)[3], startT, endT, &(*result)[1]);
4672 }
4673 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004674}
4675
reed0dc4dd62015-03-24 13:55:33 -07004676void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00004677 SkPoint edge[4];
4678 subDivide(start, end, edge);
reed@google.com277c3f82013-05-31 15:17:50 +00004679 (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004680}
4681
reed0dc4dd62015-03-24 13:55:33 -07004682void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt,
4683 const SkPoint& startPt) {
4684 int outCount = outsidePts->count();
4685 if (outCount == 0 || endPt != (*outsidePts)[outCount - 2]) {
4686 outsidePts->push_back(endPt);
4687 outsidePts->push_back(startPt);
4688 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004689}
4690
reed0dc4dd62015-03-24 13:55:33 -07004691void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoint& startPt) {
4692 int outCount = outsidePts->count();
4693 if (outCount == 0 || startPt != (*outsidePts)[outCount - 1]) {
4694 outsidePts->push_back(startPt);
4695 }
4696}
4697
4698void SkOpSegment::undoneSpan(int* start, int* end) {
4699 int tCount = fTs.count();
4700 int index;
4701 for (index = 0; index < tCount; ++index) {
4702 if (!fTs[index].fDone) {
4703 break;
4704 }
4705 }
4706 SkASSERT(index < tCount - 1);
4707 *start = index;
4708 double startT = fTs[index].fT;
4709 while (approximately_negative(fTs[++index].fT - startT))
4710 SkASSERT(index < tCount);
4711 SkASSERT(index < tCount);
4712 *end = index;
4713}
4714
4715int SkOpSegment::updateOppWinding(int index, int endIndex) const {
4716 int lesser = SkMin32(index, endIndex);
4717 int oppWinding = oppSum(lesser);
4718 int oppSpanWinding = oppSign(index, endIndex);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004719 if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
4720 && oppWinding != SK_MaxS32) {
4721 oppWinding -= oppSpanWinding;
4722 }
4723 return oppWinding;
4724}
4725
4726int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
reed0dc4dd62015-03-24 13:55:33 -07004727 int startIndex = angle->start();
4728 int endIndex = angle->end();
4729 return updateOppWinding(endIndex, startIndex);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004730}
4731
4732int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
reed0dc4dd62015-03-24 13:55:33 -07004733 int startIndex = angle->start();
4734 int endIndex = angle->end();
4735 return updateOppWinding(startIndex, endIndex);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004736}
4737
reed0dc4dd62015-03-24 13:55:33 -07004738int SkOpSegment::updateWinding(int index, int endIndex) const {
4739 int lesser = SkMin32(index, endIndex);
4740 int winding = windSum(lesser);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004741 if (winding == SK_MinS32) {
4742 return winding;
4743 }
reed0dc4dd62015-03-24 13:55:33 -07004744 int spanWinding = spanSign(index, endIndex);
caryclark@google.com570863f2013-09-16 15:55:01 +00004745 if (winding && UseInnerWinding(winding - spanWinding, winding)
4746 && winding != SK_MaxS32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00004747 winding -= spanWinding;
4748 }
4749 return winding;
4750}
4751
4752int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
reed0dc4dd62015-03-24 13:55:33 -07004753 int startIndex = angle->start();
4754 int endIndex = angle->end();
4755 return updateWinding(endIndex, startIndex);
4756}
4757
4758int SkOpSegment::updateWindingReverse(int index, int endIndex) const {
4759 int lesser = SkMin32(index, endIndex);
4760 int winding = windSum(lesser);
4761 int spanWinding = spanSign(endIndex, index);
4762 if (winding && UseInnerWindingReverse(winding - spanWinding, winding)
4763 && winding != SK_MaxS32) {
4764 winding -= spanWinding;
4765 }
4766 return winding;
caryclark@google.com570863f2013-09-16 15:55:01 +00004767}
4768
caryclark@google.com07393ca2013-04-08 11:47:37 +00004769int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
reed0dc4dd62015-03-24 13:55:33 -07004770 int startIndex = angle->start();
4771 int endIndex = angle->end();
4772 return updateWindingReverse(endIndex, startIndex);
caryclark@google.com570863f2013-09-16 15:55:01 +00004773}
4774
4775// OPTIMIZATION: does the following also work, and is it any faster?
4776// return outerWinding * innerWinding > 0
4777// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
4778bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
4779 SkASSERT(outerWinding != SK_MaxS32);
4780 SkASSERT(innerWinding != SK_MaxS32);
4781 int absOut = abs(outerWinding);
4782 int absIn = abs(innerWinding);
4783 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
4784 return result;
4785}
4786
reed0dc4dd62015-03-24 13:55:33 -07004787bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) {
4788 SkASSERT(outerWinding != SK_MaxS32);
4789 SkASSERT(innerWinding != SK_MaxS32);
4790 int absOut = abs(outerWinding);
4791 int absIn = abs(innerWinding);
4792 bool result = absOut == absIn ? true : absOut < absIn;
4793 return result;
4794}
4795
4796int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const {
4797 if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
caryclark@google.com07393ca2013-04-08 11:47:37 +00004798 return SK_MinS32;
4799 }
reed0dc4dd62015-03-24 13:55:33 -07004800 int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004801 SkASSERT(winding != SK_MinS32);
reed0dc4dd62015-03-24 13:55:33 -07004802 int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004803#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07004804 SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
reed0dc4dd62015-03-24 13:55:33 -07004805 debugID(), crossOpp, tHit, t(tIndex), winding, windVal);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004806#endif
4807 // see if a + change in T results in a +/- change in X (compute x'(T))
reed@google.com277c3f82013-05-31 15:17:50 +00004808 *dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004809 if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
4810 *dx = fPts[2].fX - fPts[1].fX - *dx;
4811 }
4812 if (*dx == 0) {
4813#if DEBUG_WINDING_AT_T
4814 SkDebugf(" dx=0 winding=SK_MinS32\n");
4815#endif
4816 return SK_MinS32;
4817 }
skia.committer@gmail.coma4aced42013-07-09 07:00:56 +00004818 if (windVal < 0) { // reverse sign if opp contour traveled in reverse
caryclark@google.com07e97fc2013-07-08 17:17:02 +00004819 *dx = -*dx;
4820 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004821 if (winding * *dx > 0) { // if same signs, result is negative
4822 winding += *dx > 0 ? -windVal : windVal;
4823 }
4824#if DEBUG_WINDING_AT_T
4825 SkDebugf(" dx=%c winding=%d\n", *dx > 0 ? '+' : '-', winding);
4826#endif
4827 return winding;
4828}
4829
4830int SkOpSegment::windSum(const SkOpAngle* angle) const {
reed0dc4dd62015-03-24 13:55:33 -07004831 int start = angle->start();
4832 int end = angle->end();
4833 int index = SkMin32(start, end);
4834 return windSum(index);
4835}
4836
4837void SkOpSegment::zeroSpan(SkOpSpan* span) {
4838 SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
4839 span->fWindValue = 0;
4840 span->fOppValue = 0;
4841 if (span->fTiny || span->fSmall) {
4842 return;
4843 }
4844 SkASSERT(!span->fDone);
4845 span->fDone = true;
4846 ++fDoneSpans;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004847}