blob: 826495b764715439a9088f88442be16bdc53ea08 [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "SkIntersections.h"
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"
caryclark@google.com7dfbb072013-04-22 14:37:05 +000011#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
caryclark@google.com570863f2013-09-16 15:55:01 +000036enum {
37 kOutsideTrackedTCount = 16, // FIXME: determine what this should be
38 kMissingSpanCount = 4, // FIXME: determine what this should be
39};
caryclark@google.comd892bd82013-06-17 14:10:36 +000040
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000041const 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)) {
44 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +000045 }
caryclark@google.com570863f2013-09-16 15:55:01 +000046 double referenceT = fTs[index].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +000047 int lesser = index;
caryclark@google.com570863f2013-09-16 15:55:01 +000048 while (--lesser >= 0
49 && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000050 if (const SkOpAngle* result = activeAngleOther(lesser, start, end, done, sortable)) {
51 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +000052 }
53 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000054 do {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000055 if (const SkOpAngle* result = activeAngleOther(index, start, end, done, sortable)) {
56 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +000057 }
caryclark@google.com570863f2013-09-16 15:55:01 +000058 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
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000069const SkOpAngle* SkOpSegment::activeAngleInner(int index, int* start, int* end, bool* done,
70 bool* sortable) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +000071 int next = nextExactSpan(index, 1);
72 if (next > 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000073 const SkOpSpan& upSpan = fTs[index];
caryclark@google.com07393ca2013-04-08 11:47:37 +000074 if (upSpan.fWindValue || upSpan.fOppValue) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000075 if (*end < 0) {
76 *start = index;
77 *end = next;
caryclark@google.com07393ca2013-04-08 11:47:37 +000078 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000079 if (!upSpan.fDone) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000080 if (upSpan.fWindSum != SK_MinS32) {
81 return spanToAngle(index, next);
82 }
83 *done = false;
84 }
85 } else {
86 SkASSERT(upSpan.fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +000087 }
88 }
89 int prev = nextExactSpan(index, -1);
90 // edge leading into junction
91 if (prev >= 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000092 const SkOpSpan& downSpan = fTs[prev];
caryclark@google.com07393ca2013-04-08 11:47:37 +000093 if (downSpan.fWindValue || downSpan.fOppValue) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000094 if (*end < 0) {
95 *start = index;
96 *end = prev;
caryclark@google.com07393ca2013-04-08 11:47:37 +000097 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000098 if (!downSpan.fDone) {
99 if (downSpan.fWindSum != SK_MinS32) {
100 return spanToAngle(index, prev);
101 }
102 *done = false;
103 }
104 } else {
105 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
111const 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
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000119SkPoint SkOpSegment::activeLeftTop(int* firstT) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000120 SkASSERT(!done());
121 SkPoint topPt = {SK_ScalarMax, SK_ScalarMax};
122 int count = fTs.count();
123 // 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;
126 for (int index = 0; index < count; ++index) {
127 const SkOpSpan& span = fTs[index];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000128 if (span.fDone && lastDone) {
129 goto next;
130 }
131 if (approximately_negative(span.fT - lastT)) {
132 goto next;
133 }
134 {
135 const SkPoint& xy = xyAtT(&span);
136 if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
137 topPt = xy;
138 if (firstT) {
139 *firstT = index;
140 }
141 }
142 if (fVerb != SkPath::kLine_Verb && !lastDone) {
reed@google.com277c3f82013-05-31 15:17:50 +0000143 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;
147 if (firstT) {
148 *firstT = index;
149 }
150 }
151 }
152 lastT = span.fT;
153 }
154next:
155 lastDone = span.fDone;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000156 }
157 return topPt;
158}
159
160bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
161 int sumMiWinding = updateWinding(endIndex, index);
162 int sumSuWinding = updateOppWinding(endIndex, index);
163 if (fOperand) {
164 SkTSwap<int>(sumMiWinding, sumSuWinding);
165 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000166 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000167}
168
169bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000170 int* sumMiWinding, int* sumSuWinding) {
171 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000172 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000173 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000174 bool miFrom;
175 bool miTo;
176 bool suFrom;
177 bool suTo;
178 if (operand()) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000179 miFrom = (oppMaxWinding & xorMiMask) != 0;
180 miTo = (oppSumWinding & xorMiMask) != 0;
181 suFrom = (maxWinding & xorSuMask) != 0;
182 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000183 } else {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000184 miFrom = (maxWinding & xorMiMask) != 0;
185 miTo = (sumWinding & xorMiMask) != 0;
186 suFrom = (oppMaxWinding & xorSuMask) != 0;
187 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000188 }
189 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
190#if DEBUG_ACTIVE_OP
caryclarkdac1d172014-06-17 05:15:38 -0700191 SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n",
192 __FUNCTION__, debugID(), span(index).fT, span(endIndex).fT,
caryclark@google.com570863f2013-09-16 15:55:01 +0000193 SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000194#endif
195 return result;
196}
197
198bool SkOpSegment::activeWinding(int index, int endIndex) {
199 int sumWinding = updateWinding(endIndex, index);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000200 return activeWinding(index, endIndex, &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000201}
202
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000203bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) {
204 int maxWinding;
205 setUpWinding(index, endIndex, &maxWinding, sumWinding);
206 bool from = maxWinding != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000207 bool to = *sumWinding != 0;
208 bool result = gUnaryActiveEdge[from][to];
209 return result;
210}
211
caryclark@google.com570863f2013-09-16 15:55:01 +0000212void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt,
213 SkOpSegment* other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000214 int tIndex = -1;
215 int tCount = fTs.count();
216 int oIndex = -1;
217 int oCount = other->fTs.count();
218 do {
219 ++tIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000220 } while (startPt != fTs[tIndex].fPt && tIndex < tCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000221 int tIndexStart = tIndex;
222 do {
223 ++oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000224 } while (endPt != other->fTs[oIndex].fPt && oIndex < oCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000225 int oIndexStart = oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000226 const SkPoint* nextPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000227 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000228 nextPt = &fTs[++tIndex].fPt;
229 SkASSERT(fTs[tIndex].fT < 1 || startPt != *nextPt);
230 } while (startPt == *nextPt);
231 double nextT = fTs[tIndex].fT;
232 const SkPoint* oNextPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000233 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000234 oNextPt = &other->fTs[++oIndex].fPt;
235 SkASSERT(other->fTs[oIndex].fT < 1 || endPt != *oNextPt);
236 } while (endPt == *oNextPt);
237 double oNextT = other->fTs[oIndex].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000238 // at this point, spans before and after are at:
239 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
240 // if tIndexStart == 0, no prior span
241 // if nextT == 1, no following span
242
243 // advance the span with zero winding
244 // if the following span exists (not past the end, non-zero winding)
245 // connect the two edges
246 if (!fTs[tIndexStart].fWindValue) {
247 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
248#if DEBUG_CONCIDENT
249 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
250 __FUNCTION__, fID, other->fID, tIndexStart - 1,
251 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
252 xyAtT(tIndexStart).fY);
253#endif
caryclarkbdbb2422014-08-20 08:11:24 -0700254 SkPoint copy = fTs[tIndexStart].fPt; // add t pair may move the point array
255 addTPair(fTs[tIndexStart].fT, other, other->fTs[oIndex].fT, false, copy);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000256 }
257 if (nextT < 1 && fTs[tIndex].fWindValue) {
258#if DEBUG_CONCIDENT
259 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
260 __FUNCTION__, fID, other->fID, tIndex,
261 fTs[tIndex].fT, xyAtT(tIndex).fX,
262 xyAtT(tIndex).fY);
263#endif
caryclarkbdbb2422014-08-20 08:11:24 -0700264 SkPoint copy = fTs[tIndex].fPt; // add t pair may move the point array
265 addTPair(fTs[tIndex].fT, other, other->fTs[oIndexStart].fT, false, copy);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000266 }
267 } else {
268 SkASSERT(!other->fTs[oIndexStart].fWindValue);
269 if (oIndexStart > 0 && other->fTs[oIndexStart - 1].fWindValue) {
270#if DEBUG_CONCIDENT
271 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
272 __FUNCTION__, fID, other->fID, oIndexStart - 1,
273 other->fTs[oIndexStart].fT, other->xyAtT(oIndexStart).fX,
274 other->xyAtT(oIndexStart).fY);
275 other->debugAddTPair(other->fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
276#endif
277 }
278 if (oNextT < 1 && other->fTs[oIndex].fWindValue) {
279#if DEBUG_CONCIDENT
280 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
281 __FUNCTION__, fID, other->fID, oIndex,
282 other->fTs[oIndex].fT, other->xyAtT(oIndex).fX,
283 other->xyAtT(oIndex).fY);
284 other->debugAddTPair(other->fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
285#endif
286 }
287 }
288}
289
caryclark@google.com570863f2013-09-16 15:55:01 +0000290void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
291 SkOpSegment* other) {
292 // walk this to startPt
293 // walk other to startPt
caryclark@google.com07393ca2013-04-08 11:47:37 +0000294 // if either is > 0, add a pointer to the other, copying adjacent winding
295 int tIndex = -1;
296 int oIndex = -1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000297 do {
298 ++tIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000299 } while (startPt != fTs[tIndex].fPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000300 int ttIndex = tIndex;
301 bool checkOtherTMatch = false;
302 do {
303 const SkOpSpan& span = fTs[ttIndex];
304 if (startPt != span.fPt) {
305 break;
skia.committer@gmail.coma1ed7ae2014-04-15 03:04:18 +0000306 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000307 if (span.fOther == other && span.fPt == startPt) {
308 checkOtherTMatch = true;
309 break;
310 }
311 } while (++ttIndex < count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000312 do {
313 ++oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000314 } while (startPt != other->fTs[oIndex].fPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000315 bool skipAdd = false;
316 if (checkOtherTMatch) {
317 int ooIndex = oIndex;
318 do {
319 const SkOpSpan& oSpan = other->fTs[ooIndex];
320 if (startPt != oSpan.fPt) {
321 break;
skia.committer@gmail.coma1ed7ae2014-04-15 03:04:18 +0000322 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000323 if (oSpan.fT == fTs[ttIndex].fOtherT) {
324 skipAdd = true;
325 break;
326 }
327 } while (++ooIndex < other->count());
328 }
329 if ((tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) && !skipAdd) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000330 addTPair(fTs[tIndex].fT, other, other->fTs[oIndex].fT, false, startPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000331 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000332 SkPoint nextPt = startPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000333 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000334 const SkPoint* workPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000335 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000336 workPt = &fTs[++tIndex].fPt;
337 } while (nextPt == *workPt);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000338 const SkPoint* oWorkPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000339 do {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000340 oWorkPt = &other->fTs[++oIndex].fPt;
341 } while (nextPt == *oWorkPt);
caryclark@google.com570863f2013-09-16 15:55:01 +0000342 nextPt = *workPt;
343 double tStart = fTs[tIndex].fT;
344 double oStart = other->fTs[oIndex].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000345 if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) {
346 break;
347 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000348 if (*workPt == *oWorkPt) {
349 addTPair(tStart, other, oStart, false, nextPt);
350 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000351 } while (endPt != nextPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000352}
353
354void SkOpSegment::addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
355 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
356 fBounds.setCubicBounds(pts);
357}
358
359void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active) const {
360 SkPoint edge[4];
361 const SkPoint* ePtr;
362 int lastT = fTs.count() - 1;
363 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
364 ePtr = fPts;
365 } else {
366 // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
367 subDivide(start, end, edge);
368 ePtr = edge;
369 }
370 if (active) {
371 bool reverse = ePtr == fPts && start != 0;
372 if (reverse) {
reed@google.com277c3f82013-05-31 15:17:50 +0000373 path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000374 switch (fVerb) {
375 case SkPath::kLine_Verb:
376 path->deferredLine(ePtr[0]);
377 break;
378 case SkPath::kQuad_Verb:
379 path->quadTo(ePtr[1], ePtr[0]);
380 break;
381 case SkPath::kCubic_Verb:
382 path->cubicTo(ePtr[2], ePtr[1], ePtr[0]);
383 break;
384 default:
385 SkASSERT(0);
386 }
387 // return ePtr[0];
388 } else {
389 path->deferredMoveLine(ePtr[0]);
390 switch (fVerb) {
391 case SkPath::kLine_Verb:
392 path->deferredLine(ePtr[1]);
393 break;
394 case SkPath::kQuad_Verb:
395 path->quadTo(ePtr[1], ePtr[2]);
396 break;
397 case SkPath::kCubic_Verb:
398 path->cubicTo(ePtr[1], ePtr[2], ePtr[3]);
399 break;
400 default:
401 SkASSERT(0);
402 }
403 }
404 }
reed@google.com277c3f82013-05-31 15:17:50 +0000405 // return ePtr[SkPathOpsVerbToPoints(fVerb)];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000406}
407
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000408void SkOpSegment::addEndSpan(int endIndex) {
caryclark19eb3b22014-07-18 05:08:14 -0700409 SkASSERT(span(endIndex).fT == 1 || (span(endIndex).fTiny
410 && approximately_greater_than_one(span(endIndex).fT)));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000411 int spanCount = fTs.count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000412 int startIndex = endIndex - 1;
413 while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) {
caryclark19eb3b22014-07-18 05:08:14 -0700414 --startIndex;
415 SkASSERT(startIndex > 0);
416 --endIndex;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000417 }
caryclarkdac1d172014-06-17 05:15:38 -0700418 SkOpAngle& angle = fAngles.push_back();
419 angle.set(this, spanCount - 1, startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000420#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000421 debugCheckPointsEqualish(endIndex, spanCount);
422#endif
caryclarkdac1d172014-06-17 05:15:38 -0700423 setFromAngle(endIndex, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000424}
425
caryclarkdac1d172014-06-17 05:15:38 -0700426void SkOpSegment::setFromAngle(int endIndex, SkOpAngle* angle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000427 int spanCount = fTs.count();
428 do {
caryclarkdac1d172014-06-17 05:15:38 -0700429 fTs[endIndex].fFromAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000430 } while (++endIndex < spanCount);
431}
432
caryclark@google.com07393ca2013-04-08 11:47:37 +0000433void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
434 init(pts, SkPath::kLine_Verb, operand, evenOdd);
435 fBounds.set(pts, 2);
436}
437
438// add 2 to edge or out of range values to get T extremes
439void SkOpSegment::addOtherT(int index, double otherT, int otherIndex) {
440 SkOpSpan& span = fTs[index];
caryclark@google.com03610322013-04-18 15:58:21 +0000441 if (precisely_zero(otherT)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000442 otherT = 0;
caryclark@google.com03610322013-04-18 15:58:21 +0000443 } else if (precisely_equal(otherT, 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000444 otherT = 1;
445 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000446 span.fOtherT = otherT;
447 span.fOtherIndex = otherIndex;
448}
449
450void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
451 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
452 fBounds.setQuadBounds(pts);
453}
454
caryclarkdac1d172014-06-17 05:15:38 -0700455SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
456 int spanIndex = count() - 1;
457 int startIndex = nextExactSpan(spanIndex, -1);
458 SkASSERT(startIndex >= 0);
459 SkOpAngle& angle = fAngles.push_back();
460 *anglePtr = &angle;
461 angle.set(this, spanIndex, startIndex);
462 setFromAngle(spanIndex, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000463 SkOpSegment* other;
caryclarkdac1d172014-06-17 05:15:38 -0700464 int oStartIndex, oEndIndex;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000465 do {
caryclarkdac1d172014-06-17 05:15:38 -0700466 const SkOpSpan& span = fTs[spanIndex];
467 SkASSERT(span.fT > 0);
468 other = span.fOther;
469 oStartIndex = span.fOtherIndex;
470 oEndIndex = other->nextExactSpan(oStartIndex, 1);
471 if (oEndIndex > 0 && other->span(oStartIndex).fWindValue) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000472 break;
473 }
caryclarkdac1d172014-06-17 05:15:38 -0700474 oEndIndex = oStartIndex;
475 oStartIndex = other->nextExactSpan(oEndIndex, -1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000476 --spanIndex;
caryclarkdac1d172014-06-17 05:15:38 -0700477 } while (oStartIndex < 0 || !other->span(oStartIndex).fWindSum);
478 SkOpAngle& oAngle = other->fAngles.push_back();
479 oAngle.set(other, oStartIndex, oEndIndex);
480 other->setToAngle(oEndIndex, &oAngle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000481 *otherPtr = other;
caryclarkdac1d172014-06-17 05:15:38 -0700482 return &oAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000483}
484
caryclarkdac1d172014-06-17 05:15:38 -0700485SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
486 int endIndex = nextExactSpan(0, 1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000487 SkASSERT(endIndex > 0);
caryclarkdac1d172014-06-17 05:15:38 -0700488 SkOpAngle& angle = fAngles.push_back();
489 *anglePtr = &angle;
490 angle.set(this, 0, endIndex);
491 setToAngle(endIndex, &angle);
492 int spanIndex = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000493 SkOpSegment* other;
caryclarkdac1d172014-06-17 05:15:38 -0700494 int oStartIndex, oEndIndex;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000495 do {
caryclarkdac1d172014-06-17 05:15:38 -0700496 const SkOpSpan& span = fTs[spanIndex];
497 SkASSERT(span.fT < 1);
498 other = span.fOther;
499 oEndIndex = span.fOtherIndex;
500 oStartIndex = other->nextExactSpan(oEndIndex, -1);
501 if (oStartIndex >= 0 && other->span(oStartIndex).fWindValue) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000502 break;
503 }
caryclarkdac1d172014-06-17 05:15:38 -0700504 oStartIndex = oEndIndex;
505 oEndIndex = other->nextExactSpan(oStartIndex, 1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000506 ++spanIndex;
caryclarkdac1d172014-06-17 05:15:38 -0700507 } while (oEndIndex < 0 || !other->span(oStartIndex).fWindValue);
508 SkOpAngle& oAngle = other->fAngles.push_back();
509 oAngle.set(other, oEndIndex, oStartIndex);
510 other->setFromAngle(oEndIndex, &oAngle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000511 *otherPtr = other;
caryclarkdac1d172014-06-17 05:15:38 -0700512 return &oAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000513}
514
caryclarkdac1d172014-06-17 05:15:38 -0700515SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000516 SkOpSegment* other;
caryclarkdac1d172014-06-17 05:15:38 -0700517 SkOpAngle* angle, * otherAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000518 if (step > 0) {
caryclarkdac1d172014-06-17 05:15:38 -0700519 otherAngle = addSingletonAngleUp(&other, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000520 } else {
caryclarkdac1d172014-06-17 05:15:38 -0700521 otherAngle = addSingletonAngleDown(&other, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000522 }
caryclarkdac1d172014-06-17 05:15:38 -0700523 angle->insert(otherAngle);
524 return angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000525}
526
527void SkOpSegment::addStartSpan(int endIndex) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000528 int index = 0;
caryclarkdac1d172014-06-17 05:15:38 -0700529 SkOpAngle& angle = fAngles.push_back();
530 angle.set(this, index, endIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000531#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000532 debugCheckPointsEqualish(index, endIndex);
533#endif
caryclarkdac1d172014-06-17 05:15:38 -0700534 setToAngle(endIndex, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000535}
536
caryclarkdac1d172014-06-17 05:15:38 -0700537void SkOpSegment::setToAngle(int endIndex, SkOpAngle* angle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000538 int index = 0;
539 do {
caryclarkdac1d172014-06-17 05:15:38 -0700540 fTs[index].fToAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000541 } while (++index < endIndex);
542}
543
caryclark@google.com07393ca2013-04-08 11:47:37 +0000544 // Defer all coincident edge processing until
545 // after normal intersections have been computed
546
547// no need to be tricky; insert in normal T order
548// resolve overlapping ts when considering coincidence later
549
550 // add non-coincident intersection. Resulting edges are sorted in T.
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000551int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000552 SkASSERT(this != other || fVerb == SkPath::kCubic_Verb);
553 #if 0 // this needs an even rougher association to be useful
554 SkASSERT(SkDPoint::RoughlyEqual(ptAtT(newT), pt));
555 #endif
caryclarkdac1d172014-06-17 05:15:38 -0700556 const SkPoint& firstPt = fPts[0];
557 const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
558 SkASSERT(newT == 0 || !precisely_zero(newT));
559 SkASSERT(newT == 1 || !precisely_equal(newT, 1));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000560 // FIXME: in the pathological case where there is a ton of intercepts,
561 // binary search?
562 int insertedAt = -1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000563 int tCount = fTs.count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000564 for (int index = 0; index < tCount; ++index) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000565 // OPTIMIZATION: if there are three or more identical Ts, then
566 // the fourth and following could be further insertion-sorted so
567 // that all the edges are clockwise or counterclockwise.
568 // This could later limit segment tests to the two adjacent
569 // neighbors, although it doesn't help with determining which
570 // circular direction to go in.
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000571 const SkOpSpan& span = fTs[index];
572 if (newT < span.fT) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000573 insertedAt = index;
574 break;
575 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000576 if (newT == span.fT) {
577 if (pt == span.fPt) {
578 insertedAt = index;
579 break;
580 }
581 if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
582 insertedAt = index;
583 break;
584 }
585 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000586 }
587 SkOpSpan* span;
588 if (insertedAt >= 0) {
589 span = fTs.insert(insertedAt);
590 } else {
591 insertedAt = tCount;
592 span = fTs.append();
593 }
594 span->fT = newT;
caryclarkdac1d172014-06-17 05:15:38 -0700595 span->fOtherT = -1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000596 span->fOther = other;
597 span->fPt = pt;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000598#if 0
599 // cubics, for instance, may not be exact enough to satisfy this check (e.g., cubicOp69d)
600 SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX)
601 && approximately_equal(xyAtT(newT).fY, pt.fY));
602#endif
caryclarkdac1d172014-06-17 05:15:38 -0700603 span->fFromAngle = NULL;
604 span->fToAngle = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000605 span->fWindSum = SK_MinS32;
606 span->fOppSum = SK_MinS32;
607 span->fWindValue = 1;
608 span->fOppValue = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000609 span->fChased = false;
caryclarkdac1d172014-06-17 05:15:38 -0700610 span->fCoincident = false;
611 span->fLoop = false;
612 span->fNear = false;
613 span->fMultiple = false;
614 span->fSmall = false;
615 span->fTiny = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000616 if ((span->fDone = newT == 1)) {
617 ++fDoneSpans;
618 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000619 int less = -1;
caryclarkdac1d172014-06-17 05:15:38 -0700620// FIXME: note that this relies on spans being a continguous array
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000621// find range of spans with nearly the same point as this one
622 while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, pt)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000623 if (fVerb == SkPath::kCubic_Verb) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000624 double tInterval = newT - span[less].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000625 double tMid = newT - tInterval / 2;
626 SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
627 if (!midPt.approximatelyEqual(xyAtT(span))) {
628 break;
629 }
630 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000631 --less;
632 }
633 int more = 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000634 while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].fPt, pt)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000635 if (fVerb == SkPath::kCubic_Verb) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000636 double tEndInterval = span[more].fT - newT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000637 double tMid = newT - tEndInterval / 2;
638 SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
639 if (!midEndPt.approximatelyEqual(xyAtT(span))) {
640 break;
641 }
642 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000643 ++more;
644 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000645 ++less;
646 --more;
647 while (more - 1 > less && span[more].fPt == span[more - 1].fPt
648 && span[more].fT == span[more - 1].fT) {
649 --more;
650 }
651 if (less == more) {
652 return insertedAt;
653 }
654 if (precisely_negative(span[more].fT - span[less].fT)) {
655 return insertedAt;
656 }
657// if the total range of t values is big enough, mark all tiny
658 bool tiny = span[less].fPt == span[more].fPt;
659 int index = less;
660 do {
661 fSmall = span[index].fSmall = true;
662 fTiny |= span[index].fTiny = tiny;
663 if (!span[index].fDone) {
664 span[index].fDone = true;
665 ++fDoneSpans;
666 }
667 } while (++index < more);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000668 return insertedAt;
669}
670
671// set spans from start to end to decrement by one
672// note this walks other backwards
caryclark@google.coma5e55922013-05-07 18:51:31 +0000673// FIXME: there's probably an edge case that can be constructed where
caryclark@google.com07393ca2013-04-08 11:47:37 +0000674// two span in one segment are separated by float epsilon on one span but
675// not the other, if one segment is very small. For this
676// case the counts asserted below may or may not be enough to separate the
677// spans. Even if the counts work out, what if the spans aren't correctly
678// sorted? It feels better in such a case to match the span's other span
679// pointer since both coincident segments must contain the same spans.
caryclark@google.coma5e55922013-05-07 18:51:31 +0000680// FIXME? It seems that decrementing by one will fail for complex paths that
681// have three or more coincident edges. Shouldn't this subtract the difference
682// between the winding values?
caryclark@google.com570863f2013-09-16 15:55:01 +0000683/* |--> |-->
684this 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4
685other 2<<<<1<<<<0 2<<<<1<<<<0 2<<<<1<<<<0
686 ^ ^ <--| <--|
687 startPt endPt test/oTest first pos test/oTest final pos
688*/
689void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000690 bool binary = fOperand != other->fOperand;
691 int index = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000692 while (startPt != fTs[index].fPt) {
693 SkASSERT(index < fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000694 ++index;
695 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000696 while (index > 0 && precisely_equal(fTs[index].fT, fTs[index - 1].fT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000697 --index;
698 }
caryclarkdac1d172014-06-17 05:15:38 -0700699 bool oFoundEnd = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000700 int oIndex = other->fTs.count();
caryclark@google.com570863f2013-09-16 15:55:01 +0000701 while (startPt != other->fTs[--oIndex].fPt) { // look for startPt match
702 SkASSERT(oIndex > 0);
703 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000704 double oStartT = other->fTs[oIndex].fT;
705 // look for first point beyond match
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000706 while (startPt == other->fTs[--oIndex].fPt || precisely_equal(oStartT, other->fTs[oIndex].fT)) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000707 SkASSERT(oIndex > 0);
708 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000709 SkOpSpan* test = &fTs[index];
710 SkOpSpan* oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +0000711 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
712 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
caryclarkdac1d172014-06-17 05:15:38 -0700713 bool decrement, track, bigger;
714 int originalWindValue;
715 const SkPoint* testPt;
716 const SkPoint* oTestPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000717 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000718 SkASSERT(test->fT < 1);
719 SkASSERT(oTest->fT < 1);
caryclarkdac1d172014-06-17 05:15:38 -0700720 decrement = test->fWindValue && oTest->fWindValue;
721 track = test->fWindValue || oTest->fWindValue;
722 bigger = test->fWindValue >= oTest->fWindValue;
723 testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000724 double testT = test->fT;
caryclarkdac1d172014-06-17 05:15:38 -0700725 oTestPt = &oTest->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000726 double oTestT = oTest->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000727 do {
728 if (decrement) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000729 if (binary && bigger) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000730 test->fOppValue--;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000731 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +0000732 decrementSpan(test);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000733 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000734 } else if (track) {
caryclarkdac1d172014-06-17 05:15:38 -0700735 TrackOutsidePair(&outsidePts, *testPt, *oTestPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000736 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000737 SkASSERT(index < fTs.count() - 1);
738 test = &fTs[++index];
caryclarkdac1d172014-06-17 05:15:38 -0700739 } while (*testPt == test->fPt || precisely_equal(testT, test->fT));
740 originalWindValue = oTest->fWindValue;
caryclark@google.com570863f2013-09-16 15:55:01 +0000741 do {
742 SkASSERT(oTest->fT < 1);
743 SkASSERT(originalWindValue == oTest->fWindValue);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000744 if (decrement) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000745 if (binary && !bigger) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000746 oTest->fOppValue--;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000747 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +0000748 other->decrementSpan(oTest);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000749 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000750 } else if (track) {
caryclarkdac1d172014-06-17 05:15:38 -0700751 TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
752 }
753 if (!oIndex) {
754 break;
755 }
756 oFoundEnd |= endPt == oTest->fPt;
757 oTest = &other->fTs[--oIndex];
758 } while (*oTestPt == oTest->fPt || precisely_equal(oTestT, oTest->fT));
759 } while (endPt != test->fPt && test->fT < 1);
760 // FIXME: determine if canceled edges need outside ts added
761 if (!oFoundEnd) {
762 for (int oIdx2 = oIndex; oIdx2 >= 0; --oIdx2) {
763 SkOpSpan* oTst2 = &other->fTs[oIdx2];
764 if (originalWindValue != oTst2->fWindValue) {
765 goto skipAdvanceOtherCancel;
766 }
767 if (!oTst2->fWindValue) {
768 goto skipAdvanceOtherCancel;
769 }
770 if (endPt == other->fTs[oIdx2].fPt) {
771 break;
772 }
773 }
774 do {
775 SkASSERT(originalWindValue == oTest->fWindValue);
776 if (decrement) {
777 if (binary && !bigger) {
778 oTest->fOppValue--;
779 } else {
780 other->decrementSpan(oTest);
781 }
782 } else if (track) {
783 TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000784 }
785 if (!oIndex) {
786 break;
787 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000788 oTest = &other->fTs[--oIndex];
caryclarkdac1d172014-06-17 05:15:38 -0700789 oFoundEnd |= endPt == oTest->fPt;
790 } while (!oFoundEnd || endPt == oTest->fPt);
791 }
792skipAdvanceOtherCancel:
caryclark@google.com570863f2013-09-16 15:55:01 +0000793 int outCount = outsidePts.count();
794 if (!done() && outCount) {
795 addCancelOutsides(outsidePts[0], outsidePts[1], other);
796 if (outCount > 2) {
797 addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000798 }
799 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000800 if (!other->done() && oOutsidePts.count()) {
801 other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000802 }
caryclarkdac1d172014-06-17 05:15:38 -0700803 setCoincidentRange(startPt, endPt, other);
804 other->setCoincidentRange(startPt, endPt, this);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000805}
806
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000807int SkOpSegment::addSelfT(const SkPoint& pt, double newT) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000808 // if the tail nearly intersects itself but not quite, the caller records this separately
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000809 int result = addT(this, pt, newT);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000810 SkOpSpan* span = &fTs[result];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000811 fLoop = span->fLoop = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000812 return result;
813}
814
caryclarkdac1d172014-06-17 05:15:38 -0700815// find the starting or ending span with an existing loop of angles
816// FIXME? replicate for all identical starting/ending spans?
817// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
818// FIXME? assert that only one other span has a valid windValue or oppValue
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000819void SkOpSegment::addSimpleAngle(int index) {
caryclarkdac1d172014-06-17 05:15:38 -0700820 SkOpSpan* span = &fTs[index];
caryclark19eb3b22014-07-18 05:08:14 -0700821 int idx;
822 int start, end;
823 if (span->fT == 0) {
824 idx = 0;
825 span = &fTs[0];
caryclarkdac1d172014-06-17 05:15:38 -0700826 do {
827 if (span->fToAngle) {
828 SkASSERT(span->fToAngle->loopCount() == 2);
829 SkASSERT(!span->fFromAngle);
830 span->fFromAngle = span->fToAngle->next();
831 return;
832 }
caryclark19eb3b22014-07-18 05:08:14 -0700833 span = &fTs[++idx];
caryclarkdac1d172014-06-17 05:15:38 -0700834 } while (span->fT == 0);
caryclark19eb3b22014-07-18 05:08:14 -0700835 SkASSERT(!fTs[0].fTiny && fTs[idx].fT > 0);
836 addStartSpan(idx);
837 start = 0;
838 end = idx;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000839 } else {
caryclark19eb3b22014-07-18 05:08:14 -0700840 idx = count() - 1;
841 span = &fTs[idx];
caryclarkdac1d172014-06-17 05:15:38 -0700842 do {
843 if (span->fFromAngle) {
844 SkASSERT(span->fFromAngle->loopCount() == 2);
845 SkASSERT(!span->fToAngle);
846 span->fToAngle = span->fFromAngle->next();
847 return;
848 }
caryclark19eb3b22014-07-18 05:08:14 -0700849 span = &fTs[--idx];
caryclarkdac1d172014-06-17 05:15:38 -0700850 } while (span->fT == 1);
caryclark19eb3b22014-07-18 05:08:14 -0700851 SkASSERT(!fTs[idx].fTiny && fTs[idx].fT < 1);
852 addEndSpan(++idx);
853 start = idx;
854 end = count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000855 }
caryclark19eb3b22014-07-18 05:08:14 -0700856 SkOpSegment* other;
857 SkOpSpan* oSpan;
858 index = start;
859 do {
860 span = &fTs[index];
861 other = span->fOther;
862 int oFrom = span->fOtherIndex;
863 oSpan = &other->fTs[oFrom];
864 if (oSpan->fT < 1 && oSpan->fWindValue) {
865 break;
866 }
867 if (oSpan->fT == 0) {
868 continue;
869 }
870 oFrom = other->nextExactSpan(oFrom, -1);
871 SkOpSpan* oFromSpan = &other->fTs[oFrom];
872 SkASSERT(oFromSpan->fT < 1);
873 if (oFromSpan->fWindValue) {
874 break;
875 }
876 } while (++index < end);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000877 SkOpAngle* angle, * oAngle;
caryclark19eb3b22014-07-18 05:08:14 -0700878 if (span->fT == 0) {
caryclarkdac1d172014-06-17 05:15:38 -0700879 SkASSERT(span->fOtherIndex - 1 >= 0);
880 SkASSERT(span->fOtherT == 1);
caryclark19eb3b22014-07-18 05:08:14 -0700881 SkDEBUGCODE(int oPriorIndex = other->nextExactSpan(span->fOtherIndex, -1));
882 SkDEBUGCODE(const SkOpSpan& oPrior = other->span(oPriorIndex));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000883 SkASSERT(!oPrior.fTiny && oPrior.fT < 1);
caryclarkdac1d172014-06-17 05:15:38 -0700884 other->addEndSpan(span->fOtherIndex);
885 angle = span->fToAngle;
caryclark19eb3b22014-07-18 05:08:14 -0700886 oAngle = oSpan->fFromAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000887 } else {
caryclarkdac1d172014-06-17 05:15:38 -0700888 SkASSERT(span->fOtherIndex + 1 < other->count());
889 SkASSERT(span->fOtherT == 0);
caryclark19eb3b22014-07-18 05:08:14 -0700890 SkASSERT(!oSpan->fTiny && (other->fTs[span->fOtherIndex + 1].fT > 0
caryclarkdac1d172014-06-17 05:15:38 -0700891 || (other->fTs[span->fOtherIndex + 1].fFromAngle == NULL
892 && other->fTs[span->fOtherIndex + 1].fToAngle == NULL)));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000893 int oIndex = 1;
894 do {
895 const SkOpSpan& osSpan = other->span(oIndex);
caryclarkdac1d172014-06-17 05:15:38 -0700896 if (osSpan.fFromAngle || osSpan.fT > 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000897 break;
898 }
899 ++oIndex;
900 SkASSERT(oIndex < other->count());
901 } while (true);
902 other->addStartSpan(oIndex);
caryclarkdac1d172014-06-17 05:15:38 -0700903 angle = span->fFromAngle;
caryclark19eb3b22014-07-18 05:08:14 -0700904 oAngle = oSpan->fToAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000905 }
906 angle->insert(oAngle);
907}
908
caryclarkdac1d172014-06-17 05:15:38 -0700909void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
910 debugValidate();
911 int count = this->count();
912 for (int index = 0; index < count; ++index) {
913 SkOpSpan& span = fTs[index];
914 if (!span.fMultiple) {
915 continue;
916 }
917 int end = nextExactSpan(index, 1);
918 SkASSERT(end > index + 1);
919 const SkPoint& thisPt = span.fPt;
920 while (index < end - 1) {
921 SkOpSegment* other1 = span.fOther;
922 int oCnt = other1->count();
923 for (int idx2 = index + 1; idx2 < end; ++idx2) {
924 SkOpSpan& span2 = fTs[idx2];
925 SkOpSegment* other2 = span2.fOther;
926 for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
927 SkOpSpan& oSpan = other1->fTs[oIdx];
928 if (oSpan.fOther != other2) {
929 continue;
930 }
931 if (oSpan.fPt == thisPt) {
932 goto skipExactMatches;
933 }
934 }
935 for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
936 SkOpSpan& oSpan = other1->fTs[oIdx];
937 if (oSpan.fOther != other2) {
938 continue;
939 }
940 if (SkDPoint::RoughlyEqual(oSpan.fPt, thisPt)) {
941 SkOpSpan& oSpan2 = other2->fTs[oSpan.fOtherIndex];
942 if (zero_or_one(span.fOtherT) || zero_or_one(oSpan.fT)
943 || zero_or_one(span2.fOtherT) || zero_or_one(oSpan2.fT)) {
944 return;
945 }
946 if (!way_roughly_equal(span.fOtherT, oSpan.fT)
947 || !way_roughly_equal(span2.fOtherT, oSpan2.fT)
948 || !way_roughly_equal(span2.fOtherT, oSpan.fOtherT)
949 || !way_roughly_equal(span.fOtherT, oSpan2.fOtherT)) {
950 return;
951 }
952 alignSpan(thisPt, span.fOtherT, other1, span2.fOtherT,
953 other2, &oSpan, alignedArray);
954 alignSpan(thisPt, span2.fOtherT, other2, span.fOtherT,
955 other1, &oSpan2, alignedArray);
956 break;
957 }
958 }
959 skipExactMatches:
960 ;
961 }
962 ++index;
963 }
964 }
965 debugValidate();
966}
967
968void SkOpSegment::alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other,
969 double otherT, const SkOpSegment* other2, SkOpSpan* oSpan,
970 SkTDArray<AlignedSpan>* alignedArray) {
971 AlignedSpan* aligned = alignedArray->append();
972 aligned->fOldPt = oSpan->fPt;
973 aligned->fPt = newPt;
974 aligned->fOldT = oSpan->fT;
975 aligned->fT = newT;
976 aligned->fSegment = this; // OPTIMIZE: may be unused, can remove
977 aligned->fOther1 = other;
978 aligned->fOther2 = other2;
979 SkASSERT(SkDPoint::RoughlyEqual(oSpan->fPt, newPt));
980 oSpan->fPt = newPt;
981// SkASSERT(way_roughly_equal(oSpan->fT, newT));
982 oSpan->fT = newT;
983// SkASSERT(way_roughly_equal(oSpan->fOtherT, otherT));
984 oSpan->fOtherT = otherT;
985}
986
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000987bool SkOpSegment::alignSpan(int index, double thisT, const SkPoint& thisPt) {
988 bool aligned = false;
989 SkOpSpan* span = &fTs[index];
990 SkOpSegment* other = span->fOther;
991 int oIndex = span->fOtherIndex;
992 SkOpSpan* oSpan = &other->fTs[oIndex];
993 if (span->fT != thisT) {
994 span->fT = thisT;
995 oSpan->fOtherT = thisT;
996 aligned = true;
997 }
998 if (span->fPt != thisPt) {
999 span->fPt = thisPt;
1000 oSpan->fPt = thisPt;
1001 aligned = true;
1002 }
1003 double oT = oSpan->fT;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001004 if (oT == 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001005 return aligned;
1006 }
1007 int oStart = other->nextSpan(oIndex, -1) + 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001008 oSpan = &other->fTs[oStart];
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001009 int otherIndex = oStart;
1010 if (oT == 1) {
1011 if (aligned) {
1012 while (oSpan->fPt == thisPt && oSpan->fT != 1) {
1013 oSpan->fTiny = true;
1014 ++oSpan;
1015 }
1016 }
1017 return aligned;
1018 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001019 oT = oSpan->fT;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001020 int oEnd = other->nextSpan(oIndex, 1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001021 bool oAligned = false;
1022 if (oSpan->fPt != thisPt) {
1023 oAligned |= other->alignSpan(oStart, oT, thisPt);
1024 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001025 while (++otherIndex < oEnd) {
1026 SkOpSpan* oNextSpan = &other->fTs[otherIndex];
1027 if (oNextSpan->fT != oT || oNextSpan->fPt != thisPt) {
1028 oAligned |= other->alignSpan(otherIndex, oT, thisPt);
1029 }
1030 }
1031 if (oAligned) {
1032 other->alignSpanState(oStart, oEnd);
1033 }
1034 return aligned;
1035}
1036
1037void SkOpSegment::alignSpanState(int start, int end) {
1038 SkOpSpan* lastSpan = &fTs[--end];
1039 bool allSmall = lastSpan->fSmall;
1040 bool allTiny = lastSpan->fTiny;
1041 bool allDone = lastSpan->fDone;
1042 SkDEBUGCODE(int winding = lastSpan->fWindValue);
1043 SkDEBUGCODE(int oppWinding = lastSpan->fOppValue);
1044 int index = start;
1045 while (index < end) {
1046 SkOpSpan* span = &fTs[index];
1047 span->fSmall = allSmall;
1048 span->fTiny = allTiny;
1049 if (span->fDone != allDone) {
1050 span->fDone = allDone;
1051 fDoneSpans += allDone ? 1 : -1;
1052 }
1053 SkASSERT(span->fWindValue == winding);
1054 SkASSERT(span->fOppValue == oppWinding);
1055 ++index;
1056 }
1057}
1058
caryclarkdac1d172014-06-17 05:15:38 -07001059void SkOpSegment::blindCancel(const SkCoincidence& coincidence, SkOpSegment* other) {
1060 bool binary = fOperand != other->fOperand;
1061 int index = 0;
1062 int last = this->count();
1063 do {
1064 SkOpSpan& span = this->fTs[--last];
1065 if (span.fT != 1 && !span.fSmall) {
1066 break;
1067 }
1068 span.fCoincident = true;
1069 } while (true);
1070 int oIndex = other->count();
1071 do {
1072 SkOpSpan& oSpan = other->fTs[--oIndex];
1073 if (oSpan.fT != 1 && !oSpan.fSmall) {
1074 break;
1075 }
1076 oSpan.fCoincident = true;
1077 } while (true);
1078 do {
1079 SkOpSpan* test = &this->fTs[index];
1080 int baseWind = test->fWindValue;
1081 int baseOpp = test->fOppValue;
1082 int endIndex = index;
1083 while (++endIndex <= last) {
1084 SkOpSpan* endSpan = &this->fTs[endIndex];
1085 SkASSERT(endSpan->fT < 1);
1086 if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
1087 break;
1088 }
1089 endSpan->fCoincident = true;
1090 }
1091 SkOpSpan* oTest = &other->fTs[oIndex];
1092 int oBaseWind = oTest->fWindValue;
1093 int oBaseOpp = oTest->fOppValue;
1094 int oStartIndex = oIndex;
1095 while (--oStartIndex >= 0) {
1096 SkOpSpan* oStartSpan = &other->fTs[oStartIndex];
1097 if (oStartSpan->fWindValue != oBaseWind || oStartSpan->fOppValue != oBaseOpp) {
1098 break;
1099 }
1100 oStartSpan->fCoincident = true;
1101 }
1102 bool decrement = baseWind && oBaseWind;
1103 bool bigger = baseWind >= oBaseWind;
1104 do {
1105 SkASSERT(test->fT < 1);
1106 if (decrement) {
1107 if (binary && bigger) {
1108 test->fOppValue--;
1109 } else {
1110 decrementSpan(test);
1111 }
1112 }
1113 test->fCoincident = true;
1114 test = &fTs[++index];
1115 } while (index < endIndex);
1116 do {
1117 SkASSERT(oTest->fT < 1);
1118 if (decrement) {
1119 if (binary && !bigger) {
1120 oTest->fOppValue--;
1121 } else {
1122 other->decrementSpan(oTest);
1123 }
1124 }
1125 oTest->fCoincident = true;
1126 oTest = &other->fTs[--oIndex];
1127 } while (oIndex > oStartIndex);
1128 } while (index <= last && oIndex >= 0);
1129 SkASSERT(index > last);
1130 SkASSERT(oIndex < 0);
1131}
1132
1133void SkOpSegment::blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other) {
1134 bool binary = fOperand != other->fOperand;
1135 int index = 0;
1136 int last = this->count();
1137 do {
1138 SkOpSpan& span = this->fTs[--last];
1139 if (span.fT != 1 && !span.fSmall) {
1140 break;
1141 }
1142 span.fCoincident = true;
1143 } while (true);
1144 int oIndex = 0;
1145 int oLast = other->count();
1146 do {
1147 SkOpSpan& oSpan = other->fTs[--oLast];
1148 if (oSpan.fT != 1 && !oSpan.fSmall) {
1149 break;
1150 }
1151 oSpan.fCoincident = true;
1152 } while (true);
1153 do {
1154 SkOpSpan* test = &this->fTs[index];
1155 int baseWind = test->fWindValue;
1156 int baseOpp = test->fOppValue;
1157 int endIndex = index;
1158 SkOpSpan* endSpan;
1159 while (++endIndex <= last) {
1160 endSpan = &this->fTs[endIndex];
1161 SkASSERT(endSpan->fT < 1);
1162 if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
1163 break;
1164 }
1165 endSpan->fCoincident = true;
1166 }
1167 SkOpSpan* oTest = &other->fTs[oIndex];
1168 int oBaseWind = oTest->fWindValue;
1169 int oBaseOpp = oTest->fOppValue;
1170 int oEndIndex = oIndex;
1171 SkOpSpan* oEndSpan;
1172 while (++oEndIndex <= oLast) {
1173 oEndSpan = &this->fTs[oEndIndex];
1174 SkASSERT(oEndSpan->fT < 1);
1175 if (oEndSpan->fWindValue != oBaseWind || oEndSpan->fOppValue != oBaseOpp) {
1176 break;
1177 }
1178 oEndSpan->fCoincident = true;
1179 }
1180 // consolidate the winding count even if done
1181 if ((test->fWindValue || test->fOppValue) && (oTest->fWindValue || oTest->fOppValue)) {
1182 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
1183 bumpCoincidentBlind(binary, index, endIndex);
1184 other->bumpCoincidentOBlind(oIndex, oEndIndex);
1185 } else {
1186 other->bumpCoincidentBlind(binary, oIndex, oEndIndex);
1187 bumpCoincidentOBlind(index, endIndex);
1188 }
1189 }
1190 index = endIndex;
1191 oIndex = oEndIndex;
1192 } while (index <= last && oIndex <= oLast);
1193 SkASSERT(index > last);
1194 SkASSERT(oIndex > oLast);
1195}
1196
1197void SkOpSegment::bumpCoincidentBlind(bool binary, int index, int endIndex) {
1198 const SkOpSpan& oTest = fTs[index];
1199 int oWindValue = oTest.fWindValue;
1200 int oOppValue = oTest.fOppValue;
1201 if (binary) {
1202 SkTSwap<int>(oWindValue, oOppValue);
1203 }
1204 do {
1205 (void) bumpSpan(&fTs[index], oWindValue, oOppValue);
1206 } while (++index < endIndex);
1207}
1208
caryclark@google.com570863f2013-09-16 15:55:01 +00001209void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
1210 SkTArray<SkPoint, true>* outsideTs) {
1211 int index = *indexPtr;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001212 int oWindValue = oTest.fWindValue;
1213 int oOppValue = oTest.fOppValue;
caryclark@google.com570863f2013-09-16 15:55:01 +00001214 if (binary) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001215 SkTSwap<int>(oWindValue, oOppValue);
1216 }
1217 SkOpSpan* const test = &fTs[index];
1218 SkOpSpan* end = test;
caryclark@google.com570863f2013-09-16 15:55:01 +00001219 const SkPoint& oStartPt = oTest.fPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001220 do {
1221 if (bumpSpan(end, oWindValue, oOppValue)) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001222 TrackOutside(outsideTs, oStartPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001223 }
1224 end = &fTs[++index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001225 } while ((end->fPt == test->fPt || precisely_equal(end->fT, test->fT)) && end->fT < 1);
caryclark@google.com570863f2013-09-16 15:55:01 +00001226 *indexPtr = index;
1227}
1228
caryclarkdac1d172014-06-17 05:15:38 -07001229void SkOpSegment::bumpCoincidentOBlind(int index, int endIndex) {
1230 do {
1231 zeroSpan(&fTs[index]);
1232 } while (++index < endIndex);
1233}
1234
caryclark@google.com07393ca2013-04-08 11:47:37 +00001235// because of the order in which coincidences are resolved, this and other
1236// may not have the same intermediate points. Compute the corresponding
1237// intermediate T values (using this as the master, other as the follower)
1238// and walk other conditionally -- hoping that it catches up in the end
caryclark@google.com570863f2013-09-16 15:55:01 +00001239void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
1240 SkTArray<SkPoint, true>* oOutsidePts) {
1241 int oIndex = *oIndexPtr;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001242 SkOpSpan* const oTest = &fTs[oIndex];
1243 SkOpSpan* oEnd = oTest;
caryclark@google.com570863f2013-09-16 15:55:01 +00001244 const SkPoint& oStartPt = oTest->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001245 double oStartT = oTest->fT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001246#if 0 // FIXME : figure out what disabling this breaks
1247 const SkPoint& startPt = test.fPt;
1248 // this is always true since oEnd == oTest && oStartPt == oTest->fPt -- find proper condition
1249 if (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001250 TrackOutside(oOutsidePts, startPt);
1251 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001252#endif
1253 while (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001254 zeroSpan(oEnd);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001255 oEnd = &fTs[++oIndex];
1256 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001257 *oIndexPtr = oIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001258}
1259
1260// FIXME: need to test this case:
1261// contourA has two segments that are coincident
1262// contourB has two segments that are coincident in the same place
1263// each ends up with +2/0 pairs for winding count
1264// since logic below doesn't transfer count (only increments/decrements) can this be
1265// resolved to +4/0 ?
1266
1267// set spans from start to end to increment the greater by one and decrement
1268// the lesser
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001269void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
caryclark@google.com570863f2013-09-16 15:55:01 +00001270 SkOpSegment* other) {
1271 bool binary = fOperand != other->fOperand;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001272 int index = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +00001273 while (startPt != fTs[index].fPt) {
1274 SkASSERT(index < fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001275 ++index;
1276 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001277 double startT = fTs[index].fT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001278 while (index > 0 && precisely_equal(fTs[index - 1].fT, startT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001279 --index;
1280 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001281 int oIndex = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +00001282 while (startPt != other->fTs[oIndex].fPt) {
1283 SkASSERT(oIndex < other->fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001284 ++oIndex;
1285 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001286 double oStartT = other->fTs[oIndex].fT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001287 while (oIndex > 0 && precisely_equal(other->fTs[oIndex - 1].fT, oStartT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001288 --oIndex;
1289 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001290 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
1291 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001292 SkOpSpan* test = &fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +00001293 const SkPoint* testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001294 double testT = test->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001295 SkOpSpan* oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +00001296 const SkPoint* oTestPt = &oTest->fPt;
caryclark361b8b02014-09-08 10:25:38 -07001297 // paths with extreme data will fail this test and eject out of pathops altogether later on
1298 // SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001299 do {
caryclark@google.com570863f2013-09-16 15:55:01 +00001300 SkASSERT(test->fT < 1);
1301 SkASSERT(oTest->fT < 1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001302
1303 // consolidate the winding count even if done
caryclark@google.com570863f2013-09-16 15:55:01 +00001304 if ((test->fWindValue == 0 && test->fOppValue == 0)
1305 || (oTest->fWindValue == 0 && oTest->fOppValue == 0)) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001306 SkDEBUGCODE(int firstWind = test->fWindValue);
1307 SkDEBUGCODE(int firstOpp = test->fOppValue);
1308 do {
1309 SkASSERT(firstWind == fTs[index].fWindValue);
1310 SkASSERT(firstOpp == fTs[index].fOppValue);
1311 ++index;
1312 SkASSERT(index < fTs.count());
1313 } while (*testPt == fTs[index].fPt);
1314 SkDEBUGCODE(firstWind = oTest->fWindValue);
1315 SkDEBUGCODE(firstOpp = oTest->fOppValue);
1316 do {
1317 SkASSERT(firstWind == other->fTs[oIndex].fWindValue);
1318 SkASSERT(firstOpp == other->fTs[oIndex].fOppValue);
1319 ++oIndex;
1320 SkASSERT(oIndex < other->fTs.count());
1321 } while (*oTestPt == other->fTs[oIndex].fPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001322 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +00001323 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
1324 bumpCoincidentThis(*oTest, binary, &index, &outsidePts);
1325 other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts);
1326 } else {
1327 other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts);
1328 bumpCoincidentOther(*oTest, &index, &outsidePts);
1329 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001330 }
1331 test = &fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +00001332 testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001333 testT = test->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001334 oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +00001335 oTestPt = &oTest->fPt;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001336 if (endPt == *testPt || precisely_equal(endT, testT)) {
1337 break;
1338 }
1339// SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
caryclark@google.com570863f2013-09-16 15:55:01 +00001340 } while (endPt != *oTestPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001341 // in rare cases, one may have ended before the other
1342 if (endPt != *testPt && !precisely_equal(endT, testT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001343 int lastWind = test[-1].fWindValue;
1344 int lastOpp = test[-1].fOppValue;
1345 bool zero = lastWind == 0 && lastOpp == 0;
1346 do {
1347 if (test->fWindValue || test->fOppValue) {
1348 test->fWindValue = lastWind;
1349 test->fOppValue = lastOpp;
1350 if (zero) {
1351 test->fDone = true;
1352 ++fDoneSpans;
1353 }
1354 }
1355 test = &fTs[++index];
1356 testPt = &test->fPt;
1357 } while (endPt != *testPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001358 }
1359 if (endPt != *oTestPt) {
1360 // look ahead to see if zeroing more spans will allows us to catch up
1361 int oPeekIndex = oIndex;
1362 bool success = true;
1363 SkOpSpan* oPeek;
caryclarkdac1d172014-06-17 05:15:38 -07001364 int oCount = other->count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001365 do {
1366 oPeek = &other->fTs[oPeekIndex];
caryclarkdac1d172014-06-17 05:15:38 -07001367 if (++oPeekIndex == oCount) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001368 success = false;
1369 break;
1370 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001371 } while (endPt != oPeek->fPt);
1372 if (success) {
1373 // make sure the matching point completes the coincidence span
1374 success = false;
1375 do {
1376 if (oPeek->fOther == this) {
1377 success = true;
1378 break;
1379 }
caryclark19eb3b22014-07-18 05:08:14 -07001380 if (++oPeekIndex == oCount) {
1381 break;
1382 }
1383 oPeek = &other->fTs[oPeekIndex];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001384 } while (endPt == oPeek->fPt);
1385 }
1386 if (success) {
1387 do {
1388 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
1389 other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts);
1390 } else {
1391 other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts);
1392 }
1393 oTest = &other->fTs[oIndex];
1394 oTestPt = &oTest->fPt;
1395 } while (endPt != *oTestPt);
1396 }
1397 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001398 int outCount = outsidePts.count();
1399 if (!done() && outCount) {
1400 addCoinOutsides(outsidePts[0], endPt, other);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001401 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001402 if (!other->done() && oOutsidePts.count()) {
1403 other->addCoinOutsides(oOutsidePts[0], endPt, this);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001404 }
caryclarkdac1d172014-06-17 05:15:38 -07001405 setCoincidentRange(startPt, endPt, other);
1406 other->setCoincidentRange(startPt, endPt, this);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001407}
1408
1409// FIXME: this doesn't prevent the same span from being added twice
1410// fix in caller, SkASSERT here?
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001411const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
caryclarkdac1d172014-06-17 05:15:38 -07001412 const SkPoint& pt, const SkPoint& pt2) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001413 int tCount = fTs.count();
1414 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1415 const SkOpSpan& span = fTs[tIndex];
1416 if (!approximately_negative(span.fT - t)) {
1417 break;
1418 }
1419 if (approximately_negative(span.fT - t) && span.fOther == other
1420 && approximately_equal(span.fOtherT, otherT)) {
1421#if DEBUG_ADD_T_PAIR
1422 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1423 __FUNCTION__, fID, t, other->fID, otherT);
1424#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001425 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001426 }
1427 }
1428#if DEBUG_ADD_T_PAIR
1429 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1430 __FUNCTION__, fID, t, other->fID, otherT);
1431#endif
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001432 int insertedAt = addT(other, pt, t);
caryclarkdac1d172014-06-17 05:15:38 -07001433 int otherInsertedAt = other->addT(this, pt2, otherT);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001434 addOtherT(insertedAt, otherT, otherInsertedAt);
1435 other->addOtherT(otherInsertedAt, t, insertedAt);
1436 matchWindingValue(insertedAt, t, borrowWind);
1437 other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclarkdac1d172014-06-17 05:15:38 -07001438 SkOpSpan& span = this->fTs[insertedAt];
1439 if (pt != pt2) {
1440 span.fNear = true;
1441 SkOpSpan& oSpan = other->fTs[otherInsertedAt];
1442 oSpan.fNear = true;
1443 }
1444 return &span;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001445}
1446
caryclarkdac1d172014-06-17 05:15:38 -07001447const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
1448 const SkPoint& pt) {
1449 return addTPair(t, other, otherT, borrowWind, pt, pt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001450}
1451
caryclark@google.com570863f2013-09-16 15:55:01 +00001452bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
1453 const SkPoint midPt = ptAtT(midT);
1454 SkPathOpsBounds bounds;
1455 bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1456 bounds.sort();
1457 return bounds.almostContains(midPt);
1458}
1459
caryclark@google.com07393ca2013-04-08 11:47:37 +00001460bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const {
1461 if (lesser > greater) {
1462 SkTSwap<int>(lesser, greater);
1463 }
1464 return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
1465}
1466
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001467// in extreme cases (like the buffer overflow test) return false to abort
1468// for now, if one t value represents two different points, then the values are too extreme
1469// to generate meaningful results
1470bool SkOpSegment::calcAngles() {
1471 int spanCount = fTs.count();
1472 if (spanCount <= 2) {
1473 return spanCount == 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001474 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001475 int index = 1;
1476 const SkOpSpan* firstSpan = &fTs[index];
1477 int activePrior = checkSetAngle(0);
1478 const SkOpSpan* span = &fTs[0];
1479 if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) {
1480 index = findStartSpan(0); // curve start intersects
caryclark361b8b02014-09-08 10:25:38 -07001481 if (fTs[index].fT == 0) {
1482 return false;
1483 }
caryclarkdac1d172014-06-17 05:15:38 -07001484 SkASSERT(index > 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001485 if (activePrior >= 0) {
1486 addStartSpan(index);
1487 }
1488 }
1489 bool addEnd;
1490 int endIndex = spanCount - 1;
1491 span = &fTs[endIndex - 1];
1492 if ((addEnd = span->fT == 1 || span->fTiny)) { // if curve end intersects
1493 endIndex = findEndSpan(endIndex);
caryclarkdac1d172014-06-17 05:15:38 -07001494 SkASSERT(endIndex > 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001495 } else {
1496 addEnd = fTs[endIndex].fOtherT != 0 || fTs[endIndex].fOther->multipleStarts();
1497 }
1498 SkASSERT(endIndex >= index);
1499 int prior = 0;
1500 while (index < endIndex) {
1501 const SkOpSpan& fromSpan = fTs[index]; // for each intermediate intersection
1502 const SkOpSpan* lastSpan;
1503 span = &fromSpan;
1504 int start = index;
1505 do {
1506 lastSpan = span;
1507 span = &fTs[++index];
1508 SkASSERT(index < spanCount);
1509 if (!precisely_negative(span->fT - lastSpan->fT) && !lastSpan->fTiny) {
1510 break;
1511 }
1512 if (!SkDPoint::ApproximatelyEqual(lastSpan->fPt, span->fPt)) {
1513 return false;
1514 }
1515 } while (true);
caryclarkdac1d172014-06-17 05:15:38 -07001516 SkOpAngle* angle = NULL;
1517 SkOpAngle* priorAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001518 if (activePrior >= 0) {
1519 int pActive = firstActive(prior);
1520 SkASSERT(pActive < start);
caryclarkdac1d172014-06-17 05:15:38 -07001521 priorAngle = &fAngles.push_back();
1522 priorAngle->set(this, start, pActive);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001523 }
1524 int active = checkSetAngle(start);
1525 if (active >= 0) {
1526 SkASSERT(active < index);
caryclarkdac1d172014-06-17 05:15:38 -07001527 angle = &fAngles.push_back();
1528 angle->set(this, active, index);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001529 }
1530 #if DEBUG_ANGLE
1531 debugCheckPointsEqualish(start, index);
1532 #endif
1533 prior = start;
1534 do {
1535 const SkOpSpan* startSpan = &fTs[start - 1];
caryclarkdac1d172014-06-17 05:15:38 -07001536 if (!startSpan->fSmall || isCanceled(start - 1) || startSpan->fFromAngle
1537 || startSpan->fToAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001538 break;
1539 }
1540 --start;
1541 } while (start > 0);
1542 do {
1543 if (activePrior >= 0) {
caryclarkdac1d172014-06-17 05:15:38 -07001544 SkASSERT(fTs[start].fFromAngle == NULL);
1545 fTs[start].fFromAngle = priorAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001546 }
1547 if (active >= 0) {
caryclarkdac1d172014-06-17 05:15:38 -07001548 SkASSERT(fTs[start].fToAngle == NULL);
1549 fTs[start].fToAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001550 }
1551 } while (++start < index);
1552 activePrior = active;
1553 }
1554 if (addEnd && activePrior >= 0) {
1555 addEndSpan(endIndex);
1556 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001557 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001558}
1559
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001560int SkOpSegment::checkSetAngle(int tIndex) const {
1561 const SkOpSpan* span = &fTs[tIndex];
1562 while (span->fTiny /* || span->fSmall */) {
1563 span = &fTs[++tIndex];
caryclark@google.com07393ca2013-04-08 11:47:37 +00001564 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001565 return isCanceled(tIndex) ? -1 : tIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001566}
1567
caryclarkdac1d172014-06-17 05:15:38 -07001568// at this point, the span is already ordered, or unorderable
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001569int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
1570 SkASSERT(includeType != SkOpAngle::kUnaryXor);
1571 SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
1572 if (NULL == firstAngle) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001573 return SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001574 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001575 // if all angles have a computed winding,
1576 // or if no adjacent angles are orderable,
1577 // or if adjacent orderable angles have no computed winding,
1578 // there's nothing to do
caryclarkdac1d172014-06-17 05:15:38 -07001579 // if two orderable angles are adjacent, and both are next to orderable angles,
1580 // and one has winding computed, transfer to the other
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001581 SkOpAngle* baseAngle = NULL;
caryclark@google.com570863f2013-09-16 15:55:01 +00001582 bool tryReverse = false;
1583 // look for counterclockwise transfers
caryclarkdac1d172014-06-17 05:15:38 -07001584 SkOpAngle* angle = firstAngle->previous();
1585 SkOpAngle* next = angle->next();
1586 firstAngle = next;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001587 do {
caryclarkdac1d172014-06-17 05:15:38 -07001588 SkOpAngle* prior = angle;
1589 angle = next;
1590 next = angle->next();
1591 SkASSERT(prior->next() == angle);
1592 SkASSERT(angle->next() == next);
1593 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
1594 baseAngle = NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001595 continue;
1596 }
caryclarkdac1d172014-06-17 05:15:38 -07001597 int testWinding = angle->segment()->windSum(angle);
1598 if (SK_MinS32 != testWinding) {
1599 baseAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001600 tryReverse = true;
1601 continue;
1602 }
1603 if (baseAngle) {
1604 ComputeOneSum(baseAngle, angle, includeType);
1605 baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001606 }
caryclarkdac1d172014-06-17 05:15:38 -07001607 } while (next != firstAngle);
1608 if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001609 firstAngle = baseAngle;
1610 tryReverse = true;
1611 }
1612 if (tryReverse) {
1613 baseAngle = NULL;
caryclarkdac1d172014-06-17 05:15:38 -07001614 SkOpAngle* prior = firstAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +00001615 do {
caryclarkdac1d172014-06-17 05:15:38 -07001616 angle = prior;
1617 prior = angle->previous();
1618 SkASSERT(prior->next() == angle);
1619 next = angle->next();
1620 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
1621 baseAngle = NULL;
1622 continue;
1623 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001624 int testWinding = angle->segment()->windSum(angle);
1625 if (SK_MinS32 != testWinding) {
1626 baseAngle = angle;
caryclark@google.com570863f2013-09-16 15:55:01 +00001627 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001628 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001629 if (baseAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001630 ComputeOneSumReverse(baseAngle, angle, includeType);
1631 baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001632 }
caryclarkdac1d172014-06-17 05:15:38 -07001633 } while (prior != firstAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001634 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001635 int minIndex = SkMin32(startIndex, endIndex);
1636 return windSum(minIndex);
1637}
1638
caryclark@google.com570863f2013-09-16 15:55:01 +00001639void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
1640 SkOpAngle::IncludeType includeType) {
1641 const SkOpSegment* baseSegment = baseAngle->segment();
1642 int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
1643 int sumSuWinding;
1644 bool binary = includeType >= SkOpAngle::kBinarySingle;
1645 if (binary) {
1646 sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
1647 if (baseSegment->operand()) {
1648 SkTSwap<int>(sumMiWinding, sumSuWinding);
1649 }
1650 }
1651 SkOpSegment* nextSegment = nextAngle->segment();
1652 int maxWinding, sumWinding;
1653 SkOpSpan* last;
1654 if (binary) {
1655 int oppMaxWinding, oppSumWinding;
1656 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
1657 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1658 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001659 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001660 } else {
1661 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
1662 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001663 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001664 }
1665 nextAngle->setLastMarked(last);
1666}
1667
1668void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
1669 SkOpAngle::IncludeType includeType) {
1670 const SkOpSegment* baseSegment = baseAngle->segment();
1671 int sumMiWinding = baseSegment->updateWinding(baseAngle);
1672 int sumSuWinding;
1673 bool binary = includeType >= SkOpAngle::kBinarySingle;
1674 if (binary) {
1675 sumSuWinding = baseSegment->updateOppWinding(baseAngle);
1676 if (baseSegment->operand()) {
1677 SkTSwap<int>(sumMiWinding, sumSuWinding);
1678 }
1679 }
1680 SkOpSegment* nextSegment = nextAngle->segment();
1681 int maxWinding, sumWinding;
1682 SkOpSpan* last;
1683 if (binary) {
1684 int oppMaxWinding, oppSumWinding;
1685 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
1686 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1687 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001688 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001689 } else {
1690 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
1691 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001692 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001693 }
1694 nextAngle->setLastMarked(last);
1695}
1696
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001697bool SkOpSegment::containsPt(const SkPoint& pt, int index, int endIndex) const {
1698 int step = index < endIndex ? 1 : -1;
1699 do {
1700 const SkOpSpan& span = this->span(index);
1701 if (span.fPt == pt) {
1702 const SkOpSpan& endSpan = this->span(endIndex);
1703 return span.fT == endSpan.fT && pt != endSpan.fPt;
1704 }
1705 index += step;
1706 } while (index != endIndex);
1707 return false;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001708}
1709
caryclarkdac1d172014-06-17 05:15:38 -07001710bool SkOpSegment::containsT(double t, const SkOpSegment* other, double otherT) const {
1711 int count = this->count();
1712 for (int index = 0; index < count; ++index) {
1713 const SkOpSpan& span = fTs[index];
1714 if (t < span.fT) {
1715 return false;
1716 }
1717 if (t == span.fT) {
1718 if (other != span.fOther) {
1719 continue;
1720 }
1721 if (other->fVerb != SkPath::kCubic_Verb) {
1722 return true;
1723 }
1724 if (!other->fLoop) {
1725 return true;
1726 }
1727 double otherMidT = (otherT + span.fOtherT) / 2;
1728 SkPoint otherPt = other->ptAtT(otherMidT);
1729 return SkDPoint::ApproximatelyEqual(span.fPt, otherPt);
1730 }
1731 }
1732 return false;
1733}
1734
caryclark@google.com07393ca2013-04-08 11:47:37 +00001735int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
1736 bool* hitSomething, double mid, bool opp, bool current) const {
1737 SkScalar bottom = fBounds.fBottom;
1738 int bestTIndex = -1;
1739 if (bottom <= *bestY) {
1740 return bestTIndex;
1741 }
1742 SkScalar top = fBounds.fTop;
1743 if (top >= basePt.fY) {
1744 return bestTIndex;
1745 }
1746 if (fBounds.fLeft > basePt.fX) {
1747 return bestTIndex;
1748 }
1749 if (fBounds.fRight < basePt.fX) {
1750 return bestTIndex;
1751 }
1752 if (fBounds.fLeft == fBounds.fRight) {
1753 // if vertical, and directly above test point, wait for another one
1754 return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
1755 }
1756 // intersect ray starting at basePt with edge
1757 SkIntersections intersections;
1758 // OPTIMIZE: use specialty function that intersects ray with curve,
1759 // returning t values only for curve (we don't care about t on ray)
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00001760 intersections.allowNear(false);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001761 int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
1762 (fPts, top, bottom, basePt.fX, false);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001763 if (pts == 0 || (current && pts == 1)) {
1764 return bestTIndex;
1765 }
1766 if (current) {
1767 SkASSERT(pts > 1);
1768 int closestIdx = 0;
1769 double closest = fabs(intersections[0][0] - mid);
1770 for (int idx = 1; idx < pts; ++idx) {
1771 double test = fabs(intersections[0][idx] - mid);
1772 if (closest > test) {
1773 closestIdx = idx;
1774 closest = test;
1775 }
1776 }
1777 intersections.quickRemoveOne(closestIdx, --pts);
1778 }
1779 double bestT = -1;
1780 for (int index = 0; index < pts; ++index) {
1781 double foundT = intersections[0][index];
1782 if (approximately_less_than_zero(foundT)
1783 || approximately_greater_than_one(foundT)) {
1784 continue;
1785 }
reed@google.com277c3f82013-05-31 15:17:50 +00001786 SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001787 if (approximately_negative(testY - *bestY)
1788 || approximately_negative(basePt.fY - testY)) {
1789 continue;
1790 }
1791 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1792 return SK_MinS32; // if the intersection is edge on, wait for another one
1793 }
1794 if (fVerb > SkPath::kLine_Verb) {
reed@google.com277c3f82013-05-31 15:17:50 +00001795 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001796 if (approximately_zero(dx)) {
1797 return SK_MinS32; // hit vertical, wait for another one
1798 }
1799 }
1800 *bestY = testY;
1801 bestT = foundT;
1802 }
1803 if (bestT < 0) {
1804 return bestTIndex;
1805 }
1806 SkASSERT(bestT >= 0);
1807 SkASSERT(bestT <= 1);
1808 int start;
1809 int end = 0;
1810 do {
1811 start = end;
1812 end = nextSpan(start, 1);
1813 } while (fTs[end].fT < bestT);
1814 // FIXME: see next candidate for a better pattern to find the next start/end pair
1815 while (start + 1 < end && fTs[start].fDone) {
1816 ++start;
1817 }
1818 if (!isCanceled(start)) {
1819 *hitT = bestT;
1820 bestTIndex = start;
1821 *hitSomething = true;
1822 }
1823 return bestTIndex;
1824}
1825
caryclark@google.com570863f2013-09-16 15:55:01 +00001826bool SkOpSegment::decrementSpan(SkOpSpan* span) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001827 SkASSERT(span->fWindValue > 0);
1828 if (--(span->fWindValue) == 0) {
1829 if (!span->fOppValue && !span->fDone) {
1830 span->fDone = true;
1831 ++fDoneSpans;
caryclark@google.com570863f2013-09-16 15:55:01 +00001832 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001833 }
1834 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001835 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001836}
1837
1838bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001839 SkASSERT(!span->fDone || span->fTiny || span->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001840 span->fWindValue += windDelta;
1841 SkASSERT(span->fWindValue >= 0);
1842 span->fOppValue += oppDelta;
1843 SkASSERT(span->fOppValue >= 0);
1844 if (fXor) {
1845 span->fWindValue &= 1;
1846 }
1847 if (fOppXor) {
1848 span->fOppValue &= 1;
1849 }
1850 if (!span->fWindValue && !span->fOppValue) {
1851 span->fDone = true;
1852 ++fDoneSpans;
1853 return true;
1854 }
1855 return false;
1856}
1857
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001858const SkOpSpan& SkOpSegment::firstSpan(const SkOpSpan& thisSpan) const {
1859 const SkOpSpan* firstSpan = &thisSpan; // rewind to the start
1860 const SkOpSpan* beginSpan = fTs.begin();
1861 const SkPoint& testPt = thisSpan.fPt;
1862 while (firstSpan > beginSpan && firstSpan[-1].fPt == testPt) {
1863 --firstSpan;
1864 }
1865 return *firstSpan;
1866}
1867
1868const SkOpSpan& SkOpSegment::lastSpan(const SkOpSpan& thisSpan) const {
1869 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
1870 const SkOpSpan* lastSpan = &thisSpan; // find the end
1871 const SkPoint& testPt = thisSpan.fPt;
1872 while (lastSpan < endSpan && lastSpan[1].fPt == testPt) {
1873 ++lastSpan;
1874 }
1875 return *lastSpan;
1876}
1877
1878// with a loop, the comparison is move involved
1879// scan backwards and forwards to count all matching points
1880// (verify that there are twp scans marked as loops)
1881// compare that against 2 matching scans for loop plus other results
1882bool SkOpSegment::calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts) {
1883 const SkOpSpan& firstSpan = this->firstSpan(thisSpan); // rewind to the start
1884 const SkOpSpan& lastSpan = this->lastSpan(thisSpan); // find the end
1885 double firstLoopT = -1, lastLoopT = -1;
1886 const SkOpSpan* testSpan = &firstSpan - 1;
1887 while (++testSpan <= &lastSpan) {
1888 if (testSpan->fLoop) {
1889 firstLoopT = testSpan->fT;
1890 break;
1891 }
1892 }
1893 testSpan = &lastSpan + 1;
1894 while (--testSpan >= &firstSpan) {
1895 if (testSpan->fLoop) {
1896 lastLoopT = testSpan->fT;
1897 break;
1898 }
1899 }
1900 SkASSERT((firstLoopT == -1) == (lastLoopT == -1));
1901 if (firstLoopT == -1) {
1902 return false;
1903 }
1904 SkASSERT(firstLoopT < lastLoopT);
1905 testSpan = &firstSpan - 1;
1906 smallCounts[0] = smallCounts[1] = 0;
1907 while (++testSpan <= &lastSpan) {
1908 SkASSERT(approximately_equal(testSpan->fT, firstLoopT) +
1909 approximately_equal(testSpan->fT, lastLoopT) == 1);
1910 smallCounts[approximately_equal(testSpan->fT, lastLoopT)]++;
1911 }
1912 return true;
1913}
1914
caryclarkdac1d172014-06-17 05:15:38 -07001915double SkOpSegment::calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
1916 double hiEnd, const SkOpSegment* other, int thisStart) {
1917 if (max >= hiEnd) {
1918 return -1;
1919 }
1920 int end = findOtherT(hiEnd, ref);
1921 if (end < 0) {
1922 return -1;
1923 }
1924 double tHi = span(end).fT;
1925 double tLo, refLo;
1926 if (thisStart >= 0) {
1927 tLo = span(thisStart).fT;
1928 refLo = min;
1929 } else {
1930 int start1 = findOtherT(loEnd, ref);
1931 SkASSERT(start1 >= 0);
1932 tLo = span(start1).fT;
1933 refLo = loEnd;
1934 }
1935 double missingT = (max - refLo) / (hiEnd - refLo);
1936 missingT = tLo + missingT * (tHi - tLo);
1937 return missingT;
1938}
1939
1940double SkOpSegment::calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
1941 double hiEnd, const SkOpSegment* other, int thisEnd) {
1942 if (min <= loEnd) {
1943 return -1;
1944 }
1945 int start = findOtherT(loEnd, ref);
1946 if (start < 0) {
1947 return -1;
1948 }
1949 double tLo = span(start).fT;
1950 double tHi, refHi;
1951 if (thisEnd >= 0) {
1952 tHi = span(thisEnd).fT;
1953 refHi = max;
1954 } else {
1955 int end1 = findOtherT(hiEnd, ref);
1956 if (end1 < 0) {
1957 return -1;
1958 }
1959 tHi = span(end1).fT;
1960 refHi = hiEnd;
1961 }
1962 double missingT = (min - loEnd) / (refHi - loEnd);
1963 missingT = tLo + missingT * (tHi - tLo);
1964 return missingT;
1965}
1966
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001967// see if spans with two or more intersections have the same number on the other end
1968void SkOpSegment::checkDuplicates() {
1969 debugValidate();
1970 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
1971 int index;
1972 int endIndex = 0;
1973 bool endFound;
1974 do {
1975 index = endIndex;
1976 endIndex = nextExactSpan(index, 1);
1977 if ((endFound = endIndex < 0)) {
1978 endIndex = count();
1979 }
1980 int dupCount = endIndex - index;
1981 if (dupCount < 2) {
1982 continue;
1983 }
1984 do {
1985 const SkOpSpan* thisSpan = &fTs[index];
caryclarkdac1d172014-06-17 05:15:38 -07001986 if (thisSpan->fNear) {
1987 continue;
1988 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001989 SkOpSegment* other = thisSpan->fOther;
1990 int oIndex = thisSpan->fOtherIndex;
1991 int oStart = other->nextExactSpan(oIndex, -1) + 1;
1992 int oEnd = other->nextExactSpan(oIndex, 1);
1993 if (oEnd < 0) {
1994 oEnd = other->count();
1995 }
1996 int oCount = oEnd - oStart;
1997 // force the other to match its t and this pt if not on an end point
1998 if (oCount != dupCount) {
1999 MissingSpan& missing = missingSpans.push_back();
2000 missing.fOther = NULL;
2001 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
2002 missing.fPt = thisSpan->fPt;
2003 const SkOpSpan& oSpan = other->span(oIndex);
2004 if (oCount > dupCount) {
2005 missing.fSegment = this;
2006 missing.fT = thisSpan->fT;
2007 other->checkLinks(&oSpan, &missingSpans);
2008 } else {
2009 missing.fSegment = other;
2010 missing.fT = oSpan.fT;
2011 checkLinks(thisSpan, &missingSpans);
2012 }
2013 if (!missingSpans.back().fOther) {
2014 missingSpans.pop_back();
2015 }
2016 }
2017 } while (++index < endIndex);
2018 } while (!endFound);
2019 int missingCount = missingSpans.count();
2020 if (missingCount == 0) {
2021 return;
2022 }
2023 SkSTArray<kMissingSpanCount, MissingSpan, true> missingCoincidence;
2024 for (index = 0; index < missingCount; ++index) {
2025 MissingSpan& missing = missingSpans[index];
2026 SkOpSegment* missingOther = missing.fOther;
2027 if (missing.fSegment == missing.fOther) {
2028 continue;
2029 }
caryclarkdac1d172014-06-17 05:15:38 -07002030#if 0 // FIXME: this eliminates spurious data from skpwww_argus_presse_fr_41 but breaks
2031 // skpwww_fashionscandal_com_94 -- calcAngles complains, but I don't understand why
2032 if (missing.fSegment->containsT(missing.fT, missing.fOther, missing.fOtherT)) {
2033#if DEBUG_DUPLICATES
2034 SkDebugf("skip 1 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fSegment->fID,
2035 missing.fT, missing.fOther->fID, missing.fOtherT);
2036#endif
2037 continue;
2038 }
2039 if (missing.fOther->containsT(missing.fOtherT, missing.fSegment, missing.fT)) {
2040#if DEBUG_DUPLICATES
2041 SkDebugf("skip 2 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fOther->fID,
2042 missing.fOtherT, missing.fSegment->fID, missing.fT);
2043#endif
2044 continue;
2045 }
2046#endif
2047 // skip if adding would insert point into an existing coincindent span
2048 if (missing.fSegment->inCoincidentSpan(missing.fT, missingOther)
2049 && missingOther->inCoincidentSpan(missing.fOtherT, this)) {
2050 continue;
2051 }
2052 // skip if the created coincident spans are small
2053 if (missing.fSegment->coincidentSmall(missing.fPt, missing.fT, missingOther)
2054 && missingOther->coincidentSmall(missing.fPt, missing.fOtherT, missing.fSegment)) {
2055 continue;
2056 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002057 const SkOpSpan* added = missing.fSegment->addTPair(missing.fT, missingOther,
2058 missing.fOtherT, false, missing.fPt);
2059 if (added && added->fSmall) {
2060 missing.fSegment->checkSmallCoincidence(*added, &missingCoincidence);
2061 }
2062 }
2063 for (index = 0; index < missingCount; ++index) {
2064 MissingSpan& missing = missingSpans[index];
2065 missing.fSegment->fixOtherTIndex();
2066 missing.fOther->fixOtherTIndex();
2067 }
2068 for (index = 0; index < missingCoincidence.count(); ++index) {
2069 MissingSpan& missing = missingCoincidence[index];
2070 missing.fSegment->fixOtherTIndex();
2071 }
2072 debugValidate();
2073}
2074
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002075// look to see if the curve end intersects an intermediary that intersects the other
2076void SkOpSegment::checkEnds() {
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002077 debugValidate();
caryclark@google.com570863f2013-09-16 15:55:01 +00002078 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002079 int count = fTs.count();
2080 for (int index = 0; index < count; ++index) {
2081 const SkOpSpan& span = fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +00002082 double otherT = span.fOtherT;
2083 if (otherT != 0 && otherT != 1) { // only check ends
2084 continue;
2085 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002086 const SkOpSegment* other = span.fOther;
caryclark@google.com570863f2013-09-16 15:55:01 +00002087 // peek start/last describe the range of spans that match the other t of this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002088 int peekStart = span.fOtherIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00002089 while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT)
2090 ;
2091 int otherCount = other->fTs.count();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002092 int peekLast = span.fOtherIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00002093 while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT)
2094 ;
2095 if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002096 continue;
2097 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002098 // t start/last describe the range of spans that match the t of this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002099 double t = span.fT;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002100 double tBottom = -1;
2101 int tStart = -1;
2102 int tLast = count;
2103 bool lastSmall = false;
2104 double afterT = t;
2105 for (int inner = 0; inner < count; ++inner) {
2106 double innerT = fTs[inner].fT;
2107 if (innerT <= t && innerT > tBottom) {
2108 if (innerT < t || !lastSmall) {
2109 tStart = inner - 1;
2110 }
2111 tBottom = innerT;
2112 }
2113 if (innerT > afterT) {
2114 if (t == afterT && lastSmall) {
2115 afterT = innerT;
2116 } else {
2117 tLast = inner;
2118 break;
2119 }
2120 }
2121 lastSmall = innerT <= t ? fTs[inner].fSmall : false;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002122 }
2123 for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) {
caryclark@google.com570863f2013-09-16 15:55:01 +00002124 if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002125 continue;
2126 }
2127 const SkOpSpan& peekSpan = other->fTs[peekIndex];
2128 SkOpSegment* match = peekSpan.fOther;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002129 if (match->done()) {
2130 continue; // if the edge has already been eaten (likely coincidence), ignore it
2131 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002132 const double matchT = peekSpan.fOtherT;
caryclark@google.com570863f2013-09-16 15:55:01 +00002133 // see if any of the spans match the other spans
2134 for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002135 const SkOpSpan& tSpan = fTs[tIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +00002136 if (tSpan.fOther == match) {
2137 if (tSpan.fOtherT == matchT) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002138 goto nextPeekIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00002139 }
2140 double midT = (tSpan.fOtherT + matchT) / 2;
2141 if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002142 goto nextPeekIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00002143 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002144 }
2145 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002146 if (missingSpans.count() > 0) {
2147 const MissingSpan& lastMissing = missingSpans.back();
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00002148 if (lastMissing.fT == t
caryclark@google.com570863f2013-09-16 15:55:01 +00002149 && lastMissing.fOther == match
2150 && lastMissing.fOtherT == matchT) {
2151 SkASSERT(lastMissing.fPt == peekSpan.fPt);
2152 continue;
2153 }
2154 }
2155#if DEBUG_CHECK_ENDS
2156 SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n",
2157 __FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY);
2158#endif
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002159 // this segment is missing a entry that the other contains
2160 // remember so we can add the missing one and recompute the indices
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002161 {
2162 MissingSpan& missing = missingSpans.push_back();
2163 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002164 missing.fT = t;
2165 missing.fOther = match;
2166 missing.fOtherT = matchT;
2167 missing.fPt = peekSpan.fPt;
2168 }
2169 break;
2170nextPeekIndex:
2171 ;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002172 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002173 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002174 if (missingSpans.count() == 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002175 debugValidate();
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002176 return;
2177 }
2178 debugValidate();
caryclark@google.com570863f2013-09-16 15:55:01 +00002179 int missingCount = missingSpans.count();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002180 for (int index = 0; index < missingCount; ++index) {
caryclark@google.com570863f2013-09-16 15:55:01 +00002181 MissingSpan& missing = missingSpans[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002182 if (this != missing.fOther) {
2183 addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
2184 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002185 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002186 fixOtherTIndex();
caryclark@google.com570863f2013-09-16 15:55:01 +00002187 // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
2188 // avoid this
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002189 for (int index = 0; index < missingCount; ++index) {
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00002190 missingSpans[index].fOther->fixOtherTIndex();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002191 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002192 debugValidate();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002193}
2194
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002195void SkOpSegment::checkLinks(const SkOpSpan* base,
2196 SkTArray<MissingSpan, true>* missingSpans) const {
2197 const SkOpSpan* first = fTs.begin();
2198 const SkOpSpan* last = fTs.end() - 1;
2199 SkASSERT(base >= first && last >= base);
2200 const SkOpSegment* other = base->fOther;
2201 const SkOpSpan* oFirst = other->fTs.begin();
2202 const SkOpSpan* oLast = other->fTs.end() - 1;
2203 const SkOpSpan* oSpan = &other->fTs[base->fOtherIndex];
2204 const SkOpSpan* test = base;
2205 const SkOpSpan* missing = NULL;
2206 while (test > first && (--test)->fPt == base->fPt) {
2207 CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
2208 }
2209 test = base;
2210 while (test < last && (++test)->fPt == base->fPt) {
2211 CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
2212 }
2213}
2214
2215// see if spans with two or more intersections all agree on common t and point values
2216void SkOpSegment::checkMultiples() {
2217 debugValidate();
2218 int index;
2219 int end = 0;
2220 while (fTs[++end].fT == 0)
2221 ;
2222 while (fTs[end].fT < 1) {
2223 int start = index = end;
2224 end = nextExactSpan(index, 1);
2225 if (end <= index) {
2226 return; // buffer overflow example triggers this
2227 }
2228 if (index + 1 == end) {
2229 continue;
2230 }
2231 // force the duplicates to agree on t and pt if not on the end
caryclarkdac1d172014-06-17 05:15:38 -07002232 SkOpSpan& span = fTs[index];
2233 double thisT = span.fT;
2234 const SkPoint& thisPt = span.fPt;
2235 span.fMultiple = true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002236 bool aligned = false;
2237 while (++index < end) {
2238 aligned |= alignSpan(index, thisT, thisPt);
2239 }
2240 if (aligned) {
2241 alignSpanState(start, end);
2242 }
caryclarkdac1d172014-06-17 05:15:38 -07002243 fMultiples = true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002244 }
2245 debugValidate();
2246}
2247
2248void SkOpSegment::CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
2249 const SkOpSpan* oFirst, const SkOpSpan* oLast, const SkOpSpan** missingPtr,
2250 SkTArray<MissingSpan, true>* missingSpans) {
2251 SkASSERT(oSpan->fPt == test->fPt);
2252 const SkOpSpan* oTest = oSpan;
2253 while (oTest > oFirst && (--oTest)->fPt == test->fPt) {
2254 if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
2255 return;
2256 }
2257 }
2258 oTest = oSpan;
2259 while (oTest < oLast && (++oTest)->fPt == test->fPt) {
2260 if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
2261 return;
2262 }
2263 }
2264 if (*missingPtr) {
2265 missingSpans->push_back();
2266 }
2267 MissingSpan& lastMissing = missingSpans->back();
2268 if (*missingPtr) {
2269 lastMissing = missingSpans->end()[-2];
2270 }
2271 *missingPtr = test;
2272 lastMissing.fOther = test->fOther;
2273 lastMissing.fOtherT = test->fOtherT;
2274}
2275
caryclark@google.com570863f2013-09-16 15:55:01 +00002276bool SkOpSegment::checkSmall(int index) const {
2277 if (fTs[index].fSmall) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002278 return true;
2279 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002280 double tBase = fTs[index].fT;
2281 while (index > 0 && precisely_negative(tBase - fTs[--index].fT))
2282 ;
2283 return fTs[index].fSmall;
2284}
2285
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002286// a pair of curves may turn into coincident lines -- small may be a hint that that happened
2287// if a cubic contains a loop, the counts must be adjusted
2288void SkOpSegment::checkSmall() {
2289 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
2290 const SkOpSpan* beginSpan = fTs.begin();
2291 const SkOpSpan* thisSpan = beginSpan - 1;
2292 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
2293 while (++thisSpan < endSpan) {
2294 if (!thisSpan->fSmall) {
2295 continue;
2296 }
2297 if (!thisSpan->fWindValue) {
2298 continue;
2299 }
2300 const SkOpSpan& firstSpan = this->firstSpan(*thisSpan);
2301 const SkOpSpan& lastSpan = this->lastSpan(*thisSpan);
caryclark5e27e0e2014-08-12 07:46:33 -07002302 const SkOpSpan* nextSpan = &firstSpan + 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002303 ptrdiff_t smallCount = &lastSpan - &firstSpan + 1;
2304 SkASSERT(1 <= smallCount && smallCount < count());
caryclark5e27e0e2014-08-12 07:46:33 -07002305 if (smallCount <= 1 && !nextSpan->fSmall) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002306 SkASSERT(1 == smallCount);
2307 checkSmallCoincidence(firstSpan, NULL);
2308 continue;
2309 }
2310 // at this point, check for missing computed intersections
2311 const SkPoint& testPt = firstSpan.fPt;
2312 thisSpan = &firstSpan - 1;
2313 SkOpSegment* other = NULL;
2314 while (++thisSpan <= &lastSpan) {
2315 other = thisSpan->fOther;
2316 if (other != this) {
2317 break;
2318 }
2319 }
2320 SkASSERT(other != this);
2321 int oIndex = thisSpan->fOtherIndex;
2322 const SkOpSpan& oSpan = other->span(oIndex);
2323 const SkOpSpan& oFirstSpan = other->firstSpan(oSpan);
2324 const SkOpSpan& oLastSpan = other->lastSpan(oSpan);
2325 ptrdiff_t oCount = &oLastSpan - &oFirstSpan + 1;
2326 if (fLoop) {
2327 int smallCounts[2];
2328 SkASSERT(!other->fLoop); // FIXME: we need more complicated logic for pair of loops
2329 if (calcLoopSpanCount(*thisSpan, smallCounts)) {
2330 if (smallCounts[0] && oCount != smallCounts[0]) {
2331 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2332 }
2333 if (smallCounts[1] && oCount != smallCounts[1]) {
2334 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2335 }
2336 goto nextSmallCheck;
2337 }
2338 }
2339 if (other->fLoop) {
2340 int otherCounts[2];
2341 if (other->calcLoopSpanCount(other->span(oIndex), otherCounts)) {
2342 if (otherCounts[0] && otherCounts[0] != smallCount) {
2343 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2344 }
2345 if (otherCounts[1] && otherCounts[1] != smallCount) {
2346 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2347 }
2348 goto nextSmallCheck;
2349 }
2350 }
2351 if (oCount != smallCount) { // check if number of pts in this match other
2352 MissingSpan& missing = missingSpans.push_back();
2353 missing.fOther = NULL;
2354 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
2355 missing.fPt = testPt;
2356 const SkOpSpan& oSpan = other->span(oIndex);
2357 if (oCount > smallCount) {
2358 missing.fSegment = this;
2359 missing.fT = thisSpan->fT;
2360 other->checkLinks(&oSpan, &missingSpans);
2361 } else {
2362 missing.fSegment = other;
2363 missing.fT = oSpan.fT;
2364 checkLinks(thisSpan, &missingSpans);
2365 }
2366 if (!missingSpans.back().fOther || missing.fSegment->done()) {
2367 missingSpans.pop_back();
2368 }
2369 }
2370nextSmallCheck:
2371 thisSpan = &lastSpan;
2372 }
2373 int missingCount = missingSpans.count();
2374 for (int index = 0; index < missingCount; ++index) {
2375 MissingSpan& missing = missingSpans[index];
2376 SkOpSegment* missingOther = missing.fOther;
2377 // note that add t pair may edit span arrays, so prior pointers to spans are no longer valid
2378 if (!missing.fSegment->addTPair(missing.fT, missingOther, missing.fOtherT, false,
2379 missing.fPt)) {
2380 continue;
2381 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002382 int otherTIndex = missingOther->findT(missing.fOtherT, missing.fPt, missing.fSegment);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002383 const SkOpSpan& otherSpan = missingOther->span(otherTIndex);
2384 if (otherSpan.fSmall) {
2385 const SkOpSpan* nextSpan = &otherSpan;
2386 do {
2387 ++nextSpan;
2388 } while (nextSpan->fSmall);
2389 missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt, nextSpan->fT,
2390 missingOther);
2391 } else if (otherSpan.fT > 0) {
2392 const SkOpSpan* priorSpan = &otherSpan;
2393 do {
2394 --priorSpan;
2395 } while (priorSpan->fT == otherSpan.fT);
2396 if (priorSpan->fSmall) {
2397 missing.fSegment->addTCancel(missing.fPt, priorSpan->fPt, missingOther);
2398 }
2399 }
2400 }
2401 // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
2402 // avoid this
2403 for (int index = 0; index < missingCount; ++index) {
2404 MissingSpan& missing = missingSpans[index];
2405 missing.fSegment->fixOtherTIndex();
2406 missing.fOther->fixOtherTIndex();
2407 }
2408 debugValidate();
2409}
2410
2411void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span,
2412 SkTArray<MissingSpan, true>* checkMultiple) {
2413 SkASSERT(span.fSmall);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002414 if (0 && !span.fWindValue) {
2415 return;
2416 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002417 SkASSERT(&span < fTs.end() - 1);
2418 const SkOpSpan* next = &span + 1;
2419 SkASSERT(!next->fSmall || checkMultiple);
2420 if (checkMultiple) {
2421 while (next->fSmall) {
2422 ++next;
2423 SkASSERT(next < fTs.end());
2424 }
2425 }
2426 SkOpSegment* other = span.fOther;
2427 while (other != next->fOther) {
2428 if (!checkMultiple) {
2429 return;
2430 }
2431 const SkOpSpan* test = next + 1;
2432 if (test == fTs.end()) {
2433 return;
2434 }
2435 if (test->fPt != next->fPt || !precisely_equal(test->fT, next->fT)) {
2436 return;
2437 }
2438 next = test;
2439 }
2440 SkASSERT(span.fT < next->fT);
2441 int oStartIndex = other->findExactT(span.fOtherT, this);
2442 int oEndIndex = other->findExactT(next->fOtherT, this);
2443 // FIXME: be overly conservative by limiting this to the caller that allows multiple smalls
2444 if (!checkMultiple || fVerb != SkPath::kLine_Verb || other->fVerb != SkPath::kLine_Verb) {
2445 SkPoint mid = ptAtT((span.fT + next->fT) / 2);
2446 const SkOpSpan& oSpanStart = other->fTs[oStartIndex];
2447 const SkOpSpan& oSpanEnd = other->fTs[oEndIndex];
2448 SkPoint oMid = other->ptAtT((oSpanStart.fT + oSpanEnd.fT) / 2);
2449 if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
2450 return;
2451 }
2452 }
2453 // FIXME: again, be overly conservative to avoid breaking existing tests
2454 const SkOpSpan& oSpan = oStartIndex < oEndIndex ? other->fTs[oStartIndex]
2455 : other->fTs[oEndIndex];
2456 if (checkMultiple && !oSpan.fSmall) {
2457 return;
2458 }
2459 SkASSERT(oSpan.fSmall);
2460 if (oStartIndex < oEndIndex) {
2461 addTCoincident(span.fPt, next->fPt, next->fT, other);
2462 } else {
2463 addTCancel(span.fPt, next->fPt, other);
2464 }
2465 if (!checkMultiple) {
2466 return;
2467 }
2468 // check to see if either segment is coincident with a third segment -- if it is, and if
2469 // the opposite segment is not already coincident with the third, make it so
2470 // OPTIMIZE: to make this check easier, add coincident and cancel could set a coincident bit
2471 if (span.fWindValue != 1 || span.fOppValue != 0) {
2472// start here;
2473 // iterate through the spans, looking for the third coincident case
2474 // if we find one, we need to return state to the caller so that the indices can be fixed
2475 // this also suggests that all of this function is fragile since it relies on a valid index
2476 }
2477 // probably should make this a common function rather than copy/paste code
2478 if (oSpan.fWindValue != 1 || oSpan.fOppValue != 0) {
2479 const SkOpSpan* oTest = &oSpan;
2480 while (--oTest >= other->fTs.begin()) {
2481 if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
2482 break;
2483 }
2484 SkOpSegment* testOther = oTest->fOther;
2485 SkASSERT(testOther != this);
2486 // look in both directions to see if there is a coincident span
2487 const SkOpSpan* tTest = testOther->fTs.begin();
2488 for (int testIndex = 0; testIndex < testOther->count(); ++testIndex) {
2489 if (tTest->fPt != span.fPt) {
2490 ++tTest;
2491 continue;
2492 }
2493 if (testOther->verb() != SkPath::kLine_Verb
2494 || other->verb() != SkPath::kLine_Verb) {
2495 SkPoint mid = ptAtT((span.fT + next->fT) / 2);
2496 SkPoint oMid = other->ptAtT((oTest->fOtherT + tTest->fT) / 2);
2497 if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
2498 continue;
2499 }
skia.committer@gmail.coma1ed7ae2014-04-15 03:04:18 +00002500 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002501#if DEBUG_CONCIDENT
2502 SkDebugf("%s coincident found=%d %1.9g %1.9g\n", __FUNCTION__, testOther->fID,
2503 oTest->fOtherT, tTest->fT);
2504#endif
2505 if (tTest->fT < oTest->fOtherT) {
2506 addTCoincident(span.fPt, next->fPt, next->fT, testOther);
2507 } else {
2508 addTCancel(span.fPt, next->fPt, testOther);
2509 }
2510 MissingSpan missing;
2511 missing.fSegment = testOther;
2512 checkMultiple->push_back(missing);
2513 break;
2514 }
2515 }
2516 oTest = &oSpan;
2517 while (++oTest < other->fTs.end()) {
2518 if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
2519 break;
2520 }
2521
2522 }
2523 }
2524}
2525
caryclark@google.com570863f2013-09-16 15:55:01 +00002526// if pair of spans on either side of tiny have the same end point and mid point, mark
2527// them as parallel
caryclark@google.com570863f2013-09-16 15:55:01 +00002528void SkOpSegment::checkTiny() {
2529 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
2530 SkOpSpan* thisSpan = fTs.begin() - 1;
2531 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be tiny
2532 while (++thisSpan < endSpan) {
2533 if (!thisSpan->fTiny) {
2534 continue;
2535 }
2536 SkOpSpan* nextSpan = thisSpan + 1;
2537 double thisT = thisSpan->fT;
2538 double nextT = nextSpan->fT;
2539 if (thisT == nextT) {
2540 continue;
2541 }
2542 SkASSERT(thisT < nextT);
2543 SkASSERT(thisSpan->fPt == nextSpan->fPt);
2544 SkOpSegment* thisOther = thisSpan->fOther;
2545 SkOpSegment* nextOther = nextSpan->fOther;
2546 int oIndex = thisSpan->fOtherIndex;
2547 for (int oStep = -1; oStep <= 1; oStep += 2) {
2548 int oEnd = thisOther->nextExactSpan(oIndex, oStep);
2549 if (oEnd < 0) {
2550 continue;
2551 }
2552 const SkOpSpan& oSpan = thisOther->span(oEnd);
2553 int nIndex = nextSpan->fOtherIndex;
2554 for (int nStep = -1; nStep <= 1; nStep += 2) {
2555 int nEnd = nextOther->nextExactSpan(nIndex, nStep);
2556 if (nEnd < 0) {
2557 continue;
2558 }
2559 const SkOpSpan& nSpan = nextOther->span(nEnd);
2560 if (oSpan.fPt != nSpan.fPt) {
2561 continue;
2562 }
2563 double oMidT = (thisSpan->fOtherT + oSpan.fT) / 2;
2564 const SkPoint& oPt = thisOther->ptAtT(oMidT);
2565 double nMidT = (nextSpan->fOtherT + nSpan.fT) / 2;
2566 const SkPoint& nPt = nextOther->ptAtT(nMidT);
2567 if (!AlmostEqualUlps(oPt, nPt)) {
2568 continue;
2569 }
2570#if DEBUG_CHECK_TINY
2571 SkDebugf("%s [%d] add coincidence [%d] [%d]\n", __FUNCTION__, fID,
2572 thisOther->fID, nextOther->fID);
2573#endif
2574 // this segment is missing a entry that the other contains
2575 // remember so we can add the missing one and recompute the indices
2576 MissingSpan& missing = missingSpans.push_back();
2577 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
caryclark@google.com570863f2013-09-16 15:55:01 +00002578 missing.fSegment = thisOther;
2579 missing.fT = thisSpan->fOtherT;
2580 missing.fOther = nextOther;
2581 missing.fOtherT = nextSpan->fOtherT;
2582 missing.fPt = thisSpan->fPt;
2583 }
2584 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002585 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002586 int missingCount = missingSpans.count();
2587 if (!missingCount) {
2588 return;
2589 }
2590 for (int index = 0; index < missingCount; ++index) {
2591 MissingSpan& missing = missingSpans[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002592 if (missing.fSegment != missing.fOther) {
2593 missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false,
2594 missing.fPt);
2595 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002596 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002597 // OPTIMIZE: consolidate to avoid multiple calls to fix index
caryclark@google.com570863f2013-09-16 15:55:01 +00002598 for (int index = 0; index < missingCount; ++index) {
2599 MissingSpan& missing = missingSpans[index];
2600 missing.fSegment->fixOtherTIndex();
2601 missing.fOther->fixOtherTIndex();
2602 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002603}
2604
caryclarkdac1d172014-06-17 05:15:38 -07002605bool SkOpSegment::coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const {
2606 int count = this->count();
2607 for (int index = 0; index < count; ++index) {
2608 const SkOpSpan& span = this->span(index);
2609 if (span.fOther != other) {
2610 continue;
2611 }
2612 if (span.fPt == pt) {
2613 continue;
2614 }
2615 if (!AlmostEqualUlps(span.fPt, pt)) {
2616 continue;
2617 }
2618 if (fVerb != SkPath::kCubic_Verb) {
2619 return true;
2620 }
2621 double tInterval = t - span.fT;
2622 double tMid = t - tInterval / 2;
2623 SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
2624 return midPt.approximatelyEqual(xyAtT(t));
2625 }
2626 return false;
2627}
2628
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002629bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart,
2630 int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const {
2631 SkASSERT(span->fT == 0 || span->fT == 1);
2632 SkASSERT(span->fOtherT == 0 || span->fOtherT == 1);
2633 const SkOpSpan* otherSpan = &other->span(oEnd);
2634 double refT = otherSpan->fT;
2635 const SkPoint& refPt = otherSpan->fPt;
2636 const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0);
2637 do {
2638 const SkOpSegment* match = span->fOther;
2639 if (match == otherSpan->fOther) {
2640 // find start of respective spans and see if both have winding
2641 int startIndex, endIndex;
2642 if (span->fOtherT == 1) {
2643 endIndex = span->fOtherIndex;
2644 startIndex = match->nextExactSpan(endIndex, -1);
2645 } else {
2646 startIndex = span->fOtherIndex;
2647 endIndex = match->nextExactSpan(startIndex, 1);
2648 }
2649 const SkOpSpan& startSpan = match->span(startIndex);
2650 if (startSpan.fWindValue != 0) {
2651 // draw ray from endSpan.fPt perpendicular to end tangent and measure distance
2652 // to other segment.
2653 const SkOpSpan& endSpan = match->span(endIndex);
2654 SkDLine ray;
2655 SkVector dxdy;
2656 if (span->fOtherT == 1) {
2657 ray.fPts[0].set(startSpan.fPt);
2658 dxdy = match->dxdy(startIndex);
2659 } else {
2660 ray.fPts[0].set(endSpan.fPt);
2661 dxdy = match->dxdy(endIndex);
2662 }
2663 ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY;
2664 ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX;
2665 SkIntersections i;
2666 int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray);
2667 for (int index = 0; index < roots; ++index) {
2668 if (ray.fPts[0].approximatelyEqual(i.pt(index))) {
2669 double matchMidT = (match->span(startIndex).fT
2670 + match->span(endIndex).fT) / 2;
2671 SkPoint matchMidPt = match->ptAtT(matchMidT);
2672 double otherMidT = (i[0][index] + other->span(oStart).fT) / 2;
2673 SkPoint otherMidPt = other->ptAtT(otherMidT);
2674 if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) {
2675 *startPt = startSpan.fPt;
2676 *endPt = endSpan.fPt;
2677 *endT = endSpan.fT;
2678 return true;
2679 }
2680 }
2681 }
2682 }
2683 return false;
2684 }
2685 if (otherSpan == lastSpan) {
2686 break;
2687 }
2688 otherSpan += step;
2689 } while (otherSpan->fT == refT || otherSpan->fPt == refPt);
2690 return false;
2691}
2692
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002693int SkOpSegment::findEndSpan(int endIndex) const {
2694 const SkOpSpan* span = &fTs[--endIndex];
2695 const SkPoint& lastPt = span->fPt;
2696 double endT = span->fT;
2697 do {
2698 span = &fTs[--endIndex];
2699 } while (SkDPoint::ApproximatelyEqual(span->fPt, lastPt) && (span->fT == endT || span->fTiny));
2700 return endIndex + 1;
2701}
2702
caryclark@google.com07393ca2013-04-08 11:47:37 +00002703/*
2704 The M and S variable name parts stand for the operators.
2705 Mi stands for Minuend (see wiki subtraction, analogous to difference)
2706 Su stands for Subtrahend
2707 The Opp variable name part designates that the value is for the Opposite operator.
2708 Opposite values result from combining coincident spans.
2709 */
2710SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
2711 bool* unsortable, SkPathOp op, const int xorMiMask,
2712 const int xorSuMask) {
2713 const int startIndex = *nextStart;
2714 const int endIndex = *nextEnd;
2715 SkASSERT(startIndex != endIndex);
2716 SkDEBUGCODE(const int count = fTs.count());
2717 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
caryclarkdac1d172014-06-17 05:15:38 -07002718 int step = SkSign32(endIndex - startIndex);
2719 *nextStart = startIndex;
2720 SkOpSegment* other = isSimple(nextStart, &step);
2721 if (other)
2722 {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002723 // mark the smaller of startIndex, endIndex done, and all adjacent
2724 // spans with the same T value (but not 'other' spans)
2725#if DEBUG_WINDING
2726 SkDebugf("%s simple\n", __FUNCTION__);
2727#endif
2728 int min = SkMin32(startIndex, endIndex);
2729 if (fTs[min].fDone) {
2730 return NULL;
2731 }
2732 markDoneBinary(min);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002733 double startT = other->fTs[*nextStart].fT;
2734 *nextEnd = *nextStart;
2735 do {
2736 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00002737 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002738 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002739 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +00002740 *unsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002741 return NULL;
2742 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002743 return other;
2744 }
caryclarkdac1d172014-06-17 05:15:38 -07002745 const int end = nextExactSpan(startIndex, step);
2746 SkASSERT(end >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002747 SkASSERT(startIndex - endIndex != 0);
2748 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002749 // more than one viable candidate -- measure angles to find best
2750
2751 int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp);
caryclark@google.com570863f2013-09-16 15:55:01 +00002752 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002753 if (!sortable) {
2754 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002755 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002756 return NULL;
2757 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002758 SkOpAngle* angle = spanToAngle(end, startIndex);
2759 if (angle->unorderable()) {
2760 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002761 markDoneBinary(SkMin32(startIndex, endIndex));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002762 return NULL;
2763 }
2764#if DEBUG_SORT
2765 SkDebugf("%s\n", __FUNCTION__);
2766 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002767#endif
2768 int sumMiWinding = updateWinding(endIndex, startIndex);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002769 if (sumMiWinding == SK_MinS32) {
2770 *unsortable = true;
2771 markDoneBinary(SkMin32(startIndex, endIndex));
2772 return NULL;
2773 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002774 int sumSuWinding = updateOppWinding(endIndex, startIndex);
2775 if (operand()) {
2776 SkTSwap<int>(sumMiWinding, sumSuWinding);
2777 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002778 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002779 const SkOpAngle* foundAngle = NULL;
2780 bool foundDone = false;
2781 // iterate through the angle, and compute everyone's winding
2782 SkOpSegment* nextSegment;
2783 int activeCount = 0;
2784 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002785 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002786 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002787 nextAngle->end(), op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002788 if (activeAngle) {
2789 ++activeCount;
2790 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002791 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002792 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002793 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002794 return NULL;
2795 }
2796 foundAngle = nextAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +00002797 foundDone = nextSegment->done(nextAngle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002798 }
2799 }
2800 if (nextSegment->done()) {
2801 continue;
2802 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002803 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002804 continue;
2805 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002806 if (!activeAngle) {
caryclarkdac1d172014-06-17 05:15:38 -07002807 (void) nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
caryclark@google.com570863f2013-09-16 15:55:01 +00002808 }
2809 SkOpSpan* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002810 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002811 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002812 *chase->append() = last;
2813#if DEBUG_WINDING
caryclark@google.com570863f2013-09-16 15:55:01 +00002814 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
2815 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
2816 last->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002817#endif
2818 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002819 } while ((nextAngle = nextAngle->next()) != angle);
2820#if DEBUG_ANGLE
2821 if (foundAngle) {
2822 foundAngle->debugSameAs(foundAngle);
2823 }
2824#endif
2825
caryclark@google.com07393ca2013-04-08 11:47:37 +00002826 markDoneBinary(SkMin32(startIndex, endIndex));
2827 if (!foundAngle) {
2828 return NULL;
2829 }
2830 *nextStart = foundAngle->start();
2831 *nextEnd = foundAngle->end();
2832 nextSegment = foundAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002833#if DEBUG_WINDING
2834 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2835 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
2836 #endif
2837 return nextSegment;
2838}
2839
2840SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart,
2841 int* nextEnd, bool* unsortable) {
2842 const int startIndex = *nextStart;
2843 const int endIndex = *nextEnd;
2844 SkASSERT(startIndex != endIndex);
2845 SkDEBUGCODE(const int count = fTs.count());
2846 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
caryclarkdac1d172014-06-17 05:15:38 -07002847 int step = SkSign32(endIndex - startIndex);
2848 *nextStart = startIndex;
2849 SkOpSegment* other = isSimple(nextStart, &step);
2850 if (other)
2851 {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002852 // mark the smaller of startIndex, endIndex done, and all adjacent
2853 // spans with the same T value (but not 'other' spans)
2854#if DEBUG_WINDING
2855 SkDebugf("%s simple\n", __FUNCTION__);
2856#endif
2857 int min = SkMin32(startIndex, endIndex);
2858 if (fTs[min].fDone) {
2859 return NULL;
2860 }
2861 markDoneUnary(min);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002862 double startT = other->fTs[*nextStart].fT;
2863 *nextEnd = *nextStart;
2864 do {
2865 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00002866 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002867 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
caryclark@google.com570863f2013-09-16 15:55:01 +00002868 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
2869 *unsortable = true;
2870 return NULL;
2871 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002872 return other;
2873 }
caryclarkdac1d172014-06-17 05:15:38 -07002874 const int end = nextExactSpan(startIndex, step);
2875 SkASSERT(end >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002876 SkASSERT(startIndex - endIndex != 0);
2877 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002878 // more than one viable candidate -- measure angles to find best
2879
2880 int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00002881 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002882 if (!sortable) {
2883 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002884 markDoneUnary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002885 return NULL;
2886 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002887 SkOpAngle* angle = spanToAngle(end, startIndex);
2888#if DEBUG_SORT
2889 SkDebugf("%s\n", __FUNCTION__);
2890 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002891#endif
2892 int sumWinding = updateWinding(endIndex, startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002893 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002894 const SkOpAngle* foundAngle = NULL;
2895 bool foundDone = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002896 SkOpSegment* nextSegment;
2897 int activeCount = 0;
2898 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002899 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002900 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002901 &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002902 if (activeAngle) {
2903 ++activeCount;
2904 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002905 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002906 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002907 markDoneUnary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002908 return NULL;
2909 }
2910 foundAngle = nextAngle;
2911 foundDone = nextSegment->done(nextAngle);
2912 }
2913 }
2914 if (nextSegment->done()) {
2915 continue;
2916 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002917 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002918 continue;
2919 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002920 if (!activeAngle) {
2921 nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
2922 }
2923 SkOpSpan* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002924 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002925 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002926 *chase->append() = last;
2927#if DEBUG_WINDING
caryclark@google.com570863f2013-09-16 15:55:01 +00002928 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
2929 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
2930 last->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002931#endif
2932 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002933 } while ((nextAngle = nextAngle->next()) != angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002934 markDoneUnary(SkMin32(startIndex, endIndex));
2935 if (!foundAngle) {
2936 return NULL;
2937 }
2938 *nextStart = foundAngle->start();
2939 *nextEnd = foundAngle->end();
2940 nextSegment = foundAngle->segment();
2941#if DEBUG_WINDING
2942 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2943 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
2944 #endif
2945 return nextSegment;
2946}
2947
2948SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsortable) {
2949 const int startIndex = *nextStart;
2950 const int endIndex = *nextEnd;
2951 SkASSERT(startIndex != endIndex);
2952 SkDEBUGCODE(int count = fTs.count());
caryclark@google.com570863f2013-09-16 15:55:01 +00002953 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002954 int step = SkSign32(endIndex - startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002955// Detect cases where all the ends canceled out (e.g.,
caryclarkdac1d172014-06-17 05:15:38 -07002956// there is no angle) and therefore there's only one valid connection
2957 *nextStart = startIndex;
2958 SkOpSegment* other = isSimple(nextStart, &step);
2959 if (other)
2960 {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002961#if DEBUG_WINDING
2962 SkDebugf("%s simple\n", __FUNCTION__);
2963#endif
2964 int min = SkMin32(startIndex, endIndex);
2965 if (fTs[min].fDone) {
2966 return NULL;
2967 }
2968 markDone(min, 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002969 double startT = other->fTs[*nextStart].fT;
2970 // FIXME: I don't know why the logic here is difference from the winding case
2971 SkDEBUGCODE(bool firstLoop = true;)
2972 if ((approximately_less_than_zero(startT) && step < 0)
2973 || (approximately_greater_than_one(startT) && step > 0)) {
2974 step = -step;
2975 SkDEBUGCODE(firstLoop = false;)
2976 }
2977 do {
2978 *nextEnd = *nextStart;
2979 do {
2980 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00002981 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002982 if (other->fTs[SkMin32(*nextStart, *nextEnd)].fWindValue) {
2983 break;
2984 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002985 SkASSERT(firstLoop);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002986 SkDEBUGCODE(firstLoop = false;)
2987 step = -step;
2988 } while (true);
2989 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
2990 return other;
2991 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002992 SkASSERT(startIndex - endIndex != 0);
2993 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002994 // parallel block above with presorted version
caryclarkdac1d172014-06-17 05:15:38 -07002995 int end = nextExactSpan(startIndex, step);
2996 SkASSERT(end >= 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002997 SkOpAngle* angle = spanToAngle(end, startIndex);
2998 SkASSERT(angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002999#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003000 SkDebugf("%s\n", __FUNCTION__);
3001 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003002#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003003 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003004 const SkOpAngle* foundAngle = NULL;
3005 bool foundDone = false;
3006 SkOpSegment* nextSegment;
3007 int activeCount = 0;
3008 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003009 nextSegment = nextAngle->segment();
3010 ++activeCount;
3011 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003012 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003013 *unsortable = true;
3014 return NULL;
3015 }
3016 foundAngle = nextAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003017 if (!(foundDone = nextSegment->done(nextAngle))) {
3018 break;
3019 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003020 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003021 nextAngle = nextAngle->next();
3022 } while (nextAngle != angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003023 markDone(SkMin32(startIndex, endIndex), 1);
3024 if (!foundAngle) {
3025 return NULL;
3026 }
3027 *nextStart = foundAngle->start();
3028 *nextEnd = foundAngle->end();
3029 nextSegment = foundAngle->segment();
3030#if DEBUG_WINDING
3031 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
3032 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
3033 #endif
3034 return nextSegment;
3035}
3036
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003037int SkOpSegment::findStartSpan(int startIndex) const {
3038 int index = startIndex;
3039 const SkOpSpan* span = &fTs[index];
3040 const SkPoint& firstPt = span->fPt;
3041 double firstT = span->fT;
caryclarkdac1d172014-06-17 05:15:38 -07003042 const SkOpSpan* prior;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003043 do {
caryclarkdac1d172014-06-17 05:15:38 -07003044 prior = span;
3045 span = &fTs[++index];
3046 } while (SkDPoint::ApproximatelyEqual(span->fPt, firstPt)
3047 && (span->fT == firstT || prior->fTiny));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003048 return index;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003049}
3050
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003051int SkOpSegment::findExactT(double t, const SkOpSegment* match) const {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003052 int count = this->count();
3053 for (int index = 0; index < count; ++index) {
3054 const SkOpSpan& span = fTs[index];
3055 if (span.fT == t && span.fOther == match) {
3056 return index;
3057 }
3058 }
3059 SkASSERT(0);
3060 return -1;
3061}
3062
caryclarkdac1d172014-06-17 05:15:38 -07003063int SkOpSegment::findOtherT(double t, const SkOpSegment* match) const {
3064 int count = this->count();
3065 for (int index = 0; index < count; ++index) {
3066 const SkOpSpan& span = fTs[index];
3067 if (span.fOtherT == t && span.fOther == match) {
3068 return index;
3069 }
3070 }
3071 return -1;
3072}
3073
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003074int SkOpSegment::findT(double t, const SkPoint& pt, const SkOpSegment* match) const {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003075 int count = this->count();
caryclark5e27e0e2014-08-12 07:46:33 -07003076 // prefer exact matches over approximate matches
3077 for (int index = 0; index < count; ++index) {
3078 const SkOpSpan& span = fTs[index];
3079 if (span.fT == t && span.fOther == match) {
3080 return index;
3081 }
3082 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003083 for (int index = 0; index < count; ++index) {
3084 const SkOpSpan& span = fTs[index];
3085 if (approximately_equal_orderable(span.fT, t) && span.fOther == match) {
3086 return index;
3087 }
3088 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003089 // Usually, the pair of ts are an exact match. It's possible that the t values have
3090 // been adjusted to make multiple intersections align. In this rare case, look for a
3091 // matching point / match pair instead.
3092 for (int index = 0; index < count; ++index) {
3093 const SkOpSpan& span = fTs[index];
3094 if (span.fPt == pt && span.fOther == match) {
3095 return index;
3096 }
3097 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003098 SkASSERT(0);
3099 return -1;
3100}
caryclark@google.com07393ca2013-04-08 11:47:37 +00003101
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003102SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable,
3103 bool firstPass) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003104 // iterate through T intersections and return topmost
3105 // topmost tangent from y-min to first pt is closer to horizontal
3106 SkASSERT(!done());
3107 int firstT = -1;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003108 /* SkPoint topPt = */ activeLeftTop(&firstT);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003109 if (firstT < 0) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003110 *unsortable = !firstPass;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003111 firstT = 0;
3112 while (fTs[firstT].fDone) {
3113 SkASSERT(firstT < fTs.count());
3114 ++firstT;
3115 }
3116 *tIndexPtr = firstT;
3117 *endIndexPtr = nextExactSpan(firstT, 1);
3118 return this;
3119 }
3120 // sort the edges to find the leftmost
3121 int step = 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003122 int end;
3123 if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003124 step = -1;
3125 end = nextSpan(firstT, step);
3126 SkASSERT(end != -1);
3127 }
3128 // if the topmost T is not on end, or is three-way or more, find left
3129 // look for left-ness from tLeft to firstT (matching y of other)
caryclark@google.com07393ca2013-04-08 11:47:37 +00003130 SkASSERT(firstT - end != 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003131 SkOpAngle* markAngle = spanToAngle(firstT, end);
3132 if (!markAngle) {
caryclarkdac1d172014-06-17 05:15:38 -07003133 markAngle = addSingletonAngles(step);
caryclark@google.com570863f2013-09-16 15:55:01 +00003134 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003135 markAngle->markStops();
caryclarke4097e32014-06-18 07:24:19 -07003136 const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
3137 : markAngle->findFirst();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003138 if (!baseAngle) {
3139 return NULL; // nothing to do
caryclark@google.com570863f2013-09-16 15:55:01 +00003140 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003141 SkScalar top = SK_ScalarMax;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003142 const SkOpAngle* firstAngle = NULL;
3143 const SkOpAngle* angle = baseAngle;
3144 do {
3145 if (!angle->unorderable()) {
3146 SkOpSegment* next = angle->segment();
3147 SkPathOpsBounds bounds;
3148 next->subDivideBounds(angle->end(), angle->start(), &bounds);
3149 if (approximately_greater(top, bounds.fTop)) {
3150 top = bounds.fTop;
3151 firstAngle = angle;
3152 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003153 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003154 angle = angle->next();
3155 } while (angle != baseAngle);
3156 SkASSERT(firstAngle);
3157#if DEBUG_SORT
3158 SkDebugf("%s\n", __FUNCTION__);
3159 firstAngle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003160#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003161 // skip edges that have already been processed
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003162 angle = firstAngle;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003163 SkOpSegment* leftSegment = NULL;
3164 bool looped = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003165 do {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003166 *unsortable = angle->unorderable();
3167 if (firstPass || !*unsortable) {
3168 leftSegment = angle->segment();
3169 *tIndexPtr = angle->end();
3170 *endIndexPtr = angle->start();
3171 if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
3172 break;
3173 }
3174 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003175 angle = angle->next();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003176 looped = true;
3177 } while (angle != firstAngle);
3178 if (angle == firstAngle && looped) {
3179 return NULL;
3180 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003181 if (leftSegment->verb() >= SkPath::kQuad_Verb) {
3182 const int tIndex = *tIndexPtr;
3183 const int endIndex = *endIndexPtr;
caryclarke4097e32014-06-18 07:24:19 -07003184 bool swap;
3185 if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003186 #if DEBUG_SWAP_TOP
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003187 SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
3188 __FUNCTION__,
3189 swap, leftSegment->debugInflections(tIndex, endIndex),
caryclark@google.com07393ca2013-04-08 11:47:37 +00003190 leftSegment->serpentine(tIndex, endIndex),
3191 leftSegment->controlsContainedByEnds(tIndex, endIndex),
3192 leftSegment->monotonicInY(tIndex, endIndex));
3193 #endif
3194 if (swap) {
3195 // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
3196 // sorted but merely the first not already processed (i.e., not done)
3197 SkTSwap(*tIndexPtr, *endIndexPtr);
3198 }
3199 }
3200 }
3201 SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
3202 return leftSegment;
3203}
3204
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003205int SkOpSegment::firstActive(int tIndex) const {
3206 while (fTs[tIndex].fTiny) {
3207 SkASSERT(!isCanceled(tIndex));
3208 ++tIndex;
3209 }
3210 return tIndex;
3211}
3212
caryclark@google.com07393ca2013-04-08 11:47:37 +00003213// FIXME: not crazy about this
3214// when the intersections are performed, the other index is into an
3215// incomplete array. As the array grows, the indices become incorrect
3216// while the following fixes the indices up again, it isn't smart about
3217// skipping segments whose indices are already correct
3218// assuming we leave the code that wrote the index in the first place
caryclark@google.com570863f2013-09-16 15:55:01 +00003219// FIXME: if called after remove, this needs to correct tiny
caryclark@google.com07393ca2013-04-08 11:47:37 +00003220void SkOpSegment::fixOtherTIndex() {
3221 int iCount = fTs.count();
3222 for (int i = 0; i < iCount; ++i) {
3223 SkOpSpan& iSpan = fTs[i];
3224 double oT = iSpan.fOtherT;
3225 SkOpSegment* other = iSpan.fOther;
3226 int oCount = other->fTs.count();
caryclark@google.com4fdbb222013-07-23 15:27:41 +00003227 SkDEBUGCODE(iSpan.fOtherIndex = -1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003228 for (int o = 0; o < oCount; ++o) {
3229 SkOpSpan& oSpan = other->fTs[o];
3230 if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) {
3231 iSpan.fOtherIndex = o;
caryclark@google.com4fdbb222013-07-23 15:27:41 +00003232 oSpan.fOtherIndex = i;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003233 break;
3234 }
3235 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00003236 SkASSERT(iSpan.fOtherIndex >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003237 }
3238}
3239
caryclarkdac1d172014-06-17 05:15:38 -07003240bool SkOpSegment::inCoincidentSpan(double t, const SkOpSegment* other) const {
3241 int foundEnds = 0;
3242 int count = this->count();
3243 for (int index = 0; index < count; ++index) {
3244 const SkOpSpan& span = this->span(index);
3245 if (span.fCoincident) {
3246 foundEnds |= (span.fOther == other) << ((t > span.fT) + (t >= span.fT));
3247 }
3248 }
3249 SkASSERT(foundEnds != 7);
3250 return foundEnds == 0x3 || foundEnds == 0x5 || foundEnds == 0x6; // two bits set
3251}
3252
caryclark@google.com07393ca2013-04-08 11:47:37 +00003253void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
3254 fDoneSpans = 0;
3255 fOperand = operand;
3256 fXor = evenOdd;
3257 fPts = pts;
3258 fVerb = verb;
caryclarkdac1d172014-06-17 05:15:38 -07003259 fLoop = fMultiples = fSmall = fTiny = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003260}
3261
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003262void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003263 int local = spanSign(start, end);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003264 if (angleIncludeType == SkOpAngle::kBinarySingle) {
3265 int oppLocal = oppSign(start, end);
3266 (void) markAndChaseWinding(start, end, local, oppLocal);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003267 // OPTIMIZATION: the reverse mark and chase could skip the first marking
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003268 (void) markAndChaseWinding(end, start, local, oppLocal);
3269 } else {
3270 (void) markAndChaseWinding(start, end, local);
3271 // OPTIMIZATION: the reverse mark and chase could skip the first marking
3272 (void) markAndChaseWinding(end, start, local);
3273 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003274}
3275
3276/*
3277when we start with a vertical intersect, we try to use the dx to determine if the edge is to
3278the left or the right of vertical. This determines if we need to add the span's
3279sign or not. However, this isn't enough.
3280If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
3281If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
3282from has the same x direction as this span, the winding should change. If the dx is opposite, then
3283the same winding is shared by both.
3284*/
3285void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
3286 int oppWind, SkScalar hitOppDx) {
3287 SkASSERT(hitDx || !winding);
reed@google.com277c3f82013-05-31 15:17:50 +00003288 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003289 SkASSERT(dx);
3290 int windVal = windValue(SkMin32(start, end));
3291#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07003292 SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), winding,
caryclark@google.com07393ca2013-04-08 11:47:37 +00003293 hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
3294#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003295 int sideWind = winding + (dx < 0 ? windVal : -windVal);
3296 if (abs(winding) < abs(sideWind)) {
3297 winding = sideWind;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003298 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003299 SkDEBUGCODE(int oppLocal = oppSign(start, end));
3300 SkASSERT(hitOppDx || !oppWind || !oppLocal);
3301 int oppWindVal = oppValue(SkMin32(start, end));
3302 if (!oppWind) {
3303 oppWind = dx < 0 ? oppWindVal : -oppWindVal;
3304 } else if (hitOppDx * dx >= 0) {
3305 int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
3306 if (abs(oppWind) < abs(oppSideWind)) {
3307 oppWind = oppSideWind;
3308 }
3309 }
caryclarkdac1d172014-06-17 05:15:38 -07003310#if DEBUG_WINDING_AT_T
3311 SkDebugf(" winding=%d oppWind=%d\n", winding, oppWind);
3312#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003313 (void) markAndChaseWinding(start, end, winding, oppWind);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003314 // OPTIMIZATION: the reverse mark and chase could skip the first marking
3315 (void) markAndChaseWinding(end, start, winding, oppWind);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003316}
3317
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003318bool SkOpSegment::inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const {
3319 if (!baseAngle->inLoop()) {
3320 return false;
3321 }
3322 int index = *indexPtr;
caryclarkdac1d172014-06-17 05:15:38 -07003323 SkOpAngle* from = fTs[index].fFromAngle;
3324 SkOpAngle* to = fTs[index].fToAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003325 while (++index < spanCount) {
caryclarkdac1d172014-06-17 05:15:38 -07003326 SkOpAngle* nextFrom = fTs[index].fFromAngle;
3327 SkOpAngle* nextTo = fTs[index].fToAngle;
3328 if (from != nextFrom || to != nextTo) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003329 break;
3330 }
3331 }
3332 *indexPtr = index;
3333 return true;
3334}
3335
caryclark@google.com07393ca2013-04-08 11:47:37 +00003336// OPTIMIZE: successive calls could start were the last leaves off
3337// or calls could specialize to walk forwards or backwards
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003338bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003339 int tCount = fTs.count();
3340 for (int index = 0; index < tCount; ++index) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003341 const SkOpSpan& span = fTs[index];
3342 if (approximately_zero(startT - span.fT) && pt == span.fPt) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003343 return false;
3344 }
3345 }
3346 return true;
3347}
3348
caryclarkdac1d172014-06-17 05:15:38 -07003349
3350SkOpSegment* SkOpSegment::isSimple(int* end, int* step) {
3351 return nextChase(end, step, NULL, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003352}
3353
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003354bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
3355 int start = angle->start();
3356 int end = angle->end();
3357 const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
3358 return mSpan.fTiny;
3359}
3360
3361bool SkOpSegment::isTiny(int index) const {
3362 return fTs[index].fTiny;
3363}
3364
3365// look pair of active edges going away from coincident edge
3366// one of them should be the continuation of other
3367// if both are active, look to see if they both the connect to another coincident pair
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003368// if at least one is a line, then make the pair coincident
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003369// if neither is a line, test for coincidence
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003370bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt,
3371 int step, bool cancel) {
3372 int otherTIndex = other->findT(otherT, otherPt, this);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003373 int next = other->nextExactSpan(otherTIndex, step);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003374 int otherMin = SkMin32(otherTIndex, next);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003375 int otherWind = other->span(otherMin).fWindValue;
3376 if (otherWind == 0) {
3377 return false;
3378 }
3379 SkASSERT(next >= 0);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003380 int tIndex = 0;
3381 do {
3382 SkOpSpan* test = &fTs[tIndex];
3383 SkASSERT(test->fT == 0);
3384 if (test->fOther == other || test->fOtherT != 1) {
3385 continue;
3386 }
3387 SkPoint startPt, endPt;
3388 double endT;
3389 if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) {
3390 SkOpSegment* match = test->fOther;
3391 if (cancel) {
3392 match->addTCancel(startPt, endPt, other);
3393 } else {
3394 match->addTCoincident(startPt, endPt, endT, other);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003395 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003396 return true;
3397 }
3398 } while (fTs[++tIndex].fT == 0);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003399 return false;
3400}
3401
caryclark@google.com07393ca2013-04-08 11:47:37 +00003402// this span is excluded by the winding rule -- chase the ends
3403// as long as they are unambiguous to mark connections as done
3404// and give them the same winding value
caryclark@google.com07393ca2013-04-08 11:47:37 +00003405
3406SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
3407 int step = SkSign32(endIndex - index);
3408 int min = SkMin32(index, endIndex);
3409 markDoneBinary(min);
caryclarkdac1d172014-06-17 05:15:38 -07003410 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003411 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003412 while ((other = other->nextChase(&index, &step, &min, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003413 if (other->done()) {
caryclarkdac1d172014-06-17 05:15:38 -07003414 SkASSERT(!last);
3415 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003416 }
3417 other->markDoneBinary(min);
3418 }
3419 return last;
3420}
3421
3422SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
3423 int step = SkSign32(endIndex - index);
3424 int min = SkMin32(index, endIndex);
3425 markDoneUnary(min);
caryclarkdac1d172014-06-17 05:15:38 -07003426 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003427 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003428 while ((other = other->nextChase(&index, &step, &min, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003429 if (other->done()) {
caryclarkdac1d172014-06-17 05:15:38 -07003430 SkASSERT(!last);
3431 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003432 }
3433 other->markDoneUnary(min);
3434 }
3435 return last;
3436}
3437
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003438SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003439 int index = angle->start();
3440 int endIndex = angle->end();
3441 int step = SkSign32(endIndex - index);
3442 int min = SkMin32(index, endIndex);
3443 markWinding(min, winding);
caryclarkdac1d172014-06-17 05:15:38 -07003444 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003445 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003446 while ((other = other->nextChase(&index, &step, &min, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003447 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark19eb3b22014-07-18 05:08:14 -07003448// SkASSERT(other->fTs[min].fWindSum == winding);
caryclarkdac1d172014-06-17 05:15:38 -07003449 SkASSERT(!last);
3450 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003451 }
3452 other->markWinding(min, winding);
3453 }
3454 return last;
3455}
3456
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003457SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding) {
3458 int min = SkMin32(index, endIndex);
3459 int step = SkSign32(endIndex - index);
3460 markWinding(min, winding);
caryclarkdac1d172014-06-17 05:15:38 -07003461 SkOpSpan* last = NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003462 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003463 while ((other = other->nextChase(&index, &step, &min, &last))) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003464 if (other->fTs[min].fWindSum != SK_MinS32) {
3465 SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
caryclarkdac1d172014-06-17 05:15:38 -07003466 SkASSERT(!last);
3467 break;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003468 }
3469 other->markWinding(min, winding);
3470 }
3471 return last;
3472}
3473
caryclark@google.com07393ca2013-04-08 11:47:37 +00003474SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
3475 int min = SkMin32(index, endIndex);
3476 int step = SkSign32(endIndex - index);
3477 markWinding(min, winding, oppWinding);
caryclarkdac1d172014-06-17 05:15:38 -07003478 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003479 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003480 while ((other = other->nextChase(&index, &step, &min, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003481 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclarkda085e62014-06-17 05:41:12 -07003482#ifdef SK_DEBUG
caryclarkdac1d172014-06-17 05:15:38 -07003483 if (!other->fTs[min].fLoop) {
3484 if (fOperand == other->fOperand) {
3485// FIXME: this is probably a bug -- rects4 asserts here
3486// SkASSERT(other->fTs[min].fWindSum == winding);
3487// FIXME: this is probably a bug -- rects3 asserts here
3488// SkASSERT(other->fTs[min].fOppSum == oppWinding);
3489 } else {
3490 SkASSERT(other->fTs[min].fWindSum == oppWinding);
3491// FIXME: this is probably a bug -- skpwww_joomla_org_23 asserts here
3492// SkASSERT(other->fTs[min].fOppSum == winding);
3493 }
3494 }
3495 SkASSERT(!last);
3496#endif
3497 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003498 }
caryclarkdac1d172014-06-17 05:15:38 -07003499 if (fOperand == other->fOperand) {
3500 other->markWinding(min, winding, oppWinding);
3501 } else {
3502 other->markWinding(min, oppWinding, winding);
3503 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003504 }
3505 return last;
3506}
3507
3508SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding) {
3509 int start = angle->start();
3510 int end = angle->end();
3511 return markAndChaseWinding(start, end, winding, oppWinding);
3512}
3513
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003514SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003515 SkASSERT(angle->segment() == this);
3516 if (UseInnerWinding(maxWinding, sumWinding)) {
3517 maxWinding = sumWinding;
3518 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003519 SkOpSpan* last = markAndChaseWinding(angle, maxWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00003520#if DEBUG_WINDING
3521 if (last) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003522 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
3523 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
3524 SkPathOpsDebug::WindingPrintf(last->fWindSum);
3525 SkDebugf(" small=%d\n", last->fSmall);
caryclark@google.com570863f2013-09-16 15:55:01 +00003526 }
3527#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003528 return last;
3529}
3530
3531SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003532 int oppSumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003533 SkASSERT(angle->segment() == this);
3534 if (UseInnerWinding(maxWinding, sumWinding)) {
3535 maxWinding = sumWinding;
3536 }
3537 if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
3538 oppMaxWinding = oppSumWinding;
3539 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003540 SkOpSpan* last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00003541#if DEBUG_WINDING
3542 if (last) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003543 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
3544 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
3545 SkPathOpsDebug::WindingPrintf(last->fWindSum);
3546 SkDebugf(" small=%d\n", last->fSmall);
caryclark@google.com570863f2013-09-16 15:55:01 +00003547 }
3548#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003549 return last;
3550}
3551
3552// FIXME: this should also mark spans with equal (x,y)
3553// This may be called when the segment is already marked done. While this
3554// wastes time, it shouldn't do any more than spin through the T spans.
3555// OPTIMIZATION: abort on first done found (assuming that this code is
3556// always called to mark segments done).
3557void SkOpSegment::markDone(int index, int winding) {
3558 // SkASSERT(!done());
3559 SkASSERT(winding);
3560 double referenceT = fTs[index].fT;
3561 int lesser = index;
3562 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3563 markOneDone(__FUNCTION__, lesser, winding);
3564 }
3565 do {
3566 markOneDone(__FUNCTION__, index, winding);
3567 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003568 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003569}
3570
caryclark@google.com07393ca2013-04-08 11:47:37 +00003571void SkOpSegment::markDoneBinary(int index) {
3572 double referenceT = fTs[index].fT;
3573 int lesser = index;
3574 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3575 markOneDoneBinary(__FUNCTION__, lesser);
3576 }
3577 do {
3578 markOneDoneBinary(__FUNCTION__, index);
3579 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003580 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003581}
3582
3583void SkOpSegment::markDoneUnary(int index) {
3584 double referenceT = fTs[index].fT;
3585 int lesser = index;
3586 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3587 markOneDoneUnary(__FUNCTION__, lesser);
3588 }
3589 do {
3590 markOneDoneUnary(__FUNCTION__, index);
3591 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003592 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003593}
3594
3595void SkOpSegment::markOneDone(const char* funName, int tIndex, int winding) {
3596 SkOpSpan* span = markOneWinding(funName, tIndex, winding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003597 if (!span || span->fDone) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003598 return;
3599 }
3600 span->fDone = true;
3601 fDoneSpans++;
3602}
3603
3604void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
3605 SkOpSpan* span = verifyOneWinding(funName, tIndex);
3606 if (!span) {
3607 return;
3608 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003609 SkASSERT(!span->fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003610 span->fDone = true;
3611 fDoneSpans++;
3612}
3613
caryclark@google.com07393ca2013-04-08 11:47:37 +00003614void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
3615 SkOpSpan* span = verifyOneWindingU(funName, tIndex);
3616 if (!span) {
3617 return;
3618 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003619 if (span->fWindSum == SK_MinS32) {
3620 SkDebugf("%s uncomputed\n", __FUNCTION__);
3621 }
3622 SkASSERT(!span->fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003623 span->fDone = true;
3624 fDoneSpans++;
3625}
3626
3627SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding) {
3628 SkOpSpan& span = fTs[tIndex];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003629 if (span.fDone && !span.fSmall) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003630 return NULL;
3631 }
3632#if DEBUG_MARK_DONE
3633 debugShowNewWinding(funName, span, winding);
3634#endif
3635 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003636#if DEBUG_LIMIT_WIND_SUM
3637 SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
3638#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003639 span.fWindSum = winding;
3640 return &span;
3641}
3642
3643SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
3644 int oppWinding) {
3645 SkOpSpan& span = fTs[tIndex];
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003646 if (span.fDone && !span.fSmall) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003647 return NULL;
3648 }
3649#if DEBUG_MARK_DONE
3650 debugShowNewWinding(funName, span, winding, oppWinding);
3651#endif
3652 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003653#if DEBUG_LIMIT_WIND_SUM
3654 SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
3655#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003656 span.fWindSum = winding;
3657 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003658#if DEBUG_LIMIT_WIND_SUM
3659 SkASSERT(abs(oppWinding) <= DEBUG_LIMIT_WIND_SUM);
3660#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003661 span.fOppSum = oppWinding;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003662 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003663 return &span;
3664}
3665
3666// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
caryclarke4097e32014-06-18 07:24:19 -07003667bool SkOpSegment::clockwise(int tStart, int tEnd, bool* swap) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003668 SkASSERT(fVerb != SkPath::kLine_Verb);
3669 SkPoint edge[4];
3670 subDivide(tStart, tEnd, edge);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003671 int points = SkPathOpsVerbToPoints(fVerb);
3672 double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
caryclarke4097e32014-06-18 07:24:19 -07003673 bool sumSet = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003674 if (fVerb == SkPath::kCubic_Verb) {
caryclarke4097e32014-06-18 07:24:19 -07003675 SkDCubic cubic;
3676 cubic.set(edge);
3677 double inflectionTs[2];
3678 int inflections = cubic.findInflections(inflectionTs);
3679 // FIXME: this fixes cubicOp114 and breaks cubicOp58d
3680 // the trouble is that cubics with inflections confuse whether the curve breaks towards
3681 // or away, which in turn is used to determine if it is on the far right or left.
3682 // Probably a totally different approach is in order. At one time I tried to project a
3683 // horizontal ray to determine winding, but was confused by how to map the vertically
3684 // oriented winding computation over.
3685 if (0 && inflections) {
3686 double tLo = this->span(tStart).fT;
3687 double tHi = this->span(tEnd).fT;
3688 double tLoStart = tLo;
3689 for (int index = 0; index < inflections; ++index) {
3690 if (between(tLo, inflectionTs[index], tHi)) {
3691 tLo = inflectionTs[index];
3692 }
3693 }
3694 if (tLo != tLoStart && tLo != tHi) {
3695 SkDPoint sub[2];
3696 sub[0] = cubic.ptAtT(tLo);
3697 sub[1].set(edge[3]);
3698 SkDPoint ctrl[2];
3699 SkDCubic::SubDivide(fPts, sub[0], sub[1], tLo, tHi, ctrl);
3700 edge[0] = sub[0].asSkPoint();
3701 edge[1] = ctrl[0].asSkPoint();
3702 edge[2] = ctrl[1].asSkPoint();
3703 sum = (edge[0].fX - edge[3].fX) * (edge[0].fY + edge[3].fY);
3704 }
3705 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003706 SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
3707 if (edge[1].fY < lesser && edge[2].fY < lesser) {
3708 SkDLine tangent1 = {{ {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} }};
3709 SkDLine tangent2 = {{ {edge[2].fX, edge[2].fY}, {edge[3].fX, edge[3].fY} }};
3710 if (SkIntersections::Test(tangent1, tangent2)) {
3711 SkPoint topPt = cubic_top(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3712 sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
3713 sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
caryclarke4097e32014-06-18 07:24:19 -07003714 sumSet = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003715 }
3716 }
3717 }
caryclarke4097e32014-06-18 07:24:19 -07003718 if (!sumSet) {
3719 for (int idx = 0; idx < points; ++idx){
3720 sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
3721 }
3722 }
3723 if (fVerb == SkPath::kCubic_Verb) {
3724 SkDCubic cubic;
3725 cubic.set(edge);
3726 *swap = sum > 0 && !cubic.monotonicInY() && !cubic.serpentine();
3727 } else {
3728 SkDQuad quad;
3729 quad.set(edge);
3730 *swap = sum > 0 && !quad.monotonicInY();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003731 }
3732 return sum <= 0;
3733}
3734
3735bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003736 SkASSERT(fVerb != SkPath::kLine_Verb);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003737 if (fVerb == SkPath::kQuad_Verb) {
3738 SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3739 return dst.monotonicInY();
3740 }
3741 SkASSERT(fVerb == SkPath::kCubic_Verb);
3742 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3743 return dst.monotonicInY();
3744}
3745
3746bool SkOpSegment::serpentine(int tStart, int tEnd) const {
3747 if (fVerb != SkPath::kCubic_Verb) {
3748 return false;
3749 }
3750 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3751 return dst.serpentine();
3752}
3753
3754SkOpSpan* SkOpSegment::verifyOneWinding(const char* funName, int tIndex) {
3755 SkOpSpan& span = fTs[tIndex];
3756 if (span.fDone) {
3757 return NULL;
3758 }
3759#if DEBUG_MARK_DONE
3760 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
3761#endif
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003762// If the prior angle in the sort is unorderable, the winding sum may not be computable.
3763// To enable the assert, the 'prior is unorderable' state could be
3764// piped down to this test, but not sure it's worth it.
3765// (Once the sort order is stored in the span, this test may be feasible.)
3766// SkASSERT(span.fWindSum != SK_MinS32);
3767// SkASSERT(span.fOppSum != SK_MinS32);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003768 return &span;
3769}
3770
3771SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
3772 SkOpSpan& span = fTs[tIndex];
3773 if (span.fDone) {
3774 return NULL;
3775 }
3776#if DEBUG_MARK_DONE
3777 debugShowNewWinding(funName, span, span.fWindSum);
3778#endif
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003779// If the prior angle in the sort is unorderable, the winding sum may not be computable.
3780// To enable the assert, the 'prior is unorderable' state could be
3781// piped down to this test, but not sure it's worth it.
3782// (Once the sort order is stored in the span, this test may be feasible.)
3783// SkASSERT(span.fWindSum != SK_MinS32);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003784 return &span;
3785}
3786
caryclark@google.com07393ca2013-04-08 11:47:37 +00003787void SkOpSegment::markWinding(int index, int winding) {
3788// SkASSERT(!done());
3789 SkASSERT(winding);
3790 double referenceT = fTs[index].fT;
3791 int lesser = index;
3792 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3793 markOneWinding(__FUNCTION__, lesser, winding);
3794 }
3795 do {
3796 markOneWinding(__FUNCTION__, index, winding);
3797 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003798 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003799}
3800
3801void SkOpSegment::markWinding(int index, int winding, int oppWinding) {
3802// SkASSERT(!done());
3803 SkASSERT(winding || oppWinding);
3804 double referenceT = fTs[index].fT;
3805 int lesser = index;
3806 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3807 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
3808 }
3809 do {
3810 markOneWinding(__FUNCTION__, index, winding, oppWinding);
3811 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003812 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003813}
3814
3815void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
3816 int nextDoorWind = SK_MaxS32;
3817 int nextOppWind = SK_MaxS32;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003818 // prefer exact matches
caryclark@google.com07393ca2013-04-08 11:47:37 +00003819 if (tIndex > 0) {
3820 const SkOpSpan& below = fTs[tIndex - 1];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003821 if (below.fT == t) {
3822 nextDoorWind = below.fWindValue;
3823 nextOppWind = below.fOppValue;
3824 }
3825 }
3826 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3827 const SkOpSpan& above = fTs[tIndex + 1];
3828 if (above.fT == t) {
3829 nextDoorWind = above.fWindValue;
3830 nextOppWind = above.fOppValue;
3831 }
3832 }
3833 if (nextDoorWind == SK_MaxS32 && tIndex > 0) {
3834 const SkOpSpan& below = fTs[tIndex - 1];
caryclark@google.com07393ca2013-04-08 11:47:37 +00003835 if (approximately_negative(t - below.fT)) {
3836 nextDoorWind = below.fWindValue;
3837 nextOppWind = below.fOppValue;
3838 }
3839 }
3840 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3841 const SkOpSpan& above = fTs[tIndex + 1];
3842 if (approximately_negative(above.fT - t)) {
3843 nextDoorWind = above.fWindValue;
3844 nextOppWind = above.fOppValue;
3845 }
3846 }
3847 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
3848 const SkOpSpan& below = fTs[tIndex - 1];
3849 nextDoorWind = below.fWindValue;
3850 nextOppWind = below.fOppValue;
3851 }
3852 if (nextDoorWind != SK_MaxS32) {
3853 SkOpSpan& newSpan = fTs[tIndex];
3854 newSpan.fWindValue = nextDoorWind;
3855 newSpan.fOppValue = nextOppWind;
3856 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
3857 newSpan.fDone = true;
3858 ++fDoneSpans;
3859 }
3860 }
3861}
3862
caryclark@google.com07393ca2013-04-08 11:47:37 +00003863bool SkOpSegment::nextCandidate(int* start, int* end) const {
3864 while (fTs[*end].fDone) {
3865 if (fTs[*end].fT == 1) {
3866 return false;
3867 }
3868 ++(*end);
3869 }
3870 *start = *end;
3871 *end = nextExactSpan(*start, 1);
3872 return true;
3873}
3874
caryclarkdac1d172014-06-17 05:15:38 -07003875static SkOpSegment* set_last(SkOpSpan** last, SkOpSpan* endSpan) {
3876 if (last && !endSpan->fSmall) {
3877 *last = endSpan;
3878 }
3879 return NULL;
3880}
3881
3882SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr, SkOpSpan** last) {
3883 int origIndex = *indexPtr;
3884 int step = *stepPtr;
3885 int end = nextExactSpan(origIndex, step);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003886 SkASSERT(end >= 0);
caryclarkdac1d172014-06-17 05:15:38 -07003887 SkOpSpan& endSpan = fTs[end];
3888 SkOpAngle* angle = step > 0 ? endSpan.fFromAngle : endSpan.fToAngle;
3889 int foundIndex;
3890 int otherEnd;
3891 SkOpSegment* other;
3892 if (angle == NULL) {
3893 if (endSpan.fT != 0 && endSpan.fT != 1) {
3894 return NULL;
3895 }
3896 other = endSpan.fOther;
3897 foundIndex = endSpan.fOtherIndex;
3898 otherEnd = other->nextExactSpan(foundIndex, step);
3899 } else {
3900 int loopCount = angle->loopCount();
3901 if (loopCount > 2) {
3902 return set_last(last, &endSpan);
3903 }
3904 const SkOpAngle* next = angle->next();
3905 if (angle->sign() != next->sign()) {
3906#if DEBUG_WINDING
3907 SkDebugf("%s mismatched signs\n", __FUNCTION__);
3908#endif
3909 // return set_last(last, &endSpan);
3910 }
3911 other = next->segment();
3912 foundIndex = end = next->start();
3913 otherEnd = next->end();
caryclark@google.com570863f2013-09-16 15:55:01 +00003914 }
caryclarkdac1d172014-06-17 05:15:38 -07003915 int foundStep = foundIndex < otherEnd ? 1 : -1;
3916 if (*stepPtr != foundStep) {
3917 return set_last(last, &endSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003918 }
caryclarkdac1d172014-06-17 05:15:38 -07003919 SkASSERT(*indexPtr >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003920 SkASSERT(otherEnd >= 0);
caryclarkdac1d172014-06-17 05:15:38 -07003921#if 1
3922 int origMin = origIndex + (step < 0 ? step : 0);
3923 const SkOpSpan& orig = this->span(origMin);
3924#endif
3925 int foundMin = SkMin32(foundIndex, otherEnd);
3926#if 1
3927 const SkOpSpan& found = other->span(foundMin);
3928 if (found.fWindValue != orig.fWindValue || found.fOppValue != orig.fOppValue) {
3929 return set_last(last, &endSpan);
3930 }
3931#endif
3932 *indexPtr = foundIndex;
3933 *stepPtr = foundStep;
3934 if (minPtr) {
3935 *minPtr = foundMin;
caryclark@google.coma5e55922013-05-07 18:51:31 +00003936 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003937 return other;
3938}
3939
3940// This has callers for two different situations: one establishes the end
3941// of the current span, and one establishes the beginning of the next span
3942// (thus the name). When this is looking for the end of the current span,
3943// coincidence is found when the beginning Ts contain -step and the end
3944// contains step. When it is looking for the beginning of the next, the
3945// first Ts found can be ignored and the last Ts should contain -step.
3946// OPTIMIZATION: probably should split into two functions
3947int SkOpSegment::nextSpan(int from, int step) const {
3948 const SkOpSpan& fromSpan = fTs[from];
3949 int count = fTs.count();
3950 int to = from;
3951 while (step > 0 ? ++to < count : --to >= 0) {
3952 const SkOpSpan& span = fTs[to];
3953 if (approximately_zero(span.fT - fromSpan.fT)) {
3954 continue;
3955 }
3956 return to;
3957 }
3958 return -1;
3959}
3960
3961// FIXME
3962// this returns at any difference in T, vs. a preset minimum. It may be
3963// that all callers to nextSpan should use this instead.
caryclark@google.com07393ca2013-04-08 11:47:37 +00003964int SkOpSegment::nextExactSpan(int from, int step) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003965 int to = from;
caryclark@google.com570863f2013-09-16 15:55:01 +00003966 if (step < 0) {
3967 const SkOpSpan& fromSpan = fTs[from];
3968 while (--to >= 0) {
3969 const SkOpSpan& span = fTs[to];
3970 if (precisely_negative(fromSpan.fT - span.fT) || span.fTiny) {
3971 continue;
3972 }
3973 return to;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003974 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003975 } else {
3976 while (fTs[from].fTiny) {
3977 from++;
3978 }
3979 const SkOpSpan& fromSpan = fTs[from];
3980 int count = fTs.count();
3981 while (++to < count) {
3982 const SkOpSpan& span = fTs[to];
3983 if (precisely_negative(span.fT - fromSpan.fT)) {
3984 continue;
3985 }
3986 return to;
3987 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003988 }
3989 return -1;
3990}
3991
caryclarkdac1d172014-06-17 05:15:38 -07003992void SkOpSegment::pinT(const SkPoint& pt, double* t) {
3993 if (pt == fPts[0]) {
3994 *t = 0;
3995 }
3996 int count = SkPathOpsVerbToPoints(fVerb);
3997 if (pt == fPts[count]) {
3998 *t = 1;
3999 }
4000}
4001
caryclark5e27e0e2014-08-12 07:46:33 -07004002bool SkOpSegment::reversePoints(const SkPoint& p1, const SkPoint& p2) const {
4003 SkASSERT(p1 != p2);
4004 int spanCount = count();
4005 int p1IndexMin = -1;
4006 int p2IndexMax = spanCount;
4007 for (int index = 0; index < spanCount; ++index) {
4008 const SkOpSpan& span = fTs[index];
4009 if (span.fPt == p1) {
4010 if (p1IndexMin < 0) {
4011 p1IndexMin = index;
4012 }
4013 } else if (span.fPt == p2) {
4014 p2IndexMax = index;
4015 }
4016 }
4017 return p1IndexMin > p2IndexMax;
4018}
4019
caryclarkdac1d172014-06-17 05:15:38 -07004020void SkOpSegment::setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt,
4021 SkOpSegment* other) {
4022 int count = this->count();
4023 for (int index = 0; index < count; ++index) {
4024 SkOpSpan &span = fTs[index];
4025 if ((startPt == span.fPt || endPt == span.fPt) && other == span.fOther) {
4026 span.fCoincident = true;
4027 }
4028 }
4029}
4030
caryclark@google.com07393ca2013-04-08 11:47:37 +00004031void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
4032 int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
4033 int deltaSum = spanSign(index, endIndex);
4034 int oppDeltaSum = oppSign(index, endIndex);
4035 if (operand()) {
4036 *maxWinding = *sumSuWinding;
4037 *sumWinding = *sumSuWinding -= deltaSum;
4038 *oppMaxWinding = *sumMiWinding;
4039 *oppSumWinding = *sumMiWinding -= oppDeltaSum;
4040 } else {
4041 *maxWinding = *sumMiWinding;
4042 *sumWinding = *sumMiWinding -= deltaSum;
4043 *oppMaxWinding = *sumSuWinding;
4044 *oppSumWinding = *sumSuWinding -= oppDeltaSum;
4045 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004046#if DEBUG_LIMIT_WIND_SUM
4047 SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
4048 SkASSERT(abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
4049#endif
caryclark@google.com570863f2013-09-16 15:55:01 +00004050}
4051
4052void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
4053 int* maxWinding, int* sumWinding) {
4054 int deltaSum = spanSign(index, endIndex);
4055 *maxWinding = *sumMiWinding;
4056 *sumWinding = *sumMiWinding -= deltaSum;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004057#if DEBUG_LIMIT_WIND_SUM
4058 SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
4059#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00004060}
4061
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004062void SkOpSegment::sortAngles() {
4063 int spanCount = fTs.count();
4064 if (spanCount <= 2) {
4065 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004066 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004067 int index = 0;
4068 do {
caryclarkdac1d172014-06-17 05:15:38 -07004069 SkOpAngle* fromAngle = fTs[index].fFromAngle;
4070 SkOpAngle* toAngle = fTs[index].fToAngle;
4071 if (!fromAngle && !toAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004072 index += 1;
4073 continue;
4074 }
4075 SkOpAngle* baseAngle = NULL;
caryclarkdac1d172014-06-17 05:15:38 -07004076 if (fromAngle) {
4077 baseAngle = fromAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004078 if (inLoop(baseAngle, spanCount, &index)) {
4079 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004080 }
4081 }
caryclark@google.com570863f2013-09-16 15:55:01 +00004082#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004083 bool wroteAfterHeader = false;
caryclark@google.com570863f2013-09-16 15:55:01 +00004084#endif
caryclarkdac1d172014-06-17 05:15:38 -07004085 if (toAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004086 if (!baseAngle) {
4087 baseAngle = toAngle;
4088 if (inLoop(baseAngle, spanCount, &index)) {
4089 continue;
4090 }
4091 } else {
4092 SkDEBUGCODE(int newIndex = index);
4093 SkASSERT(!inLoop(baseAngle, spanCount, &newIndex) && newIndex == index);
4094#if DEBUG_ANGLE
4095 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
4096 index);
4097 wroteAfterHeader = true;
4098#endif
4099 baseAngle->insert(toAngle);
4100 }
4101 }
caryclarkdac1d172014-06-17 05:15:38 -07004102 SkOpAngle* nextFrom, * nextTo;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004103 int firstIndex = index;
4104 do {
4105 SkOpSpan& span = fTs[index];
4106 SkOpSegment* other = span.fOther;
4107 SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
caryclarkdac1d172014-06-17 05:15:38 -07004108 SkOpAngle* oAngle = oSpan.fFromAngle;
4109 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004110#if DEBUG_ANGLE
4111 if (!wroteAfterHeader) {
4112 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
4113 index);
4114 wroteAfterHeader = true;
4115 }
4116#endif
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004117 if (!oAngle->loopContains(*baseAngle)) {
4118 baseAngle->insert(oAngle);
4119 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004120 }
caryclarkdac1d172014-06-17 05:15:38 -07004121 oAngle = oSpan.fToAngle;
4122 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004123#if DEBUG_ANGLE
4124 if (!wroteAfterHeader) {
4125 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
4126 index);
4127 wroteAfterHeader = true;
4128 }
4129#endif
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004130 if (!oAngle->loopContains(*baseAngle)) {
4131 baseAngle->insert(oAngle);
4132 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004133 }
4134 if (++index == spanCount) {
4135 break;
4136 }
caryclarkdac1d172014-06-17 05:15:38 -07004137 nextFrom = fTs[index].fFromAngle;
4138 nextTo = fTs[index].fToAngle;
4139 } while (fromAngle == nextFrom && toAngle == nextTo);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004140 if (baseAngle && baseAngle->loopCount() == 1) {
4141 index = firstIndex;
4142 do {
4143 SkOpSpan& span = fTs[index];
caryclarkdac1d172014-06-17 05:15:38 -07004144 span.fFromAngle = span.fToAngle = NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004145 if (++index == spanCount) {
4146 break;
4147 }
caryclarkdac1d172014-06-17 05:15:38 -07004148 nextFrom = fTs[index].fFromAngle;
4149 nextTo = fTs[index].fToAngle;
4150 } while (fromAngle == nextFrom && toAngle == nextTo);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004151 baseAngle = NULL;
4152 }
4153#if DEBUG_SORT
4154 SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
4155#endif
4156 } while (index < spanCount);
caryclark@google.com570863f2013-09-16 15:55:01 +00004157}
4158
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004159// return true if midpoints were computed
4160bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
4161 SkASSERT(start != end);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004162 edge[0] = fTs[start].fPt;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004163 int points = SkPathOpsVerbToPoints(fVerb);
4164 edge[points] = fTs[end].fPt;
4165 if (fVerb == SkPath::kLine_Verb) {
4166 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004167 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004168 double startT = fTs[start].fT;
4169 double endT = fTs[end].fT;
4170 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
4171 // don't compute midpoints if we already have them
4172 if (fVerb == SkPath::kQuad_Verb) {
4173 edge[1] = fPts[1];
4174 return false;
4175 }
4176 SkASSERT(fVerb == SkPath::kCubic_Verb);
4177 if (start < end) {
4178 edge[1] = fPts[1];
4179 edge[2] = fPts[2];
4180 return false;
4181 }
4182 edge[1] = fPts[2];
4183 edge[2] = fPts[1];
4184 return false;
4185 }
4186 const SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[points].fX, edge[points].fY }};
4187 if (fVerb == SkPath::kQuad_Verb) {
4188 edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint();
4189 } else {
4190 SkASSERT(fVerb == SkPath::kCubic_Verb);
4191 SkDPoint ctrl[2];
4192 SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl);
4193 edge[1] = ctrl[0].asSkPoint();
4194 edge[2] = ctrl[1].asSkPoint();
4195 }
4196 return true;
4197}
4198
4199// return true if midpoints were computed
4200bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
4201 SkASSERT(start != end);
4202 (*result)[0].set(fTs[start].fPt);
4203 int points = SkPathOpsVerbToPoints(fVerb);
4204 (*result)[points].set(fTs[end].fPt);
4205 if (fVerb == SkPath::kLine_Verb) {
4206 return false;
4207 }
4208 double startT = fTs[start].fT;
4209 double endT = fTs[end].fT;
4210 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
4211 // don't compute midpoints if we already have them
4212 if (fVerb == SkPath::kQuad_Verb) {
4213 (*result)[1].set(fPts[1]);
4214 return false;
4215 }
4216 SkASSERT(fVerb == SkPath::kCubic_Verb);
4217 if (start < end) {
4218 (*result)[1].set(fPts[1]);
4219 (*result)[2].set(fPts[2]);
4220 return false;
4221 }
4222 (*result)[1].set(fPts[2]);
4223 (*result)[2].set(fPts[1]);
4224 return false;
4225 }
4226 if (fVerb == SkPath::kQuad_Verb) {
4227 (*result)[1] = SkDQuad::SubDivide(fPts, (*result)[0], (*result)[2], startT, endT);
4228 } else {
4229 SkASSERT(fVerb == SkPath::kCubic_Verb);
4230 SkDCubic::SubDivide(fPts, (*result)[0], (*result)[3], startT, endT, &(*result)[1]);
4231 }
4232 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004233}
4234
4235void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
4236 SkPoint edge[4];
4237 subDivide(start, end, edge);
reed@google.com277c3f82013-05-31 15:17:50 +00004238 (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004239}
4240
caryclark@google.com570863f2013-09-16 15:55:01 +00004241void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt,
4242 const SkPoint& startPt) {
4243 int outCount = outsidePts->count();
4244 if (outCount == 0 || endPt != (*outsidePts)[outCount - 2]) {
4245 outsidePts->push_back(endPt);
4246 outsidePts->push_back(startPt);
4247 }
4248}
4249
4250void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoint& startPt) {
4251 int outCount = outsidePts->count();
4252 if (outCount == 0 || startPt != (*outsidePts)[outCount - 1]) {
4253 outsidePts->push_back(startPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004254 }
4255}
4256
4257void SkOpSegment::undoneSpan(int* start, int* end) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004258 int tCount = fTs.count();
4259 int index;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004260 for (index = 0; index < tCount; ++index) {
4261 if (!fTs[index].fDone) {
4262 break;
4263 }
4264 }
4265 SkASSERT(index < tCount - 1);
4266 *start = index;
4267 double startT = fTs[index].fT;
4268 while (approximately_negative(fTs[++index].fT - startT))
4269 SkASSERT(index < tCount);
4270 SkASSERT(index < tCount);
4271 *end = index;
4272}
4273
4274int SkOpSegment::updateOppWinding(int index, int endIndex) const {
4275 int lesser = SkMin32(index, endIndex);
4276 int oppWinding = oppSum(lesser);
4277 int oppSpanWinding = oppSign(index, endIndex);
4278 if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
4279 && oppWinding != SK_MaxS32) {
4280 oppWinding -= oppSpanWinding;
4281 }
4282 return oppWinding;
4283}
4284
4285int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
4286 int startIndex = angle->start();
4287 int endIndex = angle->end();
4288 return updateOppWinding(endIndex, startIndex);
4289}
4290
4291int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
4292 int startIndex = angle->start();
4293 int endIndex = angle->end();
4294 return updateOppWinding(startIndex, endIndex);
4295}
4296
4297int SkOpSegment::updateWinding(int index, int endIndex) const {
4298 int lesser = SkMin32(index, endIndex);
4299 int winding = windSum(lesser);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004300 if (winding == SK_MinS32) {
4301 return winding;
4302 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004303 int spanWinding = spanSign(index, endIndex);
caryclark@google.com570863f2013-09-16 15:55:01 +00004304 if (winding && UseInnerWinding(winding - spanWinding, winding)
4305 && winding != SK_MaxS32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00004306 winding -= spanWinding;
4307 }
4308 return winding;
4309}
4310
4311int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
4312 int startIndex = angle->start();
4313 int endIndex = angle->end();
4314 return updateWinding(endIndex, startIndex);
4315}
4316
caryclark@google.com570863f2013-09-16 15:55:01 +00004317int SkOpSegment::updateWindingReverse(int index, int endIndex) const {
4318 int lesser = SkMin32(index, endIndex);
4319 int winding = windSum(lesser);
4320 int spanWinding = spanSign(endIndex, index);
4321 if (winding && UseInnerWindingReverse(winding - spanWinding, winding)
4322 && winding != SK_MaxS32) {
4323 winding -= spanWinding;
4324 }
4325 return winding;
4326}
4327
caryclark@google.com07393ca2013-04-08 11:47:37 +00004328int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
4329 int startIndex = angle->start();
4330 int endIndex = angle->end();
caryclark@google.com570863f2013-09-16 15:55:01 +00004331 return updateWindingReverse(endIndex, startIndex);
4332}
4333
4334// OPTIMIZATION: does the following also work, and is it any faster?
4335// return outerWinding * innerWinding > 0
4336// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
4337bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
4338 SkASSERT(outerWinding != SK_MaxS32);
4339 SkASSERT(innerWinding != SK_MaxS32);
4340 int absOut = abs(outerWinding);
4341 int absIn = abs(innerWinding);
4342 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
4343 return result;
4344}
4345
4346bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) {
4347 SkASSERT(outerWinding != SK_MaxS32);
4348 SkASSERT(innerWinding != SK_MaxS32);
4349 int absOut = abs(outerWinding);
4350 int absIn = abs(innerWinding);
4351 bool result = absOut == absIn ? true : absOut < absIn;
4352 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004353}
4354
4355int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const {
4356 if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
4357 return SK_MinS32;
4358 }
4359 int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
4360 SkASSERT(winding != SK_MinS32);
4361 int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
4362#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07004363 SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
4364 debugID(), crossOpp, tHit, t(tIndex), winding, windVal);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004365#endif
4366 // see if a + change in T results in a +/- change in X (compute x'(T))
reed@google.com277c3f82013-05-31 15:17:50 +00004367 *dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004368 if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
4369 *dx = fPts[2].fX - fPts[1].fX - *dx;
4370 }
4371 if (*dx == 0) {
4372#if DEBUG_WINDING_AT_T
4373 SkDebugf(" dx=0 winding=SK_MinS32\n");
4374#endif
4375 return SK_MinS32;
4376 }
skia.committer@gmail.coma4aced42013-07-09 07:00:56 +00004377 if (windVal < 0) { // reverse sign if opp contour traveled in reverse
caryclark@google.com07e97fc2013-07-08 17:17:02 +00004378 *dx = -*dx;
4379 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004380 if (winding * *dx > 0) { // if same signs, result is negative
4381 winding += *dx > 0 ? -windVal : windVal;
4382 }
4383#if DEBUG_WINDING_AT_T
4384 SkDebugf(" dx=%c winding=%d\n", *dx > 0 ? '+' : '-', winding);
4385#endif
4386 return winding;
4387}
4388
4389int SkOpSegment::windSum(const SkOpAngle* angle) const {
4390 int start = angle->start();
4391 int end = angle->end();
4392 int index = SkMin32(start, end);
4393 return windSum(index);
4394}
4395
caryclark@google.com07393ca2013-04-08 11:47:37 +00004396void SkOpSegment::zeroSpan(SkOpSpan* span) {
caryclark@google.coma5e55922013-05-07 18:51:31 +00004397 SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004398 span->fWindValue = 0;
4399 span->fOppValue = 0;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00004400 if (span->fTiny || span->fSmall) {
4401 return;
4402 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004403 SkASSERT(!span->fDone);
4404 span->fDone = true;
4405 ++fDoneSpans;
4406}