blob: 1fb5afa028af33e894a82fe890e8b03af185f52a [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
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);
caryclark65f55312014-11-13 06:58:52 -0800163#if DEBUG_LIMIT_WIND_SUM
164 SkASSERT(abs(sumMiWinding) <= DEBUG_LIMIT_WIND_SUM);
165 SkASSERT(abs(sumSuWinding) <= DEBUG_LIMIT_WIND_SUM);
166#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000167 if (fOperand) {
168 SkTSwap<int>(sumMiWinding, sumSuWinding);
169 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000170 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000171}
172
173bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000174 int* sumMiWinding, int* sumSuWinding) {
175 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000176 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000177 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000178 bool miFrom;
179 bool miTo;
180 bool suFrom;
181 bool suTo;
182 if (operand()) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000183 miFrom = (oppMaxWinding & xorMiMask) != 0;
184 miTo = (oppSumWinding & xorMiMask) != 0;
185 suFrom = (maxWinding & xorSuMask) != 0;
186 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000187 } else {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000188 miFrom = (maxWinding & xorMiMask) != 0;
189 miTo = (sumWinding & xorMiMask) != 0;
190 suFrom = (oppMaxWinding & xorSuMask) != 0;
191 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000192 }
193 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
194#if DEBUG_ACTIVE_OP
caryclarkdac1d172014-06-17 05:15:38 -0700195 SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n",
196 __FUNCTION__, debugID(), span(index).fT, span(endIndex).fT,
caryclark@google.com570863f2013-09-16 15:55:01 +0000197 SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000198#endif
199 return result;
200}
201
202bool SkOpSegment::activeWinding(int index, int endIndex) {
203 int sumWinding = updateWinding(endIndex, index);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000204 return activeWinding(index, endIndex, &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000205}
206
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000207bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) {
208 int maxWinding;
209 setUpWinding(index, endIndex, &maxWinding, sumWinding);
210 bool from = maxWinding != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000211 bool to = *sumWinding != 0;
212 bool result = gUnaryActiveEdge[from][to];
213 return result;
214}
215
caryclark@google.com570863f2013-09-16 15:55:01 +0000216void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt,
217 SkOpSegment* other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000218 int tIndex = -1;
219 int tCount = fTs.count();
220 int oIndex = -1;
221 int oCount = other->fTs.count();
222 do {
223 ++tIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000224 } while (startPt != fTs[tIndex].fPt && tIndex < tCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000225 int tIndexStart = tIndex;
226 do {
227 ++oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000228 } while (endPt != other->fTs[oIndex].fPt && oIndex < oCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000229 int oIndexStart = oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000230 const SkPoint* nextPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000231 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000232 nextPt = &fTs[++tIndex].fPt;
233 SkASSERT(fTs[tIndex].fT < 1 || startPt != *nextPt);
234 } while (startPt == *nextPt);
235 double nextT = fTs[tIndex].fT;
236 const SkPoint* oNextPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000237 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000238 oNextPt = &other->fTs[++oIndex].fPt;
239 SkASSERT(other->fTs[oIndex].fT < 1 || endPt != *oNextPt);
240 } while (endPt == *oNextPt);
241 double oNextT = other->fTs[oIndex].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000242 // at this point, spans before and after are at:
243 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
244 // if tIndexStart == 0, no prior span
245 // if nextT == 1, no following span
246
247 // advance the span with zero winding
248 // if the following span exists (not past the end, non-zero winding)
249 // connect the two edges
250 if (!fTs[tIndexStart].fWindValue) {
251 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
252#if DEBUG_CONCIDENT
253 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
254 __FUNCTION__, fID, other->fID, tIndexStart - 1,
255 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
256 xyAtT(tIndexStart).fY);
257#endif
caryclarkbdbb2422014-08-20 08:11:24 -0700258 SkPoint copy = fTs[tIndexStart].fPt; // add t pair may move the point array
259 addTPair(fTs[tIndexStart].fT, other, other->fTs[oIndex].fT, false, copy);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000260 }
261 if (nextT < 1 && fTs[tIndex].fWindValue) {
262#if DEBUG_CONCIDENT
263 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
264 __FUNCTION__, fID, other->fID, tIndex,
265 fTs[tIndex].fT, xyAtT(tIndex).fX,
266 xyAtT(tIndex).fY);
267#endif
caryclarkbdbb2422014-08-20 08:11:24 -0700268 SkPoint copy = fTs[tIndex].fPt; // add t pair may move the point array
269 addTPair(fTs[tIndex].fT, other, other->fTs[oIndexStart].fT, false, copy);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000270 }
271 } else {
272 SkASSERT(!other->fTs[oIndexStart].fWindValue);
273 if (oIndexStart > 0 && other->fTs[oIndexStart - 1].fWindValue) {
274#if DEBUG_CONCIDENT
275 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
276 __FUNCTION__, fID, other->fID, oIndexStart - 1,
277 other->fTs[oIndexStart].fT, other->xyAtT(oIndexStart).fX,
278 other->xyAtT(oIndexStart).fY);
279 other->debugAddTPair(other->fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
280#endif
281 }
282 if (oNextT < 1 && other->fTs[oIndex].fWindValue) {
283#if DEBUG_CONCIDENT
284 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
285 __FUNCTION__, fID, other->fID, oIndex,
286 other->fTs[oIndex].fT, other->xyAtT(oIndex).fX,
287 other->xyAtT(oIndex).fY);
288 other->debugAddTPair(other->fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
289#endif
290 }
291 }
292}
293
caryclark@google.com570863f2013-09-16 15:55:01 +0000294void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
295 SkOpSegment* other) {
296 // walk this to startPt
297 // walk other to startPt
caryclark@google.com07393ca2013-04-08 11:47:37 +0000298 // if either is > 0, add a pointer to the other, copying adjacent winding
299 int tIndex = -1;
300 int oIndex = -1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000301 do {
302 ++tIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000303 } while (startPt != fTs[tIndex].fPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000304 int ttIndex = tIndex;
305 bool checkOtherTMatch = false;
306 do {
307 const SkOpSpan& span = fTs[ttIndex];
308 if (startPt != span.fPt) {
309 break;
skia.committer@gmail.coma1ed7ae2014-04-15 03:04:18 +0000310 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000311 if (span.fOther == other && span.fPt == startPt) {
312 checkOtherTMatch = true;
313 break;
314 }
315 } while (++ttIndex < count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000316 do {
317 ++oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000318 } while (startPt != other->fTs[oIndex].fPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000319 bool skipAdd = false;
320 if (checkOtherTMatch) {
321 int ooIndex = oIndex;
322 do {
323 const SkOpSpan& oSpan = other->fTs[ooIndex];
324 if (startPt != oSpan.fPt) {
325 break;
skia.committer@gmail.coma1ed7ae2014-04-15 03:04:18 +0000326 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000327 if (oSpan.fT == fTs[ttIndex].fOtherT) {
328 skipAdd = true;
329 break;
330 }
331 } while (++ooIndex < other->count());
332 }
333 if ((tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) && !skipAdd) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000334 addTPair(fTs[tIndex].fT, other, other->fTs[oIndex].fT, false, startPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000335 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000336 SkPoint nextPt = startPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000337 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000338 const SkPoint* workPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000339 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000340 workPt = &fTs[++tIndex].fPt;
341 } while (nextPt == *workPt);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000342 const SkPoint* oWorkPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000343 do {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000344 oWorkPt = &other->fTs[++oIndex].fPt;
345 } while (nextPt == *oWorkPt);
caryclark@google.com570863f2013-09-16 15:55:01 +0000346 nextPt = *workPt;
347 double tStart = fTs[tIndex].fT;
348 double oStart = other->fTs[oIndex].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000349 if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) {
350 break;
351 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000352 if (*workPt == *oWorkPt) {
353 addTPair(tStart, other, oStart, false, nextPt);
354 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000355 } while (endPt != nextPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000356}
357
358void SkOpSegment::addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
359 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
360 fBounds.setCubicBounds(pts);
361}
362
363void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active) const {
364 SkPoint edge[4];
365 const SkPoint* ePtr;
366 int lastT = fTs.count() - 1;
367 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
368 ePtr = fPts;
369 } else {
370 // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
371 subDivide(start, end, edge);
372 ePtr = edge;
373 }
374 if (active) {
375 bool reverse = ePtr == fPts && start != 0;
376 if (reverse) {
reed@google.com277c3f82013-05-31 15:17:50 +0000377 path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000378 switch (fVerb) {
379 case SkPath::kLine_Verb:
380 path->deferredLine(ePtr[0]);
381 break;
382 case SkPath::kQuad_Verb:
383 path->quadTo(ePtr[1], ePtr[0]);
384 break;
385 case SkPath::kCubic_Verb:
386 path->cubicTo(ePtr[2], ePtr[1], ePtr[0]);
387 break;
388 default:
389 SkASSERT(0);
390 }
391 // return ePtr[0];
392 } else {
393 path->deferredMoveLine(ePtr[0]);
394 switch (fVerb) {
395 case SkPath::kLine_Verb:
396 path->deferredLine(ePtr[1]);
397 break;
398 case SkPath::kQuad_Verb:
399 path->quadTo(ePtr[1], ePtr[2]);
400 break;
401 case SkPath::kCubic_Verb:
402 path->cubicTo(ePtr[1], ePtr[2], ePtr[3]);
403 break;
404 default:
405 SkASSERT(0);
406 }
407 }
408 }
reed@google.com277c3f82013-05-31 15:17:50 +0000409 // return ePtr[SkPathOpsVerbToPoints(fVerb)];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000410}
411
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000412void SkOpSegment::addEndSpan(int endIndex) {
caryclark19eb3b22014-07-18 05:08:14 -0700413 SkASSERT(span(endIndex).fT == 1 || (span(endIndex).fTiny
caryclark65b427c2014-09-18 10:32:57 -0700414// && approximately_greater_than_one(span(endIndex).fT)
415 ));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000416 int spanCount = fTs.count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000417 int startIndex = endIndex - 1;
418 while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) {
caryclark19eb3b22014-07-18 05:08:14 -0700419 --startIndex;
420 SkASSERT(startIndex > 0);
421 --endIndex;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000422 }
caryclarkdac1d172014-06-17 05:15:38 -0700423 SkOpAngle& angle = fAngles.push_back();
424 angle.set(this, spanCount - 1, startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000425#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000426 debugCheckPointsEqualish(endIndex, spanCount);
427#endif
caryclarkdac1d172014-06-17 05:15:38 -0700428 setFromAngle(endIndex, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000429}
430
caryclarkdac1d172014-06-17 05:15:38 -0700431void SkOpSegment::setFromAngle(int endIndex, SkOpAngle* angle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000432 int spanCount = fTs.count();
433 do {
caryclarkdac1d172014-06-17 05:15:38 -0700434 fTs[endIndex].fFromAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000435 } while (++endIndex < spanCount);
436}
437
caryclark@google.com07393ca2013-04-08 11:47:37 +0000438void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
439 init(pts, SkPath::kLine_Verb, operand, evenOdd);
440 fBounds.set(pts, 2);
441}
442
443// add 2 to edge or out of range values to get T extremes
444void SkOpSegment::addOtherT(int index, double otherT, int otherIndex) {
445 SkOpSpan& span = fTs[index];
caryclark@google.com03610322013-04-18 15:58:21 +0000446 if (precisely_zero(otherT)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000447 otherT = 0;
caryclark@google.com03610322013-04-18 15:58:21 +0000448 } else if (precisely_equal(otherT, 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000449 otherT = 1;
450 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000451 span.fOtherT = otherT;
452 span.fOtherIndex = otherIndex;
453}
454
455void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
456 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
457 fBounds.setQuadBounds(pts);
458}
459
caryclarkdac1d172014-06-17 05:15:38 -0700460SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
461 int spanIndex = count() - 1;
462 int startIndex = nextExactSpan(spanIndex, -1);
463 SkASSERT(startIndex >= 0);
464 SkOpAngle& angle = fAngles.push_back();
465 *anglePtr = &angle;
466 angle.set(this, spanIndex, startIndex);
467 setFromAngle(spanIndex, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000468 SkOpSegment* other;
caryclarkdac1d172014-06-17 05:15:38 -0700469 int oStartIndex, oEndIndex;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000470 do {
caryclarkdac1d172014-06-17 05:15:38 -0700471 const SkOpSpan& span = fTs[spanIndex];
472 SkASSERT(span.fT > 0);
473 other = span.fOther;
474 oStartIndex = span.fOtherIndex;
475 oEndIndex = other->nextExactSpan(oStartIndex, 1);
476 if (oEndIndex > 0 && other->span(oStartIndex).fWindValue) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000477 break;
478 }
caryclarkdac1d172014-06-17 05:15:38 -0700479 oEndIndex = oStartIndex;
480 oStartIndex = other->nextExactSpan(oEndIndex, -1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000481 --spanIndex;
caryclarkdac1d172014-06-17 05:15:38 -0700482 } while (oStartIndex < 0 || !other->span(oStartIndex).fWindSum);
483 SkOpAngle& oAngle = other->fAngles.push_back();
484 oAngle.set(other, oStartIndex, oEndIndex);
485 other->setToAngle(oEndIndex, &oAngle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000486 *otherPtr = other;
caryclarkdac1d172014-06-17 05:15:38 -0700487 return &oAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000488}
489
caryclarkdac1d172014-06-17 05:15:38 -0700490SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
491 int endIndex = nextExactSpan(0, 1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000492 SkASSERT(endIndex > 0);
caryclarkdac1d172014-06-17 05:15:38 -0700493 SkOpAngle& angle = fAngles.push_back();
494 *anglePtr = &angle;
495 angle.set(this, 0, endIndex);
496 setToAngle(endIndex, &angle);
497 int spanIndex = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000498 SkOpSegment* other;
caryclarkdac1d172014-06-17 05:15:38 -0700499 int oStartIndex, oEndIndex;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000500 do {
caryclarkdac1d172014-06-17 05:15:38 -0700501 const SkOpSpan& span = fTs[spanIndex];
502 SkASSERT(span.fT < 1);
503 other = span.fOther;
504 oEndIndex = span.fOtherIndex;
505 oStartIndex = other->nextExactSpan(oEndIndex, -1);
506 if (oStartIndex >= 0 && other->span(oStartIndex).fWindValue) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000507 break;
508 }
caryclarkdac1d172014-06-17 05:15:38 -0700509 oStartIndex = oEndIndex;
510 oEndIndex = other->nextExactSpan(oStartIndex, 1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000511 ++spanIndex;
caryclarkdac1d172014-06-17 05:15:38 -0700512 } while (oEndIndex < 0 || !other->span(oStartIndex).fWindValue);
513 SkOpAngle& oAngle = other->fAngles.push_back();
514 oAngle.set(other, oEndIndex, oStartIndex);
515 other->setFromAngle(oEndIndex, &oAngle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000516 *otherPtr = other;
caryclarkdac1d172014-06-17 05:15:38 -0700517 return &oAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000518}
519
caryclarkdac1d172014-06-17 05:15:38 -0700520SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000521 SkOpSegment* other;
caryclarkdac1d172014-06-17 05:15:38 -0700522 SkOpAngle* angle, * otherAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000523 if (step > 0) {
caryclarkdac1d172014-06-17 05:15:38 -0700524 otherAngle = addSingletonAngleUp(&other, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000525 } else {
caryclarkdac1d172014-06-17 05:15:38 -0700526 otherAngle = addSingletonAngleDown(&other, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000527 }
caryclarkdac1d172014-06-17 05:15:38 -0700528 angle->insert(otherAngle);
529 return angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000530}
531
532void SkOpSegment::addStartSpan(int endIndex) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000533 int index = 0;
caryclarkdac1d172014-06-17 05:15:38 -0700534 SkOpAngle& angle = fAngles.push_back();
535 angle.set(this, index, endIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000536#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000537 debugCheckPointsEqualish(index, endIndex);
538#endif
caryclarkdac1d172014-06-17 05:15:38 -0700539 setToAngle(endIndex, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000540}
541
caryclarkdac1d172014-06-17 05:15:38 -0700542void SkOpSegment::setToAngle(int endIndex, SkOpAngle* angle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000543 int index = 0;
544 do {
caryclarkdac1d172014-06-17 05:15:38 -0700545 fTs[index].fToAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000546 } while (++index < endIndex);
547}
548
caryclark@google.com07393ca2013-04-08 11:47:37 +0000549 // Defer all coincident edge processing until
550 // after normal intersections have been computed
551
552// no need to be tricky; insert in normal T order
553// resolve overlapping ts when considering coincidence later
554
555 // add non-coincident intersection. Resulting edges are sorted in T.
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000556int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000557 SkASSERT(this != other || fVerb == SkPath::kCubic_Verb);
558 #if 0 // this needs an even rougher association to be useful
559 SkASSERT(SkDPoint::RoughlyEqual(ptAtT(newT), pt));
560 #endif
caryclarkdac1d172014-06-17 05:15:38 -0700561 const SkPoint& firstPt = fPts[0];
562 const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
563 SkASSERT(newT == 0 || !precisely_zero(newT));
564 SkASSERT(newT == 1 || !precisely_equal(newT, 1));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000565 // FIXME: in the pathological case where there is a ton of intercepts,
566 // binary search?
567 int insertedAt = -1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000568 int tCount = fTs.count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000569 for (int index = 0; index < tCount; ++index) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000570 // OPTIMIZATION: if there are three or more identical Ts, then
571 // the fourth and following could be further insertion-sorted so
572 // that all the edges are clockwise or counterclockwise.
573 // This could later limit segment tests to the two adjacent
574 // neighbors, although it doesn't help with determining which
575 // circular direction to go in.
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000576 const SkOpSpan& span = fTs[index];
577 if (newT < span.fT) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000578 insertedAt = index;
579 break;
580 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000581 if (newT == span.fT) {
582 if (pt == span.fPt) {
583 insertedAt = index;
584 break;
585 }
586 if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
587 insertedAt = index;
588 break;
589 }
590 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000591 }
592 SkOpSpan* span;
593 if (insertedAt >= 0) {
594 span = fTs.insert(insertedAt);
595 } else {
596 insertedAt = tCount;
597 span = fTs.append();
598 }
599 span->fT = newT;
caryclarkdac1d172014-06-17 05:15:38 -0700600 span->fOtherT = -1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000601 span->fOther = other;
602 span->fPt = pt;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000603#if 0
604 // cubics, for instance, may not be exact enough to satisfy this check (e.g., cubicOp69d)
605 SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX)
606 && approximately_equal(xyAtT(newT).fY, pt.fY));
607#endif
caryclarkdac1d172014-06-17 05:15:38 -0700608 span->fFromAngle = NULL;
609 span->fToAngle = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000610 span->fWindSum = SK_MinS32;
611 span->fOppSum = SK_MinS32;
612 span->fWindValue = 1;
613 span->fOppValue = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000614 span->fChased = false;
caryclarkdac1d172014-06-17 05:15:38 -0700615 span->fCoincident = false;
616 span->fLoop = false;
617 span->fNear = false;
618 span->fMultiple = false;
619 span->fSmall = false;
620 span->fTiny = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000621 if ((span->fDone = newT == 1)) {
622 ++fDoneSpans;
623 }
caryclark65f55312014-11-13 06:58:52 -0800624 setSpanFlags(pt, newT, span);
625 return insertedAt;
626}
627
628void SkOpSegment::setSpanFlags(const SkPoint& pt, double newT, SkOpSpan* span) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000629 int less = -1;
caryclarkdac1d172014-06-17 05:15:38 -0700630// FIXME: note that this relies on spans being a continguous array
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000631// find range of spans with nearly the same point as this one
caryclark65b427c2014-09-18 10:32:57 -0700632 // FIXME: SkDPoint::ApproximatelyEqual is better but breaks tests at the moment
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000633 while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, pt)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000634 if (fVerb == SkPath::kCubic_Verb) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000635 double tInterval = newT - span[less].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000636 double tMid = newT - tInterval / 2;
637 SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
638 if (!midPt.approximatelyEqual(xyAtT(span))) {
639 break;
640 }
641 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000642 --less;
643 }
644 int more = 1;
caryclark65b427c2014-09-18 10:32:57 -0700645 // FIXME: SkDPoint::ApproximatelyEqual is better but breaks tests at the moment
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000646 while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].fPt, pt)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000647 if (fVerb == SkPath::kCubic_Verb) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000648 double tEndInterval = span[more].fT - newT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000649 double tMid = newT - tEndInterval / 2;
650 SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
651 if (!midEndPt.approximatelyEqual(xyAtT(span))) {
652 break;
653 }
654 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000655 ++more;
656 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000657 ++less;
658 --more;
659 while (more - 1 > less && span[more].fPt == span[more - 1].fPt
660 && span[more].fT == span[more - 1].fT) {
661 --more;
662 }
663 if (less == more) {
caryclark65f55312014-11-13 06:58:52 -0800664 return;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000665 }
666 if (precisely_negative(span[more].fT - span[less].fT)) {
caryclark65f55312014-11-13 06:58:52 -0800667 return;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000668 }
669// if the total range of t values is big enough, mark all tiny
670 bool tiny = span[less].fPt == span[more].fPt;
671 int index = less;
672 do {
673 fSmall = span[index].fSmall = true;
674 fTiny |= span[index].fTiny = tiny;
675 if (!span[index].fDone) {
676 span[index].fDone = true;
677 ++fDoneSpans;
678 }
679 } while (++index < more);
caryclark65f55312014-11-13 06:58:52 -0800680 return;
681}
682
683void SkOpSegment::resetSpanFlags() {
684 fSmall = fTiny = false;
685 fDoneSpans = 0;
686 int start = 0;
687 int last = this->count() - 1;
688 do {
689 SkOpSpan* startSpan = &this->fTs[start];
690 double startT = startSpan->fT;
691 startSpan->fSmall = startSpan->fTiny = false; // sets range initial
692 bool terminus = startT == 1;
693 if ((startSpan->fDone = !startSpan->fWindValue | terminus)) {
694 ++fDoneSpans;
695 }
696 ++start; // range initial + 1
697 if (terminus) {
698 continue;
699 }
700 const SkPoint& pt = startSpan->fPt;
701 int end = start; // range initial + 1
702 while (end <= last) {
703 const SkOpSpan& endSpan = this->span(end);
704 if (!AlmostEqualUlps(endSpan.fPt, pt)) {
705 break;
706 }
707 if (fVerb == SkPath::kCubic_Verb) {
708 double tMid = (startSpan->fT + endSpan.fT) / 2;
709 SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
710 if (!midEndPt.approximatelyEqual(xyAtT(startSpan))) {
711 break;
712 }
713 }
714 ++end;
715 }
716 if (start == end) { // end == range final + 1
717 continue;
718 }
719 while (--end >= start) { // end == range final
720 const SkOpSpan& endSpan = this->span(end);
721 const SkOpSpan& priorSpan = this->span(end - 1);
722 if (endSpan.fPt != priorSpan.fPt || endSpan.fT != priorSpan.fT) {
723 break; // end == range final + 1
724 }
725 }
726 if (end < start) { // end == range final + 1
727 continue;
728 }
729 int index = start - 1; // index == range initial
730 start = end; // start = range final + 1
731 const SkOpSpan& nextSpan = this->span(end);
732 if (precisely_negative(nextSpan.fT - startSpan->fT)) {
733 while (++index < end) {
734 startSpan = &this->fTs[index];
735 startSpan->fSmall = startSpan->fTiny = false; // sets range initial + 1
736 if ((startSpan->fDone = !startSpan->fWindValue)) {
737 ++fDoneSpans;
738 }
739 }
740 continue;
741 }
742 if (!startSpan->fWindValue) {
743 --fDoneSpans; // added back below
744 }
745 bool tiny = nextSpan.fPt == startSpan->fPt;
746 do {
747 fSmall = startSpan->fSmall = true; // sets range initial
748 fTiny |= startSpan->fTiny = tiny;
749 startSpan->fDone = true;
750 ++fDoneSpans;
751 startSpan = &this->fTs[++index];
752 } while (index < end); // loop through tiny small range end (last)
753 } while (start <= last);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000754}
755
756// set spans from start to end to decrement by one
757// note this walks other backwards
caryclark@google.coma5e55922013-05-07 18:51:31 +0000758// FIXME: there's probably an edge case that can be constructed where
caryclark@google.com07393ca2013-04-08 11:47:37 +0000759// two span in one segment are separated by float epsilon on one span but
760// not the other, if one segment is very small. For this
761// case the counts asserted below may or may not be enough to separate the
762// spans. Even if the counts work out, what if the spans aren't correctly
763// sorted? It feels better in such a case to match the span's other span
764// pointer since both coincident segments must contain the same spans.
caryclark@google.coma5e55922013-05-07 18:51:31 +0000765// FIXME? It seems that decrementing by one will fail for complex paths that
766// have three or more coincident edges. Shouldn't this subtract the difference
767// between the winding values?
caryclark@google.com570863f2013-09-16 15:55:01 +0000768/* |--> |-->
769this 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4
770other 2<<<<1<<<<0 2<<<<1<<<<0 2<<<<1<<<<0
771 ^ ^ <--| <--|
772 startPt endPt test/oTest first pos test/oTest final pos
773*/
774void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000775 bool binary = fOperand != other->fOperand;
776 int index = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000777 while (startPt != fTs[index].fPt) {
778 SkASSERT(index < fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000779 ++index;
780 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000781 while (index > 0 && precisely_equal(fTs[index].fT, fTs[index - 1].fT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000782 --index;
783 }
caryclarkdac1d172014-06-17 05:15:38 -0700784 bool oFoundEnd = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000785 int oIndex = other->fTs.count();
caryclark@google.com570863f2013-09-16 15:55:01 +0000786 while (startPt != other->fTs[--oIndex].fPt) { // look for startPt match
787 SkASSERT(oIndex > 0);
788 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000789 double oStartT = other->fTs[oIndex].fT;
790 // look for first point beyond match
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000791 while (startPt == other->fTs[--oIndex].fPt || precisely_equal(oStartT, other->fTs[oIndex].fT)) {
caryclark65b427c2014-09-18 10:32:57 -0700792 if (!oIndex) {
793 return; // tiny spans may move in the wrong direction
794 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000795 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000796 SkOpSpan* test = &fTs[index];
797 SkOpSpan* oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +0000798 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
799 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
caryclarkdac1d172014-06-17 05:15:38 -0700800 bool decrement, track, bigger;
801 int originalWindValue;
802 const SkPoint* testPt;
803 const SkPoint* oTestPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000804 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000805 SkASSERT(test->fT < 1);
806 SkASSERT(oTest->fT < 1);
caryclarkdac1d172014-06-17 05:15:38 -0700807 decrement = test->fWindValue && oTest->fWindValue;
808 track = test->fWindValue || oTest->fWindValue;
809 bigger = test->fWindValue >= oTest->fWindValue;
810 testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000811 double testT = test->fT;
caryclarkdac1d172014-06-17 05:15:38 -0700812 oTestPt = &oTest->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000813 double oTestT = oTest->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000814 do {
815 if (decrement) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000816 if (binary && bigger) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000817 test->fOppValue--;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000818 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +0000819 decrementSpan(test);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000820 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000821 } else if (track) {
caryclarkdac1d172014-06-17 05:15:38 -0700822 TrackOutsidePair(&outsidePts, *testPt, *oTestPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000823 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000824 SkASSERT(index < fTs.count() - 1);
825 test = &fTs[++index];
caryclarkdac1d172014-06-17 05:15:38 -0700826 } while (*testPt == test->fPt || precisely_equal(testT, test->fT));
827 originalWindValue = oTest->fWindValue;
caryclark@google.com570863f2013-09-16 15:55:01 +0000828 do {
829 SkASSERT(oTest->fT < 1);
830 SkASSERT(originalWindValue == oTest->fWindValue);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000831 if (decrement) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000832 if (binary && !bigger) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000833 oTest->fOppValue--;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000834 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +0000835 other->decrementSpan(oTest);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000836 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000837 } else if (track) {
caryclarkdac1d172014-06-17 05:15:38 -0700838 TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
839 }
840 if (!oIndex) {
841 break;
842 }
843 oFoundEnd |= endPt == oTest->fPt;
844 oTest = &other->fTs[--oIndex];
845 } while (*oTestPt == oTest->fPt || precisely_equal(oTestT, oTest->fT));
846 } while (endPt != test->fPt && test->fT < 1);
847 // FIXME: determine if canceled edges need outside ts added
848 if (!oFoundEnd) {
849 for (int oIdx2 = oIndex; oIdx2 >= 0; --oIdx2) {
850 SkOpSpan* oTst2 = &other->fTs[oIdx2];
851 if (originalWindValue != oTst2->fWindValue) {
852 goto skipAdvanceOtherCancel;
853 }
854 if (!oTst2->fWindValue) {
855 goto skipAdvanceOtherCancel;
856 }
857 if (endPt == other->fTs[oIdx2].fPt) {
858 break;
859 }
860 }
caryclark65f55312014-11-13 06:58:52 -0800861 oFoundEnd = endPt == oTest->fPt;
caryclarkdac1d172014-06-17 05:15:38 -0700862 do {
863 SkASSERT(originalWindValue == oTest->fWindValue);
864 if (decrement) {
865 if (binary && !bigger) {
866 oTest->fOppValue--;
867 } else {
868 other->decrementSpan(oTest);
869 }
870 } else if (track) {
871 TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000872 }
873 if (!oIndex) {
874 break;
875 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000876 oTest = &other->fTs[--oIndex];
caryclarkdac1d172014-06-17 05:15:38 -0700877 oFoundEnd |= endPt == oTest->fPt;
878 } while (!oFoundEnd || endPt == oTest->fPt);
879 }
880skipAdvanceOtherCancel:
caryclark@google.com570863f2013-09-16 15:55:01 +0000881 int outCount = outsidePts.count();
882 if (!done() && outCount) {
883 addCancelOutsides(outsidePts[0], outsidePts[1], other);
884 if (outCount > 2) {
885 addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000886 }
887 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000888 if (!other->done() && oOutsidePts.count()) {
889 other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000890 }
caryclarkdac1d172014-06-17 05:15:38 -0700891 setCoincidentRange(startPt, endPt, other);
892 other->setCoincidentRange(startPt, endPt, this);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000893}
894
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000895int SkOpSegment::addSelfT(const SkPoint& pt, double newT) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000896 // if the tail nearly intersects itself but not quite, the caller records this separately
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000897 int result = addT(this, pt, newT);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000898 SkOpSpan* span = &fTs[result];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000899 fLoop = span->fLoop = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000900 return result;
901}
902
caryclarkdac1d172014-06-17 05:15:38 -0700903// find the starting or ending span with an existing loop of angles
904// FIXME? replicate for all identical starting/ending spans?
905// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
906// FIXME? assert that only one other span has a valid windValue or oppValue
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000907void SkOpSegment::addSimpleAngle(int index) {
caryclarkdac1d172014-06-17 05:15:38 -0700908 SkOpSpan* span = &fTs[index];
caryclark19eb3b22014-07-18 05:08:14 -0700909 int idx;
910 int start, end;
911 if (span->fT == 0) {
912 idx = 0;
913 span = &fTs[0];
caryclarkdac1d172014-06-17 05:15:38 -0700914 do {
915 if (span->fToAngle) {
916 SkASSERT(span->fToAngle->loopCount() == 2);
917 SkASSERT(!span->fFromAngle);
918 span->fFromAngle = span->fToAngle->next();
919 return;
920 }
caryclark19eb3b22014-07-18 05:08:14 -0700921 span = &fTs[++idx];
caryclarkdac1d172014-06-17 05:15:38 -0700922 } while (span->fT == 0);
caryclark19eb3b22014-07-18 05:08:14 -0700923 SkASSERT(!fTs[0].fTiny && fTs[idx].fT > 0);
924 addStartSpan(idx);
925 start = 0;
926 end = idx;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000927 } else {
caryclark19eb3b22014-07-18 05:08:14 -0700928 idx = count() - 1;
929 span = &fTs[idx];
caryclarkdac1d172014-06-17 05:15:38 -0700930 do {
931 if (span->fFromAngle) {
932 SkASSERT(span->fFromAngle->loopCount() == 2);
933 SkASSERT(!span->fToAngle);
934 span->fToAngle = span->fFromAngle->next();
935 return;
936 }
caryclark19eb3b22014-07-18 05:08:14 -0700937 span = &fTs[--idx];
caryclarkdac1d172014-06-17 05:15:38 -0700938 } while (span->fT == 1);
caryclark19eb3b22014-07-18 05:08:14 -0700939 SkASSERT(!fTs[idx].fTiny && fTs[idx].fT < 1);
940 addEndSpan(++idx);
941 start = idx;
942 end = count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000943 }
caryclark19eb3b22014-07-18 05:08:14 -0700944 SkOpSegment* other;
945 SkOpSpan* oSpan;
946 index = start;
947 do {
948 span = &fTs[index];
949 other = span->fOther;
950 int oFrom = span->fOtherIndex;
951 oSpan = &other->fTs[oFrom];
952 if (oSpan->fT < 1 && oSpan->fWindValue) {
953 break;
954 }
955 if (oSpan->fT == 0) {
956 continue;
957 }
958 oFrom = other->nextExactSpan(oFrom, -1);
959 SkOpSpan* oFromSpan = &other->fTs[oFrom];
960 SkASSERT(oFromSpan->fT < 1);
961 if (oFromSpan->fWindValue) {
962 break;
963 }
964 } while (++index < end);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000965 SkOpAngle* angle, * oAngle;
caryclark19eb3b22014-07-18 05:08:14 -0700966 if (span->fT == 0) {
caryclarkdac1d172014-06-17 05:15:38 -0700967 SkASSERT(span->fOtherIndex - 1 >= 0);
968 SkASSERT(span->fOtherT == 1);
caryclark19eb3b22014-07-18 05:08:14 -0700969 SkDEBUGCODE(int oPriorIndex = other->nextExactSpan(span->fOtherIndex, -1));
970 SkDEBUGCODE(const SkOpSpan& oPrior = other->span(oPriorIndex));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000971 SkASSERT(!oPrior.fTiny && oPrior.fT < 1);
caryclarkdac1d172014-06-17 05:15:38 -0700972 other->addEndSpan(span->fOtherIndex);
973 angle = span->fToAngle;
caryclark19eb3b22014-07-18 05:08:14 -0700974 oAngle = oSpan->fFromAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000975 } else {
caryclarkdac1d172014-06-17 05:15:38 -0700976 SkASSERT(span->fOtherIndex + 1 < other->count());
977 SkASSERT(span->fOtherT == 0);
caryclark19eb3b22014-07-18 05:08:14 -0700978 SkASSERT(!oSpan->fTiny && (other->fTs[span->fOtherIndex + 1].fT > 0
caryclarkdac1d172014-06-17 05:15:38 -0700979 || (other->fTs[span->fOtherIndex + 1].fFromAngle == NULL
980 && other->fTs[span->fOtherIndex + 1].fToAngle == NULL)));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000981 int oIndex = 1;
982 do {
983 const SkOpSpan& osSpan = other->span(oIndex);
caryclarkdac1d172014-06-17 05:15:38 -0700984 if (osSpan.fFromAngle || osSpan.fT > 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000985 break;
986 }
987 ++oIndex;
988 SkASSERT(oIndex < other->count());
989 } while (true);
990 other->addStartSpan(oIndex);
caryclarkdac1d172014-06-17 05:15:38 -0700991 angle = span->fFromAngle;
caryclark19eb3b22014-07-18 05:08:14 -0700992 oAngle = oSpan->fToAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000993 }
994 angle->insert(oAngle);
995}
996
caryclarkdac1d172014-06-17 05:15:38 -0700997void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
998 debugValidate();
999 int count = this->count();
1000 for (int index = 0; index < count; ++index) {
1001 SkOpSpan& span = fTs[index];
1002 if (!span.fMultiple) {
1003 continue;
1004 }
1005 int end = nextExactSpan(index, 1);
1006 SkASSERT(end > index + 1);
1007 const SkPoint& thisPt = span.fPt;
1008 while (index < end - 1) {
1009 SkOpSegment* other1 = span.fOther;
1010 int oCnt = other1->count();
1011 for (int idx2 = index + 1; idx2 < end; ++idx2) {
1012 SkOpSpan& span2 = fTs[idx2];
1013 SkOpSegment* other2 = span2.fOther;
1014 for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
1015 SkOpSpan& oSpan = other1->fTs[oIdx];
1016 if (oSpan.fOther != other2) {
1017 continue;
1018 }
1019 if (oSpan.fPt == thisPt) {
1020 goto skipExactMatches;
1021 }
1022 }
1023 for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
1024 SkOpSpan& oSpan = other1->fTs[oIdx];
1025 if (oSpan.fOther != other2) {
1026 continue;
1027 }
1028 if (SkDPoint::RoughlyEqual(oSpan.fPt, thisPt)) {
1029 SkOpSpan& oSpan2 = other2->fTs[oSpan.fOtherIndex];
1030 if (zero_or_one(span.fOtherT) || zero_or_one(oSpan.fT)
1031 || zero_or_one(span2.fOtherT) || zero_or_one(oSpan2.fT)) {
1032 return;
1033 }
1034 if (!way_roughly_equal(span.fOtherT, oSpan.fT)
1035 || !way_roughly_equal(span2.fOtherT, oSpan2.fT)
1036 || !way_roughly_equal(span2.fOtherT, oSpan.fOtherT)
1037 || !way_roughly_equal(span.fOtherT, oSpan2.fOtherT)) {
1038 return;
1039 }
1040 alignSpan(thisPt, span.fOtherT, other1, span2.fOtherT,
1041 other2, &oSpan, alignedArray);
1042 alignSpan(thisPt, span2.fOtherT, other2, span.fOtherT,
1043 other1, &oSpan2, alignedArray);
1044 break;
1045 }
1046 }
1047 skipExactMatches:
1048 ;
1049 }
1050 ++index;
1051 }
1052 }
1053 debugValidate();
1054}
1055
caryclark65f55312014-11-13 06:58:52 -08001056void SkOpSegment::alignRange(int lower, int upper,
1057 const SkOpSegment* other, int oLower, int oUpper) {
1058 for (int oIndex = oLower; oIndex <= oUpper; ++oIndex) {
1059 const SkOpSpan& oSpan = other->span(oIndex);
1060 const SkOpSegment* oOther = oSpan.fOther;
1061 if (oOther == this) {
1062 continue;
1063 }
1064 SkOpSpan* matchSpan;
1065 int matchIndex;
1066 const SkOpSpan* refSpan;
1067 for (int iIndex = lower; iIndex <= upper; ++iIndex) {
1068 const SkOpSpan& iSpan = this->span(iIndex);
1069 const SkOpSegment* iOther = iSpan.fOther;
1070 if (iOther == other) {
1071 continue;
1072 }
1073 if (iOther == oOther) {
1074 goto nextI;
1075 }
1076 }
1077 {
1078 // oSpan does not have a match in this
1079 int iCount = this->count();
1080 const SkOpSpan* iMatch = NULL;
1081 double iMatchTDiff;
1082 matchIndex = -1;
1083 for (int iIndex = 0; iIndex < iCount; ++iIndex) {
1084 const SkOpSpan& iSpan = this->span(iIndex);
1085 const SkOpSegment* iOther = iSpan.fOther;
1086 if (iOther != oOther) {
1087 continue;
1088 }
1089 double testTDiff = fabs(iSpan.fOtherT - oSpan.fOtherT);
1090 if (!iMatch || testTDiff < iMatchTDiff) {
1091 matchIndex = iIndex;
1092 iMatch = &iSpan;
1093 iMatchTDiff = testTDiff;
1094 }
1095 }
1096 if (matchIndex < 0) {
1097 continue; // the entry is missing, & will be picked up later (FIXME: fix it here?)
1098 }
1099 matchSpan = &this->fTs[matchIndex];
1100 refSpan = &this->span(lower);
1101 if (!SkDPoint::ApproximatelyEqual(matchSpan->fPt, refSpan->fPt)) {
1102 goto nextI;
1103 }
1104 if (matchIndex != lower - 1 && matchIndex != upper + 1) {
1105 // the consecutive spans need to be rearranged to get the missing one close
1106 continue; // FIXME: more work to do
1107 }
1108 }
1109 {
1110 this->fixOtherTIndex();
1111 SkScalar newT;
1112 if (matchSpan->fT != 0 && matchSpan->fT != 1) {
1113 newT = matchSpan->fT = refSpan->fT;
1114 matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = refSpan->fT;
1115 } else { // leave span at the start or end there and adjust the neighbors
1116 newT = matchSpan->fT;
1117 for (int iIndex = lower; iIndex <= upper; ++iIndex) {
1118 matchSpan = &this->fTs[iIndex];
1119 matchSpan->fT = newT;
1120 matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = newT;
1121 }
1122 }
1123 this->resetSpanFlags(); // fix up small / tiny / done
1124 // align ts of other ranges with adjacent spans that match the aligned points
1125 lower = SkTMin(lower, matchIndex);
1126 while (lower > 0) {
1127 const SkOpSpan& span = this->span(lower - 1);
1128 if (span.fT != newT) {
1129 break;
1130 }
1131 --lower;
1132 }
1133 upper = SkTMax(upper, matchIndex);
1134 int last = this->count() - 1;
1135 while (upper < last) {
1136 const SkOpSpan& span = this->span(upper + 1);
1137 if (span.fT != newT) {
1138 break;
1139 }
1140 ++upper;
1141 }
1142 for (int iIndex = lower; iIndex <= upper; ++iIndex) {
1143 const SkOpSpan& span = this->span(iIndex);
1144 SkOpSegment* aOther = span.fOther;
1145 int aLower = span.fOtherIndex;
1146 SkScalar aT = span.fOtherT;
1147 bool aResetFlags = false;
1148 while (aLower > 0) {
1149 SkOpSpan* aSpan = &aOther->fTs[aLower - 1];
1150 for (int iIndex = lower; iIndex <= upper; ++iIndex) {
1151 if (aSpan->fPt == this->fTs[iIndex].fPt) {
1152 goto matchFound;
1153 }
1154 }
1155 break;
1156 matchFound:
1157 --aLower;
1158 }
1159 int aUpper = span.fOtherIndex;
1160 int aLast = aOther->count() - 1;
1161 while (aUpper < aLast) {
1162 SkOpSpan* aSpan = &aOther->fTs[aUpper + 1];
1163 for (int iIndex = lower; iIndex <= upper; ++iIndex) {
1164 if (aSpan->fPt == this->fTs[iIndex].fPt) {
1165 goto matchFound2;
1166 }
1167 }
1168 break;
1169 matchFound2:
1170 ++aUpper;
1171 }
1172 if (aOther->fTs[aLower].fT == 0) {
1173 aT = 0;
1174 } else if (aOther->fTs[aUpper].fT == 1) {
1175 aT = 1;
1176 }
1177 bool aFixed = false;
1178 for (int aIndex = aLower; aIndex <= aUpper; ++aIndex) {
1179 SkOpSpan* aSpan = &aOther->fTs[aIndex];
1180 if (aSpan->fT == aT) {
1181 continue;
1182 }
1183 SkASSERT(way_roughly_equal(aSpan->fT, aT));
1184 if (!aFixed) {
1185 aOther->fixOtherTIndex();
1186 aFixed = true;
1187 }
1188 aSpan->fT = aT;
1189 aSpan->fOther->fTs[aSpan->fOtherIndex].fOtherT = aT;
1190 aResetFlags = true;
1191 }
1192 if (aResetFlags) {
1193 aOther->resetSpanFlags();
1194 }
1195 }
1196 }
1197nextI: ;
1198 }
1199}
1200
caryclarkdac1d172014-06-17 05:15:38 -07001201void SkOpSegment::alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other,
1202 double otherT, const SkOpSegment* other2, SkOpSpan* oSpan,
1203 SkTDArray<AlignedSpan>* alignedArray) {
1204 AlignedSpan* aligned = alignedArray->append();
1205 aligned->fOldPt = oSpan->fPt;
1206 aligned->fPt = newPt;
1207 aligned->fOldT = oSpan->fT;
1208 aligned->fT = newT;
1209 aligned->fSegment = this; // OPTIMIZE: may be unused, can remove
1210 aligned->fOther1 = other;
1211 aligned->fOther2 = other2;
1212 SkASSERT(SkDPoint::RoughlyEqual(oSpan->fPt, newPt));
1213 oSpan->fPt = newPt;
1214// SkASSERT(way_roughly_equal(oSpan->fT, newT));
1215 oSpan->fT = newT;
1216// SkASSERT(way_roughly_equal(oSpan->fOtherT, otherT));
1217 oSpan->fOtherT = otherT;
1218}
1219
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001220bool SkOpSegment::alignSpan(int index, double thisT, const SkPoint& thisPt) {
1221 bool aligned = false;
1222 SkOpSpan* span = &fTs[index];
1223 SkOpSegment* other = span->fOther;
1224 int oIndex = span->fOtherIndex;
1225 SkOpSpan* oSpan = &other->fTs[oIndex];
1226 if (span->fT != thisT) {
1227 span->fT = thisT;
1228 oSpan->fOtherT = thisT;
1229 aligned = true;
1230 }
1231 if (span->fPt != thisPt) {
1232 span->fPt = thisPt;
1233 oSpan->fPt = thisPt;
1234 aligned = true;
1235 }
1236 double oT = oSpan->fT;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001237 if (oT == 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001238 return aligned;
1239 }
1240 int oStart = other->nextSpan(oIndex, -1) + 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001241 oSpan = &other->fTs[oStart];
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001242 int otherIndex = oStart;
1243 if (oT == 1) {
1244 if (aligned) {
1245 while (oSpan->fPt == thisPt && oSpan->fT != 1) {
1246 oSpan->fTiny = true;
1247 ++oSpan;
1248 }
1249 }
1250 return aligned;
1251 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001252 oT = oSpan->fT;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001253 int oEnd = other->nextSpan(oIndex, 1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001254 bool oAligned = false;
1255 if (oSpan->fPt != thisPt) {
1256 oAligned |= other->alignSpan(oStart, oT, thisPt);
1257 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001258 while (++otherIndex < oEnd) {
1259 SkOpSpan* oNextSpan = &other->fTs[otherIndex];
1260 if (oNextSpan->fT != oT || oNextSpan->fPt != thisPt) {
1261 oAligned |= other->alignSpan(otherIndex, oT, thisPt);
1262 }
1263 }
1264 if (oAligned) {
1265 other->alignSpanState(oStart, oEnd);
1266 }
1267 return aligned;
1268}
1269
1270void SkOpSegment::alignSpanState(int start, int end) {
1271 SkOpSpan* lastSpan = &fTs[--end];
1272 bool allSmall = lastSpan->fSmall;
1273 bool allTiny = lastSpan->fTiny;
1274 bool allDone = lastSpan->fDone;
1275 SkDEBUGCODE(int winding = lastSpan->fWindValue);
1276 SkDEBUGCODE(int oppWinding = lastSpan->fOppValue);
1277 int index = start;
1278 while (index < end) {
1279 SkOpSpan* span = &fTs[index];
1280 span->fSmall = allSmall;
1281 span->fTiny = allTiny;
1282 if (span->fDone != allDone) {
1283 span->fDone = allDone;
1284 fDoneSpans += allDone ? 1 : -1;
1285 }
1286 SkASSERT(span->fWindValue == winding);
1287 SkASSERT(span->fOppValue == oppWinding);
1288 ++index;
1289 }
1290}
1291
caryclarkdac1d172014-06-17 05:15:38 -07001292void SkOpSegment::blindCancel(const SkCoincidence& coincidence, SkOpSegment* other) {
1293 bool binary = fOperand != other->fOperand;
1294 int index = 0;
1295 int last = this->count();
1296 do {
1297 SkOpSpan& span = this->fTs[--last];
1298 if (span.fT != 1 && !span.fSmall) {
1299 break;
1300 }
1301 span.fCoincident = true;
1302 } while (true);
1303 int oIndex = other->count();
1304 do {
1305 SkOpSpan& oSpan = other->fTs[--oIndex];
1306 if (oSpan.fT != 1 && !oSpan.fSmall) {
1307 break;
1308 }
1309 oSpan.fCoincident = true;
1310 } while (true);
1311 do {
1312 SkOpSpan* test = &this->fTs[index];
1313 int baseWind = test->fWindValue;
1314 int baseOpp = test->fOppValue;
1315 int endIndex = index;
1316 while (++endIndex <= last) {
1317 SkOpSpan* endSpan = &this->fTs[endIndex];
1318 SkASSERT(endSpan->fT < 1);
1319 if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
1320 break;
1321 }
1322 endSpan->fCoincident = true;
1323 }
1324 SkOpSpan* oTest = &other->fTs[oIndex];
1325 int oBaseWind = oTest->fWindValue;
1326 int oBaseOpp = oTest->fOppValue;
1327 int oStartIndex = oIndex;
1328 while (--oStartIndex >= 0) {
1329 SkOpSpan* oStartSpan = &other->fTs[oStartIndex];
1330 if (oStartSpan->fWindValue != oBaseWind || oStartSpan->fOppValue != oBaseOpp) {
1331 break;
1332 }
1333 oStartSpan->fCoincident = true;
1334 }
1335 bool decrement = baseWind && oBaseWind;
1336 bool bigger = baseWind >= oBaseWind;
1337 do {
1338 SkASSERT(test->fT < 1);
1339 if (decrement) {
1340 if (binary && bigger) {
1341 test->fOppValue--;
1342 } else {
1343 decrementSpan(test);
1344 }
1345 }
1346 test->fCoincident = true;
1347 test = &fTs[++index];
1348 } while (index < endIndex);
1349 do {
1350 SkASSERT(oTest->fT < 1);
1351 if (decrement) {
1352 if (binary && !bigger) {
1353 oTest->fOppValue--;
1354 } else {
1355 other->decrementSpan(oTest);
1356 }
1357 }
1358 oTest->fCoincident = true;
1359 oTest = &other->fTs[--oIndex];
1360 } while (oIndex > oStartIndex);
1361 } while (index <= last && oIndex >= 0);
1362 SkASSERT(index > last);
1363 SkASSERT(oIndex < 0);
1364}
1365
1366void SkOpSegment::blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other) {
1367 bool binary = fOperand != other->fOperand;
1368 int index = 0;
1369 int last = this->count();
1370 do {
1371 SkOpSpan& span = this->fTs[--last];
1372 if (span.fT != 1 && !span.fSmall) {
1373 break;
1374 }
1375 span.fCoincident = true;
1376 } while (true);
1377 int oIndex = 0;
1378 int oLast = other->count();
1379 do {
1380 SkOpSpan& oSpan = other->fTs[--oLast];
1381 if (oSpan.fT != 1 && !oSpan.fSmall) {
1382 break;
1383 }
1384 oSpan.fCoincident = true;
1385 } while (true);
1386 do {
1387 SkOpSpan* test = &this->fTs[index];
1388 int baseWind = test->fWindValue;
1389 int baseOpp = test->fOppValue;
1390 int endIndex = index;
1391 SkOpSpan* endSpan;
1392 while (++endIndex <= last) {
1393 endSpan = &this->fTs[endIndex];
1394 SkASSERT(endSpan->fT < 1);
1395 if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
1396 break;
1397 }
1398 endSpan->fCoincident = true;
1399 }
1400 SkOpSpan* oTest = &other->fTs[oIndex];
1401 int oBaseWind = oTest->fWindValue;
1402 int oBaseOpp = oTest->fOppValue;
1403 int oEndIndex = oIndex;
1404 SkOpSpan* oEndSpan;
1405 while (++oEndIndex <= oLast) {
1406 oEndSpan = &this->fTs[oEndIndex];
1407 SkASSERT(oEndSpan->fT < 1);
1408 if (oEndSpan->fWindValue != oBaseWind || oEndSpan->fOppValue != oBaseOpp) {
1409 break;
1410 }
1411 oEndSpan->fCoincident = true;
1412 }
1413 // consolidate the winding count even if done
1414 if ((test->fWindValue || test->fOppValue) && (oTest->fWindValue || oTest->fOppValue)) {
1415 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
1416 bumpCoincidentBlind(binary, index, endIndex);
1417 other->bumpCoincidentOBlind(oIndex, oEndIndex);
1418 } else {
1419 other->bumpCoincidentBlind(binary, oIndex, oEndIndex);
1420 bumpCoincidentOBlind(index, endIndex);
1421 }
1422 }
1423 index = endIndex;
1424 oIndex = oEndIndex;
1425 } while (index <= last && oIndex <= oLast);
1426 SkASSERT(index > last);
1427 SkASSERT(oIndex > oLast);
1428}
1429
1430void SkOpSegment::bumpCoincidentBlind(bool binary, int index, int endIndex) {
1431 const SkOpSpan& oTest = fTs[index];
1432 int oWindValue = oTest.fWindValue;
1433 int oOppValue = oTest.fOppValue;
1434 if (binary) {
1435 SkTSwap<int>(oWindValue, oOppValue);
1436 }
1437 do {
1438 (void) bumpSpan(&fTs[index], oWindValue, oOppValue);
1439 } while (++index < endIndex);
1440}
1441
caryclarkc06d9a72014-09-29 06:58:41 -07001442bool SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
caryclark@google.com570863f2013-09-16 15:55:01 +00001443 SkTArray<SkPoint, true>* outsideTs) {
1444 int index = *indexPtr;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001445 int oWindValue = oTest.fWindValue;
1446 int oOppValue = oTest.fOppValue;
caryclark@google.com570863f2013-09-16 15:55:01 +00001447 if (binary) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001448 SkTSwap<int>(oWindValue, oOppValue);
1449 }
1450 SkOpSpan* const test = &fTs[index];
1451 SkOpSpan* end = test;
caryclark@google.com570863f2013-09-16 15:55:01 +00001452 const SkPoint& oStartPt = oTest.fPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001453 do {
caryclarkc06d9a72014-09-29 06:58:41 -07001454 if (end->fDone && !end->fTiny && !end->fSmall) { // extremely large paths trigger this
1455 return false;
1456 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001457 if (bumpSpan(end, oWindValue, oOppValue)) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001458 TrackOutside(outsideTs, oStartPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001459 }
1460 end = &fTs[++index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001461 } while ((end->fPt == test->fPt || precisely_equal(end->fT, test->fT)) && end->fT < 1);
caryclark@google.com570863f2013-09-16 15:55:01 +00001462 *indexPtr = index;
caryclarkc06d9a72014-09-29 06:58:41 -07001463 return true;
caryclark@google.com570863f2013-09-16 15:55:01 +00001464}
1465
caryclarkdac1d172014-06-17 05:15:38 -07001466void SkOpSegment::bumpCoincidentOBlind(int index, int endIndex) {
1467 do {
1468 zeroSpan(&fTs[index]);
1469 } while (++index < endIndex);
1470}
1471
caryclark@google.com07393ca2013-04-08 11:47:37 +00001472// because of the order in which coincidences are resolved, this and other
1473// may not have the same intermediate points. Compute the corresponding
1474// intermediate T values (using this as the master, other as the follower)
1475// and walk other conditionally -- hoping that it catches up in the end
caryclark65f55312014-11-13 06:58:52 -08001476bool SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
1477 SkTArray<SkPoint, true>* oOutsidePts, const SkPoint& oEndPt) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001478 int oIndex = *oIndexPtr;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001479 SkOpSpan* const oTest = &fTs[oIndex];
1480 SkOpSpan* oEnd = oTest;
caryclark@google.com570863f2013-09-16 15:55:01 +00001481 const SkPoint& oStartPt = oTest->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001482 double oStartT = oTest->fT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001483#if 0 // FIXME : figure out what disabling this breaks
1484 const SkPoint& startPt = test.fPt;
1485 // this is always true since oEnd == oTest && oStartPt == oTest->fPt -- find proper condition
1486 if (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001487 TrackOutside(oOutsidePts, startPt);
1488 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001489#endif
caryclark65f55312014-11-13 06:58:52 -08001490 bool foundEnd = false;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001491 while (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
caryclark65f55312014-11-13 06:58:52 -08001492 foundEnd |= oEndPt == oEnd->fPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001493 zeroSpan(oEnd);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001494 oEnd = &fTs[++oIndex];
1495 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001496 *oIndexPtr = oIndex;
caryclark65f55312014-11-13 06:58:52 -08001497 return foundEnd;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001498}
1499
1500// FIXME: need to test this case:
1501// contourA has two segments that are coincident
1502// contourB has two segments that are coincident in the same place
1503// each ends up with +2/0 pairs for winding count
1504// since logic below doesn't transfer count (only increments/decrements) can this be
1505// resolved to +4/0 ?
1506
1507// set spans from start to end to increment the greater by one and decrement
1508// the lesser
caryclark630240d2014-09-19 06:33:31 -07001509bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
caryclark@google.com570863f2013-09-16 15:55:01 +00001510 SkOpSegment* other) {
1511 bool binary = fOperand != other->fOperand;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001512 int index = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +00001513 while (startPt != fTs[index].fPt) {
1514 SkASSERT(index < fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001515 ++index;
1516 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001517 double startT = fTs[index].fT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001518 while (index > 0 && precisely_equal(fTs[index - 1].fT, startT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001519 --index;
1520 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001521 int oIndex = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +00001522 while (startPt != other->fTs[oIndex].fPt) {
1523 SkASSERT(oIndex < other->fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001524 ++oIndex;
1525 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001526 double oStartT = other->fTs[oIndex].fT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001527 while (oIndex > 0 && precisely_equal(other->fTs[oIndex - 1].fT, oStartT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001528 --oIndex;
1529 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001530 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
1531 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001532 SkOpSpan* test = &fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +00001533 const SkPoint* testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001534 double testT = test->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001535 SkOpSpan* oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +00001536 const SkPoint* oTestPt = &oTest->fPt;
caryclark361b8b02014-09-08 10:25:38 -07001537 // paths with extreme data will fail this test and eject out of pathops altogether later on
1538 // SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001539 do {
caryclark@google.com570863f2013-09-16 15:55:01 +00001540 SkASSERT(test->fT < 1);
caryclark630240d2014-09-19 06:33:31 -07001541 if (oTest->fT == 1) {
1542 // paths with extreme data may be so mismatched that we fail here
1543 return false;
1544 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001545
1546 // consolidate the winding count even if done
caryclark65f55312014-11-13 06:58:52 -08001547 bool foundEnd = false;
caryclark@google.com570863f2013-09-16 15:55:01 +00001548 if ((test->fWindValue == 0 && test->fOppValue == 0)
1549 || (oTest->fWindValue == 0 && oTest->fOppValue == 0)) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001550 SkDEBUGCODE(int firstWind = test->fWindValue);
1551 SkDEBUGCODE(int firstOpp = test->fOppValue);
1552 do {
1553 SkASSERT(firstWind == fTs[index].fWindValue);
1554 SkASSERT(firstOpp == fTs[index].fOppValue);
1555 ++index;
1556 SkASSERT(index < fTs.count());
1557 } while (*testPt == fTs[index].fPt);
1558 SkDEBUGCODE(firstWind = oTest->fWindValue);
1559 SkDEBUGCODE(firstOpp = oTest->fOppValue);
1560 do {
1561 SkASSERT(firstWind == other->fTs[oIndex].fWindValue);
1562 SkASSERT(firstOpp == other->fTs[oIndex].fOppValue);
1563 ++oIndex;
1564 SkASSERT(oIndex < other->fTs.count());
1565 } while (*oTestPt == other->fTs[oIndex].fPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001566 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +00001567 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
caryclarkc06d9a72014-09-29 06:58:41 -07001568 if (!bumpCoincidentThis(*oTest, binary, &index, &outsidePts)) {
1569 return false;
1570 }
caryclark65f55312014-11-13 06:58:52 -08001571 foundEnd = other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt);
caryclark@google.com570863f2013-09-16 15:55:01 +00001572 } else {
caryclarkc06d9a72014-09-29 06:58:41 -07001573 if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
1574 return false;
1575 }
caryclark65f55312014-11-13 06:58:52 -08001576 foundEnd = bumpCoincidentOther(*oTest, &index, &outsidePts, endPt);
caryclark@google.com570863f2013-09-16 15:55:01 +00001577 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001578 }
1579 test = &fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +00001580 testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001581 testT = test->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001582 oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +00001583 oTestPt = &oTest->fPt;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001584 if (endPt == *testPt || precisely_equal(endT, testT)) {
1585 break;
1586 }
caryclark65f55312014-11-13 06:58:52 -08001587 if (0 && foundEnd) { // FIXME: this is likely needed but wait until a test case triggers it
1588 break;
1589 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001590// SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
caryclark@google.com570863f2013-09-16 15:55:01 +00001591 } while (endPt != *oTestPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001592 // in rare cases, one may have ended before the other
1593 if (endPt != *testPt && !precisely_equal(endT, testT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001594 int lastWind = test[-1].fWindValue;
1595 int lastOpp = test[-1].fOppValue;
1596 bool zero = lastWind == 0 && lastOpp == 0;
1597 do {
1598 if (test->fWindValue || test->fOppValue) {
1599 test->fWindValue = lastWind;
1600 test->fOppValue = lastOpp;
1601 if (zero) {
caryclark65f55312014-11-13 06:58:52 -08001602 SkASSERT(!test->fDone);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001603 test->fDone = true;
1604 ++fDoneSpans;
1605 }
1606 }
1607 test = &fTs[++index];
1608 testPt = &test->fPt;
1609 } while (endPt != *testPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001610 }
1611 if (endPt != *oTestPt) {
1612 // look ahead to see if zeroing more spans will allows us to catch up
1613 int oPeekIndex = oIndex;
1614 bool success = true;
1615 SkOpSpan* oPeek;
caryclarkdac1d172014-06-17 05:15:38 -07001616 int oCount = other->count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001617 do {
1618 oPeek = &other->fTs[oPeekIndex];
caryclarkdac1d172014-06-17 05:15:38 -07001619 if (++oPeekIndex == oCount) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001620 success = false;
1621 break;
1622 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001623 } while (endPt != oPeek->fPt);
1624 if (success) {
1625 // make sure the matching point completes the coincidence span
1626 success = false;
1627 do {
1628 if (oPeek->fOther == this) {
1629 success = true;
1630 break;
1631 }
caryclark19eb3b22014-07-18 05:08:14 -07001632 if (++oPeekIndex == oCount) {
1633 break;
1634 }
1635 oPeek = &other->fTs[oPeekIndex];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001636 } while (endPt == oPeek->fPt);
1637 }
1638 if (success) {
1639 do {
1640 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
caryclark65f55312014-11-13 06:58:52 -08001641 if (other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt)) {
1642 break;
1643 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001644 } else {
caryclarkc06d9a72014-09-29 06:58:41 -07001645 if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
1646 return false;
1647 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001648 }
1649 oTest = &other->fTs[oIndex];
1650 oTestPt = &oTest->fPt;
1651 } while (endPt != *oTestPt);
1652 }
1653 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001654 int outCount = outsidePts.count();
1655 if (!done() && outCount) {
1656 addCoinOutsides(outsidePts[0], endPt, other);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001657 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001658 if (!other->done() && oOutsidePts.count()) {
1659 other->addCoinOutsides(oOutsidePts[0], endPt, this);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001660 }
caryclarkdac1d172014-06-17 05:15:38 -07001661 setCoincidentRange(startPt, endPt, other);
1662 other->setCoincidentRange(startPt, endPt, this);
caryclark630240d2014-09-19 06:33:31 -07001663 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001664}
1665
1666// FIXME: this doesn't prevent the same span from being added twice
1667// fix in caller, SkASSERT here?
caryclark65b427c2014-09-18 10:32:57 -07001668// FIXME: this may erroneously reject adds for cubic loops
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001669const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
caryclarkdac1d172014-06-17 05:15:38 -07001670 const SkPoint& pt, const SkPoint& pt2) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001671 int tCount = fTs.count();
1672 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1673 const SkOpSpan& span = fTs[tIndex];
1674 if (!approximately_negative(span.fT - t)) {
1675 break;
1676 }
caryclark65b427c2014-09-18 10:32:57 -07001677 if (span.fOther == other) {
1678 bool tsMatch = approximately_equal(span.fT, t);
1679 bool otherTsMatch = approximately_equal(span.fOtherT, otherT);
1680 // FIXME: add cubic loop detecting logic here
1681 // if fLoop bit is set on span, that could be enough if addOtherT copies the bit
1682 // or if a new bit is added ala fOtherLoop
1683 if (tsMatch || otherTsMatch) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001684#if DEBUG_ADD_T_PAIR
caryclark65b427c2014-09-18 10:32:57 -07001685 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1686 __FUNCTION__, fID, t, other->fID, otherT);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001687#endif
caryclark65b427c2014-09-18 10:32:57 -07001688 return NULL;
1689 }
1690 }
1691 }
1692 int oCount = other->count();
1693 for (int oIndex = 0; oIndex < oCount; ++oIndex) {
1694 const SkOpSpan& oSpan = other->span(oIndex);
1695 if (!approximately_negative(oSpan.fT - otherT)) {
1696 break;
1697 }
1698 if (oSpan.fOther == this) {
1699 bool otherTsMatch = approximately_equal(oSpan.fT, otherT);
1700 bool tsMatch = approximately_equal(oSpan.fOtherT, t);
1701 if (otherTsMatch || tsMatch) {
1702#if DEBUG_ADD_T_PAIR
1703 SkDebugf("%s addTPair other duplicate this=%d %1.9g other=%d %1.9g\n",
1704 __FUNCTION__, fID, t, other->fID, otherT);
1705#endif
1706 return NULL;
1707 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001708 }
1709 }
1710#if DEBUG_ADD_T_PAIR
1711 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1712 __FUNCTION__, fID, t, other->fID, otherT);
1713#endif
caryclark65b427c2014-09-18 10:32:57 -07001714 SkASSERT(other != this);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001715 int insertedAt = addT(other, pt, t);
caryclarkdac1d172014-06-17 05:15:38 -07001716 int otherInsertedAt = other->addT(this, pt2, otherT);
caryclark65f55312014-11-13 06:58:52 -08001717 this->addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001718 other->addOtherT(otherInsertedAt, t, insertedAt);
caryclark65f55312014-11-13 06:58:52 -08001719 this->matchWindingValue(insertedAt, t, borrowWind);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001720 other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclarkdac1d172014-06-17 05:15:38 -07001721 SkOpSpan& span = this->fTs[insertedAt];
1722 if (pt != pt2) {
1723 span.fNear = true;
1724 SkOpSpan& oSpan = other->fTs[otherInsertedAt];
1725 oSpan.fNear = true;
1726 }
caryclark65f55312014-11-13 06:58:52 -08001727 // if the newly inserted spans match a neighbor on one but not the other, make them agree
1728 int lower = this->nextExactSpan(insertedAt, -1) + 1;
1729 int upper = this->nextExactSpan(insertedAt, 1) - 1;
1730 if (upper < 0) {
1731 upper = this->count() - 1;
1732 }
1733 int oLower = other->nextExactSpan(otherInsertedAt, -1) + 1;
1734 int oUpper = other->nextExactSpan(otherInsertedAt, 1) - 1;
1735 if (oUpper < 0) {
1736 oUpper = other->count() - 1;
1737 }
1738 if (lower == upper && oLower == oUpper) {
1739 return &span;
1740 }
1741#if DEBUG_CONCIDENT
1742 SkDebugf("%s id=%d lower=%d upper=%d other=%d oLower=%d oUpper=%d\n", __FUNCTION__,
1743 debugID(), lower, upper, other->debugID(), oLower, oUpper);
1744#endif
1745 // find the nearby spans in one range missing in the other
1746 this->alignRange(lower, upper, other, oLower, oUpper);
1747 other->alignRange(oLower, oUpper, this, lower, upper);
caryclarkdac1d172014-06-17 05:15:38 -07001748 return &span;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001749}
1750
caryclarkdac1d172014-06-17 05:15:38 -07001751const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
1752 const SkPoint& pt) {
1753 return addTPair(t, other, otherT, borrowWind, pt, pt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001754}
1755
caryclark@google.com570863f2013-09-16 15:55:01 +00001756bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
1757 const SkPoint midPt = ptAtT(midT);
1758 SkPathOpsBounds bounds;
1759 bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1760 bounds.sort();
1761 return bounds.almostContains(midPt);
1762}
1763
caryclark@google.com07393ca2013-04-08 11:47:37 +00001764bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const {
1765 if (lesser > greater) {
1766 SkTSwap<int>(lesser, greater);
1767 }
1768 return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
1769}
1770
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001771// in extreme cases (like the buffer overflow test) return false to abort
1772// for now, if one t value represents two different points, then the values are too extreme
1773// to generate meaningful results
1774bool SkOpSegment::calcAngles() {
1775 int spanCount = fTs.count();
1776 if (spanCount <= 2) {
1777 return spanCount == 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001778 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001779 int index = 1;
1780 const SkOpSpan* firstSpan = &fTs[index];
1781 int activePrior = checkSetAngle(0);
1782 const SkOpSpan* span = &fTs[0];
1783 if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) {
1784 index = findStartSpan(0); // curve start intersects
caryclark361b8b02014-09-08 10:25:38 -07001785 if (fTs[index].fT == 0) {
1786 return false;
1787 }
caryclarkdac1d172014-06-17 05:15:38 -07001788 SkASSERT(index > 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001789 if (activePrior >= 0) {
1790 addStartSpan(index);
1791 }
1792 }
1793 bool addEnd;
1794 int endIndex = spanCount - 1;
1795 span = &fTs[endIndex - 1];
1796 if ((addEnd = span->fT == 1 || span->fTiny)) { // if curve end intersects
1797 endIndex = findEndSpan(endIndex);
caryclarkdac1d172014-06-17 05:15:38 -07001798 SkASSERT(endIndex > 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001799 } else {
1800 addEnd = fTs[endIndex].fOtherT != 0 || fTs[endIndex].fOther->multipleStarts();
1801 }
1802 SkASSERT(endIndex >= index);
1803 int prior = 0;
1804 while (index < endIndex) {
1805 const SkOpSpan& fromSpan = fTs[index]; // for each intermediate intersection
1806 const SkOpSpan* lastSpan;
1807 span = &fromSpan;
1808 int start = index;
1809 do {
1810 lastSpan = span;
1811 span = &fTs[++index];
1812 SkASSERT(index < spanCount);
1813 if (!precisely_negative(span->fT - lastSpan->fT) && !lastSpan->fTiny) {
1814 break;
1815 }
1816 if (!SkDPoint::ApproximatelyEqual(lastSpan->fPt, span->fPt)) {
1817 return false;
1818 }
1819 } while (true);
caryclarkdac1d172014-06-17 05:15:38 -07001820 SkOpAngle* angle = NULL;
1821 SkOpAngle* priorAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001822 if (activePrior >= 0) {
1823 int pActive = firstActive(prior);
1824 SkASSERT(pActive < start);
caryclarkdac1d172014-06-17 05:15:38 -07001825 priorAngle = &fAngles.push_back();
1826 priorAngle->set(this, start, pActive);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001827 }
1828 int active = checkSetAngle(start);
1829 if (active >= 0) {
1830 SkASSERT(active < index);
caryclarkdac1d172014-06-17 05:15:38 -07001831 angle = &fAngles.push_back();
1832 angle->set(this, active, index);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001833 }
1834 #if DEBUG_ANGLE
1835 debugCheckPointsEqualish(start, index);
1836 #endif
1837 prior = start;
1838 do {
1839 const SkOpSpan* startSpan = &fTs[start - 1];
caryclarkdac1d172014-06-17 05:15:38 -07001840 if (!startSpan->fSmall || isCanceled(start - 1) || startSpan->fFromAngle
1841 || startSpan->fToAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001842 break;
1843 }
1844 --start;
1845 } while (start > 0);
1846 do {
1847 if (activePrior >= 0) {
caryclarkdac1d172014-06-17 05:15:38 -07001848 SkASSERT(fTs[start].fFromAngle == NULL);
1849 fTs[start].fFromAngle = priorAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001850 }
1851 if (active >= 0) {
caryclarkdac1d172014-06-17 05:15:38 -07001852 SkASSERT(fTs[start].fToAngle == NULL);
1853 fTs[start].fToAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001854 }
1855 } while (++start < index);
1856 activePrior = active;
1857 }
1858 if (addEnd && activePrior >= 0) {
1859 addEndSpan(endIndex);
1860 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001861 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001862}
1863
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001864int SkOpSegment::checkSetAngle(int tIndex) const {
1865 const SkOpSpan* span = &fTs[tIndex];
1866 while (span->fTiny /* || span->fSmall */) {
1867 span = &fTs[++tIndex];
caryclark@google.com07393ca2013-04-08 11:47:37 +00001868 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001869 return isCanceled(tIndex) ? -1 : tIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001870}
1871
caryclarkdac1d172014-06-17 05:15:38 -07001872// at this point, the span is already ordered, or unorderable
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001873int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
1874 SkASSERT(includeType != SkOpAngle::kUnaryXor);
1875 SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
caryclark65b427c2014-09-18 10:32:57 -07001876 if (NULL == firstAngle || NULL == firstAngle->next()) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001877 return SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001878 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001879 // if all angles have a computed winding,
1880 // or if no adjacent angles are orderable,
1881 // or if adjacent orderable angles have no computed winding,
1882 // there's nothing to do
caryclarkdac1d172014-06-17 05:15:38 -07001883 // if two orderable angles are adjacent, and both are next to orderable angles,
1884 // and one has winding computed, transfer to the other
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001885 SkOpAngle* baseAngle = NULL;
caryclark@google.com570863f2013-09-16 15:55:01 +00001886 bool tryReverse = false;
1887 // look for counterclockwise transfers
caryclarkdac1d172014-06-17 05:15:38 -07001888 SkOpAngle* angle = firstAngle->previous();
1889 SkOpAngle* next = angle->next();
1890 firstAngle = next;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001891 do {
caryclarkdac1d172014-06-17 05:15:38 -07001892 SkOpAngle* prior = angle;
1893 angle = next;
1894 next = angle->next();
1895 SkASSERT(prior->next() == angle);
1896 SkASSERT(angle->next() == next);
1897 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
1898 baseAngle = NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001899 continue;
1900 }
caryclarkdac1d172014-06-17 05:15:38 -07001901 int testWinding = angle->segment()->windSum(angle);
1902 if (SK_MinS32 != testWinding) {
1903 baseAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001904 tryReverse = true;
1905 continue;
1906 }
1907 if (baseAngle) {
1908 ComputeOneSum(baseAngle, angle, includeType);
1909 baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001910 }
caryclarkdac1d172014-06-17 05:15:38 -07001911 } while (next != firstAngle);
1912 if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001913 firstAngle = baseAngle;
1914 tryReverse = true;
1915 }
1916 if (tryReverse) {
1917 baseAngle = NULL;
caryclarkdac1d172014-06-17 05:15:38 -07001918 SkOpAngle* prior = firstAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +00001919 do {
caryclarkdac1d172014-06-17 05:15:38 -07001920 angle = prior;
1921 prior = angle->previous();
1922 SkASSERT(prior->next() == angle);
1923 next = angle->next();
1924 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
1925 baseAngle = NULL;
1926 continue;
1927 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001928 int testWinding = angle->segment()->windSum(angle);
1929 if (SK_MinS32 != testWinding) {
1930 baseAngle = angle;
caryclark@google.com570863f2013-09-16 15:55:01 +00001931 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001932 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001933 if (baseAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001934 ComputeOneSumReverse(baseAngle, angle, includeType);
1935 baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001936 }
caryclarkdac1d172014-06-17 05:15:38 -07001937 } while (prior != firstAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001938 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001939 int minIndex = SkMin32(startIndex, endIndex);
1940 return windSum(minIndex);
1941}
1942
caryclark@google.com570863f2013-09-16 15:55:01 +00001943void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
1944 SkOpAngle::IncludeType includeType) {
1945 const SkOpSegment* baseSegment = baseAngle->segment();
1946 int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
1947 int sumSuWinding;
1948 bool binary = includeType >= SkOpAngle::kBinarySingle;
1949 if (binary) {
1950 sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
1951 if (baseSegment->operand()) {
1952 SkTSwap<int>(sumMiWinding, sumSuWinding);
1953 }
1954 }
1955 SkOpSegment* nextSegment = nextAngle->segment();
1956 int maxWinding, sumWinding;
1957 SkOpSpan* last;
1958 if (binary) {
1959 int oppMaxWinding, oppSumWinding;
1960 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
1961 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1962 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001963 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001964 } else {
1965 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
1966 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001967 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001968 }
1969 nextAngle->setLastMarked(last);
1970}
1971
1972void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
1973 SkOpAngle::IncludeType includeType) {
1974 const SkOpSegment* baseSegment = baseAngle->segment();
1975 int sumMiWinding = baseSegment->updateWinding(baseAngle);
1976 int sumSuWinding;
1977 bool binary = includeType >= SkOpAngle::kBinarySingle;
1978 if (binary) {
1979 sumSuWinding = baseSegment->updateOppWinding(baseAngle);
1980 if (baseSegment->operand()) {
1981 SkTSwap<int>(sumMiWinding, sumSuWinding);
1982 }
1983 }
1984 SkOpSegment* nextSegment = nextAngle->segment();
1985 int maxWinding, sumWinding;
1986 SkOpSpan* last;
1987 if (binary) {
1988 int oppMaxWinding, oppSumWinding;
1989 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
1990 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1991 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001992 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001993 } else {
1994 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
1995 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001996 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001997 }
1998 nextAngle->setLastMarked(last);
1999}
2000
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002001bool SkOpSegment::containsPt(const SkPoint& pt, int index, int endIndex) const {
2002 int step = index < endIndex ? 1 : -1;
2003 do {
2004 const SkOpSpan& span = this->span(index);
2005 if (span.fPt == pt) {
2006 const SkOpSpan& endSpan = this->span(endIndex);
2007 return span.fT == endSpan.fT && pt != endSpan.fPt;
2008 }
2009 index += step;
2010 } while (index != endIndex);
2011 return false;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002012}
2013
caryclarkdac1d172014-06-17 05:15:38 -07002014bool SkOpSegment::containsT(double t, const SkOpSegment* other, double otherT) const {
2015 int count = this->count();
2016 for (int index = 0; index < count; ++index) {
2017 const SkOpSpan& span = fTs[index];
2018 if (t < span.fT) {
2019 return false;
2020 }
2021 if (t == span.fT) {
2022 if (other != span.fOther) {
2023 continue;
2024 }
2025 if (other->fVerb != SkPath::kCubic_Verb) {
2026 return true;
2027 }
2028 if (!other->fLoop) {
2029 return true;
2030 }
2031 double otherMidT = (otherT + span.fOtherT) / 2;
2032 SkPoint otherPt = other->ptAtT(otherMidT);
2033 return SkDPoint::ApproximatelyEqual(span.fPt, otherPt);
2034 }
2035 }
2036 return false;
2037}
2038
caryclark@google.com07393ca2013-04-08 11:47:37 +00002039int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
2040 bool* hitSomething, double mid, bool opp, bool current) const {
2041 SkScalar bottom = fBounds.fBottom;
2042 int bestTIndex = -1;
2043 if (bottom <= *bestY) {
2044 return bestTIndex;
2045 }
2046 SkScalar top = fBounds.fTop;
2047 if (top >= basePt.fY) {
2048 return bestTIndex;
2049 }
2050 if (fBounds.fLeft > basePt.fX) {
2051 return bestTIndex;
2052 }
2053 if (fBounds.fRight < basePt.fX) {
2054 return bestTIndex;
2055 }
2056 if (fBounds.fLeft == fBounds.fRight) {
2057 // if vertical, and directly above test point, wait for another one
2058 return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
2059 }
2060 // intersect ray starting at basePt with edge
2061 SkIntersections intersections;
2062 // OPTIMIZE: use specialty function that intersects ray with curve,
2063 // returning t values only for curve (we don't care about t on ray)
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002064 intersections.allowNear(false);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002065 int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
2066 (fPts, top, bottom, basePt.fX, false);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002067 if (pts == 0 || (current && pts == 1)) {
2068 return bestTIndex;
2069 }
2070 if (current) {
2071 SkASSERT(pts > 1);
2072 int closestIdx = 0;
2073 double closest = fabs(intersections[0][0] - mid);
2074 for (int idx = 1; idx < pts; ++idx) {
2075 double test = fabs(intersections[0][idx] - mid);
2076 if (closest > test) {
2077 closestIdx = idx;
2078 closest = test;
2079 }
2080 }
2081 intersections.quickRemoveOne(closestIdx, --pts);
2082 }
2083 double bestT = -1;
2084 for (int index = 0; index < pts; ++index) {
2085 double foundT = intersections[0][index];
2086 if (approximately_less_than_zero(foundT)
2087 || approximately_greater_than_one(foundT)) {
2088 continue;
2089 }
reed@google.com277c3f82013-05-31 15:17:50 +00002090 SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002091 if (approximately_negative(testY - *bestY)
2092 || approximately_negative(basePt.fY - testY)) {
2093 continue;
2094 }
2095 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
2096 return SK_MinS32; // if the intersection is edge on, wait for another one
2097 }
2098 if (fVerb > SkPath::kLine_Verb) {
reed@google.com277c3f82013-05-31 15:17:50 +00002099 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002100 if (approximately_zero(dx)) {
2101 return SK_MinS32; // hit vertical, wait for another one
2102 }
2103 }
2104 *bestY = testY;
2105 bestT = foundT;
2106 }
2107 if (bestT < 0) {
2108 return bestTIndex;
2109 }
2110 SkASSERT(bestT >= 0);
2111 SkASSERT(bestT <= 1);
2112 int start;
2113 int end = 0;
2114 do {
2115 start = end;
2116 end = nextSpan(start, 1);
2117 } while (fTs[end].fT < bestT);
2118 // FIXME: see next candidate for a better pattern to find the next start/end pair
2119 while (start + 1 < end && fTs[start].fDone) {
2120 ++start;
2121 }
2122 if (!isCanceled(start)) {
2123 *hitT = bestT;
2124 bestTIndex = start;
2125 *hitSomething = true;
2126 }
2127 return bestTIndex;
2128}
2129
caryclark@google.com570863f2013-09-16 15:55:01 +00002130bool SkOpSegment::decrementSpan(SkOpSpan* span) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002131 SkASSERT(span->fWindValue > 0);
2132 if (--(span->fWindValue) == 0) {
2133 if (!span->fOppValue && !span->fDone) {
2134 span->fDone = true;
2135 ++fDoneSpans;
caryclark@google.com570863f2013-09-16 15:55:01 +00002136 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002137 }
2138 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002139 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002140}
2141
2142bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002143 SkASSERT(!span->fDone || span->fTiny || span->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002144 span->fWindValue += windDelta;
2145 SkASSERT(span->fWindValue >= 0);
2146 span->fOppValue += oppDelta;
2147 SkASSERT(span->fOppValue >= 0);
2148 if (fXor) {
2149 span->fWindValue &= 1;
2150 }
2151 if (fOppXor) {
2152 span->fOppValue &= 1;
2153 }
2154 if (!span->fWindValue && !span->fOppValue) {
caryclark65f55312014-11-13 06:58:52 -08002155 if (!span->fDone) {
2156 span->fDone = true;
2157 ++fDoneSpans;
2158 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002159 return true;
2160 }
2161 return false;
2162}
2163
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002164const SkOpSpan& SkOpSegment::firstSpan(const SkOpSpan& thisSpan) const {
2165 const SkOpSpan* firstSpan = &thisSpan; // rewind to the start
2166 const SkOpSpan* beginSpan = fTs.begin();
2167 const SkPoint& testPt = thisSpan.fPt;
2168 while (firstSpan > beginSpan && firstSpan[-1].fPt == testPt) {
2169 --firstSpan;
2170 }
2171 return *firstSpan;
2172}
2173
2174const SkOpSpan& SkOpSegment::lastSpan(const SkOpSpan& thisSpan) const {
2175 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
2176 const SkOpSpan* lastSpan = &thisSpan; // find the end
2177 const SkPoint& testPt = thisSpan.fPt;
2178 while (lastSpan < endSpan && lastSpan[1].fPt == testPt) {
2179 ++lastSpan;
2180 }
2181 return *lastSpan;
2182}
2183
2184// with a loop, the comparison is move involved
2185// scan backwards and forwards to count all matching points
2186// (verify that there are twp scans marked as loops)
2187// compare that against 2 matching scans for loop plus other results
2188bool SkOpSegment::calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts) {
2189 const SkOpSpan& firstSpan = this->firstSpan(thisSpan); // rewind to the start
2190 const SkOpSpan& lastSpan = this->lastSpan(thisSpan); // find the end
2191 double firstLoopT = -1, lastLoopT = -1;
2192 const SkOpSpan* testSpan = &firstSpan - 1;
2193 while (++testSpan <= &lastSpan) {
2194 if (testSpan->fLoop) {
2195 firstLoopT = testSpan->fT;
2196 break;
2197 }
2198 }
2199 testSpan = &lastSpan + 1;
2200 while (--testSpan >= &firstSpan) {
2201 if (testSpan->fLoop) {
2202 lastLoopT = testSpan->fT;
2203 break;
2204 }
2205 }
2206 SkASSERT((firstLoopT == -1) == (lastLoopT == -1));
2207 if (firstLoopT == -1) {
2208 return false;
2209 }
2210 SkASSERT(firstLoopT < lastLoopT);
2211 testSpan = &firstSpan - 1;
2212 smallCounts[0] = smallCounts[1] = 0;
2213 while (++testSpan <= &lastSpan) {
2214 SkASSERT(approximately_equal(testSpan->fT, firstLoopT) +
2215 approximately_equal(testSpan->fT, lastLoopT) == 1);
2216 smallCounts[approximately_equal(testSpan->fT, lastLoopT)]++;
2217 }
2218 return true;
2219}
2220
caryclarkdac1d172014-06-17 05:15:38 -07002221double SkOpSegment::calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
2222 double hiEnd, const SkOpSegment* other, int thisStart) {
2223 if (max >= hiEnd) {
2224 return -1;
2225 }
2226 int end = findOtherT(hiEnd, ref);
2227 if (end < 0) {
2228 return -1;
2229 }
2230 double tHi = span(end).fT;
2231 double tLo, refLo;
2232 if (thisStart >= 0) {
2233 tLo = span(thisStart).fT;
2234 refLo = min;
2235 } else {
2236 int start1 = findOtherT(loEnd, ref);
2237 SkASSERT(start1 >= 0);
2238 tLo = span(start1).fT;
2239 refLo = loEnd;
2240 }
2241 double missingT = (max - refLo) / (hiEnd - refLo);
2242 missingT = tLo + missingT * (tHi - tLo);
2243 return missingT;
2244}
2245
2246double SkOpSegment::calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
2247 double hiEnd, const SkOpSegment* other, int thisEnd) {
2248 if (min <= loEnd) {
2249 return -1;
2250 }
2251 int start = findOtherT(loEnd, ref);
2252 if (start < 0) {
2253 return -1;
2254 }
2255 double tLo = span(start).fT;
2256 double tHi, refHi;
2257 if (thisEnd >= 0) {
2258 tHi = span(thisEnd).fT;
2259 refHi = max;
2260 } else {
2261 int end1 = findOtherT(hiEnd, ref);
2262 if (end1 < 0) {
2263 return -1;
2264 }
2265 tHi = span(end1).fT;
2266 refHi = hiEnd;
2267 }
2268 double missingT = (min - loEnd) / (refHi - loEnd);
2269 missingT = tLo + missingT * (tHi - tLo);
2270 return missingT;
2271}
2272
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002273// see if spans with two or more intersections have the same number on the other end
2274void SkOpSegment::checkDuplicates() {
2275 debugValidate();
2276 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
2277 int index;
2278 int endIndex = 0;
2279 bool endFound;
2280 do {
2281 index = endIndex;
2282 endIndex = nextExactSpan(index, 1);
2283 if ((endFound = endIndex < 0)) {
2284 endIndex = count();
2285 }
2286 int dupCount = endIndex - index;
2287 if (dupCount < 2) {
2288 continue;
2289 }
2290 do {
2291 const SkOpSpan* thisSpan = &fTs[index];
caryclarkdac1d172014-06-17 05:15:38 -07002292 if (thisSpan->fNear) {
2293 continue;
2294 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002295 SkOpSegment* other = thisSpan->fOther;
2296 int oIndex = thisSpan->fOtherIndex;
2297 int oStart = other->nextExactSpan(oIndex, -1) + 1;
2298 int oEnd = other->nextExactSpan(oIndex, 1);
2299 if (oEnd < 0) {
2300 oEnd = other->count();
2301 }
2302 int oCount = oEnd - oStart;
2303 // force the other to match its t and this pt if not on an end point
2304 if (oCount != dupCount) {
2305 MissingSpan& missing = missingSpans.push_back();
2306 missing.fOther = NULL;
2307 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
2308 missing.fPt = thisSpan->fPt;
2309 const SkOpSpan& oSpan = other->span(oIndex);
2310 if (oCount > dupCount) {
2311 missing.fSegment = this;
2312 missing.fT = thisSpan->fT;
2313 other->checkLinks(&oSpan, &missingSpans);
2314 } else {
2315 missing.fSegment = other;
2316 missing.fT = oSpan.fT;
2317 checkLinks(thisSpan, &missingSpans);
2318 }
2319 if (!missingSpans.back().fOther) {
2320 missingSpans.pop_back();
2321 }
2322 }
2323 } while (++index < endIndex);
2324 } while (!endFound);
2325 int missingCount = missingSpans.count();
2326 if (missingCount == 0) {
2327 return;
2328 }
2329 SkSTArray<kMissingSpanCount, MissingSpan, true> missingCoincidence;
2330 for (index = 0; index < missingCount; ++index) {
2331 MissingSpan& missing = missingSpans[index];
2332 SkOpSegment* missingOther = missing.fOther;
2333 if (missing.fSegment == missing.fOther) {
2334 continue;
2335 }
caryclarkdac1d172014-06-17 05:15:38 -07002336#if 0 // FIXME: this eliminates spurious data from skpwww_argus_presse_fr_41 but breaks
2337 // skpwww_fashionscandal_com_94 -- calcAngles complains, but I don't understand why
2338 if (missing.fSegment->containsT(missing.fT, missing.fOther, missing.fOtherT)) {
2339#if DEBUG_DUPLICATES
2340 SkDebugf("skip 1 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fSegment->fID,
2341 missing.fT, missing.fOther->fID, missing.fOtherT);
2342#endif
2343 continue;
2344 }
2345 if (missing.fOther->containsT(missing.fOtherT, missing.fSegment, missing.fT)) {
2346#if DEBUG_DUPLICATES
2347 SkDebugf("skip 2 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fOther->fID,
2348 missing.fOtherT, missing.fSegment->fID, missing.fT);
2349#endif
2350 continue;
2351 }
2352#endif
2353 // skip if adding would insert point into an existing coincindent span
2354 if (missing.fSegment->inCoincidentSpan(missing.fT, missingOther)
2355 && missingOther->inCoincidentSpan(missing.fOtherT, this)) {
2356 continue;
2357 }
2358 // skip if the created coincident spans are small
2359 if (missing.fSegment->coincidentSmall(missing.fPt, missing.fT, missingOther)
2360 && missingOther->coincidentSmall(missing.fPt, missing.fOtherT, missing.fSegment)) {
2361 continue;
2362 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002363 const SkOpSpan* added = missing.fSegment->addTPair(missing.fT, missingOther,
2364 missing.fOtherT, false, missing.fPt);
2365 if (added && added->fSmall) {
2366 missing.fSegment->checkSmallCoincidence(*added, &missingCoincidence);
2367 }
2368 }
2369 for (index = 0; index < missingCount; ++index) {
2370 MissingSpan& missing = missingSpans[index];
2371 missing.fSegment->fixOtherTIndex();
2372 missing.fOther->fixOtherTIndex();
2373 }
2374 for (index = 0; index < missingCoincidence.count(); ++index) {
2375 MissingSpan& missing = missingCoincidence[index];
2376 missing.fSegment->fixOtherTIndex();
2377 }
2378 debugValidate();
2379}
2380
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002381// look to see if the curve end intersects an intermediary that intersects the other
caryclark65f55312014-11-13 06:58:52 -08002382bool SkOpSegment::checkEnds() {
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002383 debugValidate();
caryclark@google.com570863f2013-09-16 15:55:01 +00002384 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002385 int count = fTs.count();
2386 for (int index = 0; index < count; ++index) {
2387 const SkOpSpan& span = fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +00002388 double otherT = span.fOtherT;
2389 if (otherT != 0 && otherT != 1) { // only check ends
2390 continue;
2391 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002392 const SkOpSegment* other = span.fOther;
caryclark@google.com570863f2013-09-16 15:55:01 +00002393 // peek start/last describe the range of spans that match the other t of this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002394 int peekStart = span.fOtherIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00002395 while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT)
2396 ;
2397 int otherCount = other->fTs.count();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002398 int peekLast = span.fOtherIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00002399 while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT)
2400 ;
2401 if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002402 continue;
2403 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002404 // t start/last describe the range of spans that match the t of this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002405 double t = span.fT;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002406 double tBottom = -1;
2407 int tStart = -1;
2408 int tLast = count;
2409 bool lastSmall = false;
2410 double afterT = t;
2411 for (int inner = 0; inner < count; ++inner) {
2412 double innerT = fTs[inner].fT;
2413 if (innerT <= t && innerT > tBottom) {
2414 if (innerT < t || !lastSmall) {
2415 tStart = inner - 1;
2416 }
2417 tBottom = innerT;
2418 }
2419 if (innerT > afterT) {
2420 if (t == afterT && lastSmall) {
2421 afterT = innerT;
2422 } else {
2423 tLast = inner;
2424 break;
2425 }
2426 }
2427 lastSmall = innerT <= t ? fTs[inner].fSmall : false;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002428 }
2429 for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) {
caryclark@google.com570863f2013-09-16 15:55:01 +00002430 if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002431 continue;
2432 }
2433 const SkOpSpan& peekSpan = other->fTs[peekIndex];
2434 SkOpSegment* match = peekSpan.fOther;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002435 if (match->done()) {
2436 continue; // if the edge has already been eaten (likely coincidence), ignore it
2437 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002438 const double matchT = peekSpan.fOtherT;
caryclark@google.com570863f2013-09-16 15:55:01 +00002439 // see if any of the spans match the other spans
2440 for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002441 const SkOpSpan& tSpan = fTs[tIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +00002442 if (tSpan.fOther == match) {
2443 if (tSpan.fOtherT == matchT) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002444 goto nextPeekIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00002445 }
2446 double midT = (tSpan.fOtherT + matchT) / 2;
2447 if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002448 goto nextPeekIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00002449 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002450 }
2451 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002452 if (missingSpans.count() > 0) {
2453 const MissingSpan& lastMissing = missingSpans.back();
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00002454 if (lastMissing.fT == t
caryclark@google.com570863f2013-09-16 15:55:01 +00002455 && lastMissing.fOther == match
2456 && lastMissing.fOtherT == matchT) {
caryclark65f55312014-11-13 06:58:52 -08002457 SkASSERT(SkDPoint::ApproximatelyEqual(lastMissing.fPt, peekSpan.fPt));
caryclark@google.com570863f2013-09-16 15:55:01 +00002458 continue;
2459 }
2460 }
caryclark65f55312014-11-13 06:58:52 -08002461 if (this == match) {
2462 return false; // extremely large paths can trigger this
2463 }
2464#if DEBUG_CHECK_ALIGN
caryclark@google.com570863f2013-09-16 15:55:01 +00002465 SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n",
2466 __FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY);
2467#endif
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002468 // this segment is missing a entry that the other contains
2469 // remember so we can add the missing one and recompute the indices
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002470 {
2471 MissingSpan& missing = missingSpans.push_back();
2472 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002473 missing.fT = t;
caryclark65b427c2014-09-18 10:32:57 -07002474 SkASSERT(this != match);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002475 missing.fOther = match;
2476 missing.fOtherT = matchT;
2477 missing.fPt = peekSpan.fPt;
2478 }
2479 break;
2480nextPeekIndex:
2481 ;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002482 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002483 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002484 if (missingSpans.count() == 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002485 debugValidate();
caryclark65f55312014-11-13 06:58:52 -08002486 return true;
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002487 }
2488 debugValidate();
caryclark@google.com570863f2013-09-16 15:55:01 +00002489 int missingCount = missingSpans.count();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002490 for (int index = 0; index < missingCount; ++index) {
caryclark@google.com570863f2013-09-16 15:55:01 +00002491 MissingSpan& missing = missingSpans[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002492 if (this != missing.fOther) {
2493 addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
2494 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002495 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002496 fixOtherTIndex();
caryclark@google.com570863f2013-09-16 15:55:01 +00002497 // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
2498 // avoid this
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002499 for (int index = 0; index < missingCount; ++index) {
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00002500 missingSpans[index].fOther->fixOtherTIndex();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002501 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002502 debugValidate();
caryclark65f55312014-11-13 06:58:52 -08002503 return true;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002504}
2505
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002506void SkOpSegment::checkLinks(const SkOpSpan* base,
2507 SkTArray<MissingSpan, true>* missingSpans) const {
2508 const SkOpSpan* first = fTs.begin();
2509 const SkOpSpan* last = fTs.end() - 1;
2510 SkASSERT(base >= first && last >= base);
2511 const SkOpSegment* other = base->fOther;
2512 const SkOpSpan* oFirst = other->fTs.begin();
2513 const SkOpSpan* oLast = other->fTs.end() - 1;
2514 const SkOpSpan* oSpan = &other->fTs[base->fOtherIndex];
2515 const SkOpSpan* test = base;
2516 const SkOpSpan* missing = NULL;
2517 while (test > first && (--test)->fPt == base->fPt) {
caryclark65b427c2014-09-18 10:32:57 -07002518 if (this == test->fOther) {
2519 continue;
2520 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002521 CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
2522 }
2523 test = base;
2524 while (test < last && (++test)->fPt == base->fPt) {
caryclark65f55312014-11-13 06:58:52 -08002525 SkASSERT(this != test->fOther || test->fLoop);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002526 CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
2527 }
2528}
2529
2530// see if spans with two or more intersections all agree on common t and point values
2531void SkOpSegment::checkMultiples() {
2532 debugValidate();
2533 int index;
2534 int end = 0;
2535 while (fTs[++end].fT == 0)
2536 ;
2537 while (fTs[end].fT < 1) {
2538 int start = index = end;
2539 end = nextExactSpan(index, 1);
2540 if (end <= index) {
2541 return; // buffer overflow example triggers this
2542 }
2543 if (index + 1 == end) {
2544 continue;
2545 }
2546 // force the duplicates to agree on t and pt if not on the end
caryclarkdac1d172014-06-17 05:15:38 -07002547 SkOpSpan& span = fTs[index];
2548 double thisT = span.fT;
2549 const SkPoint& thisPt = span.fPt;
2550 span.fMultiple = true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002551 bool aligned = false;
2552 while (++index < end) {
2553 aligned |= alignSpan(index, thisT, thisPt);
2554 }
2555 if (aligned) {
2556 alignSpanState(start, end);
2557 }
caryclarkdac1d172014-06-17 05:15:38 -07002558 fMultiples = true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002559 }
2560 debugValidate();
2561}
2562
2563void SkOpSegment::CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
2564 const SkOpSpan* oFirst, const SkOpSpan* oLast, const SkOpSpan** missingPtr,
2565 SkTArray<MissingSpan, true>* missingSpans) {
2566 SkASSERT(oSpan->fPt == test->fPt);
2567 const SkOpSpan* oTest = oSpan;
2568 while (oTest > oFirst && (--oTest)->fPt == test->fPt) {
2569 if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
2570 return;
2571 }
2572 }
2573 oTest = oSpan;
2574 while (oTest < oLast && (++oTest)->fPt == test->fPt) {
2575 if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
2576 return;
2577 }
2578 }
2579 if (*missingPtr) {
2580 missingSpans->push_back();
2581 }
2582 MissingSpan& lastMissing = missingSpans->back();
2583 if (*missingPtr) {
2584 lastMissing = missingSpans->end()[-2];
2585 }
2586 *missingPtr = test;
2587 lastMissing.fOther = test->fOther;
2588 lastMissing.fOtherT = test->fOtherT;
2589}
2590
caryclark@google.com570863f2013-09-16 15:55:01 +00002591bool SkOpSegment::checkSmall(int index) const {
2592 if (fTs[index].fSmall) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002593 return true;
2594 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002595 double tBase = fTs[index].fT;
2596 while (index > 0 && precisely_negative(tBase - fTs[--index].fT))
2597 ;
2598 return fTs[index].fSmall;
2599}
2600
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002601// a pair of curves may turn into coincident lines -- small may be a hint that that happened
2602// if a cubic contains a loop, the counts must be adjusted
2603void SkOpSegment::checkSmall() {
2604 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
2605 const SkOpSpan* beginSpan = fTs.begin();
2606 const SkOpSpan* thisSpan = beginSpan - 1;
2607 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
2608 while (++thisSpan < endSpan) {
2609 if (!thisSpan->fSmall) {
2610 continue;
2611 }
2612 if (!thisSpan->fWindValue) {
2613 continue;
2614 }
2615 const SkOpSpan& firstSpan = this->firstSpan(*thisSpan);
2616 const SkOpSpan& lastSpan = this->lastSpan(*thisSpan);
caryclark5e27e0e2014-08-12 07:46:33 -07002617 const SkOpSpan* nextSpan = &firstSpan + 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002618 ptrdiff_t smallCount = &lastSpan - &firstSpan + 1;
2619 SkASSERT(1 <= smallCount && smallCount < count());
caryclark5e27e0e2014-08-12 07:46:33 -07002620 if (smallCount <= 1 && !nextSpan->fSmall) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002621 SkASSERT(1 == smallCount);
2622 checkSmallCoincidence(firstSpan, NULL);
2623 continue;
2624 }
2625 // at this point, check for missing computed intersections
2626 const SkPoint& testPt = firstSpan.fPt;
2627 thisSpan = &firstSpan - 1;
2628 SkOpSegment* other = NULL;
2629 while (++thisSpan <= &lastSpan) {
2630 other = thisSpan->fOther;
2631 if (other != this) {
2632 break;
2633 }
2634 }
2635 SkASSERT(other != this);
2636 int oIndex = thisSpan->fOtherIndex;
2637 const SkOpSpan& oSpan = other->span(oIndex);
2638 const SkOpSpan& oFirstSpan = other->firstSpan(oSpan);
2639 const SkOpSpan& oLastSpan = other->lastSpan(oSpan);
2640 ptrdiff_t oCount = &oLastSpan - &oFirstSpan + 1;
2641 if (fLoop) {
2642 int smallCounts[2];
2643 SkASSERT(!other->fLoop); // FIXME: we need more complicated logic for pair of loops
2644 if (calcLoopSpanCount(*thisSpan, smallCounts)) {
2645 if (smallCounts[0] && oCount != smallCounts[0]) {
2646 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2647 }
2648 if (smallCounts[1] && oCount != smallCounts[1]) {
2649 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2650 }
2651 goto nextSmallCheck;
2652 }
2653 }
2654 if (other->fLoop) {
2655 int otherCounts[2];
2656 if (other->calcLoopSpanCount(other->span(oIndex), otherCounts)) {
2657 if (otherCounts[0] && otherCounts[0] != smallCount) {
2658 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2659 }
2660 if (otherCounts[1] && otherCounts[1] != smallCount) {
2661 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2662 }
2663 goto nextSmallCheck;
2664 }
2665 }
2666 if (oCount != smallCount) { // check if number of pts in this match other
2667 MissingSpan& missing = missingSpans.push_back();
2668 missing.fOther = NULL;
2669 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
2670 missing.fPt = testPt;
2671 const SkOpSpan& oSpan = other->span(oIndex);
2672 if (oCount > smallCount) {
2673 missing.fSegment = this;
2674 missing.fT = thisSpan->fT;
2675 other->checkLinks(&oSpan, &missingSpans);
2676 } else {
2677 missing.fSegment = other;
2678 missing.fT = oSpan.fT;
2679 checkLinks(thisSpan, &missingSpans);
2680 }
2681 if (!missingSpans.back().fOther || missing.fSegment->done()) {
2682 missingSpans.pop_back();
2683 }
2684 }
2685nextSmallCheck:
2686 thisSpan = &lastSpan;
2687 }
2688 int missingCount = missingSpans.count();
2689 for (int index = 0; index < missingCount; ++index) {
2690 MissingSpan& missing = missingSpans[index];
2691 SkOpSegment* missingOther = missing.fOther;
2692 // note that add t pair may edit span arrays, so prior pointers to spans are no longer valid
2693 if (!missing.fSegment->addTPair(missing.fT, missingOther, missing.fOtherT, false,
2694 missing.fPt)) {
2695 continue;
2696 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002697 int otherTIndex = missingOther->findT(missing.fOtherT, missing.fPt, missing.fSegment);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002698 const SkOpSpan& otherSpan = missingOther->span(otherTIndex);
2699 if (otherSpan.fSmall) {
2700 const SkOpSpan* nextSpan = &otherSpan;
caryclark65f55312014-11-13 06:58:52 -08002701 if (nextSpan->fPt == missing.fPt) {
2702 continue;
2703 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002704 do {
2705 ++nextSpan;
2706 } while (nextSpan->fSmall);
caryclark65f55312014-11-13 06:58:52 -08002707 if (nextSpan->fT == 1) {
2708 continue;
2709 }
caryclark630240d2014-09-19 06:33:31 -07002710 SkAssertResult(missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt,
2711 nextSpan->fT, missingOther));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002712 } else if (otherSpan.fT > 0) {
2713 const SkOpSpan* priorSpan = &otherSpan;
2714 do {
2715 --priorSpan;
2716 } while (priorSpan->fT == otherSpan.fT);
2717 if (priorSpan->fSmall) {
2718 missing.fSegment->addTCancel(missing.fPt, priorSpan->fPt, missingOther);
2719 }
2720 }
2721 }
2722 // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
2723 // avoid this
2724 for (int index = 0; index < missingCount; ++index) {
2725 MissingSpan& missing = missingSpans[index];
2726 missing.fSegment->fixOtherTIndex();
2727 missing.fOther->fixOtherTIndex();
2728 }
2729 debugValidate();
2730}
2731
2732void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span,
2733 SkTArray<MissingSpan, true>* checkMultiple) {
2734 SkASSERT(span.fSmall);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002735 if (0 && !span.fWindValue) {
2736 return;
2737 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002738 SkASSERT(&span < fTs.end() - 1);
2739 const SkOpSpan* next = &span + 1;
2740 SkASSERT(!next->fSmall || checkMultiple);
2741 if (checkMultiple) {
2742 while (next->fSmall) {
2743 ++next;
2744 SkASSERT(next < fTs.end());
2745 }
2746 }
2747 SkOpSegment* other = span.fOther;
2748 while (other != next->fOther) {
2749 if (!checkMultiple) {
2750 return;
2751 }
2752 const SkOpSpan* test = next + 1;
2753 if (test == fTs.end()) {
2754 return;
2755 }
2756 if (test->fPt != next->fPt || !precisely_equal(test->fT, next->fT)) {
2757 return;
2758 }
2759 next = test;
2760 }
2761 SkASSERT(span.fT < next->fT);
2762 int oStartIndex = other->findExactT(span.fOtherT, this);
2763 int oEndIndex = other->findExactT(next->fOtherT, this);
2764 // FIXME: be overly conservative by limiting this to the caller that allows multiple smalls
2765 if (!checkMultiple || fVerb != SkPath::kLine_Verb || other->fVerb != SkPath::kLine_Verb) {
2766 SkPoint mid = ptAtT((span.fT + next->fT) / 2);
2767 const SkOpSpan& oSpanStart = other->fTs[oStartIndex];
2768 const SkOpSpan& oSpanEnd = other->fTs[oEndIndex];
2769 SkPoint oMid = other->ptAtT((oSpanStart.fT + oSpanEnd.fT) / 2);
2770 if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
2771 return;
2772 }
2773 }
2774 // FIXME: again, be overly conservative to avoid breaking existing tests
2775 const SkOpSpan& oSpan = oStartIndex < oEndIndex ? other->fTs[oStartIndex]
2776 : other->fTs[oEndIndex];
2777 if (checkMultiple && !oSpan.fSmall) {
2778 return;
2779 }
caryclark65b427c2014-09-18 10:32:57 -07002780// SkASSERT(oSpan.fSmall);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002781 if (oStartIndex < oEndIndex) {
caryclark630240d2014-09-19 06:33:31 -07002782 SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, other));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002783 } else {
2784 addTCancel(span.fPt, next->fPt, other);
2785 }
2786 if (!checkMultiple) {
2787 return;
2788 }
2789 // check to see if either segment is coincident with a third segment -- if it is, and if
2790 // the opposite segment is not already coincident with the third, make it so
2791 // OPTIMIZE: to make this check easier, add coincident and cancel could set a coincident bit
2792 if (span.fWindValue != 1 || span.fOppValue != 0) {
2793// start here;
2794 // iterate through the spans, looking for the third coincident case
2795 // if we find one, we need to return state to the caller so that the indices can be fixed
2796 // this also suggests that all of this function is fragile since it relies on a valid index
2797 }
2798 // probably should make this a common function rather than copy/paste code
2799 if (oSpan.fWindValue != 1 || oSpan.fOppValue != 0) {
2800 const SkOpSpan* oTest = &oSpan;
2801 while (--oTest >= other->fTs.begin()) {
2802 if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
2803 break;
2804 }
2805 SkOpSegment* testOther = oTest->fOther;
2806 SkASSERT(testOther != this);
2807 // look in both directions to see if there is a coincident span
2808 const SkOpSpan* tTest = testOther->fTs.begin();
2809 for (int testIndex = 0; testIndex < testOther->count(); ++testIndex) {
2810 if (tTest->fPt != span.fPt) {
2811 ++tTest;
2812 continue;
2813 }
2814 if (testOther->verb() != SkPath::kLine_Verb
2815 || other->verb() != SkPath::kLine_Verb) {
2816 SkPoint mid = ptAtT((span.fT + next->fT) / 2);
2817 SkPoint oMid = other->ptAtT((oTest->fOtherT + tTest->fT) / 2);
2818 if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
2819 continue;
2820 }
skia.committer@gmail.coma1ed7ae2014-04-15 03:04:18 +00002821 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002822#if DEBUG_CONCIDENT
2823 SkDebugf("%s coincident found=%d %1.9g %1.9g\n", __FUNCTION__, testOther->fID,
2824 oTest->fOtherT, tTest->fT);
2825#endif
2826 if (tTest->fT < oTest->fOtherT) {
caryclark630240d2014-09-19 06:33:31 -07002827 SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, testOther));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002828 } else {
2829 addTCancel(span.fPt, next->fPt, testOther);
2830 }
2831 MissingSpan missing;
2832 missing.fSegment = testOther;
2833 checkMultiple->push_back(missing);
2834 break;
2835 }
2836 }
2837 oTest = &oSpan;
2838 while (++oTest < other->fTs.end()) {
2839 if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
2840 break;
2841 }
2842
2843 }
2844 }
2845}
2846
caryclark@google.com570863f2013-09-16 15:55:01 +00002847// if pair of spans on either side of tiny have the same end point and mid point, mark
2848// them as parallel
caryclark@google.com570863f2013-09-16 15:55:01 +00002849void SkOpSegment::checkTiny() {
2850 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
2851 SkOpSpan* thisSpan = fTs.begin() - 1;
2852 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be tiny
2853 while (++thisSpan < endSpan) {
2854 if (!thisSpan->fTiny) {
2855 continue;
2856 }
2857 SkOpSpan* nextSpan = thisSpan + 1;
2858 double thisT = thisSpan->fT;
2859 double nextT = nextSpan->fT;
2860 if (thisT == nextT) {
2861 continue;
2862 }
2863 SkASSERT(thisT < nextT);
2864 SkASSERT(thisSpan->fPt == nextSpan->fPt);
2865 SkOpSegment* thisOther = thisSpan->fOther;
2866 SkOpSegment* nextOther = nextSpan->fOther;
2867 int oIndex = thisSpan->fOtherIndex;
2868 for (int oStep = -1; oStep <= 1; oStep += 2) {
2869 int oEnd = thisOther->nextExactSpan(oIndex, oStep);
2870 if (oEnd < 0) {
2871 continue;
2872 }
2873 const SkOpSpan& oSpan = thisOther->span(oEnd);
2874 int nIndex = nextSpan->fOtherIndex;
2875 for (int nStep = -1; nStep <= 1; nStep += 2) {
2876 int nEnd = nextOther->nextExactSpan(nIndex, nStep);
2877 if (nEnd < 0) {
2878 continue;
2879 }
2880 const SkOpSpan& nSpan = nextOther->span(nEnd);
2881 if (oSpan.fPt != nSpan.fPt) {
2882 continue;
2883 }
2884 double oMidT = (thisSpan->fOtherT + oSpan.fT) / 2;
2885 const SkPoint& oPt = thisOther->ptAtT(oMidT);
2886 double nMidT = (nextSpan->fOtherT + nSpan.fT) / 2;
2887 const SkPoint& nPt = nextOther->ptAtT(nMidT);
2888 if (!AlmostEqualUlps(oPt, nPt)) {
2889 continue;
2890 }
2891#if DEBUG_CHECK_TINY
2892 SkDebugf("%s [%d] add coincidence [%d] [%d]\n", __FUNCTION__, fID,
2893 thisOther->fID, nextOther->fID);
2894#endif
2895 // this segment is missing a entry that the other contains
2896 // remember so we can add the missing one and recompute the indices
2897 MissingSpan& missing = missingSpans.push_back();
2898 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
caryclark@google.com570863f2013-09-16 15:55:01 +00002899 missing.fSegment = thisOther;
2900 missing.fT = thisSpan->fOtherT;
caryclark65b427c2014-09-18 10:32:57 -07002901 SkASSERT(this != nextOther);
caryclark@google.com570863f2013-09-16 15:55:01 +00002902 missing.fOther = nextOther;
2903 missing.fOtherT = nextSpan->fOtherT;
2904 missing.fPt = thisSpan->fPt;
2905 }
2906 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002907 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002908 int missingCount = missingSpans.count();
2909 if (!missingCount) {
2910 return;
2911 }
2912 for (int index = 0; index < missingCount; ++index) {
2913 MissingSpan& missing = missingSpans[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002914 if (missing.fSegment != missing.fOther) {
2915 missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false,
2916 missing.fPt);
2917 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002918 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002919 // OPTIMIZE: consolidate to avoid multiple calls to fix index
caryclark@google.com570863f2013-09-16 15:55:01 +00002920 for (int index = 0; index < missingCount; ++index) {
2921 MissingSpan& missing = missingSpans[index];
2922 missing.fSegment->fixOtherTIndex();
2923 missing.fOther->fixOtherTIndex();
2924 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002925}
2926
caryclarkdac1d172014-06-17 05:15:38 -07002927bool SkOpSegment::coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const {
2928 int count = this->count();
2929 for (int index = 0; index < count; ++index) {
2930 const SkOpSpan& span = this->span(index);
2931 if (span.fOther != other) {
2932 continue;
2933 }
2934 if (span.fPt == pt) {
2935 continue;
2936 }
2937 if (!AlmostEqualUlps(span.fPt, pt)) {
2938 continue;
2939 }
2940 if (fVerb != SkPath::kCubic_Verb) {
2941 return true;
2942 }
2943 double tInterval = t - span.fT;
2944 double tMid = t - tInterval / 2;
2945 SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
2946 return midPt.approximatelyEqual(xyAtT(t));
2947 }
2948 return false;
2949}
2950
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002951bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart,
2952 int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const {
2953 SkASSERT(span->fT == 0 || span->fT == 1);
2954 SkASSERT(span->fOtherT == 0 || span->fOtherT == 1);
2955 const SkOpSpan* otherSpan = &other->span(oEnd);
2956 double refT = otherSpan->fT;
2957 const SkPoint& refPt = otherSpan->fPt;
2958 const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0);
2959 do {
2960 const SkOpSegment* match = span->fOther;
2961 if (match == otherSpan->fOther) {
2962 // find start of respective spans and see if both have winding
2963 int startIndex, endIndex;
2964 if (span->fOtherT == 1) {
2965 endIndex = span->fOtherIndex;
2966 startIndex = match->nextExactSpan(endIndex, -1);
2967 } else {
2968 startIndex = span->fOtherIndex;
2969 endIndex = match->nextExactSpan(startIndex, 1);
2970 }
2971 const SkOpSpan& startSpan = match->span(startIndex);
2972 if (startSpan.fWindValue != 0) {
2973 // draw ray from endSpan.fPt perpendicular to end tangent and measure distance
2974 // to other segment.
2975 const SkOpSpan& endSpan = match->span(endIndex);
2976 SkDLine ray;
2977 SkVector dxdy;
2978 if (span->fOtherT == 1) {
2979 ray.fPts[0].set(startSpan.fPt);
2980 dxdy = match->dxdy(startIndex);
2981 } else {
2982 ray.fPts[0].set(endSpan.fPt);
2983 dxdy = match->dxdy(endIndex);
2984 }
2985 ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY;
2986 ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX;
2987 SkIntersections i;
2988 int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray);
2989 for (int index = 0; index < roots; ++index) {
2990 if (ray.fPts[0].approximatelyEqual(i.pt(index))) {
2991 double matchMidT = (match->span(startIndex).fT
2992 + match->span(endIndex).fT) / 2;
2993 SkPoint matchMidPt = match->ptAtT(matchMidT);
2994 double otherMidT = (i[0][index] + other->span(oStart).fT) / 2;
2995 SkPoint otherMidPt = other->ptAtT(otherMidT);
2996 if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) {
2997 *startPt = startSpan.fPt;
2998 *endPt = endSpan.fPt;
2999 *endT = endSpan.fT;
3000 return true;
3001 }
3002 }
3003 }
3004 }
3005 return false;
3006 }
3007 if (otherSpan == lastSpan) {
3008 break;
3009 }
3010 otherSpan += step;
3011 } while (otherSpan->fT == refT || otherSpan->fPt == refPt);
3012 return false;
3013}
3014
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003015int SkOpSegment::findEndSpan(int endIndex) const {
3016 const SkOpSpan* span = &fTs[--endIndex];
3017 const SkPoint& lastPt = span->fPt;
3018 double endT = span->fT;
3019 do {
3020 span = &fTs[--endIndex];
3021 } while (SkDPoint::ApproximatelyEqual(span->fPt, lastPt) && (span->fT == endT || span->fTiny));
3022 return endIndex + 1;
3023}
3024
caryclark@google.com07393ca2013-04-08 11:47:37 +00003025/*
3026 The M and S variable name parts stand for the operators.
3027 Mi stands for Minuend (see wiki subtraction, analogous to difference)
3028 Su stands for Subtrahend
3029 The Opp variable name part designates that the value is for the Opposite operator.
3030 Opposite values result from combining coincident spans.
3031 */
3032SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
3033 bool* unsortable, SkPathOp op, const int xorMiMask,
3034 const int xorSuMask) {
3035 const int startIndex = *nextStart;
3036 const int endIndex = *nextEnd;
3037 SkASSERT(startIndex != endIndex);
3038 SkDEBUGCODE(const int count = fTs.count());
3039 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
caryclarkdac1d172014-06-17 05:15:38 -07003040 int step = SkSign32(endIndex - startIndex);
3041 *nextStart = startIndex;
3042 SkOpSegment* other = isSimple(nextStart, &step);
3043 if (other)
3044 {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003045 // mark the smaller of startIndex, endIndex done, and all adjacent
3046 // spans with the same T value (but not 'other' spans)
3047#if DEBUG_WINDING
3048 SkDebugf("%s simple\n", __FUNCTION__);
3049#endif
3050 int min = SkMin32(startIndex, endIndex);
3051 if (fTs[min].fDone) {
3052 return NULL;
3053 }
3054 markDoneBinary(min);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003055 double startT = other->fTs[*nextStart].fT;
3056 *nextEnd = *nextStart;
3057 do {
3058 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00003059 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003060 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003061 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +00003062 *unsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003063 return NULL;
3064 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003065 return other;
3066 }
caryclarkdac1d172014-06-17 05:15:38 -07003067 const int end = nextExactSpan(startIndex, step);
3068 SkASSERT(end >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003069 SkASSERT(startIndex - endIndex != 0);
3070 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003071 // more than one viable candidate -- measure angles to find best
3072
3073 int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp);
caryclark@google.com570863f2013-09-16 15:55:01 +00003074 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003075 if (!sortable) {
3076 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003077 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003078 return NULL;
3079 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003080 SkOpAngle* angle = spanToAngle(end, startIndex);
3081 if (angle->unorderable()) {
3082 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003083 markDoneBinary(SkMin32(startIndex, endIndex));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003084 return NULL;
3085 }
3086#if DEBUG_SORT
3087 SkDebugf("%s\n", __FUNCTION__);
3088 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003089#endif
3090 int sumMiWinding = updateWinding(endIndex, startIndex);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003091 if (sumMiWinding == SK_MinS32) {
3092 *unsortable = true;
3093 markDoneBinary(SkMin32(startIndex, endIndex));
3094 return NULL;
3095 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003096 int sumSuWinding = updateOppWinding(endIndex, startIndex);
3097 if (operand()) {
3098 SkTSwap<int>(sumMiWinding, sumSuWinding);
3099 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003100 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003101 const SkOpAngle* foundAngle = NULL;
3102 bool foundDone = false;
3103 // iterate through the angle, and compute everyone's winding
3104 SkOpSegment* nextSegment;
3105 int activeCount = 0;
3106 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003107 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003108 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003109 nextAngle->end(), op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003110 if (activeAngle) {
3111 ++activeCount;
3112 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003113 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003114 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003115 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003116 return NULL;
3117 }
3118 foundAngle = nextAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +00003119 foundDone = nextSegment->done(nextAngle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003120 }
3121 }
3122 if (nextSegment->done()) {
3123 continue;
3124 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003125 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003126 continue;
3127 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003128 if (!activeAngle) {
caryclarkdac1d172014-06-17 05:15:38 -07003129 (void) nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
caryclark@google.com570863f2013-09-16 15:55:01 +00003130 }
3131 SkOpSpan* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003132 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003133 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003134 *chase->append() = last;
3135#if DEBUG_WINDING
caryclark@google.com570863f2013-09-16 15:55:01 +00003136 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
3137 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
3138 last->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003139#endif
3140 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003141 } while ((nextAngle = nextAngle->next()) != angle);
3142#if DEBUG_ANGLE
3143 if (foundAngle) {
3144 foundAngle->debugSameAs(foundAngle);
3145 }
3146#endif
3147
caryclark@google.com07393ca2013-04-08 11:47:37 +00003148 markDoneBinary(SkMin32(startIndex, endIndex));
3149 if (!foundAngle) {
3150 return NULL;
3151 }
3152 *nextStart = foundAngle->start();
3153 *nextEnd = foundAngle->end();
3154 nextSegment = foundAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003155#if DEBUG_WINDING
3156 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
3157 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
3158 #endif
3159 return nextSegment;
3160}
3161
3162SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart,
3163 int* nextEnd, bool* unsortable) {
3164 const int startIndex = *nextStart;
3165 const int endIndex = *nextEnd;
3166 SkASSERT(startIndex != endIndex);
3167 SkDEBUGCODE(const int count = fTs.count());
3168 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
caryclarkdac1d172014-06-17 05:15:38 -07003169 int step = SkSign32(endIndex - startIndex);
3170 *nextStart = startIndex;
3171 SkOpSegment* other = isSimple(nextStart, &step);
3172 if (other)
3173 {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003174 // mark the smaller of startIndex, endIndex done, and all adjacent
3175 // spans with the same T value (but not 'other' spans)
3176#if DEBUG_WINDING
3177 SkDebugf("%s simple\n", __FUNCTION__);
3178#endif
3179 int min = SkMin32(startIndex, endIndex);
3180 if (fTs[min].fDone) {
3181 return NULL;
3182 }
3183 markDoneUnary(min);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003184 double startT = other->fTs[*nextStart].fT;
3185 *nextEnd = *nextStart;
3186 do {
3187 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00003188 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003189 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
caryclark@google.com570863f2013-09-16 15:55:01 +00003190 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
3191 *unsortable = true;
3192 return NULL;
3193 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003194 return other;
3195 }
caryclarkdac1d172014-06-17 05:15:38 -07003196 const int end = nextExactSpan(startIndex, step);
3197 SkASSERT(end >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003198 SkASSERT(startIndex - endIndex != 0);
3199 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003200 // more than one viable candidate -- measure angles to find best
3201
3202 int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00003203 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003204 if (!sortable) {
3205 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003206 markDoneUnary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003207 return NULL;
3208 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003209 SkOpAngle* angle = spanToAngle(end, startIndex);
3210#if DEBUG_SORT
3211 SkDebugf("%s\n", __FUNCTION__);
3212 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003213#endif
3214 int sumWinding = updateWinding(endIndex, startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003215 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003216 const SkOpAngle* foundAngle = NULL;
3217 bool foundDone = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003218 SkOpSegment* nextSegment;
3219 int activeCount = 0;
3220 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003221 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003222 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003223 &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003224 if (activeAngle) {
3225 ++activeCount;
3226 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003227 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003228 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003229 markDoneUnary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003230 return NULL;
3231 }
3232 foundAngle = nextAngle;
3233 foundDone = nextSegment->done(nextAngle);
3234 }
3235 }
3236 if (nextSegment->done()) {
3237 continue;
3238 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003239 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003240 continue;
3241 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003242 if (!activeAngle) {
3243 nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
3244 }
3245 SkOpSpan* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003246 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003247 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003248 *chase->append() = last;
3249#if DEBUG_WINDING
caryclark@google.com570863f2013-09-16 15:55:01 +00003250 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
3251 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
3252 last->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003253#endif
3254 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003255 } while ((nextAngle = nextAngle->next()) != angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003256 markDoneUnary(SkMin32(startIndex, endIndex));
3257 if (!foundAngle) {
3258 return NULL;
3259 }
3260 *nextStart = foundAngle->start();
3261 *nextEnd = foundAngle->end();
3262 nextSegment = foundAngle->segment();
3263#if DEBUG_WINDING
3264 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
3265 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
3266 #endif
3267 return nextSegment;
3268}
3269
3270SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsortable) {
3271 const int startIndex = *nextStart;
3272 const int endIndex = *nextEnd;
3273 SkASSERT(startIndex != endIndex);
3274 SkDEBUGCODE(int count = fTs.count());
caryclark@google.com570863f2013-09-16 15:55:01 +00003275 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003276 int step = SkSign32(endIndex - startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003277// Detect cases where all the ends canceled out (e.g.,
caryclarkdac1d172014-06-17 05:15:38 -07003278// there is no angle) and therefore there's only one valid connection
3279 *nextStart = startIndex;
3280 SkOpSegment* other = isSimple(nextStart, &step);
3281 if (other)
3282 {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003283#if DEBUG_WINDING
3284 SkDebugf("%s simple\n", __FUNCTION__);
3285#endif
3286 int min = SkMin32(startIndex, endIndex);
3287 if (fTs[min].fDone) {
3288 return NULL;
3289 }
3290 markDone(min, 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003291 double startT = other->fTs[*nextStart].fT;
3292 // FIXME: I don't know why the logic here is difference from the winding case
3293 SkDEBUGCODE(bool firstLoop = true;)
3294 if ((approximately_less_than_zero(startT) && step < 0)
3295 || (approximately_greater_than_one(startT) && step > 0)) {
3296 step = -step;
3297 SkDEBUGCODE(firstLoop = false;)
3298 }
3299 do {
3300 *nextEnd = *nextStart;
3301 do {
3302 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00003303 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00003304 if (other->fTs[SkMin32(*nextStart, *nextEnd)].fWindValue) {
3305 break;
3306 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003307 SkASSERT(firstLoop);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003308 SkDEBUGCODE(firstLoop = false;)
3309 step = -step;
3310 } while (true);
3311 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
3312 return other;
3313 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003314 SkASSERT(startIndex - endIndex != 0);
3315 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003316 // parallel block above with presorted version
caryclarkdac1d172014-06-17 05:15:38 -07003317 int end = nextExactSpan(startIndex, step);
3318 SkASSERT(end >= 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003319 SkOpAngle* angle = spanToAngle(end, startIndex);
3320 SkASSERT(angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003321#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003322 SkDebugf("%s\n", __FUNCTION__);
3323 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003324#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003325 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003326 const SkOpAngle* foundAngle = NULL;
3327 bool foundDone = false;
3328 SkOpSegment* nextSegment;
3329 int activeCount = 0;
3330 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003331 nextSegment = nextAngle->segment();
3332 ++activeCount;
3333 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003334 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003335 *unsortable = true;
3336 return NULL;
3337 }
3338 foundAngle = nextAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003339 if (!(foundDone = nextSegment->done(nextAngle))) {
3340 break;
3341 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003342 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003343 nextAngle = nextAngle->next();
3344 } while (nextAngle != angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003345 markDone(SkMin32(startIndex, endIndex), 1);
3346 if (!foundAngle) {
3347 return NULL;
3348 }
3349 *nextStart = foundAngle->start();
3350 *nextEnd = foundAngle->end();
3351 nextSegment = foundAngle->segment();
3352#if DEBUG_WINDING
3353 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
3354 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
3355 #endif
3356 return nextSegment;
3357}
3358
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003359int SkOpSegment::findStartSpan(int startIndex) const {
3360 int index = startIndex;
3361 const SkOpSpan* span = &fTs[index];
3362 const SkPoint& firstPt = span->fPt;
3363 double firstT = span->fT;
caryclarkdac1d172014-06-17 05:15:38 -07003364 const SkOpSpan* prior;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003365 do {
caryclarkdac1d172014-06-17 05:15:38 -07003366 prior = span;
3367 span = &fTs[++index];
3368 } while (SkDPoint::ApproximatelyEqual(span->fPt, firstPt)
3369 && (span->fT == firstT || prior->fTiny));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003370 return index;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003371}
3372
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003373int SkOpSegment::findExactT(double t, const SkOpSegment* match) const {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003374 int count = this->count();
3375 for (int index = 0; index < count; ++index) {
3376 const SkOpSpan& span = fTs[index];
3377 if (span.fT == t && span.fOther == match) {
3378 return index;
3379 }
3380 }
3381 SkASSERT(0);
3382 return -1;
3383}
3384
caryclark65f55312014-11-13 06:58:52 -08003385
3386
caryclarkdac1d172014-06-17 05:15:38 -07003387int SkOpSegment::findOtherT(double t, const SkOpSegment* match) const {
3388 int count = this->count();
3389 for (int index = 0; index < count; ++index) {
3390 const SkOpSpan& span = fTs[index];
3391 if (span.fOtherT == t && span.fOther == match) {
3392 return index;
3393 }
3394 }
3395 return -1;
3396}
3397
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003398int SkOpSegment::findT(double t, const SkPoint& pt, const SkOpSegment* match) const {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003399 int count = this->count();
caryclark5e27e0e2014-08-12 07:46:33 -07003400 // prefer exact matches over approximate matches
3401 for (int index = 0; index < count; ++index) {
3402 const SkOpSpan& span = fTs[index];
3403 if (span.fT == t && span.fOther == match) {
3404 return index;
3405 }
3406 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003407 for (int index = 0; index < count; ++index) {
3408 const SkOpSpan& span = fTs[index];
3409 if (approximately_equal_orderable(span.fT, t) && span.fOther == match) {
3410 return index;
3411 }
3412 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003413 // Usually, the pair of ts are an exact match. It's possible that the t values have
3414 // been adjusted to make multiple intersections align. In this rare case, look for a
3415 // matching point / match pair instead.
3416 for (int index = 0; index < count; ++index) {
3417 const SkOpSpan& span = fTs[index];
3418 if (span.fPt == pt && span.fOther == match) {
3419 return index;
3420 }
3421 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003422 SkASSERT(0);
3423 return -1;
3424}
caryclark@google.com07393ca2013-04-08 11:47:37 +00003425
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003426SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable,
3427 bool firstPass) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003428 // iterate through T intersections and return topmost
3429 // topmost tangent from y-min to first pt is closer to horizontal
3430 SkASSERT(!done());
3431 int firstT = -1;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003432 /* SkPoint topPt = */ activeLeftTop(&firstT);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003433 if (firstT < 0) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003434 *unsortable = !firstPass;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003435 firstT = 0;
3436 while (fTs[firstT].fDone) {
3437 SkASSERT(firstT < fTs.count());
3438 ++firstT;
3439 }
3440 *tIndexPtr = firstT;
3441 *endIndexPtr = nextExactSpan(firstT, 1);
3442 return this;
3443 }
3444 // sort the edges to find the leftmost
3445 int step = 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003446 int end;
3447 if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003448 step = -1;
3449 end = nextSpan(firstT, step);
3450 SkASSERT(end != -1);
3451 }
3452 // if the topmost T is not on end, or is three-way or more, find left
3453 // look for left-ness from tLeft to firstT (matching y of other)
caryclark@google.com07393ca2013-04-08 11:47:37 +00003454 SkASSERT(firstT - end != 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003455 SkOpAngle* markAngle = spanToAngle(firstT, end);
3456 if (!markAngle) {
caryclarkdac1d172014-06-17 05:15:38 -07003457 markAngle = addSingletonAngles(step);
caryclark@google.com570863f2013-09-16 15:55:01 +00003458 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003459 markAngle->markStops();
caryclarke4097e32014-06-18 07:24:19 -07003460 const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
3461 : markAngle->findFirst();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003462 if (!baseAngle) {
3463 return NULL; // nothing to do
caryclark@google.com570863f2013-09-16 15:55:01 +00003464 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003465 SkScalar top = SK_ScalarMax;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003466 const SkOpAngle* firstAngle = NULL;
3467 const SkOpAngle* angle = baseAngle;
3468 do {
3469 if (!angle->unorderable()) {
3470 SkOpSegment* next = angle->segment();
3471 SkPathOpsBounds bounds;
3472 next->subDivideBounds(angle->end(), angle->start(), &bounds);
caryclark65f55312014-11-13 06:58:52 -08003473 bool nearSame = AlmostEqualUlps(top, bounds.top());
3474 bool lowerSector = !firstAngle || angle->sectorEnd() < firstAngle->sectorStart();
3475 bool lesserSector = top > bounds.fTop;
3476 if (lesserSector && (!nearSame || lowerSector)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003477 top = bounds.fTop;
3478 firstAngle = angle;
3479 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003480 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003481 angle = angle->next();
3482 } while (angle != baseAngle);
caryclark65f55312014-11-13 06:58:52 -08003483 if (!firstAngle) {
3484 return NULL; // if all are unorderable, give up
3485 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003486#if DEBUG_SORT
3487 SkDebugf("%s\n", __FUNCTION__);
3488 firstAngle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003489#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003490 // skip edges that have already been processed
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003491 angle = firstAngle;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003492 SkOpSegment* leftSegment = NULL;
3493 bool looped = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003494 do {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003495 *unsortable = angle->unorderable();
3496 if (firstPass || !*unsortable) {
3497 leftSegment = angle->segment();
3498 *tIndexPtr = angle->end();
3499 *endIndexPtr = angle->start();
3500 if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
3501 break;
3502 }
3503 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003504 angle = angle->next();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003505 looped = true;
3506 } while (angle != firstAngle);
3507 if (angle == firstAngle && looped) {
3508 return NULL;
3509 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003510 if (leftSegment->verb() >= SkPath::kQuad_Verb) {
3511 const int tIndex = *tIndexPtr;
3512 const int endIndex = *endIndexPtr;
caryclarke4097e32014-06-18 07:24:19 -07003513 bool swap;
3514 if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003515 #if DEBUG_SWAP_TOP
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003516 SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
3517 __FUNCTION__,
3518 swap, leftSegment->debugInflections(tIndex, endIndex),
caryclark@google.com07393ca2013-04-08 11:47:37 +00003519 leftSegment->serpentine(tIndex, endIndex),
3520 leftSegment->controlsContainedByEnds(tIndex, endIndex),
3521 leftSegment->monotonicInY(tIndex, endIndex));
3522 #endif
3523 if (swap) {
3524 // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
3525 // sorted but merely the first not already processed (i.e., not done)
3526 SkTSwap(*tIndexPtr, *endIndexPtr);
3527 }
3528 }
3529 }
3530 SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
3531 return leftSegment;
3532}
3533
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003534int SkOpSegment::firstActive(int tIndex) const {
3535 while (fTs[tIndex].fTiny) {
3536 SkASSERT(!isCanceled(tIndex));
3537 ++tIndex;
3538 }
3539 return tIndex;
3540}
3541
caryclark@google.com07393ca2013-04-08 11:47:37 +00003542// FIXME: not crazy about this
3543// when the intersections are performed, the other index is into an
3544// incomplete array. As the array grows, the indices become incorrect
3545// while the following fixes the indices up again, it isn't smart about
3546// skipping segments whose indices are already correct
3547// assuming we leave the code that wrote the index in the first place
caryclark@google.com570863f2013-09-16 15:55:01 +00003548// FIXME: if called after remove, this needs to correct tiny
caryclark@google.com07393ca2013-04-08 11:47:37 +00003549void SkOpSegment::fixOtherTIndex() {
3550 int iCount = fTs.count();
3551 for (int i = 0; i < iCount; ++i) {
3552 SkOpSpan& iSpan = fTs[i];
3553 double oT = iSpan.fOtherT;
3554 SkOpSegment* other = iSpan.fOther;
3555 int oCount = other->fTs.count();
caryclark@google.com4fdbb222013-07-23 15:27:41 +00003556 SkDEBUGCODE(iSpan.fOtherIndex = -1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003557 for (int o = 0; o < oCount; ++o) {
3558 SkOpSpan& oSpan = other->fTs[o];
3559 if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) {
3560 iSpan.fOtherIndex = o;
caryclark@google.com4fdbb222013-07-23 15:27:41 +00003561 oSpan.fOtherIndex = i;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003562 break;
3563 }
3564 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00003565 SkASSERT(iSpan.fOtherIndex >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003566 }
3567}
3568
caryclarkdac1d172014-06-17 05:15:38 -07003569bool SkOpSegment::inCoincidentSpan(double t, const SkOpSegment* other) const {
3570 int foundEnds = 0;
3571 int count = this->count();
3572 for (int index = 0; index < count; ++index) {
3573 const SkOpSpan& span = this->span(index);
3574 if (span.fCoincident) {
3575 foundEnds |= (span.fOther == other) << ((t > span.fT) + (t >= span.fT));
3576 }
3577 }
3578 SkASSERT(foundEnds != 7);
3579 return foundEnds == 0x3 || foundEnds == 0x5 || foundEnds == 0x6; // two bits set
3580}
3581
caryclark65f55312014-11-13 06:58:52 -08003582bool SkOpSegment::inconsistentAngle(int maxWinding, int sumWinding, int oppMaxWinding,
3583 int oppSumWinding, const SkOpAngle* angle) const {
3584 SkASSERT(angle->segment() == this);
3585 if (UseInnerWinding(maxWinding, sumWinding)) {
3586 maxWinding = sumWinding;
3587 }
3588 if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
3589 oppMaxWinding = oppSumWinding;
3590 }
3591 return inconsistentWinding(angle, maxWinding, oppMaxWinding);
3592}
3593
3594bool SkOpSegment::inconsistentWinding(const SkOpAngle* angle, int winding,
3595 int oppWinding) const {
3596 int index = angle->start();
3597 int endIndex = angle->end();
3598 int min = SkMin32(index, endIndex);
3599 int step = SkSign32(endIndex - index);
3600 if (inconsistentWinding(min, winding, oppWinding)) {
3601 return true;
3602 }
3603 const SkOpSegment* other = this;
3604 while ((other = other->nextChase(&index, &step, &min, NULL))) {
3605 if (other->fTs[min].fWindSum != SK_MinS32) {
3606 break;
3607 }
3608 if (fOperand == other->fOperand) {
3609 if (other->inconsistentWinding(min, winding, oppWinding)) {
3610 return true;
3611 }
3612 } else {
3613 if (other->inconsistentWinding(min, oppWinding, winding)) {
3614 return true;
3615 }
3616 }
3617 }
3618 return false;
3619}
3620
3621bool SkOpSegment::inconsistentWinding(int index, int winding, int oppWinding) const {
3622 SkASSERT(winding || oppWinding);
3623 double referenceT = this->span(index).fT;
3624 int lesser = index;
3625 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3626 if (inconsistentWinding(__FUNCTION__, lesser, winding, oppWinding)) {
3627 return true;
3628 }
3629 }
3630 do {
3631 if (inconsistentWinding(__FUNCTION__, index, winding, oppWinding)) {
3632 return true;
3633 }
3634 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3635 return false;
3636}
3637
3638bool SkOpSegment::inconsistentWinding(const char* funName, int tIndex, int winding,
3639 int oppWinding) const {
3640 const SkOpSpan& span = this->span(tIndex);
3641 if (span.fDone && !span.fSmall) {
3642 return false;
3643 }
3644 return (span.fWindSum != SK_MinS32 && span.fWindSum != winding)
3645 || (span.fOppSum != SK_MinS32 && span.fOppSum != oppWinding);
3646}
3647
caryclark@google.com07393ca2013-04-08 11:47:37 +00003648void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
3649 fDoneSpans = 0;
3650 fOperand = operand;
3651 fXor = evenOdd;
3652 fPts = pts;
3653 fVerb = verb;
caryclarkdac1d172014-06-17 05:15:38 -07003654 fLoop = fMultiples = fSmall = fTiny = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003655}
3656
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003657void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003658 int local = spanSign(start, end);
caryclark65f55312014-11-13 06:58:52 -08003659 SkDEBUGCODE(bool success);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003660 if (angleIncludeType == SkOpAngle::kBinarySingle) {
3661 int oppLocal = oppSign(start, end);
caryclark65f55312014-11-13 06:58:52 -08003662 SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, oppLocal, NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003663 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08003664 SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, oppLocal, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003665 } else {
caryclark65f55312014-11-13 06:58:52 -08003666 SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003667 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08003668 SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003669 }
caryclark65f55312014-11-13 06:58:52 -08003670 SkASSERT(success);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003671}
3672
3673/*
3674when we start with a vertical intersect, we try to use the dx to determine if the edge is to
3675the left or the right of vertical. This determines if we need to add the span's
3676sign or not. However, this isn't enough.
3677If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
3678If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
3679from has the same x direction as this span, the winding should change. If the dx is opposite, then
3680the same winding is shared by both.
3681*/
caryclark65f55312014-11-13 06:58:52 -08003682bool SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
caryclark@google.com07393ca2013-04-08 11:47:37 +00003683 int oppWind, SkScalar hitOppDx) {
3684 SkASSERT(hitDx || !winding);
reed@google.com277c3f82013-05-31 15:17:50 +00003685 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003686 SkASSERT(dx);
3687 int windVal = windValue(SkMin32(start, end));
3688#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07003689 SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), winding,
caryclark@google.com07393ca2013-04-08 11:47:37 +00003690 hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
3691#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003692 int sideWind = winding + (dx < 0 ? windVal : -windVal);
3693 if (abs(winding) < abs(sideWind)) {
3694 winding = sideWind;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003695 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003696 SkDEBUGCODE(int oppLocal = oppSign(start, end));
3697 SkASSERT(hitOppDx || !oppWind || !oppLocal);
3698 int oppWindVal = oppValue(SkMin32(start, end));
3699 if (!oppWind) {
3700 oppWind = dx < 0 ? oppWindVal : -oppWindVal;
3701 } else if (hitOppDx * dx >= 0) {
3702 int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
3703 if (abs(oppWind) < abs(oppSideWind)) {
3704 oppWind = oppSideWind;
3705 }
3706 }
caryclarkdac1d172014-06-17 05:15:38 -07003707#if DEBUG_WINDING_AT_T
3708 SkDebugf(" winding=%d oppWind=%d\n", winding, oppWind);
3709#endif
caryclark65f55312014-11-13 06:58:52 -08003710 // if this fails to mark (because the edges are too small) inform caller to try again
3711 bool success = markAndChaseWinding(start, end, winding, oppWind, NULL);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003712 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08003713 success |= markAndChaseWinding(end, start, winding, oppWind, NULL);
3714 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003715}
3716
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003717bool SkOpSegment::inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const {
3718 if (!baseAngle->inLoop()) {
3719 return false;
3720 }
3721 int index = *indexPtr;
caryclarkdac1d172014-06-17 05:15:38 -07003722 SkOpAngle* from = fTs[index].fFromAngle;
3723 SkOpAngle* to = fTs[index].fToAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003724 while (++index < spanCount) {
caryclarkdac1d172014-06-17 05:15:38 -07003725 SkOpAngle* nextFrom = fTs[index].fFromAngle;
3726 SkOpAngle* nextTo = fTs[index].fToAngle;
3727 if (from != nextFrom || to != nextTo) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003728 break;
3729 }
3730 }
3731 *indexPtr = index;
3732 return true;
3733}
3734
caryclark@google.com07393ca2013-04-08 11:47:37 +00003735// OPTIMIZE: successive calls could start were the last leaves off
3736// or calls could specialize to walk forwards or backwards
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003737bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003738 int tCount = fTs.count();
3739 for (int index = 0; index < tCount; ++index) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003740 const SkOpSpan& span = fTs[index];
3741 if (approximately_zero(startT - span.fT) && pt == span.fPt) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003742 return false;
3743 }
3744 }
3745 return true;
3746}
3747
caryclarkdac1d172014-06-17 05:15:38 -07003748
3749SkOpSegment* SkOpSegment::isSimple(int* end, int* step) {
3750 return nextChase(end, step, NULL, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003751}
3752
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003753bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
3754 int start = angle->start();
3755 int end = angle->end();
3756 const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
3757 return mSpan.fTiny;
3758}
3759
3760bool SkOpSegment::isTiny(int index) const {
3761 return fTs[index].fTiny;
3762}
3763
3764// look pair of active edges going away from coincident edge
3765// one of them should be the continuation of other
3766// if both are active, look to see if they both the connect to another coincident pair
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003767// if at least one is a line, then make the pair coincident
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003768// if neither is a line, test for coincidence
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003769bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt,
3770 int step, bool cancel) {
3771 int otherTIndex = other->findT(otherT, otherPt, this);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003772 int next = other->nextExactSpan(otherTIndex, step);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003773 int otherMin = SkMin32(otherTIndex, next);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003774 int otherWind = other->span(otherMin).fWindValue;
3775 if (otherWind == 0) {
3776 return false;
3777 }
caryclark65f55312014-11-13 06:58:52 -08003778 if (next < 0) {
3779 return false; // can happen if t values were adjusted but coincident ts were not
3780 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003781 int tIndex = 0;
3782 do {
3783 SkOpSpan* test = &fTs[tIndex];
3784 SkASSERT(test->fT == 0);
3785 if (test->fOther == other || test->fOtherT != 1) {
3786 continue;
3787 }
3788 SkPoint startPt, endPt;
3789 double endT;
3790 if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) {
3791 SkOpSegment* match = test->fOther;
3792 if (cancel) {
3793 match->addTCancel(startPt, endPt, other);
3794 } else {
caryclark65f55312014-11-13 06:58:52 -08003795 if (!match->addTCoincident(startPt, endPt, endT, other)) {
3796 return false;
3797 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003798 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003799 return true;
3800 }
3801 } while (fTs[++tIndex].fT == 0);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003802 return false;
3803}
3804
caryclark@google.com07393ca2013-04-08 11:47:37 +00003805// this span is excluded by the winding rule -- chase the ends
3806// as long as they are unambiguous to mark connections as done
3807// and give them the same winding value
caryclark@google.com07393ca2013-04-08 11:47:37 +00003808
3809SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
3810 int step = SkSign32(endIndex - index);
3811 int min = SkMin32(index, endIndex);
3812 markDoneBinary(min);
caryclarkdac1d172014-06-17 05:15:38 -07003813 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003814 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003815 while ((other = other->nextChase(&index, &step, &min, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003816 if (other->done()) {
caryclarkdac1d172014-06-17 05:15:38 -07003817 SkASSERT(!last);
3818 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003819 }
3820 other->markDoneBinary(min);
3821 }
3822 return last;
3823}
3824
3825SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
3826 int step = SkSign32(endIndex - index);
3827 int min = SkMin32(index, endIndex);
3828 markDoneUnary(min);
caryclarkdac1d172014-06-17 05:15:38 -07003829 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003830 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003831 while ((other = other->nextChase(&index, &step, &min, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003832 if (other->done()) {
caryclarkdac1d172014-06-17 05:15:38 -07003833 SkASSERT(!last);
3834 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003835 }
3836 other->markDoneUnary(min);
3837 }
3838 return last;
3839}
3840
caryclark65f55312014-11-13 06:58:52 -08003841bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, SkOpSpan** lastPtr) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003842 int index = angle->start();
3843 int endIndex = angle->end();
caryclark65f55312014-11-13 06:58:52 -08003844 return markAndChaseWinding(index, endIndex, winding, lastPtr);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003845}
3846
caryclark65f55312014-11-13 06:58:52 -08003847bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, SkOpSpan** lastPtr) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003848 int min = SkMin32(index, endIndex);
3849 int step = SkSign32(endIndex - index);
caryclark65f55312014-11-13 06:58:52 -08003850 bool success = markWinding(min, winding);
caryclarkdac1d172014-06-17 05:15:38 -07003851 SkOpSpan* last = NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003852 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003853 while ((other = other->nextChase(&index, &step, &min, &last))) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003854 if (other->fTs[min].fWindSum != SK_MinS32) {
3855 SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
caryclarkdac1d172014-06-17 05:15:38 -07003856 SkASSERT(!last);
3857 break;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003858 }
caryclark65f55312014-11-13 06:58:52 -08003859 (void) other->markWinding(min, winding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003860 }
caryclark65f55312014-11-13 06:58:52 -08003861 if (lastPtr) {
3862 *lastPtr = last;
3863 }
3864 return success;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003865}
3866
caryclark65f55312014-11-13 06:58:52 -08003867bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding,
3868 SkOpSpan** lastPtr) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003869 int min = SkMin32(index, endIndex);
3870 int step = SkSign32(endIndex - index);
caryclark65f55312014-11-13 06:58:52 -08003871 bool success = markWinding(min, winding, oppWinding);
caryclarkdac1d172014-06-17 05:15:38 -07003872 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003873 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003874 while ((other = other->nextChase(&index, &step, &min, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003875 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclarkda085e62014-06-17 05:41:12 -07003876#ifdef SK_DEBUG
caryclarkdac1d172014-06-17 05:15:38 -07003877 if (!other->fTs[min].fLoop) {
3878 if (fOperand == other->fOperand) {
3879// FIXME: this is probably a bug -- rects4 asserts here
3880// SkASSERT(other->fTs[min].fWindSum == winding);
3881// FIXME: this is probably a bug -- rects3 asserts here
3882// SkASSERT(other->fTs[min].fOppSum == oppWinding);
3883 } else {
caryclark65b427c2014-09-18 10:32:57 -07003884// FIXME: this is probably a bug -- issue414409b asserts here
3885// SkASSERT(other->fTs[min].fWindSum == oppWinding);
caryclarkdac1d172014-06-17 05:15:38 -07003886// FIXME: this is probably a bug -- skpwww_joomla_org_23 asserts here
3887// SkASSERT(other->fTs[min].fOppSum == winding);
3888 }
3889 }
3890 SkASSERT(!last);
3891#endif
3892 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003893 }
caryclarkdac1d172014-06-17 05:15:38 -07003894 if (fOperand == other->fOperand) {
caryclark65f55312014-11-13 06:58:52 -08003895 (void) other->markWinding(min, winding, oppWinding);
caryclarkdac1d172014-06-17 05:15:38 -07003896 } else {
caryclark65f55312014-11-13 06:58:52 -08003897 (void) other->markWinding(min, oppWinding, winding);
caryclarkdac1d172014-06-17 05:15:38 -07003898 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003899 }
caryclark65f55312014-11-13 06:58:52 -08003900 if (lastPtr) {
3901 *lastPtr = last;
3902 }
3903 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003904}
3905
caryclark65f55312014-11-13 06:58:52 -08003906bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding,
3907 SkOpSpan** lastPtr) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003908 int start = angle->start();
3909 int end = angle->end();
caryclark65f55312014-11-13 06:58:52 -08003910 return markAndChaseWinding(start, end, winding, oppWinding, lastPtr);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003911}
3912
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003913SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003914 SkASSERT(angle->segment() == this);
3915 if (UseInnerWinding(maxWinding, sumWinding)) {
3916 maxWinding = sumWinding;
3917 }
caryclark65f55312014-11-13 06:58:52 -08003918 SkOpSpan* last;
3919 SkAssertResult(markAndChaseWinding(angle, maxWinding, &last));
caryclark@google.com570863f2013-09-16 15:55:01 +00003920#if DEBUG_WINDING
3921 if (last) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003922 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
3923 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
3924 SkPathOpsDebug::WindingPrintf(last->fWindSum);
3925 SkDebugf(" small=%d\n", last->fSmall);
caryclark@google.com570863f2013-09-16 15:55:01 +00003926 }
3927#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003928 return last;
3929}
3930
3931SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003932 int oppSumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003933 SkASSERT(angle->segment() == this);
3934 if (UseInnerWinding(maxWinding, sumWinding)) {
3935 maxWinding = sumWinding;
3936 }
3937 if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
3938 oppMaxWinding = oppSumWinding;
3939 }
caryclark65f55312014-11-13 06:58:52 -08003940 SkOpSpan* last;
3941 // caller doesn't require that this marks anything
3942 (void) markAndChaseWinding(angle, maxWinding, oppMaxWinding, &last);
caryclark@google.com570863f2013-09-16 15:55:01 +00003943#if DEBUG_WINDING
3944 if (last) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003945 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
3946 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
3947 SkPathOpsDebug::WindingPrintf(last->fWindSum);
3948 SkDebugf(" small=%d\n", last->fSmall);
caryclark@google.com570863f2013-09-16 15:55:01 +00003949 }
3950#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003951 return last;
3952}
3953
3954// FIXME: this should also mark spans with equal (x,y)
3955// This may be called when the segment is already marked done. While this
3956// wastes time, it shouldn't do any more than spin through the T spans.
3957// OPTIMIZATION: abort on first done found (assuming that this code is
3958// always called to mark segments done).
3959void SkOpSegment::markDone(int index, int winding) {
3960 // SkASSERT(!done());
3961 SkASSERT(winding);
3962 double referenceT = fTs[index].fT;
3963 int lesser = index;
3964 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3965 markOneDone(__FUNCTION__, lesser, winding);
3966 }
3967 do {
3968 markOneDone(__FUNCTION__, index, winding);
3969 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003970 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003971}
3972
caryclark@google.com07393ca2013-04-08 11:47:37 +00003973void SkOpSegment::markDoneBinary(int index) {
3974 double referenceT = fTs[index].fT;
3975 int lesser = index;
3976 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3977 markOneDoneBinary(__FUNCTION__, lesser);
3978 }
3979 do {
3980 markOneDoneBinary(__FUNCTION__, index);
3981 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003982 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003983}
3984
caryclark65f55312014-11-13 06:58:52 -08003985void SkOpSegment::markDoneFinal(int index) {
3986 double referenceT = fTs[index].fT;
3987 int lesser = index;
3988 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3989 markOneDoneFinal(__FUNCTION__, lesser);
3990 }
3991 do {
3992 markOneDoneFinal(__FUNCTION__, index);
3993 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3994 debugValidate();
3995}
3996
caryclark@google.com07393ca2013-04-08 11:47:37 +00003997void SkOpSegment::markDoneUnary(int index) {
3998 double referenceT = fTs[index].fT;
3999 int lesser = index;
4000 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
4001 markOneDoneUnary(__FUNCTION__, lesser);
4002 }
4003 do {
4004 markOneDoneUnary(__FUNCTION__, index);
4005 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004006 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00004007}
4008
4009void SkOpSegment::markOneDone(const char* funName, int tIndex, int winding) {
caryclark65f55312014-11-13 06:58:52 -08004010 SkOpSpan* span;
4011 (void) markOneWinding(funName, tIndex, winding, &span); // allowed to do nothing
4012 if (span->fDone) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00004013 return;
4014 }
4015 span->fDone = true;
caryclark65f55312014-11-13 06:58:52 -08004016 ++fDoneSpans;
4017}
4018
4019void SkOpSegment::markOneDoneFinal(const char* funName, int tIndex) {
4020 SkOpSpan* span = &fTs[tIndex];
4021 if (span->fDone) {
4022 return;
4023 }
4024 span->fDone = true;
4025 ++fDoneSpans;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004026}
4027
4028void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
4029 SkOpSpan* span = verifyOneWinding(funName, tIndex);
4030 if (!span) {
4031 return;
4032 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004033 SkASSERT(!span->fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004034 span->fDone = true;
caryclark65f55312014-11-13 06:58:52 -08004035 ++fDoneSpans;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004036}
4037
caryclark@google.com07393ca2013-04-08 11:47:37 +00004038void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
4039 SkOpSpan* span = verifyOneWindingU(funName, tIndex);
4040 if (!span) {
4041 return;
4042 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004043 if (span->fWindSum == SK_MinS32) {
4044 SkDebugf("%s uncomputed\n", __FUNCTION__);
4045 }
4046 SkASSERT(!span->fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004047 span->fDone = true;
caryclark65f55312014-11-13 06:58:52 -08004048 ++fDoneSpans;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004049}
4050
caryclark65f55312014-11-13 06:58:52 -08004051bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding, SkOpSpan** lastPtr) {
4052 SkOpSpan* span = &fTs[tIndex];
4053 if (lastPtr) {
4054 *lastPtr = span;
4055 }
4056 if (span->fDone && !span->fSmall) {
4057 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004058 }
4059#if DEBUG_MARK_DONE
caryclark65f55312014-11-13 06:58:52 -08004060 debugShowNewWinding(funName, *span, winding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004061#endif
caryclark65f55312014-11-13 06:58:52 -08004062 SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004063#if DEBUG_LIMIT_WIND_SUM
4064 SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
4065#endif
caryclark65f55312014-11-13 06:58:52 -08004066 span->fWindSum = winding;
4067 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004068}
4069
caryclark65f55312014-11-13 06:58:52 -08004070bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
4071 int oppWinding, SkOpSpan** lastPtr) {
4072 SkOpSpan* span = &fTs[tIndex];
4073 if (span->fDone && !span->fSmall) {
4074 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004075 }
4076#if DEBUG_MARK_DONE
caryclark65f55312014-11-13 06:58:52 -08004077 debugShowNewWinding(funName, *span, winding, oppWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004078#endif
caryclark65f55312014-11-13 06:58:52 -08004079 SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004080#if DEBUG_LIMIT_WIND_SUM
4081 SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
4082#endif
caryclark65f55312014-11-13 06:58:52 -08004083 span->fWindSum = winding;
4084 SkASSERT(span->fOppSum == SK_MinS32 || span->fOppSum == oppWinding);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004085#if DEBUG_LIMIT_WIND_SUM
4086 SkASSERT(abs(oppWinding) <= DEBUG_LIMIT_WIND_SUM);
4087#endif
caryclark65f55312014-11-13 06:58:52 -08004088 span->fOppSum = oppWinding;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004089 debugValidate();
caryclark65f55312014-11-13 06:58:52 -08004090 if (lastPtr) {
4091 *lastPtr = span;
4092 }
4093 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004094}
4095
4096// 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 -07004097bool SkOpSegment::clockwise(int tStart, int tEnd, bool* swap) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00004098 SkASSERT(fVerb != SkPath::kLine_Verb);
4099 SkPoint edge[4];
4100 subDivide(tStart, tEnd, edge);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004101 int points = SkPathOpsVerbToPoints(fVerb);
4102 double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
caryclarke4097e32014-06-18 07:24:19 -07004103 bool sumSet = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004104 if (fVerb == SkPath::kCubic_Verb) {
caryclarke4097e32014-06-18 07:24:19 -07004105 SkDCubic cubic;
4106 cubic.set(edge);
4107 double inflectionTs[2];
4108 int inflections = cubic.findInflections(inflectionTs);
4109 // FIXME: this fixes cubicOp114 and breaks cubicOp58d
4110 // the trouble is that cubics with inflections confuse whether the curve breaks towards
4111 // or away, which in turn is used to determine if it is on the far right or left.
4112 // Probably a totally different approach is in order. At one time I tried to project a
4113 // horizontal ray to determine winding, but was confused by how to map the vertically
4114 // oriented winding computation over.
4115 if (0 && inflections) {
4116 double tLo = this->span(tStart).fT;
4117 double tHi = this->span(tEnd).fT;
4118 double tLoStart = tLo;
4119 for (int index = 0; index < inflections; ++index) {
4120 if (between(tLo, inflectionTs[index], tHi)) {
4121 tLo = inflectionTs[index];
4122 }
4123 }
4124 if (tLo != tLoStart && tLo != tHi) {
4125 SkDPoint sub[2];
4126 sub[0] = cubic.ptAtT(tLo);
4127 sub[1].set(edge[3]);
4128 SkDPoint ctrl[2];
4129 SkDCubic::SubDivide(fPts, sub[0], sub[1], tLo, tHi, ctrl);
4130 edge[0] = sub[0].asSkPoint();
4131 edge[1] = ctrl[0].asSkPoint();
4132 edge[2] = ctrl[1].asSkPoint();
4133 sum = (edge[0].fX - edge[3].fX) * (edge[0].fY + edge[3].fY);
4134 }
4135 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004136 SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
4137 if (edge[1].fY < lesser && edge[2].fY < lesser) {
4138 SkDLine tangent1 = {{ {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} }};
4139 SkDLine tangent2 = {{ {edge[2].fX, edge[2].fY}, {edge[3].fX, edge[3].fY} }};
4140 if (SkIntersections::Test(tangent1, tangent2)) {
4141 SkPoint topPt = cubic_top(fPts, fTs[tStart].fT, fTs[tEnd].fT);
4142 sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
4143 sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
caryclarke4097e32014-06-18 07:24:19 -07004144 sumSet = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004145 }
4146 }
4147 }
caryclarke4097e32014-06-18 07:24:19 -07004148 if (!sumSet) {
4149 for (int idx = 0; idx < points; ++idx){
4150 sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
4151 }
4152 }
4153 if (fVerb == SkPath::kCubic_Verb) {
4154 SkDCubic cubic;
4155 cubic.set(edge);
4156 *swap = sum > 0 && !cubic.monotonicInY() && !cubic.serpentine();
4157 } else {
4158 SkDQuad quad;
4159 quad.set(edge);
4160 *swap = sum > 0 && !quad.monotonicInY();
caryclark@google.com07393ca2013-04-08 11:47:37 +00004161 }
4162 return sum <= 0;
4163}
4164
4165bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004166 SkASSERT(fVerb != SkPath::kLine_Verb);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004167 if (fVerb == SkPath::kQuad_Verb) {
4168 SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
4169 return dst.monotonicInY();
4170 }
4171 SkASSERT(fVerb == SkPath::kCubic_Verb);
4172 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
4173 return dst.monotonicInY();
4174}
4175
4176bool SkOpSegment::serpentine(int tStart, int tEnd) const {
4177 if (fVerb != SkPath::kCubic_Verb) {
4178 return false;
4179 }
4180 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
4181 return dst.serpentine();
4182}
4183
4184SkOpSpan* SkOpSegment::verifyOneWinding(const char* funName, int tIndex) {
4185 SkOpSpan& span = fTs[tIndex];
4186 if (span.fDone) {
4187 return NULL;
4188 }
4189#if DEBUG_MARK_DONE
4190 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
4191#endif
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00004192// If the prior angle in the sort is unorderable, the winding sum may not be computable.
4193// To enable the assert, the 'prior is unorderable' state could be
4194// piped down to this test, but not sure it's worth it.
4195// (Once the sort order is stored in the span, this test may be feasible.)
4196// SkASSERT(span.fWindSum != SK_MinS32);
4197// SkASSERT(span.fOppSum != SK_MinS32);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004198 return &span;
4199}
4200
4201SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
4202 SkOpSpan& span = fTs[tIndex];
4203 if (span.fDone) {
4204 return NULL;
4205 }
4206#if DEBUG_MARK_DONE
4207 debugShowNewWinding(funName, span, span.fWindSum);
4208#endif
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00004209// If the prior angle in the sort is unorderable, the winding sum may not be computable.
4210// To enable the assert, the 'prior is unorderable' state could be
4211// piped down to this test, but not sure it's worth it.
4212// (Once the sort order is stored in the span, this test may be feasible.)
4213// SkASSERT(span.fWindSum != SK_MinS32);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004214 return &span;
4215}
4216
caryclark65f55312014-11-13 06:58:52 -08004217bool SkOpSegment::markWinding(int index, int winding) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00004218// SkASSERT(!done());
4219 SkASSERT(winding);
4220 double referenceT = fTs[index].fT;
4221 int lesser = index;
caryclark65f55312014-11-13 06:58:52 -08004222 bool success = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004223 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark65f55312014-11-13 06:58:52 -08004224 success |= markOneWinding(__FUNCTION__, lesser, winding, NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004225 }
4226 do {
caryclark65f55312014-11-13 06:58:52 -08004227 success |= markOneWinding(__FUNCTION__, index, winding, NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004228 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004229 debugValidate();
caryclark65f55312014-11-13 06:58:52 -08004230 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004231}
4232
caryclark65f55312014-11-13 06:58:52 -08004233bool SkOpSegment::markWinding(int index, int winding, int oppWinding) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00004234// SkASSERT(!done());
4235 SkASSERT(winding || oppWinding);
4236 double referenceT = fTs[index].fT;
4237 int lesser = index;
caryclark65f55312014-11-13 06:58:52 -08004238 bool success = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004239 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark65f55312014-11-13 06:58:52 -08004240 success |= markOneWinding(__FUNCTION__, lesser, winding, oppWinding, NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004241 }
4242 do {
caryclark65f55312014-11-13 06:58:52 -08004243 success |= markOneWinding(__FUNCTION__, index, winding, oppWinding, NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004244 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004245 debugValidate();
caryclark65f55312014-11-13 06:58:52 -08004246 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004247}
4248
4249void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
4250 int nextDoorWind = SK_MaxS32;
4251 int nextOppWind = SK_MaxS32;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004252 // prefer exact matches
caryclark@google.com07393ca2013-04-08 11:47:37 +00004253 if (tIndex > 0) {
4254 const SkOpSpan& below = fTs[tIndex - 1];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004255 if (below.fT == t) {
4256 nextDoorWind = below.fWindValue;
4257 nextOppWind = below.fOppValue;
4258 }
4259 }
4260 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
4261 const SkOpSpan& above = fTs[tIndex + 1];
4262 if (above.fT == t) {
4263 nextDoorWind = above.fWindValue;
4264 nextOppWind = above.fOppValue;
4265 }
4266 }
4267 if (nextDoorWind == SK_MaxS32 && tIndex > 0) {
4268 const SkOpSpan& below = fTs[tIndex - 1];
caryclark@google.com07393ca2013-04-08 11:47:37 +00004269 if (approximately_negative(t - below.fT)) {
4270 nextDoorWind = below.fWindValue;
4271 nextOppWind = below.fOppValue;
4272 }
4273 }
4274 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
4275 const SkOpSpan& above = fTs[tIndex + 1];
4276 if (approximately_negative(above.fT - t)) {
4277 nextDoorWind = above.fWindValue;
4278 nextOppWind = above.fOppValue;
4279 }
4280 }
4281 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
4282 const SkOpSpan& below = fTs[tIndex - 1];
4283 nextDoorWind = below.fWindValue;
4284 nextOppWind = below.fOppValue;
4285 }
4286 if (nextDoorWind != SK_MaxS32) {
4287 SkOpSpan& newSpan = fTs[tIndex];
4288 newSpan.fWindValue = nextDoorWind;
4289 newSpan.fOppValue = nextOppWind;
4290 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
4291 newSpan.fDone = true;
4292 ++fDoneSpans;
4293 }
4294 }
4295}
4296
caryclark@google.com07393ca2013-04-08 11:47:37 +00004297bool SkOpSegment::nextCandidate(int* start, int* end) const {
4298 while (fTs[*end].fDone) {
4299 if (fTs[*end].fT == 1) {
4300 return false;
4301 }
4302 ++(*end);
4303 }
4304 *start = *end;
4305 *end = nextExactSpan(*start, 1);
4306 return true;
4307}
4308
caryclark65f55312014-11-13 06:58:52 -08004309static SkOpSegment* set_last(SkOpSpan** last, const SkOpSpan* endSpan) {
caryclarkdac1d172014-06-17 05:15:38 -07004310 if (last && !endSpan->fSmall) {
caryclark65f55312014-11-13 06:58:52 -08004311 *last = const_cast<SkOpSpan*>(endSpan); // FIXME: get rid of cast
caryclarkdac1d172014-06-17 05:15:38 -07004312 }
4313 return NULL;
4314}
4315
caryclark65f55312014-11-13 06:58:52 -08004316SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr,
4317 SkOpSpan** last) const {
caryclarkdac1d172014-06-17 05:15:38 -07004318 int origIndex = *indexPtr;
4319 int step = *stepPtr;
4320 int end = nextExactSpan(origIndex, step);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004321 SkASSERT(end >= 0);
caryclark65f55312014-11-13 06:58:52 -08004322 const SkOpSpan& endSpan = this->span(end);
caryclarkdac1d172014-06-17 05:15:38 -07004323 SkOpAngle* angle = step > 0 ? endSpan.fFromAngle : endSpan.fToAngle;
4324 int foundIndex;
4325 int otherEnd;
4326 SkOpSegment* other;
4327 if (angle == NULL) {
4328 if (endSpan.fT != 0 && endSpan.fT != 1) {
4329 return NULL;
4330 }
4331 other = endSpan.fOther;
4332 foundIndex = endSpan.fOtherIndex;
4333 otherEnd = other->nextExactSpan(foundIndex, step);
4334 } else {
4335 int loopCount = angle->loopCount();
4336 if (loopCount > 2) {
4337 return set_last(last, &endSpan);
4338 }
4339 const SkOpAngle* next = angle->next();
caryclark65b427c2014-09-18 10:32:57 -07004340 if (NULL == next) {
4341 return NULL;
4342 }
caryclarkdac1d172014-06-17 05:15:38 -07004343 if (angle->sign() != next->sign()) {
4344#if DEBUG_WINDING
4345 SkDebugf("%s mismatched signs\n", __FUNCTION__);
4346#endif
4347 // return set_last(last, &endSpan);
4348 }
4349 other = next->segment();
4350 foundIndex = end = next->start();
4351 otherEnd = next->end();
caryclark@google.com570863f2013-09-16 15:55:01 +00004352 }
caryclarkdac1d172014-06-17 05:15:38 -07004353 int foundStep = foundIndex < otherEnd ? 1 : -1;
4354 if (*stepPtr != foundStep) {
4355 return set_last(last, &endSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004356 }
caryclarkdac1d172014-06-17 05:15:38 -07004357 SkASSERT(*indexPtr >= 0);
caryclark65b427c2014-09-18 10:32:57 -07004358 if (otherEnd < 0) {
4359 return NULL;
4360 }
4361// SkASSERT(otherEnd >= 0);
caryclarkdac1d172014-06-17 05:15:38 -07004362#if 1
4363 int origMin = origIndex + (step < 0 ? step : 0);
4364 const SkOpSpan& orig = this->span(origMin);
4365#endif
4366 int foundMin = SkMin32(foundIndex, otherEnd);
4367#if 1
4368 const SkOpSpan& found = other->span(foundMin);
4369 if (found.fWindValue != orig.fWindValue || found.fOppValue != orig.fOppValue) {
4370 return set_last(last, &endSpan);
4371 }
4372#endif
4373 *indexPtr = foundIndex;
4374 *stepPtr = foundStep;
4375 if (minPtr) {
4376 *minPtr = foundMin;
caryclark@google.coma5e55922013-05-07 18:51:31 +00004377 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004378 return other;
4379}
4380
4381// This has callers for two different situations: one establishes the end
4382// of the current span, and one establishes the beginning of the next span
4383// (thus the name). When this is looking for the end of the current span,
4384// coincidence is found when the beginning Ts contain -step and the end
4385// contains step. When it is looking for the beginning of the next, the
4386// first Ts found can be ignored and the last Ts should contain -step.
4387// OPTIMIZATION: probably should split into two functions
4388int SkOpSegment::nextSpan(int from, int step) const {
4389 const SkOpSpan& fromSpan = fTs[from];
4390 int count = fTs.count();
4391 int to = from;
4392 while (step > 0 ? ++to < count : --to >= 0) {
4393 const SkOpSpan& span = fTs[to];
4394 if (approximately_zero(span.fT - fromSpan.fT)) {
4395 continue;
4396 }
4397 return to;
4398 }
4399 return -1;
4400}
4401
4402// FIXME
4403// this returns at any difference in T, vs. a preset minimum. It may be
4404// that all callers to nextSpan should use this instead.
caryclark@google.com07393ca2013-04-08 11:47:37 +00004405int SkOpSegment::nextExactSpan(int from, int step) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00004406 int to = from;
caryclark@google.com570863f2013-09-16 15:55:01 +00004407 if (step < 0) {
4408 const SkOpSpan& fromSpan = fTs[from];
4409 while (--to >= 0) {
4410 const SkOpSpan& span = fTs[to];
4411 if (precisely_negative(fromSpan.fT - span.fT) || span.fTiny) {
4412 continue;
4413 }
4414 return to;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004415 }
caryclark@google.com570863f2013-09-16 15:55:01 +00004416 } else {
4417 while (fTs[from].fTiny) {
4418 from++;
4419 }
4420 const SkOpSpan& fromSpan = fTs[from];
4421 int count = fTs.count();
4422 while (++to < count) {
4423 const SkOpSpan& span = fTs[to];
4424 if (precisely_negative(span.fT - fromSpan.fT)) {
4425 continue;
4426 }
4427 return to;
4428 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004429 }
4430 return -1;
4431}
4432
caryclarkdac1d172014-06-17 05:15:38 -07004433void SkOpSegment::pinT(const SkPoint& pt, double* t) {
4434 if (pt == fPts[0]) {
4435 *t = 0;
4436 }
4437 int count = SkPathOpsVerbToPoints(fVerb);
4438 if (pt == fPts[count]) {
4439 *t = 1;
4440 }
4441}
4442
caryclark5e27e0e2014-08-12 07:46:33 -07004443bool SkOpSegment::reversePoints(const SkPoint& p1, const SkPoint& p2) const {
4444 SkASSERT(p1 != p2);
4445 int spanCount = count();
4446 int p1IndexMin = -1;
4447 int p2IndexMax = spanCount;
4448 for (int index = 0; index < spanCount; ++index) {
4449 const SkOpSpan& span = fTs[index];
4450 if (span.fPt == p1) {
4451 if (p1IndexMin < 0) {
4452 p1IndexMin = index;
4453 }
4454 } else if (span.fPt == p2) {
4455 p2IndexMax = index;
4456 }
4457 }
4458 return p1IndexMin > p2IndexMax;
4459}
4460
caryclarkdac1d172014-06-17 05:15:38 -07004461void SkOpSegment::setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt,
4462 SkOpSegment* other) {
4463 int count = this->count();
4464 for (int index = 0; index < count; ++index) {
4465 SkOpSpan &span = fTs[index];
4466 if ((startPt == span.fPt || endPt == span.fPt) && other == span.fOther) {
4467 span.fCoincident = true;
4468 }
4469 }
4470}
4471
caryclark@google.com07393ca2013-04-08 11:47:37 +00004472void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
4473 int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
4474 int deltaSum = spanSign(index, endIndex);
4475 int oppDeltaSum = oppSign(index, endIndex);
4476 if (operand()) {
4477 *maxWinding = *sumSuWinding;
4478 *sumWinding = *sumSuWinding -= deltaSum;
4479 *oppMaxWinding = *sumMiWinding;
4480 *oppSumWinding = *sumMiWinding -= oppDeltaSum;
4481 } else {
4482 *maxWinding = *sumMiWinding;
4483 *sumWinding = *sumMiWinding -= deltaSum;
4484 *oppMaxWinding = *sumSuWinding;
4485 *oppSumWinding = *sumSuWinding -= oppDeltaSum;
4486 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004487#if DEBUG_LIMIT_WIND_SUM
4488 SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
4489 SkASSERT(abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
4490#endif
caryclark@google.com570863f2013-09-16 15:55:01 +00004491}
4492
4493void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
4494 int* maxWinding, int* sumWinding) {
4495 int deltaSum = spanSign(index, endIndex);
4496 *maxWinding = *sumMiWinding;
4497 *sumWinding = *sumMiWinding -= deltaSum;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004498#if DEBUG_LIMIT_WIND_SUM
4499 SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
4500#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00004501}
4502
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004503void SkOpSegment::sortAngles() {
4504 int spanCount = fTs.count();
4505 if (spanCount <= 2) {
4506 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004507 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004508 int index = 0;
4509 do {
caryclarkdac1d172014-06-17 05:15:38 -07004510 SkOpAngle* fromAngle = fTs[index].fFromAngle;
4511 SkOpAngle* toAngle = fTs[index].fToAngle;
4512 if (!fromAngle && !toAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004513 index += 1;
4514 continue;
4515 }
4516 SkOpAngle* baseAngle = NULL;
caryclarkdac1d172014-06-17 05:15:38 -07004517 if (fromAngle) {
4518 baseAngle = fromAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004519 if (inLoop(baseAngle, spanCount, &index)) {
4520 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004521 }
4522 }
caryclark@google.com570863f2013-09-16 15:55:01 +00004523#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004524 bool wroteAfterHeader = false;
caryclark@google.com570863f2013-09-16 15:55:01 +00004525#endif
caryclarkdac1d172014-06-17 05:15:38 -07004526 if (toAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004527 if (!baseAngle) {
4528 baseAngle = toAngle;
4529 if (inLoop(baseAngle, spanCount, &index)) {
4530 continue;
4531 }
4532 } else {
4533 SkDEBUGCODE(int newIndex = index);
4534 SkASSERT(!inLoop(baseAngle, spanCount, &newIndex) && newIndex == index);
4535#if DEBUG_ANGLE
4536 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
4537 index);
4538 wroteAfterHeader = true;
4539#endif
4540 baseAngle->insert(toAngle);
4541 }
4542 }
caryclarkdac1d172014-06-17 05:15:38 -07004543 SkOpAngle* nextFrom, * nextTo;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004544 int firstIndex = index;
4545 do {
4546 SkOpSpan& span = fTs[index];
4547 SkOpSegment* other = span.fOther;
4548 SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
caryclarkdac1d172014-06-17 05:15:38 -07004549 SkOpAngle* oAngle = oSpan.fFromAngle;
4550 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004551#if DEBUG_ANGLE
4552 if (!wroteAfterHeader) {
4553 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
4554 index);
4555 wroteAfterHeader = true;
4556 }
4557#endif
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004558 if (!oAngle->loopContains(*baseAngle)) {
4559 baseAngle->insert(oAngle);
4560 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004561 }
caryclarkdac1d172014-06-17 05:15:38 -07004562 oAngle = oSpan.fToAngle;
4563 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004564#if DEBUG_ANGLE
4565 if (!wroteAfterHeader) {
4566 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
4567 index);
4568 wroteAfterHeader = true;
4569 }
4570#endif
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004571 if (!oAngle->loopContains(*baseAngle)) {
4572 baseAngle->insert(oAngle);
4573 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004574 }
4575 if (++index == spanCount) {
4576 break;
4577 }
caryclarkdac1d172014-06-17 05:15:38 -07004578 nextFrom = fTs[index].fFromAngle;
4579 nextTo = fTs[index].fToAngle;
4580 } while (fromAngle == nextFrom && toAngle == nextTo);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004581 if (baseAngle && baseAngle->loopCount() == 1) {
4582 index = firstIndex;
4583 do {
4584 SkOpSpan& span = fTs[index];
caryclarkdac1d172014-06-17 05:15:38 -07004585 span.fFromAngle = span.fToAngle = NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004586 if (++index == spanCount) {
4587 break;
4588 }
caryclarkdac1d172014-06-17 05:15:38 -07004589 nextFrom = fTs[index].fFromAngle;
4590 nextTo = fTs[index].fToAngle;
4591 } while (fromAngle == nextFrom && toAngle == nextTo);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004592 baseAngle = NULL;
4593 }
4594#if DEBUG_SORT
4595 SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
4596#endif
4597 } while (index < spanCount);
caryclark@google.com570863f2013-09-16 15:55:01 +00004598}
4599
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004600// return true if midpoints were computed
4601bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
4602 SkASSERT(start != end);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004603 edge[0] = fTs[start].fPt;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004604 int points = SkPathOpsVerbToPoints(fVerb);
4605 edge[points] = fTs[end].fPt;
4606 if (fVerb == SkPath::kLine_Verb) {
4607 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004608 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004609 double startT = fTs[start].fT;
4610 double endT = fTs[end].fT;
4611 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
4612 // don't compute midpoints if we already have them
4613 if (fVerb == SkPath::kQuad_Verb) {
4614 edge[1] = fPts[1];
4615 return false;
4616 }
4617 SkASSERT(fVerb == SkPath::kCubic_Verb);
4618 if (start < end) {
4619 edge[1] = fPts[1];
4620 edge[2] = fPts[2];
4621 return false;
4622 }
4623 edge[1] = fPts[2];
4624 edge[2] = fPts[1];
4625 return false;
4626 }
4627 const SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[points].fX, edge[points].fY }};
4628 if (fVerb == SkPath::kQuad_Verb) {
4629 edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint();
4630 } else {
4631 SkASSERT(fVerb == SkPath::kCubic_Verb);
4632 SkDPoint ctrl[2];
4633 SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl);
4634 edge[1] = ctrl[0].asSkPoint();
4635 edge[2] = ctrl[1].asSkPoint();
4636 }
4637 return true;
4638}
4639
4640// return true if midpoints were computed
4641bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
4642 SkASSERT(start != end);
4643 (*result)[0].set(fTs[start].fPt);
4644 int points = SkPathOpsVerbToPoints(fVerb);
4645 (*result)[points].set(fTs[end].fPt);
4646 if (fVerb == SkPath::kLine_Verb) {
4647 return false;
4648 }
4649 double startT = fTs[start].fT;
4650 double endT = fTs[end].fT;
4651 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
4652 // don't compute midpoints if we already have them
4653 if (fVerb == SkPath::kQuad_Verb) {
4654 (*result)[1].set(fPts[1]);
4655 return false;
4656 }
4657 SkASSERT(fVerb == SkPath::kCubic_Verb);
4658 if (start < end) {
4659 (*result)[1].set(fPts[1]);
4660 (*result)[2].set(fPts[2]);
4661 return false;
4662 }
4663 (*result)[1].set(fPts[2]);
4664 (*result)[2].set(fPts[1]);
4665 return false;
4666 }
4667 if (fVerb == SkPath::kQuad_Verb) {
4668 (*result)[1] = SkDQuad::SubDivide(fPts, (*result)[0], (*result)[2], startT, endT);
4669 } else {
4670 SkASSERT(fVerb == SkPath::kCubic_Verb);
4671 SkDCubic::SubDivide(fPts, (*result)[0], (*result)[3], startT, endT, &(*result)[1]);
4672 }
4673 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004674}
4675
4676void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
4677 SkPoint edge[4];
4678 subDivide(start, end, edge);
reed@google.com277c3f82013-05-31 15:17:50 +00004679 (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004680}
4681
caryclark@google.com570863f2013-09-16 15:55:01 +00004682void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt,
4683 const SkPoint& startPt) {
4684 int outCount = outsidePts->count();
4685 if (outCount == 0 || endPt != (*outsidePts)[outCount - 2]) {
4686 outsidePts->push_back(endPt);
4687 outsidePts->push_back(startPt);
4688 }
4689}
4690
4691void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoint& startPt) {
4692 int outCount = outsidePts->count();
4693 if (outCount == 0 || startPt != (*outsidePts)[outCount - 1]) {
4694 outsidePts->push_back(startPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004695 }
4696}
4697
4698void SkOpSegment::undoneSpan(int* start, int* end) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004699 int tCount = fTs.count();
4700 int index;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004701 for (index = 0; index < tCount; ++index) {
4702 if (!fTs[index].fDone) {
4703 break;
4704 }
4705 }
4706 SkASSERT(index < tCount - 1);
4707 *start = index;
4708 double startT = fTs[index].fT;
4709 while (approximately_negative(fTs[++index].fT - startT))
4710 SkASSERT(index < tCount);
4711 SkASSERT(index < tCount);
4712 *end = index;
4713}
4714
4715int SkOpSegment::updateOppWinding(int index, int endIndex) const {
4716 int lesser = SkMin32(index, endIndex);
4717 int oppWinding = oppSum(lesser);
4718 int oppSpanWinding = oppSign(index, endIndex);
4719 if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
4720 && oppWinding != SK_MaxS32) {
4721 oppWinding -= oppSpanWinding;
4722 }
4723 return oppWinding;
4724}
4725
4726int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
4727 int startIndex = angle->start();
4728 int endIndex = angle->end();
4729 return updateOppWinding(endIndex, startIndex);
4730}
4731
4732int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
4733 int startIndex = angle->start();
4734 int endIndex = angle->end();
4735 return updateOppWinding(startIndex, endIndex);
4736}
4737
4738int SkOpSegment::updateWinding(int index, int endIndex) const {
4739 int lesser = SkMin32(index, endIndex);
4740 int winding = windSum(lesser);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004741 if (winding == SK_MinS32) {
4742 return winding;
4743 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004744 int spanWinding = spanSign(index, endIndex);
caryclark@google.com570863f2013-09-16 15:55:01 +00004745 if (winding && UseInnerWinding(winding - spanWinding, winding)
4746 && winding != SK_MaxS32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00004747 winding -= spanWinding;
4748 }
4749 return winding;
4750}
4751
4752int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
4753 int startIndex = angle->start();
4754 int endIndex = angle->end();
4755 return updateWinding(endIndex, startIndex);
4756}
4757
caryclark@google.com570863f2013-09-16 15:55:01 +00004758int SkOpSegment::updateWindingReverse(int index, int endIndex) const {
4759 int lesser = SkMin32(index, endIndex);
4760 int winding = windSum(lesser);
4761 int spanWinding = spanSign(endIndex, index);
4762 if (winding && UseInnerWindingReverse(winding - spanWinding, winding)
4763 && winding != SK_MaxS32) {
4764 winding -= spanWinding;
4765 }
4766 return winding;
4767}
4768
caryclark@google.com07393ca2013-04-08 11:47:37 +00004769int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
4770 int startIndex = angle->start();
4771 int endIndex = angle->end();
caryclark@google.com570863f2013-09-16 15:55:01 +00004772 return updateWindingReverse(endIndex, startIndex);
4773}
4774
4775// OPTIMIZATION: does the following also work, and is it any faster?
4776// return outerWinding * innerWinding > 0
4777// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
4778bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
4779 SkASSERT(outerWinding != SK_MaxS32);
4780 SkASSERT(innerWinding != SK_MaxS32);
4781 int absOut = abs(outerWinding);
4782 int absIn = abs(innerWinding);
4783 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
4784 return result;
4785}
4786
4787bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) {
4788 SkASSERT(outerWinding != SK_MaxS32);
4789 SkASSERT(innerWinding != SK_MaxS32);
4790 int absOut = abs(outerWinding);
4791 int absIn = abs(innerWinding);
4792 bool result = absOut == absIn ? true : absOut < absIn;
4793 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004794}
4795
4796int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const {
4797 if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
4798 return SK_MinS32;
4799 }
4800 int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
4801 SkASSERT(winding != SK_MinS32);
4802 int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
4803#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07004804 SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
4805 debugID(), crossOpp, tHit, t(tIndex), winding, windVal);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004806#endif
4807 // see if a + change in T results in a +/- change in X (compute x'(T))
reed@google.com277c3f82013-05-31 15:17:50 +00004808 *dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004809 if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
4810 *dx = fPts[2].fX - fPts[1].fX - *dx;
4811 }
4812 if (*dx == 0) {
4813#if DEBUG_WINDING_AT_T
4814 SkDebugf(" dx=0 winding=SK_MinS32\n");
4815#endif
4816 return SK_MinS32;
4817 }
skia.committer@gmail.coma4aced42013-07-09 07:00:56 +00004818 if (windVal < 0) { // reverse sign if opp contour traveled in reverse
caryclark@google.com07e97fc2013-07-08 17:17:02 +00004819 *dx = -*dx;
4820 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004821 if (winding * *dx > 0) { // if same signs, result is negative
4822 winding += *dx > 0 ? -windVal : windVal;
4823 }
4824#if DEBUG_WINDING_AT_T
4825 SkDebugf(" dx=%c winding=%d\n", *dx > 0 ? '+' : '-', winding);
4826#endif
4827 return winding;
4828}
4829
4830int SkOpSegment::windSum(const SkOpAngle* angle) const {
4831 int start = angle->start();
4832 int end = angle->end();
4833 int index = SkMin32(start, end);
4834 return windSum(index);
4835}
4836
caryclark@google.com07393ca2013-04-08 11:47:37 +00004837void SkOpSegment::zeroSpan(SkOpSpan* span) {
caryclark@google.coma5e55922013-05-07 18:51:31 +00004838 SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004839 span->fWindValue = 0;
4840 span->fOppValue = 0;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00004841 if (span->fTiny || span->fSmall) {
4842 return;
4843 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004844 SkASSERT(!span->fDone);
4845 span->fDone = true;
4846 ++fDoneSpans;
4847}