blob: 747cd9d4973b0087e3e0730b2f9894ead6e96f6f [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "SkIntersections.h"
caryclarkdac1d172014-06-17 05:15:38 -07008#include "SkOpContour.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +00009#include "SkOpSegment.h"
10#include "SkPathWriter.h"
caryclark@google.com7dfbb072013-04-22 14:37:05 +000011#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000012
13#define F (false) // discard the edge
14#define T (true) // keep the edge
15
16static const bool gUnaryActiveEdge[2][2] = {
17// from=0 from=1
18// to=0,1 to=0,1
19 {F, T}, {T, F},
20};
21
caryclark@google.com07393ca2013-04-08 11:47:37 +000022static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
23// miFrom=0 miFrom=1
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000024// miTo=0 miTo=1 miTo=0 miTo=1
25// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
caryclark@google.com07393ca2013-04-08 11:47:37 +000026// suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1
27 {{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su
28 {{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su
29 {{{{F, T}, {T, F}}, {{T, T}, {F, F}}}, {{{T, F}, {T, F}}, {{F, F}, {F, F}}}}, // mi | su
30 {{{{F, T}, {T, F}}, {{T, F}, {F, T}}}, {{{T, F}, {F, T}}, {{F, T}, {T, F}}}}, // mi ^ su
31};
32
33#undef F
34#undef T
35
caryclark@google.com570863f2013-09-16 15:55:01 +000036enum {
37 kOutsideTrackedTCount = 16, // FIXME: determine what this should be
38 kMissingSpanCount = 4, // FIXME: determine what this should be
39};
caryclark@google.comd892bd82013-06-17 14:10:36 +000040
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000041const SkOpAngle* SkOpSegment::activeAngle(int index, int* start, int* end, bool* done,
42 bool* sortable) const {
43 if (const SkOpAngle* result = activeAngleInner(index, start, end, done, sortable)) {
44 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +000045 }
caryclark@google.com570863f2013-09-16 15:55:01 +000046 double referenceT = fTs[index].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +000047 int lesser = index;
caryclark@google.com570863f2013-09-16 15:55:01 +000048 while (--lesser >= 0
49 && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000050 if (const SkOpAngle* result = activeAngleOther(lesser, start, end, done, sortable)) {
51 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +000052 }
53 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000054 do {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000055 if (const SkOpAngle* result = activeAngleOther(index, start, end, done, sortable)) {
56 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +000057 }
caryclark@google.com570863f2013-09-16 15:55:01 +000058 if (++index == fTs.count()) {
59 break;
60 }
61 if (fTs[index - 1].fTiny) {
62 referenceT = fTs[index].fT;
63 continue;
64 }
65 } while (precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000066 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +000067}
68
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000069const SkOpAngle* SkOpSegment::activeAngleInner(int index, int* start, int* end, bool* done,
70 bool* sortable) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +000071 int next = nextExactSpan(index, 1);
72 if (next > 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000073 const SkOpSpan& upSpan = fTs[index];
caryclark@google.com07393ca2013-04-08 11:47:37 +000074 if (upSpan.fWindValue || upSpan.fOppValue) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000075 if (*end < 0) {
76 *start = index;
77 *end = next;
caryclark@google.com07393ca2013-04-08 11:47:37 +000078 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000079 if (!upSpan.fDone) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000080 if (upSpan.fWindSum != SK_MinS32) {
81 return spanToAngle(index, next);
82 }
83 *done = false;
84 }
85 } else {
86 SkASSERT(upSpan.fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +000087 }
88 }
89 int prev = nextExactSpan(index, -1);
90 // edge leading into junction
91 if (prev >= 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000092 const SkOpSpan& downSpan = fTs[prev];
caryclark@google.com07393ca2013-04-08 11:47:37 +000093 if (downSpan.fWindValue || downSpan.fOppValue) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000094 if (*end < 0) {
95 *start = index;
96 *end = prev;
caryclark@google.com07393ca2013-04-08 11:47:37 +000097 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000098 if (!downSpan.fDone) {
99 if (downSpan.fWindSum != SK_MinS32) {
100 return spanToAngle(index, prev);
101 }
102 *done = false;
103 }
104 } else {
105 SkASSERT(downSpan.fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000106 }
107 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000108 return NULL;
109}
110
111const SkOpAngle* SkOpSegment::activeAngleOther(int index, int* start, int* end, bool* done,
112 bool* sortable) const {
113 const SkOpSpan* span = &fTs[index];
114 SkOpSegment* other = span->fOther;
115 int oIndex = span->fOtherIndex;
116 return other->activeAngleInner(oIndex, start, end, done, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000117}
118
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000119SkPoint SkOpSegment::activeLeftTop(int* firstT) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000120 SkASSERT(!done());
121 SkPoint topPt = {SK_ScalarMax, SK_ScalarMax};
122 int count = fTs.count();
123 // see if either end is not done since we want smaller Y of the pair
124 bool lastDone = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000125 double lastT = -1;
126 for (int index = 0; index < count; ++index) {
127 const SkOpSpan& span = fTs[index];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000128 if (span.fDone && lastDone) {
129 goto next;
130 }
131 if (approximately_negative(span.fT - lastT)) {
132 goto next;
133 }
134 {
135 const SkPoint& xy = xyAtT(&span);
136 if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
137 topPt = xy;
138 if (firstT) {
139 *firstT = index;
140 }
141 }
142 if (fVerb != SkPath::kLine_Verb && !lastDone) {
reed@google.com277c3f82013-05-31 15:17:50 +0000143 SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000144 if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
145 && topPt.fX > curveTop.fX)) {
146 topPt = curveTop;
147 if (firstT) {
148 *firstT = index;
149 }
150 }
151 }
152 lastT = span.fT;
153 }
154next:
155 lastDone = span.fDone;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000156 }
157 return topPt;
158}
159
160bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
161 int sumMiWinding = updateWinding(endIndex, index);
162 int sumSuWinding = updateOppWinding(endIndex, index);
163 if (fOperand) {
164 SkTSwap<int>(sumMiWinding, sumSuWinding);
165 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000166 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000167}
168
169bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000170 int* sumMiWinding, int* sumSuWinding) {
171 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000172 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000173 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000174 bool miFrom;
175 bool miTo;
176 bool suFrom;
177 bool suTo;
178 if (operand()) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000179 miFrom = (oppMaxWinding & xorMiMask) != 0;
180 miTo = (oppSumWinding & xorMiMask) != 0;
181 suFrom = (maxWinding & xorSuMask) != 0;
182 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000183 } else {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000184 miFrom = (maxWinding & xorMiMask) != 0;
185 miTo = (sumWinding & xorMiMask) != 0;
186 suFrom = (oppMaxWinding & xorSuMask) != 0;
187 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000188 }
189 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
190#if DEBUG_ACTIVE_OP
caryclarkdac1d172014-06-17 05:15:38 -0700191 SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n",
192 __FUNCTION__, debugID(), span(index).fT, span(endIndex).fT,
caryclark@google.com570863f2013-09-16 15:55:01 +0000193 SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000194#endif
195 return result;
196}
197
198bool SkOpSegment::activeWinding(int index, int endIndex) {
199 int sumWinding = updateWinding(endIndex, index);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000200 return activeWinding(index, endIndex, &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000201}
202
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000203bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) {
204 int maxWinding;
205 setUpWinding(index, endIndex, &maxWinding, sumWinding);
206 bool from = maxWinding != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000207 bool to = *sumWinding != 0;
208 bool result = gUnaryActiveEdge[from][to];
209 return result;
210}
211
caryclark@google.com570863f2013-09-16 15:55:01 +0000212void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt,
213 SkOpSegment* other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000214 int tIndex = -1;
215 int tCount = fTs.count();
216 int oIndex = -1;
217 int oCount = other->fTs.count();
218 do {
219 ++tIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000220 } while (startPt != fTs[tIndex].fPt && tIndex < tCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000221 int tIndexStart = tIndex;
222 do {
223 ++oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000224 } while (endPt != other->fTs[oIndex].fPt && oIndex < oCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000225 int oIndexStart = oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000226 const SkPoint* nextPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000227 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000228 nextPt = &fTs[++tIndex].fPt;
229 SkASSERT(fTs[tIndex].fT < 1 || startPt != *nextPt);
230 } while (startPt == *nextPt);
231 double nextT = fTs[tIndex].fT;
232 const SkPoint* oNextPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000233 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000234 oNextPt = &other->fTs[++oIndex].fPt;
235 SkASSERT(other->fTs[oIndex].fT < 1 || endPt != *oNextPt);
236 } while (endPt == *oNextPt);
237 double oNextT = other->fTs[oIndex].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000238 // at this point, spans before and after are at:
239 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
240 // if tIndexStart == 0, no prior span
241 // if nextT == 1, no following span
242
243 // advance the span with zero winding
244 // if the following span exists (not past the end, non-zero winding)
245 // connect the two edges
246 if (!fTs[tIndexStart].fWindValue) {
247 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
248#if DEBUG_CONCIDENT
249 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
250 __FUNCTION__, fID, other->fID, tIndexStart - 1,
251 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
252 xyAtT(tIndexStart).fY);
253#endif
254 addTPair(fTs[tIndexStart].fT, other, other->fTs[oIndex].fT, false,
255 fTs[tIndexStart].fPt);
256 }
257 if (nextT < 1 && fTs[tIndex].fWindValue) {
258#if DEBUG_CONCIDENT
259 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
260 __FUNCTION__, fID, other->fID, tIndex,
261 fTs[tIndex].fT, xyAtT(tIndex).fX,
262 xyAtT(tIndex).fY);
263#endif
264 addTPair(fTs[tIndex].fT, other, other->fTs[oIndexStart].fT, false, fTs[tIndex].fPt);
265 }
266 } else {
267 SkASSERT(!other->fTs[oIndexStart].fWindValue);
268 if (oIndexStart > 0 && other->fTs[oIndexStart - 1].fWindValue) {
269#if DEBUG_CONCIDENT
270 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
271 __FUNCTION__, fID, other->fID, oIndexStart - 1,
272 other->fTs[oIndexStart].fT, other->xyAtT(oIndexStart).fX,
273 other->xyAtT(oIndexStart).fY);
274 other->debugAddTPair(other->fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
275#endif
276 }
277 if (oNextT < 1 && other->fTs[oIndex].fWindValue) {
278#if DEBUG_CONCIDENT
279 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
280 __FUNCTION__, fID, other->fID, oIndex,
281 other->fTs[oIndex].fT, other->xyAtT(oIndex).fX,
282 other->xyAtT(oIndex).fY);
283 other->debugAddTPair(other->fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
284#endif
285 }
286 }
287}
288
caryclark@google.com570863f2013-09-16 15:55:01 +0000289void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
290 SkOpSegment* other) {
291 // walk this to startPt
292 // walk other to startPt
caryclark@google.com07393ca2013-04-08 11:47:37 +0000293 // if either is > 0, add a pointer to the other, copying adjacent winding
294 int tIndex = -1;
295 int oIndex = -1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000296 do {
297 ++tIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000298 } while (startPt != fTs[tIndex].fPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000299 int ttIndex = tIndex;
300 bool checkOtherTMatch = false;
301 do {
302 const SkOpSpan& span = fTs[ttIndex];
303 if (startPt != span.fPt) {
304 break;
skia.committer@gmail.coma1ed7ae2014-04-15 03:04:18 +0000305 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000306 if (span.fOther == other && span.fPt == startPt) {
307 checkOtherTMatch = true;
308 break;
309 }
310 } while (++ttIndex < count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000311 do {
312 ++oIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +0000313 } while (startPt != other->fTs[oIndex].fPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000314 bool skipAdd = false;
315 if (checkOtherTMatch) {
316 int ooIndex = oIndex;
317 do {
318 const SkOpSpan& oSpan = other->fTs[ooIndex];
319 if (startPt != oSpan.fPt) {
320 break;
skia.committer@gmail.coma1ed7ae2014-04-15 03:04:18 +0000321 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000322 if (oSpan.fT == fTs[ttIndex].fOtherT) {
323 skipAdd = true;
324 break;
325 }
326 } while (++ooIndex < other->count());
327 }
328 if ((tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) && !skipAdd) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000329 addTPair(fTs[tIndex].fT, other, other->fTs[oIndex].fT, false, startPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000330 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000331 SkPoint nextPt = startPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000332 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000333 const SkPoint* workPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000334 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000335 workPt = &fTs[++tIndex].fPt;
336 } while (nextPt == *workPt);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000337 const SkPoint* oWorkPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000338 do {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000339 oWorkPt = &other->fTs[++oIndex].fPt;
340 } while (nextPt == *oWorkPt);
caryclark@google.com570863f2013-09-16 15:55:01 +0000341 nextPt = *workPt;
342 double tStart = fTs[tIndex].fT;
343 double oStart = other->fTs[oIndex].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000344 if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) {
345 break;
346 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000347 if (*workPt == *oWorkPt) {
348 addTPair(tStart, other, oStart, false, nextPt);
349 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000350 } while (endPt != nextPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000351}
352
353void SkOpSegment::addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
354 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
355 fBounds.setCubicBounds(pts);
356}
357
358void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active) const {
359 SkPoint edge[4];
360 const SkPoint* ePtr;
361 int lastT = fTs.count() - 1;
362 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
363 ePtr = fPts;
364 } else {
365 // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
366 subDivide(start, end, edge);
367 ePtr = edge;
368 }
369 if (active) {
370 bool reverse = ePtr == fPts && start != 0;
371 if (reverse) {
reed@google.com277c3f82013-05-31 15:17:50 +0000372 path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000373 switch (fVerb) {
374 case SkPath::kLine_Verb:
375 path->deferredLine(ePtr[0]);
376 break;
377 case SkPath::kQuad_Verb:
378 path->quadTo(ePtr[1], ePtr[0]);
379 break;
380 case SkPath::kCubic_Verb:
381 path->cubicTo(ePtr[2], ePtr[1], ePtr[0]);
382 break;
383 default:
384 SkASSERT(0);
385 }
386 // return ePtr[0];
387 } else {
388 path->deferredMoveLine(ePtr[0]);
389 switch (fVerb) {
390 case SkPath::kLine_Verb:
391 path->deferredLine(ePtr[1]);
392 break;
393 case SkPath::kQuad_Verb:
394 path->quadTo(ePtr[1], ePtr[2]);
395 break;
396 case SkPath::kCubic_Verb:
397 path->cubicTo(ePtr[1], ePtr[2], ePtr[3]);
398 break;
399 default:
400 SkASSERT(0);
401 }
402 }
403 }
reed@google.com277c3f82013-05-31 15:17:50 +0000404 // return ePtr[SkPathOpsVerbToPoints(fVerb)];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000405}
406
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000407void SkOpSegment::addEndSpan(int endIndex) {
caryclark19eb3b22014-07-18 05:08:14 -0700408 SkASSERT(span(endIndex).fT == 1 || (span(endIndex).fTiny
409 && approximately_greater_than_one(span(endIndex).fT)));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000410 int spanCount = fTs.count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000411 int startIndex = endIndex - 1;
412 while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) {
caryclark19eb3b22014-07-18 05:08:14 -0700413 --startIndex;
414 SkASSERT(startIndex > 0);
415 --endIndex;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000416 }
caryclarkdac1d172014-06-17 05:15:38 -0700417 SkOpAngle& angle = fAngles.push_back();
418 angle.set(this, spanCount - 1, startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000419#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000420 debugCheckPointsEqualish(endIndex, spanCount);
421#endif
caryclarkdac1d172014-06-17 05:15:38 -0700422 setFromAngle(endIndex, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000423}
424
caryclarkdac1d172014-06-17 05:15:38 -0700425void SkOpSegment::setFromAngle(int endIndex, SkOpAngle* angle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000426 int spanCount = fTs.count();
427 do {
caryclarkdac1d172014-06-17 05:15:38 -0700428 fTs[endIndex].fFromAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000429 } while (++endIndex < spanCount);
430}
431
caryclark@google.com07393ca2013-04-08 11:47:37 +0000432void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
433 init(pts, SkPath::kLine_Verb, operand, evenOdd);
434 fBounds.set(pts, 2);
435}
436
437// add 2 to edge or out of range values to get T extremes
438void SkOpSegment::addOtherT(int index, double otherT, int otherIndex) {
439 SkOpSpan& span = fTs[index];
caryclark@google.com03610322013-04-18 15:58:21 +0000440 if (precisely_zero(otherT)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000441 otherT = 0;
caryclark@google.com03610322013-04-18 15:58:21 +0000442 } else if (precisely_equal(otherT, 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000443 otherT = 1;
444 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000445 span.fOtherT = otherT;
446 span.fOtherIndex = otherIndex;
447}
448
449void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
450 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
451 fBounds.setQuadBounds(pts);
452}
453
caryclarkdac1d172014-06-17 05:15:38 -0700454SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
455 int spanIndex = count() - 1;
456 int startIndex = nextExactSpan(spanIndex, -1);
457 SkASSERT(startIndex >= 0);
458 SkOpAngle& angle = fAngles.push_back();
459 *anglePtr = &angle;
460 angle.set(this, spanIndex, startIndex);
461 setFromAngle(spanIndex, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000462 SkOpSegment* other;
caryclarkdac1d172014-06-17 05:15:38 -0700463 int oStartIndex, oEndIndex;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000464 do {
caryclarkdac1d172014-06-17 05:15:38 -0700465 const SkOpSpan& span = fTs[spanIndex];
466 SkASSERT(span.fT > 0);
467 other = span.fOther;
468 oStartIndex = span.fOtherIndex;
469 oEndIndex = other->nextExactSpan(oStartIndex, 1);
470 if (oEndIndex > 0 && other->span(oStartIndex).fWindValue) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000471 break;
472 }
caryclarkdac1d172014-06-17 05:15:38 -0700473 oEndIndex = oStartIndex;
474 oStartIndex = other->nextExactSpan(oEndIndex, -1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000475 --spanIndex;
caryclarkdac1d172014-06-17 05:15:38 -0700476 } while (oStartIndex < 0 || !other->span(oStartIndex).fWindSum);
477 SkOpAngle& oAngle = other->fAngles.push_back();
478 oAngle.set(other, oStartIndex, oEndIndex);
479 other->setToAngle(oEndIndex, &oAngle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000480 *otherPtr = other;
caryclarkdac1d172014-06-17 05:15:38 -0700481 return &oAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000482}
483
caryclarkdac1d172014-06-17 05:15:38 -0700484SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
485 int endIndex = nextExactSpan(0, 1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000486 SkASSERT(endIndex > 0);
caryclarkdac1d172014-06-17 05:15:38 -0700487 SkOpAngle& angle = fAngles.push_back();
488 *anglePtr = &angle;
489 angle.set(this, 0, endIndex);
490 setToAngle(endIndex, &angle);
491 int spanIndex = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000492 SkOpSegment* other;
caryclarkdac1d172014-06-17 05:15:38 -0700493 int oStartIndex, oEndIndex;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000494 do {
caryclarkdac1d172014-06-17 05:15:38 -0700495 const SkOpSpan& span = fTs[spanIndex];
496 SkASSERT(span.fT < 1);
497 other = span.fOther;
498 oEndIndex = span.fOtherIndex;
499 oStartIndex = other->nextExactSpan(oEndIndex, -1);
500 if (oStartIndex >= 0 && other->span(oStartIndex).fWindValue) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000501 break;
502 }
caryclarkdac1d172014-06-17 05:15:38 -0700503 oStartIndex = oEndIndex;
504 oEndIndex = other->nextExactSpan(oStartIndex, 1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000505 ++spanIndex;
caryclarkdac1d172014-06-17 05:15:38 -0700506 } while (oEndIndex < 0 || !other->span(oStartIndex).fWindValue);
507 SkOpAngle& oAngle = other->fAngles.push_back();
508 oAngle.set(other, oEndIndex, oStartIndex);
509 other->setFromAngle(oEndIndex, &oAngle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000510 *otherPtr = other;
caryclarkdac1d172014-06-17 05:15:38 -0700511 return &oAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000512}
513
caryclarkdac1d172014-06-17 05:15:38 -0700514SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000515 SkOpSegment* other;
caryclarkdac1d172014-06-17 05:15:38 -0700516 SkOpAngle* angle, * otherAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000517 if (step > 0) {
caryclarkdac1d172014-06-17 05:15:38 -0700518 otherAngle = addSingletonAngleUp(&other, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000519 } else {
caryclarkdac1d172014-06-17 05:15:38 -0700520 otherAngle = addSingletonAngleDown(&other, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000521 }
caryclarkdac1d172014-06-17 05:15:38 -0700522 angle->insert(otherAngle);
523 return angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000524}
525
526void SkOpSegment::addStartSpan(int endIndex) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000527 int index = 0;
caryclarkdac1d172014-06-17 05:15:38 -0700528 SkOpAngle& angle = fAngles.push_back();
529 angle.set(this, index, endIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000530#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000531 debugCheckPointsEqualish(index, endIndex);
532#endif
caryclarkdac1d172014-06-17 05:15:38 -0700533 setToAngle(endIndex, &angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000534}
535
caryclarkdac1d172014-06-17 05:15:38 -0700536void SkOpSegment::setToAngle(int endIndex, SkOpAngle* angle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000537 int index = 0;
538 do {
caryclarkdac1d172014-06-17 05:15:38 -0700539 fTs[index].fToAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000540 } while (++index < endIndex);
541}
542
caryclark@google.com07393ca2013-04-08 11:47:37 +0000543 // Defer all coincident edge processing until
544 // after normal intersections have been computed
545
546// no need to be tricky; insert in normal T order
547// resolve overlapping ts when considering coincidence later
548
549 // add non-coincident intersection. Resulting edges are sorted in T.
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000550int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000551 SkASSERT(this != other || fVerb == SkPath::kCubic_Verb);
552 #if 0 // this needs an even rougher association to be useful
553 SkASSERT(SkDPoint::RoughlyEqual(ptAtT(newT), pt));
554 #endif
caryclarkdac1d172014-06-17 05:15:38 -0700555 const SkPoint& firstPt = fPts[0];
556 const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
557 SkASSERT(newT == 0 || !precisely_zero(newT));
558 SkASSERT(newT == 1 || !precisely_equal(newT, 1));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000559 // FIXME: in the pathological case where there is a ton of intercepts,
560 // binary search?
561 int insertedAt = -1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000562 int tCount = fTs.count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000563 for (int index = 0; index < tCount; ++index) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000564 // OPTIMIZATION: if there are three or more identical Ts, then
565 // the fourth and following could be further insertion-sorted so
566 // that all the edges are clockwise or counterclockwise.
567 // This could later limit segment tests to the two adjacent
568 // neighbors, although it doesn't help with determining which
569 // circular direction to go in.
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000570 const SkOpSpan& span = fTs[index];
571 if (newT < span.fT) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000572 insertedAt = index;
573 break;
574 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000575 if (newT == span.fT) {
576 if (pt == span.fPt) {
577 insertedAt = index;
578 break;
579 }
580 if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
581 insertedAt = index;
582 break;
583 }
584 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000585 }
586 SkOpSpan* span;
587 if (insertedAt >= 0) {
588 span = fTs.insert(insertedAt);
589 } else {
590 insertedAt = tCount;
591 span = fTs.append();
592 }
593 span->fT = newT;
caryclarkdac1d172014-06-17 05:15:38 -0700594 span->fOtherT = -1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000595 span->fOther = other;
596 span->fPt = pt;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000597#if 0
598 // cubics, for instance, may not be exact enough to satisfy this check (e.g., cubicOp69d)
599 SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX)
600 && approximately_equal(xyAtT(newT).fY, pt.fY));
601#endif
caryclarkdac1d172014-06-17 05:15:38 -0700602 span->fFromAngle = NULL;
603 span->fToAngle = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000604 span->fWindSum = SK_MinS32;
605 span->fOppSum = SK_MinS32;
606 span->fWindValue = 1;
607 span->fOppValue = 0;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000608 span->fChased = false;
caryclarkdac1d172014-06-17 05:15:38 -0700609 span->fCoincident = false;
610 span->fLoop = false;
611 span->fNear = false;
612 span->fMultiple = false;
613 span->fSmall = false;
614 span->fTiny = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000615 if ((span->fDone = newT == 1)) {
616 ++fDoneSpans;
617 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000618 int less = -1;
caryclarkdac1d172014-06-17 05:15:38 -0700619// FIXME: note that this relies on spans being a continguous array
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000620// find range of spans with nearly the same point as this one
621 while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, pt)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000622 if (fVerb == SkPath::kCubic_Verb) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000623 double tInterval = newT - span[less].fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000624 double tMid = newT - tInterval / 2;
625 SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
626 if (!midPt.approximatelyEqual(xyAtT(span))) {
627 break;
628 }
629 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000630 --less;
631 }
632 int more = 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000633 while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].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 tEndInterval = span[more].fT - newT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000636 double tMid = newT - tEndInterval / 2;
637 SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
638 if (!midEndPt.approximatelyEqual(xyAtT(span))) {
639 break;
640 }
641 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000642 ++more;
643 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000644 ++less;
645 --more;
646 while (more - 1 > less && span[more].fPt == span[more - 1].fPt
647 && span[more].fT == span[more - 1].fT) {
648 --more;
649 }
650 if (less == more) {
651 return insertedAt;
652 }
653 if (precisely_negative(span[more].fT - span[less].fT)) {
654 return insertedAt;
655 }
656// if the total range of t values is big enough, mark all tiny
657 bool tiny = span[less].fPt == span[more].fPt;
658 int index = less;
659 do {
660 fSmall = span[index].fSmall = true;
661 fTiny |= span[index].fTiny = tiny;
662 if (!span[index].fDone) {
663 span[index].fDone = true;
664 ++fDoneSpans;
665 }
666 } while (++index < more);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000667 return insertedAt;
668}
669
670// set spans from start to end to decrement by one
671// note this walks other backwards
caryclark@google.coma5e55922013-05-07 18:51:31 +0000672// FIXME: there's probably an edge case that can be constructed where
caryclark@google.com07393ca2013-04-08 11:47:37 +0000673// two span in one segment are separated by float epsilon on one span but
674// not the other, if one segment is very small. For this
675// case the counts asserted below may or may not be enough to separate the
676// spans. Even if the counts work out, what if the spans aren't correctly
677// sorted? It feels better in such a case to match the span's other span
678// pointer since both coincident segments must contain the same spans.
caryclark@google.coma5e55922013-05-07 18:51:31 +0000679// FIXME? It seems that decrementing by one will fail for complex paths that
680// have three or more coincident edges. Shouldn't this subtract the difference
681// between the winding values?
caryclark@google.com570863f2013-09-16 15:55:01 +0000682/* |--> |-->
683this 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4
684other 2<<<<1<<<<0 2<<<<1<<<<0 2<<<<1<<<<0
685 ^ ^ <--| <--|
686 startPt endPt test/oTest first pos test/oTest final pos
687*/
688void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000689 bool binary = fOperand != other->fOperand;
690 int index = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +0000691 while (startPt != fTs[index].fPt) {
692 SkASSERT(index < fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000693 ++index;
694 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000695 while (index > 0 && precisely_equal(fTs[index].fT, fTs[index - 1].fT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000696 --index;
697 }
caryclarkdac1d172014-06-17 05:15:38 -0700698 bool oFoundEnd = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000699 int oIndex = other->fTs.count();
caryclark@google.com570863f2013-09-16 15:55:01 +0000700 while (startPt != other->fTs[--oIndex].fPt) { // look for startPt match
701 SkASSERT(oIndex > 0);
702 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000703 double oStartT = other->fTs[oIndex].fT;
704 // look for first point beyond match
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000705 while (startPt == other->fTs[--oIndex].fPt || precisely_equal(oStartT, other->fTs[oIndex].fT)) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000706 SkASSERT(oIndex > 0);
707 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000708 SkOpSpan* test = &fTs[index];
709 SkOpSpan* oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +0000710 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
711 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
caryclarkdac1d172014-06-17 05:15:38 -0700712 bool decrement, track, bigger;
713 int originalWindValue;
714 const SkPoint* testPt;
715 const SkPoint* oTestPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000716 do {
caryclark@google.com570863f2013-09-16 15:55:01 +0000717 SkASSERT(test->fT < 1);
718 SkASSERT(oTest->fT < 1);
caryclarkdac1d172014-06-17 05:15:38 -0700719 decrement = test->fWindValue && oTest->fWindValue;
720 track = test->fWindValue || oTest->fWindValue;
721 bigger = test->fWindValue >= oTest->fWindValue;
722 testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000723 double testT = test->fT;
caryclarkdac1d172014-06-17 05:15:38 -0700724 oTestPt = &oTest->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000725 double oTestT = oTest->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000726 do {
727 if (decrement) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000728 if (binary && bigger) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000729 test->fOppValue--;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000730 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +0000731 decrementSpan(test);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000732 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000733 } else if (track) {
caryclarkdac1d172014-06-17 05:15:38 -0700734 TrackOutsidePair(&outsidePts, *testPt, *oTestPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000735 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000736 SkASSERT(index < fTs.count() - 1);
737 test = &fTs[++index];
caryclarkdac1d172014-06-17 05:15:38 -0700738 } while (*testPt == test->fPt || precisely_equal(testT, test->fT));
739 originalWindValue = oTest->fWindValue;
caryclark@google.com570863f2013-09-16 15:55:01 +0000740 do {
741 SkASSERT(oTest->fT < 1);
742 SkASSERT(originalWindValue == oTest->fWindValue);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000743 if (decrement) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000744 if (binary && !bigger) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000745 oTest->fOppValue--;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000746 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +0000747 other->decrementSpan(oTest);
caryclark@google.coma5e55922013-05-07 18:51:31 +0000748 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000749 } else if (track) {
caryclarkdac1d172014-06-17 05:15:38 -0700750 TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
751 }
752 if (!oIndex) {
753 break;
754 }
755 oFoundEnd |= endPt == oTest->fPt;
756 oTest = &other->fTs[--oIndex];
757 } while (*oTestPt == oTest->fPt || precisely_equal(oTestT, oTest->fT));
758 } while (endPt != test->fPt && test->fT < 1);
759 // FIXME: determine if canceled edges need outside ts added
760 if (!oFoundEnd) {
761 for (int oIdx2 = oIndex; oIdx2 >= 0; --oIdx2) {
762 SkOpSpan* oTst2 = &other->fTs[oIdx2];
763 if (originalWindValue != oTst2->fWindValue) {
764 goto skipAdvanceOtherCancel;
765 }
766 if (!oTst2->fWindValue) {
767 goto skipAdvanceOtherCancel;
768 }
769 if (endPt == other->fTs[oIdx2].fPt) {
770 break;
771 }
772 }
773 do {
774 SkASSERT(originalWindValue == oTest->fWindValue);
775 if (decrement) {
776 if (binary && !bigger) {
777 oTest->fOppValue--;
778 } else {
779 other->decrementSpan(oTest);
780 }
781 } else if (track) {
782 TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000783 }
784 if (!oIndex) {
785 break;
786 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000787 oTest = &other->fTs[--oIndex];
caryclarkdac1d172014-06-17 05:15:38 -0700788 oFoundEnd |= endPt == oTest->fPt;
789 } while (!oFoundEnd || endPt == oTest->fPt);
790 }
791skipAdvanceOtherCancel:
caryclark@google.com570863f2013-09-16 15:55:01 +0000792 int outCount = outsidePts.count();
793 if (!done() && outCount) {
794 addCancelOutsides(outsidePts[0], outsidePts[1], other);
795 if (outCount > 2) {
796 addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000797 }
798 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000799 if (!other->done() && oOutsidePts.count()) {
800 other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000801 }
caryclarkdac1d172014-06-17 05:15:38 -0700802 setCoincidentRange(startPt, endPt, other);
803 other->setCoincidentRange(startPt, endPt, this);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000804}
805
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000806int SkOpSegment::addSelfT(const SkPoint& pt, double newT) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000807 // if the tail nearly intersects itself but not quite, the caller records this separately
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000808 int result = addT(this, pt, newT);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000809 SkOpSpan* span = &fTs[result];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000810 fLoop = span->fLoop = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000811 return result;
812}
813
caryclarkdac1d172014-06-17 05:15:38 -0700814// find the starting or ending span with an existing loop of angles
815// FIXME? replicate for all identical starting/ending spans?
816// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
817// FIXME? assert that only one other span has a valid windValue or oppValue
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000818void SkOpSegment::addSimpleAngle(int index) {
caryclarkdac1d172014-06-17 05:15:38 -0700819 SkOpSpan* span = &fTs[index];
caryclark19eb3b22014-07-18 05:08:14 -0700820 int idx;
821 int start, end;
822 if (span->fT == 0) {
823 idx = 0;
824 span = &fTs[0];
caryclarkdac1d172014-06-17 05:15:38 -0700825 do {
826 if (span->fToAngle) {
827 SkASSERT(span->fToAngle->loopCount() == 2);
828 SkASSERT(!span->fFromAngle);
829 span->fFromAngle = span->fToAngle->next();
830 return;
831 }
caryclark19eb3b22014-07-18 05:08:14 -0700832 span = &fTs[++idx];
caryclarkdac1d172014-06-17 05:15:38 -0700833 } while (span->fT == 0);
caryclark19eb3b22014-07-18 05:08:14 -0700834 SkASSERT(!fTs[0].fTiny && fTs[idx].fT > 0);
835 addStartSpan(idx);
836 start = 0;
837 end = idx;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000838 } else {
caryclark19eb3b22014-07-18 05:08:14 -0700839 idx = count() - 1;
840 span = &fTs[idx];
caryclarkdac1d172014-06-17 05:15:38 -0700841 do {
842 if (span->fFromAngle) {
843 SkASSERT(span->fFromAngle->loopCount() == 2);
844 SkASSERT(!span->fToAngle);
845 span->fToAngle = span->fFromAngle->next();
846 return;
847 }
caryclark19eb3b22014-07-18 05:08:14 -0700848 span = &fTs[--idx];
caryclarkdac1d172014-06-17 05:15:38 -0700849 } while (span->fT == 1);
caryclark19eb3b22014-07-18 05:08:14 -0700850 SkASSERT(!fTs[idx].fTiny && fTs[idx].fT < 1);
851 addEndSpan(++idx);
852 start = idx;
853 end = count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000854 }
caryclark19eb3b22014-07-18 05:08:14 -0700855 SkOpSegment* other;
856 SkOpSpan* oSpan;
857 index = start;
858 do {
859 span = &fTs[index];
860 other = span->fOther;
861 int oFrom = span->fOtherIndex;
862 oSpan = &other->fTs[oFrom];
863 if (oSpan->fT < 1 && oSpan->fWindValue) {
864 break;
865 }
866 if (oSpan->fT == 0) {
867 continue;
868 }
869 oFrom = other->nextExactSpan(oFrom, -1);
870 SkOpSpan* oFromSpan = &other->fTs[oFrom];
871 SkASSERT(oFromSpan->fT < 1);
872 if (oFromSpan->fWindValue) {
873 break;
874 }
875 } while (++index < end);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000876 SkOpAngle* angle, * oAngle;
caryclark19eb3b22014-07-18 05:08:14 -0700877 if (span->fT == 0) {
caryclarkdac1d172014-06-17 05:15:38 -0700878 SkASSERT(span->fOtherIndex - 1 >= 0);
879 SkASSERT(span->fOtherT == 1);
caryclark19eb3b22014-07-18 05:08:14 -0700880 SkDEBUGCODE(int oPriorIndex = other->nextExactSpan(span->fOtherIndex, -1));
881 SkDEBUGCODE(const SkOpSpan& oPrior = other->span(oPriorIndex));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000882 SkASSERT(!oPrior.fTiny && oPrior.fT < 1);
caryclarkdac1d172014-06-17 05:15:38 -0700883 other->addEndSpan(span->fOtherIndex);
884 angle = span->fToAngle;
caryclark19eb3b22014-07-18 05:08:14 -0700885 oAngle = oSpan->fFromAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000886 } else {
caryclarkdac1d172014-06-17 05:15:38 -0700887 SkASSERT(span->fOtherIndex + 1 < other->count());
888 SkASSERT(span->fOtherT == 0);
caryclark19eb3b22014-07-18 05:08:14 -0700889 SkASSERT(!oSpan->fTiny && (other->fTs[span->fOtherIndex + 1].fT > 0
caryclarkdac1d172014-06-17 05:15:38 -0700890 || (other->fTs[span->fOtherIndex + 1].fFromAngle == NULL
891 && other->fTs[span->fOtherIndex + 1].fToAngle == NULL)));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000892 int oIndex = 1;
893 do {
894 const SkOpSpan& osSpan = other->span(oIndex);
caryclarkdac1d172014-06-17 05:15:38 -0700895 if (osSpan.fFromAngle || osSpan.fT > 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000896 break;
897 }
898 ++oIndex;
899 SkASSERT(oIndex < other->count());
900 } while (true);
901 other->addStartSpan(oIndex);
caryclarkdac1d172014-06-17 05:15:38 -0700902 angle = span->fFromAngle;
caryclark19eb3b22014-07-18 05:08:14 -0700903 oAngle = oSpan->fToAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000904 }
905 angle->insert(oAngle);
906}
907
caryclarkdac1d172014-06-17 05:15:38 -0700908void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
909 debugValidate();
910 int count = this->count();
911 for (int index = 0; index < count; ++index) {
912 SkOpSpan& span = fTs[index];
913 if (!span.fMultiple) {
914 continue;
915 }
916 int end = nextExactSpan(index, 1);
917 SkASSERT(end > index + 1);
918 const SkPoint& thisPt = span.fPt;
919 while (index < end - 1) {
920 SkOpSegment* other1 = span.fOther;
921 int oCnt = other1->count();
922 for (int idx2 = index + 1; idx2 < end; ++idx2) {
923 SkOpSpan& span2 = fTs[idx2];
924 SkOpSegment* other2 = span2.fOther;
925 for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
926 SkOpSpan& oSpan = other1->fTs[oIdx];
927 if (oSpan.fOther != other2) {
928 continue;
929 }
930 if (oSpan.fPt == thisPt) {
931 goto skipExactMatches;
932 }
933 }
934 for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
935 SkOpSpan& oSpan = other1->fTs[oIdx];
936 if (oSpan.fOther != other2) {
937 continue;
938 }
939 if (SkDPoint::RoughlyEqual(oSpan.fPt, thisPt)) {
940 SkOpSpan& oSpan2 = other2->fTs[oSpan.fOtherIndex];
941 if (zero_or_one(span.fOtherT) || zero_or_one(oSpan.fT)
942 || zero_or_one(span2.fOtherT) || zero_or_one(oSpan2.fT)) {
943 return;
944 }
945 if (!way_roughly_equal(span.fOtherT, oSpan.fT)
946 || !way_roughly_equal(span2.fOtherT, oSpan2.fT)
947 || !way_roughly_equal(span2.fOtherT, oSpan.fOtherT)
948 || !way_roughly_equal(span.fOtherT, oSpan2.fOtherT)) {
949 return;
950 }
951 alignSpan(thisPt, span.fOtherT, other1, span2.fOtherT,
952 other2, &oSpan, alignedArray);
953 alignSpan(thisPt, span2.fOtherT, other2, span.fOtherT,
954 other1, &oSpan2, alignedArray);
955 break;
956 }
957 }
958 skipExactMatches:
959 ;
960 }
961 ++index;
962 }
963 }
964 debugValidate();
965}
966
967void SkOpSegment::alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other,
968 double otherT, const SkOpSegment* other2, SkOpSpan* oSpan,
969 SkTDArray<AlignedSpan>* alignedArray) {
970 AlignedSpan* aligned = alignedArray->append();
971 aligned->fOldPt = oSpan->fPt;
972 aligned->fPt = newPt;
973 aligned->fOldT = oSpan->fT;
974 aligned->fT = newT;
975 aligned->fSegment = this; // OPTIMIZE: may be unused, can remove
976 aligned->fOther1 = other;
977 aligned->fOther2 = other2;
978 SkASSERT(SkDPoint::RoughlyEqual(oSpan->fPt, newPt));
979 oSpan->fPt = newPt;
980// SkASSERT(way_roughly_equal(oSpan->fT, newT));
981 oSpan->fT = newT;
982// SkASSERT(way_roughly_equal(oSpan->fOtherT, otherT));
983 oSpan->fOtherT = otherT;
984}
985
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000986bool SkOpSegment::alignSpan(int index, double thisT, const SkPoint& thisPt) {
987 bool aligned = false;
988 SkOpSpan* span = &fTs[index];
989 SkOpSegment* other = span->fOther;
990 int oIndex = span->fOtherIndex;
991 SkOpSpan* oSpan = &other->fTs[oIndex];
992 if (span->fT != thisT) {
993 span->fT = thisT;
994 oSpan->fOtherT = thisT;
995 aligned = true;
996 }
997 if (span->fPt != thisPt) {
998 span->fPt = thisPt;
999 oSpan->fPt = thisPt;
1000 aligned = true;
1001 }
1002 double oT = oSpan->fT;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001003 if (oT == 0) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001004 return aligned;
1005 }
1006 int oStart = other->nextSpan(oIndex, -1) + 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001007 oSpan = &other->fTs[oStart];
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001008 int otherIndex = oStart;
1009 if (oT == 1) {
1010 if (aligned) {
1011 while (oSpan->fPt == thisPt && oSpan->fT != 1) {
1012 oSpan->fTiny = true;
1013 ++oSpan;
1014 }
1015 }
1016 return aligned;
1017 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001018 oT = oSpan->fT;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001019 int oEnd = other->nextSpan(oIndex, 1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001020 bool oAligned = false;
1021 if (oSpan->fPt != thisPt) {
1022 oAligned |= other->alignSpan(oStart, oT, thisPt);
1023 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001024 while (++otherIndex < oEnd) {
1025 SkOpSpan* oNextSpan = &other->fTs[otherIndex];
1026 if (oNextSpan->fT != oT || oNextSpan->fPt != thisPt) {
1027 oAligned |= other->alignSpan(otherIndex, oT, thisPt);
1028 }
1029 }
1030 if (oAligned) {
1031 other->alignSpanState(oStart, oEnd);
1032 }
1033 return aligned;
1034}
1035
1036void SkOpSegment::alignSpanState(int start, int end) {
1037 SkOpSpan* lastSpan = &fTs[--end];
1038 bool allSmall = lastSpan->fSmall;
1039 bool allTiny = lastSpan->fTiny;
1040 bool allDone = lastSpan->fDone;
1041 SkDEBUGCODE(int winding = lastSpan->fWindValue);
1042 SkDEBUGCODE(int oppWinding = lastSpan->fOppValue);
1043 int index = start;
1044 while (index < end) {
1045 SkOpSpan* span = &fTs[index];
1046 span->fSmall = allSmall;
1047 span->fTiny = allTiny;
1048 if (span->fDone != allDone) {
1049 span->fDone = allDone;
1050 fDoneSpans += allDone ? 1 : -1;
1051 }
1052 SkASSERT(span->fWindValue == winding);
1053 SkASSERT(span->fOppValue == oppWinding);
1054 ++index;
1055 }
1056}
1057
caryclarkdac1d172014-06-17 05:15:38 -07001058void SkOpSegment::blindCancel(const SkCoincidence& coincidence, SkOpSegment* other) {
1059 bool binary = fOperand != other->fOperand;
1060 int index = 0;
1061 int last = this->count();
1062 do {
1063 SkOpSpan& span = this->fTs[--last];
1064 if (span.fT != 1 && !span.fSmall) {
1065 break;
1066 }
1067 span.fCoincident = true;
1068 } while (true);
1069 int oIndex = other->count();
1070 do {
1071 SkOpSpan& oSpan = other->fTs[--oIndex];
1072 if (oSpan.fT != 1 && !oSpan.fSmall) {
1073 break;
1074 }
1075 oSpan.fCoincident = true;
1076 } while (true);
1077 do {
1078 SkOpSpan* test = &this->fTs[index];
1079 int baseWind = test->fWindValue;
1080 int baseOpp = test->fOppValue;
1081 int endIndex = index;
1082 while (++endIndex <= last) {
1083 SkOpSpan* endSpan = &this->fTs[endIndex];
1084 SkASSERT(endSpan->fT < 1);
1085 if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
1086 break;
1087 }
1088 endSpan->fCoincident = true;
1089 }
1090 SkOpSpan* oTest = &other->fTs[oIndex];
1091 int oBaseWind = oTest->fWindValue;
1092 int oBaseOpp = oTest->fOppValue;
1093 int oStartIndex = oIndex;
1094 while (--oStartIndex >= 0) {
1095 SkOpSpan* oStartSpan = &other->fTs[oStartIndex];
1096 if (oStartSpan->fWindValue != oBaseWind || oStartSpan->fOppValue != oBaseOpp) {
1097 break;
1098 }
1099 oStartSpan->fCoincident = true;
1100 }
1101 bool decrement = baseWind && oBaseWind;
1102 bool bigger = baseWind >= oBaseWind;
1103 do {
1104 SkASSERT(test->fT < 1);
1105 if (decrement) {
1106 if (binary && bigger) {
1107 test->fOppValue--;
1108 } else {
1109 decrementSpan(test);
1110 }
1111 }
1112 test->fCoincident = true;
1113 test = &fTs[++index];
1114 } while (index < endIndex);
1115 do {
1116 SkASSERT(oTest->fT < 1);
1117 if (decrement) {
1118 if (binary && !bigger) {
1119 oTest->fOppValue--;
1120 } else {
1121 other->decrementSpan(oTest);
1122 }
1123 }
1124 oTest->fCoincident = true;
1125 oTest = &other->fTs[--oIndex];
1126 } while (oIndex > oStartIndex);
1127 } while (index <= last && oIndex >= 0);
1128 SkASSERT(index > last);
1129 SkASSERT(oIndex < 0);
1130}
1131
1132void SkOpSegment::blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other) {
1133 bool binary = fOperand != other->fOperand;
1134 int index = 0;
1135 int last = this->count();
1136 do {
1137 SkOpSpan& span = this->fTs[--last];
1138 if (span.fT != 1 && !span.fSmall) {
1139 break;
1140 }
1141 span.fCoincident = true;
1142 } while (true);
1143 int oIndex = 0;
1144 int oLast = other->count();
1145 do {
1146 SkOpSpan& oSpan = other->fTs[--oLast];
1147 if (oSpan.fT != 1 && !oSpan.fSmall) {
1148 break;
1149 }
1150 oSpan.fCoincident = true;
1151 } while (true);
1152 do {
1153 SkOpSpan* test = &this->fTs[index];
1154 int baseWind = test->fWindValue;
1155 int baseOpp = test->fOppValue;
1156 int endIndex = index;
1157 SkOpSpan* endSpan;
1158 while (++endIndex <= last) {
1159 endSpan = &this->fTs[endIndex];
1160 SkASSERT(endSpan->fT < 1);
1161 if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
1162 break;
1163 }
1164 endSpan->fCoincident = true;
1165 }
1166 SkOpSpan* oTest = &other->fTs[oIndex];
1167 int oBaseWind = oTest->fWindValue;
1168 int oBaseOpp = oTest->fOppValue;
1169 int oEndIndex = oIndex;
1170 SkOpSpan* oEndSpan;
1171 while (++oEndIndex <= oLast) {
1172 oEndSpan = &this->fTs[oEndIndex];
1173 SkASSERT(oEndSpan->fT < 1);
1174 if (oEndSpan->fWindValue != oBaseWind || oEndSpan->fOppValue != oBaseOpp) {
1175 break;
1176 }
1177 oEndSpan->fCoincident = true;
1178 }
1179 // consolidate the winding count even if done
1180 if ((test->fWindValue || test->fOppValue) && (oTest->fWindValue || oTest->fOppValue)) {
1181 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
1182 bumpCoincidentBlind(binary, index, endIndex);
1183 other->bumpCoincidentOBlind(oIndex, oEndIndex);
1184 } else {
1185 other->bumpCoincidentBlind(binary, oIndex, oEndIndex);
1186 bumpCoincidentOBlind(index, endIndex);
1187 }
1188 }
1189 index = endIndex;
1190 oIndex = oEndIndex;
1191 } while (index <= last && oIndex <= oLast);
1192 SkASSERT(index > last);
1193 SkASSERT(oIndex > oLast);
1194}
1195
1196void SkOpSegment::bumpCoincidentBlind(bool binary, int index, int endIndex) {
1197 const SkOpSpan& oTest = fTs[index];
1198 int oWindValue = oTest.fWindValue;
1199 int oOppValue = oTest.fOppValue;
1200 if (binary) {
1201 SkTSwap<int>(oWindValue, oOppValue);
1202 }
1203 do {
1204 (void) bumpSpan(&fTs[index], oWindValue, oOppValue);
1205 } while (++index < endIndex);
1206}
1207
caryclark@google.com570863f2013-09-16 15:55:01 +00001208void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
1209 SkTArray<SkPoint, true>* outsideTs) {
1210 int index = *indexPtr;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001211 int oWindValue = oTest.fWindValue;
1212 int oOppValue = oTest.fOppValue;
caryclark@google.com570863f2013-09-16 15:55:01 +00001213 if (binary) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001214 SkTSwap<int>(oWindValue, oOppValue);
1215 }
1216 SkOpSpan* const test = &fTs[index];
1217 SkOpSpan* end = test;
caryclark@google.com570863f2013-09-16 15:55:01 +00001218 const SkPoint& oStartPt = oTest.fPt;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001219 do {
1220 if (bumpSpan(end, oWindValue, oOppValue)) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001221 TrackOutside(outsideTs, oStartPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001222 }
1223 end = &fTs[++index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001224 } while ((end->fPt == test->fPt || precisely_equal(end->fT, test->fT)) && end->fT < 1);
caryclark@google.com570863f2013-09-16 15:55:01 +00001225 *indexPtr = index;
1226}
1227
caryclarkdac1d172014-06-17 05:15:38 -07001228void SkOpSegment::bumpCoincidentOBlind(int index, int endIndex) {
1229 do {
1230 zeroSpan(&fTs[index]);
1231 } while (++index < endIndex);
1232}
1233
caryclark@google.com07393ca2013-04-08 11:47:37 +00001234// because of the order in which coincidences are resolved, this and other
1235// may not have the same intermediate points. Compute the corresponding
1236// intermediate T values (using this as the master, other as the follower)
1237// and walk other conditionally -- hoping that it catches up in the end
caryclark@google.com570863f2013-09-16 15:55:01 +00001238void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
1239 SkTArray<SkPoint, true>* oOutsidePts) {
1240 int oIndex = *oIndexPtr;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001241 SkOpSpan* const oTest = &fTs[oIndex];
1242 SkOpSpan* oEnd = oTest;
caryclark@google.com570863f2013-09-16 15:55:01 +00001243 const SkPoint& oStartPt = oTest->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001244 double oStartT = oTest->fT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001245#if 0 // FIXME : figure out what disabling this breaks
1246 const SkPoint& startPt = test.fPt;
1247 // this is always true since oEnd == oTest && oStartPt == oTest->fPt -- find proper condition
1248 if (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001249 TrackOutside(oOutsidePts, startPt);
1250 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001251#endif
1252 while (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001253 zeroSpan(oEnd);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001254 oEnd = &fTs[++oIndex];
1255 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001256 *oIndexPtr = oIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001257}
1258
1259// FIXME: need to test this case:
1260// contourA has two segments that are coincident
1261// contourB has two segments that are coincident in the same place
1262// each ends up with +2/0 pairs for winding count
1263// since logic below doesn't transfer count (only increments/decrements) can this be
1264// resolved to +4/0 ?
1265
1266// set spans from start to end to increment the greater by one and decrement
1267// the lesser
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001268void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
caryclark@google.com570863f2013-09-16 15:55:01 +00001269 SkOpSegment* other) {
1270 bool binary = fOperand != other->fOperand;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001271 int index = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +00001272 while (startPt != fTs[index].fPt) {
1273 SkASSERT(index < fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001274 ++index;
1275 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001276 double startT = fTs[index].fT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001277 while (index > 0 && precisely_equal(fTs[index - 1].fT, startT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001278 --index;
1279 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001280 int oIndex = 0;
caryclark@google.com570863f2013-09-16 15:55:01 +00001281 while (startPt != other->fTs[oIndex].fPt) {
1282 SkASSERT(oIndex < other->fTs.count());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001283 ++oIndex;
1284 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001285 double oStartT = other->fTs[oIndex].fT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001286 while (oIndex > 0 && precisely_equal(other->fTs[oIndex - 1].fT, oStartT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001287 --oIndex;
1288 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001289 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
1290 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001291 SkOpSpan* test = &fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +00001292 const SkPoint* testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001293 double testT = test->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001294 SkOpSpan* oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +00001295 const SkPoint* oTestPt = &oTest->fPt;
1296 SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001297 do {
caryclark@google.com570863f2013-09-16 15:55:01 +00001298 SkASSERT(test->fT < 1);
1299 SkASSERT(oTest->fT < 1);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001300
1301 // consolidate the winding count even if done
caryclark@google.com570863f2013-09-16 15:55:01 +00001302 if ((test->fWindValue == 0 && test->fOppValue == 0)
1303 || (oTest->fWindValue == 0 && oTest->fOppValue == 0)) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001304 SkDEBUGCODE(int firstWind = test->fWindValue);
1305 SkDEBUGCODE(int firstOpp = test->fOppValue);
1306 do {
1307 SkASSERT(firstWind == fTs[index].fWindValue);
1308 SkASSERT(firstOpp == fTs[index].fOppValue);
1309 ++index;
1310 SkASSERT(index < fTs.count());
1311 } while (*testPt == fTs[index].fPt);
1312 SkDEBUGCODE(firstWind = oTest->fWindValue);
1313 SkDEBUGCODE(firstOpp = oTest->fOppValue);
1314 do {
1315 SkASSERT(firstWind == other->fTs[oIndex].fWindValue);
1316 SkASSERT(firstOpp == other->fTs[oIndex].fOppValue);
1317 ++oIndex;
1318 SkASSERT(oIndex < other->fTs.count());
1319 } while (*oTestPt == other->fTs[oIndex].fPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001320 } else {
caryclark@google.com570863f2013-09-16 15:55:01 +00001321 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
1322 bumpCoincidentThis(*oTest, binary, &index, &outsidePts);
1323 other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts);
1324 } else {
1325 other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts);
1326 bumpCoincidentOther(*oTest, &index, &outsidePts);
1327 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001328 }
1329 test = &fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +00001330 testPt = &test->fPt;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001331 testT = test->fT;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001332 oTest = &other->fTs[oIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +00001333 oTestPt = &oTest->fPt;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001334 if (endPt == *testPt || precisely_equal(endT, testT)) {
1335 break;
1336 }
1337// SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
caryclark@google.com570863f2013-09-16 15:55:01 +00001338 } while (endPt != *oTestPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001339 // in rare cases, one may have ended before the other
1340 if (endPt != *testPt && !precisely_equal(endT, testT)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001341 int lastWind = test[-1].fWindValue;
1342 int lastOpp = test[-1].fOppValue;
1343 bool zero = lastWind == 0 && lastOpp == 0;
1344 do {
1345 if (test->fWindValue || test->fOppValue) {
1346 test->fWindValue = lastWind;
1347 test->fOppValue = lastOpp;
1348 if (zero) {
1349 test->fDone = true;
1350 ++fDoneSpans;
1351 }
1352 }
1353 test = &fTs[++index];
1354 testPt = &test->fPt;
1355 } while (endPt != *testPt);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001356 }
1357 if (endPt != *oTestPt) {
1358 // look ahead to see if zeroing more spans will allows us to catch up
1359 int oPeekIndex = oIndex;
1360 bool success = true;
1361 SkOpSpan* oPeek;
caryclarkdac1d172014-06-17 05:15:38 -07001362 int oCount = other->count();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001363 do {
1364 oPeek = &other->fTs[oPeekIndex];
caryclarkdac1d172014-06-17 05:15:38 -07001365 if (++oPeekIndex == oCount) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001366 success = false;
1367 break;
1368 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001369 } while (endPt != oPeek->fPt);
1370 if (success) {
1371 // make sure the matching point completes the coincidence span
1372 success = false;
1373 do {
1374 if (oPeek->fOther == this) {
1375 success = true;
1376 break;
1377 }
caryclark19eb3b22014-07-18 05:08:14 -07001378 if (++oPeekIndex == oCount) {
1379 break;
1380 }
1381 oPeek = &other->fTs[oPeekIndex];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001382 } while (endPt == oPeek->fPt);
1383 }
1384 if (success) {
1385 do {
1386 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
1387 other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts);
1388 } else {
1389 other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts);
1390 }
1391 oTest = &other->fTs[oIndex];
1392 oTestPt = &oTest->fPt;
1393 } while (endPt != *oTestPt);
1394 }
1395 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001396 int outCount = outsidePts.count();
1397 if (!done() && outCount) {
1398 addCoinOutsides(outsidePts[0], endPt, other);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001399 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001400 if (!other->done() && oOutsidePts.count()) {
1401 other->addCoinOutsides(oOutsidePts[0], endPt, this);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001402 }
caryclarkdac1d172014-06-17 05:15:38 -07001403 setCoincidentRange(startPt, endPt, other);
1404 other->setCoincidentRange(startPt, endPt, this);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001405}
1406
1407// FIXME: this doesn't prevent the same span from being added twice
1408// fix in caller, SkASSERT here?
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001409const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
caryclarkdac1d172014-06-17 05:15:38 -07001410 const SkPoint& pt, const SkPoint& pt2) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001411 int tCount = fTs.count();
1412 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1413 const SkOpSpan& span = fTs[tIndex];
1414 if (!approximately_negative(span.fT - t)) {
1415 break;
1416 }
1417 if (approximately_negative(span.fT - t) && span.fOther == other
1418 && approximately_equal(span.fOtherT, otherT)) {
1419#if DEBUG_ADD_T_PAIR
1420 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1421 __FUNCTION__, fID, t, other->fID, otherT);
1422#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001423 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001424 }
1425 }
1426#if DEBUG_ADD_T_PAIR
1427 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1428 __FUNCTION__, fID, t, other->fID, otherT);
1429#endif
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001430 int insertedAt = addT(other, pt, t);
caryclarkdac1d172014-06-17 05:15:38 -07001431 int otherInsertedAt = other->addT(this, pt2, otherT);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001432 addOtherT(insertedAt, otherT, otherInsertedAt);
1433 other->addOtherT(otherInsertedAt, t, insertedAt);
1434 matchWindingValue(insertedAt, t, borrowWind);
1435 other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclarkdac1d172014-06-17 05:15:38 -07001436 SkOpSpan& span = this->fTs[insertedAt];
1437 if (pt != pt2) {
1438 span.fNear = true;
1439 SkOpSpan& oSpan = other->fTs[otherInsertedAt];
1440 oSpan.fNear = true;
1441 }
1442 return &span;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001443}
1444
caryclarkdac1d172014-06-17 05:15:38 -07001445const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
1446 const SkPoint& pt) {
1447 return addTPair(t, other, otherT, borrowWind, pt, pt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001448}
1449
caryclark@google.com570863f2013-09-16 15:55:01 +00001450bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
1451 const SkPoint midPt = ptAtT(midT);
1452 SkPathOpsBounds bounds;
1453 bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1454 bounds.sort();
1455 return bounds.almostContains(midPt);
1456}
1457
caryclark@google.com07393ca2013-04-08 11:47:37 +00001458bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const {
1459 if (lesser > greater) {
1460 SkTSwap<int>(lesser, greater);
1461 }
1462 return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
1463}
1464
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001465// in extreme cases (like the buffer overflow test) return false to abort
1466// for now, if one t value represents two different points, then the values are too extreme
1467// to generate meaningful results
1468bool SkOpSegment::calcAngles() {
1469 int spanCount = fTs.count();
1470 if (spanCount <= 2) {
1471 return spanCount == 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001472 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001473 int index = 1;
1474 const SkOpSpan* firstSpan = &fTs[index];
1475 int activePrior = checkSetAngle(0);
1476 const SkOpSpan* span = &fTs[0];
1477 if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) {
1478 index = findStartSpan(0); // curve start intersects
caryclarkdac1d172014-06-17 05:15:38 -07001479 SkASSERT(index > 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001480 if (activePrior >= 0) {
1481 addStartSpan(index);
1482 }
1483 }
1484 bool addEnd;
1485 int endIndex = spanCount - 1;
1486 span = &fTs[endIndex - 1];
1487 if ((addEnd = span->fT == 1 || span->fTiny)) { // if curve end intersects
1488 endIndex = findEndSpan(endIndex);
caryclarkdac1d172014-06-17 05:15:38 -07001489 SkASSERT(endIndex > 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001490 } else {
1491 addEnd = fTs[endIndex].fOtherT != 0 || fTs[endIndex].fOther->multipleStarts();
1492 }
1493 SkASSERT(endIndex >= index);
1494 int prior = 0;
1495 while (index < endIndex) {
1496 const SkOpSpan& fromSpan = fTs[index]; // for each intermediate intersection
1497 const SkOpSpan* lastSpan;
1498 span = &fromSpan;
1499 int start = index;
1500 do {
1501 lastSpan = span;
1502 span = &fTs[++index];
1503 SkASSERT(index < spanCount);
1504 if (!precisely_negative(span->fT - lastSpan->fT) && !lastSpan->fTiny) {
1505 break;
1506 }
1507 if (!SkDPoint::ApproximatelyEqual(lastSpan->fPt, span->fPt)) {
1508 return false;
1509 }
1510 } while (true);
caryclarkdac1d172014-06-17 05:15:38 -07001511 SkOpAngle* angle = NULL;
1512 SkOpAngle* priorAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001513 if (activePrior >= 0) {
1514 int pActive = firstActive(prior);
1515 SkASSERT(pActive < start);
caryclarkdac1d172014-06-17 05:15:38 -07001516 priorAngle = &fAngles.push_back();
1517 priorAngle->set(this, start, pActive);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001518 }
1519 int active = checkSetAngle(start);
1520 if (active >= 0) {
1521 SkASSERT(active < index);
caryclarkdac1d172014-06-17 05:15:38 -07001522 angle = &fAngles.push_back();
1523 angle->set(this, active, index);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001524 }
1525 #if DEBUG_ANGLE
1526 debugCheckPointsEqualish(start, index);
1527 #endif
1528 prior = start;
1529 do {
1530 const SkOpSpan* startSpan = &fTs[start - 1];
caryclarkdac1d172014-06-17 05:15:38 -07001531 if (!startSpan->fSmall || isCanceled(start - 1) || startSpan->fFromAngle
1532 || startSpan->fToAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001533 break;
1534 }
1535 --start;
1536 } while (start > 0);
1537 do {
1538 if (activePrior >= 0) {
caryclarkdac1d172014-06-17 05:15:38 -07001539 SkASSERT(fTs[start].fFromAngle == NULL);
1540 fTs[start].fFromAngle = priorAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001541 }
1542 if (active >= 0) {
caryclarkdac1d172014-06-17 05:15:38 -07001543 SkASSERT(fTs[start].fToAngle == NULL);
1544 fTs[start].fToAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001545 }
1546 } while (++start < index);
1547 activePrior = active;
1548 }
1549 if (addEnd && activePrior >= 0) {
1550 addEndSpan(endIndex);
1551 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001552 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001553}
1554
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001555int SkOpSegment::checkSetAngle(int tIndex) const {
1556 const SkOpSpan* span = &fTs[tIndex];
1557 while (span->fTiny /* || span->fSmall */) {
1558 span = &fTs[++tIndex];
caryclark@google.com07393ca2013-04-08 11:47:37 +00001559 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001560 return isCanceled(tIndex) ? -1 : tIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001561}
1562
caryclarkdac1d172014-06-17 05:15:38 -07001563// at this point, the span is already ordered, or unorderable
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001564int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
1565 SkASSERT(includeType != SkOpAngle::kUnaryXor);
1566 SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
1567 if (NULL == firstAngle) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001568 return SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001569 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001570 // if all angles have a computed winding,
1571 // or if no adjacent angles are orderable,
1572 // or if adjacent orderable angles have no computed winding,
1573 // there's nothing to do
caryclarkdac1d172014-06-17 05:15:38 -07001574 // if two orderable angles are adjacent, and both are next to orderable angles,
1575 // and one has winding computed, transfer to the other
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001576 SkOpAngle* baseAngle = NULL;
caryclark@google.com570863f2013-09-16 15:55:01 +00001577 bool tryReverse = false;
1578 // look for counterclockwise transfers
caryclarkdac1d172014-06-17 05:15:38 -07001579 SkOpAngle* angle = firstAngle->previous();
1580 SkOpAngle* next = angle->next();
1581 firstAngle = next;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001582 do {
caryclarkdac1d172014-06-17 05:15:38 -07001583 SkOpAngle* prior = angle;
1584 angle = next;
1585 next = angle->next();
1586 SkASSERT(prior->next() == angle);
1587 SkASSERT(angle->next() == next);
1588 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
1589 baseAngle = NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001590 continue;
1591 }
caryclarkdac1d172014-06-17 05:15:38 -07001592 int testWinding = angle->segment()->windSum(angle);
1593 if (SK_MinS32 != testWinding) {
1594 baseAngle = angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001595 tryReverse = true;
1596 continue;
1597 }
1598 if (baseAngle) {
1599 ComputeOneSum(baseAngle, angle, includeType);
1600 baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001601 }
caryclarkdac1d172014-06-17 05:15:38 -07001602 } while (next != firstAngle);
1603 if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001604 firstAngle = baseAngle;
1605 tryReverse = true;
1606 }
1607 if (tryReverse) {
1608 baseAngle = NULL;
caryclarkdac1d172014-06-17 05:15:38 -07001609 SkOpAngle* prior = firstAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +00001610 do {
caryclarkdac1d172014-06-17 05:15:38 -07001611 angle = prior;
1612 prior = angle->previous();
1613 SkASSERT(prior->next() == angle);
1614 next = angle->next();
1615 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
1616 baseAngle = NULL;
1617 continue;
1618 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001619 int testWinding = angle->segment()->windSum(angle);
1620 if (SK_MinS32 != testWinding) {
1621 baseAngle = angle;
caryclark@google.com570863f2013-09-16 15:55:01 +00001622 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001623 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001624 if (baseAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001625 ComputeOneSumReverse(baseAngle, angle, includeType);
1626 baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001627 }
caryclarkdac1d172014-06-17 05:15:38 -07001628 } while (prior != firstAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001629 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001630 int minIndex = SkMin32(startIndex, endIndex);
1631 return windSum(minIndex);
1632}
1633
caryclark@google.com570863f2013-09-16 15:55:01 +00001634void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
1635 SkOpAngle::IncludeType includeType) {
1636 const SkOpSegment* baseSegment = baseAngle->segment();
1637 int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
1638 int sumSuWinding;
1639 bool binary = includeType >= SkOpAngle::kBinarySingle;
1640 if (binary) {
1641 sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
1642 if (baseSegment->operand()) {
1643 SkTSwap<int>(sumMiWinding, sumSuWinding);
1644 }
1645 }
1646 SkOpSegment* nextSegment = nextAngle->segment();
1647 int maxWinding, sumWinding;
1648 SkOpSpan* last;
1649 if (binary) {
1650 int oppMaxWinding, oppSumWinding;
1651 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
1652 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1653 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001654 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001655 } else {
1656 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
1657 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001658 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001659 }
1660 nextAngle->setLastMarked(last);
1661}
1662
1663void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
1664 SkOpAngle::IncludeType includeType) {
1665 const SkOpSegment* baseSegment = baseAngle->segment();
1666 int sumMiWinding = baseSegment->updateWinding(baseAngle);
1667 int sumSuWinding;
1668 bool binary = includeType >= SkOpAngle::kBinarySingle;
1669 if (binary) {
1670 sumSuWinding = baseSegment->updateOppWinding(baseAngle);
1671 if (baseSegment->operand()) {
1672 SkTSwap<int>(sumMiWinding, sumSuWinding);
1673 }
1674 }
1675 SkOpSegment* nextSegment = nextAngle->segment();
1676 int maxWinding, sumWinding;
1677 SkOpSpan* last;
1678 if (binary) {
1679 int oppMaxWinding, oppSumWinding;
1680 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
1681 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
1682 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001683 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001684 } else {
1685 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
1686 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001687 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +00001688 }
1689 nextAngle->setLastMarked(last);
1690}
1691
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001692bool SkOpSegment::containsPt(const SkPoint& pt, int index, int endIndex) const {
1693 int step = index < endIndex ? 1 : -1;
1694 do {
1695 const SkOpSpan& span = this->span(index);
1696 if (span.fPt == pt) {
1697 const SkOpSpan& endSpan = this->span(endIndex);
1698 return span.fT == endSpan.fT && pt != endSpan.fPt;
1699 }
1700 index += step;
1701 } while (index != endIndex);
1702 return false;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001703}
1704
caryclarkdac1d172014-06-17 05:15:38 -07001705bool SkOpSegment::containsT(double t, const SkOpSegment* other, double otherT) const {
1706 int count = this->count();
1707 for (int index = 0; index < count; ++index) {
1708 const SkOpSpan& span = fTs[index];
1709 if (t < span.fT) {
1710 return false;
1711 }
1712 if (t == span.fT) {
1713 if (other != span.fOther) {
1714 continue;
1715 }
1716 if (other->fVerb != SkPath::kCubic_Verb) {
1717 return true;
1718 }
1719 if (!other->fLoop) {
1720 return true;
1721 }
1722 double otherMidT = (otherT + span.fOtherT) / 2;
1723 SkPoint otherPt = other->ptAtT(otherMidT);
1724 return SkDPoint::ApproximatelyEqual(span.fPt, otherPt);
1725 }
1726 }
1727 return false;
1728}
1729
caryclark@google.com07393ca2013-04-08 11:47:37 +00001730int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
1731 bool* hitSomething, double mid, bool opp, bool current) const {
1732 SkScalar bottom = fBounds.fBottom;
1733 int bestTIndex = -1;
1734 if (bottom <= *bestY) {
1735 return bestTIndex;
1736 }
1737 SkScalar top = fBounds.fTop;
1738 if (top >= basePt.fY) {
1739 return bestTIndex;
1740 }
1741 if (fBounds.fLeft > basePt.fX) {
1742 return bestTIndex;
1743 }
1744 if (fBounds.fRight < basePt.fX) {
1745 return bestTIndex;
1746 }
1747 if (fBounds.fLeft == fBounds.fRight) {
1748 // if vertical, and directly above test point, wait for another one
1749 return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
1750 }
1751 // intersect ray starting at basePt with edge
1752 SkIntersections intersections;
1753 // OPTIMIZE: use specialty function that intersects ray with curve,
1754 // returning t values only for curve (we don't care about t on ray)
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00001755 intersections.allowNear(false);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001756 int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
1757 (fPts, top, bottom, basePt.fX, false);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001758 if (pts == 0 || (current && pts == 1)) {
1759 return bestTIndex;
1760 }
1761 if (current) {
1762 SkASSERT(pts > 1);
1763 int closestIdx = 0;
1764 double closest = fabs(intersections[0][0] - mid);
1765 for (int idx = 1; idx < pts; ++idx) {
1766 double test = fabs(intersections[0][idx] - mid);
1767 if (closest > test) {
1768 closestIdx = idx;
1769 closest = test;
1770 }
1771 }
1772 intersections.quickRemoveOne(closestIdx, --pts);
1773 }
1774 double bestT = -1;
1775 for (int index = 0; index < pts; ++index) {
1776 double foundT = intersections[0][index];
1777 if (approximately_less_than_zero(foundT)
1778 || approximately_greater_than_one(foundT)) {
1779 continue;
1780 }
reed@google.com277c3f82013-05-31 15:17:50 +00001781 SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001782 if (approximately_negative(testY - *bestY)
1783 || approximately_negative(basePt.fY - testY)) {
1784 continue;
1785 }
1786 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1787 return SK_MinS32; // if the intersection is edge on, wait for another one
1788 }
1789 if (fVerb > SkPath::kLine_Verb) {
reed@google.com277c3f82013-05-31 15:17:50 +00001790 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001791 if (approximately_zero(dx)) {
1792 return SK_MinS32; // hit vertical, wait for another one
1793 }
1794 }
1795 *bestY = testY;
1796 bestT = foundT;
1797 }
1798 if (bestT < 0) {
1799 return bestTIndex;
1800 }
1801 SkASSERT(bestT >= 0);
1802 SkASSERT(bestT <= 1);
1803 int start;
1804 int end = 0;
1805 do {
1806 start = end;
1807 end = nextSpan(start, 1);
1808 } while (fTs[end].fT < bestT);
1809 // FIXME: see next candidate for a better pattern to find the next start/end pair
1810 while (start + 1 < end && fTs[start].fDone) {
1811 ++start;
1812 }
1813 if (!isCanceled(start)) {
1814 *hitT = bestT;
1815 bestTIndex = start;
1816 *hitSomething = true;
1817 }
1818 return bestTIndex;
1819}
1820
caryclark@google.com570863f2013-09-16 15:55:01 +00001821bool SkOpSegment::decrementSpan(SkOpSpan* span) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001822 SkASSERT(span->fWindValue > 0);
1823 if (--(span->fWindValue) == 0) {
1824 if (!span->fOppValue && !span->fDone) {
1825 span->fDone = true;
1826 ++fDoneSpans;
caryclark@google.com570863f2013-09-16 15:55:01 +00001827 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001828 }
1829 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001830 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001831}
1832
1833bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001834 SkASSERT(!span->fDone || span->fTiny || span->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001835 span->fWindValue += windDelta;
1836 SkASSERT(span->fWindValue >= 0);
1837 span->fOppValue += oppDelta;
1838 SkASSERT(span->fOppValue >= 0);
1839 if (fXor) {
1840 span->fWindValue &= 1;
1841 }
1842 if (fOppXor) {
1843 span->fOppValue &= 1;
1844 }
1845 if (!span->fWindValue && !span->fOppValue) {
1846 span->fDone = true;
1847 ++fDoneSpans;
1848 return true;
1849 }
1850 return false;
1851}
1852
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001853const SkOpSpan& SkOpSegment::firstSpan(const SkOpSpan& thisSpan) const {
1854 const SkOpSpan* firstSpan = &thisSpan; // rewind to the start
1855 const SkOpSpan* beginSpan = fTs.begin();
1856 const SkPoint& testPt = thisSpan.fPt;
1857 while (firstSpan > beginSpan && firstSpan[-1].fPt == testPt) {
1858 --firstSpan;
1859 }
1860 return *firstSpan;
1861}
1862
1863const SkOpSpan& SkOpSegment::lastSpan(const SkOpSpan& thisSpan) const {
1864 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
1865 const SkOpSpan* lastSpan = &thisSpan; // find the end
1866 const SkPoint& testPt = thisSpan.fPt;
1867 while (lastSpan < endSpan && lastSpan[1].fPt == testPt) {
1868 ++lastSpan;
1869 }
1870 return *lastSpan;
1871}
1872
1873// with a loop, the comparison is move involved
1874// scan backwards and forwards to count all matching points
1875// (verify that there are twp scans marked as loops)
1876// compare that against 2 matching scans for loop plus other results
1877bool SkOpSegment::calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts) {
1878 const SkOpSpan& firstSpan = this->firstSpan(thisSpan); // rewind to the start
1879 const SkOpSpan& lastSpan = this->lastSpan(thisSpan); // find the end
1880 double firstLoopT = -1, lastLoopT = -1;
1881 const SkOpSpan* testSpan = &firstSpan - 1;
1882 while (++testSpan <= &lastSpan) {
1883 if (testSpan->fLoop) {
1884 firstLoopT = testSpan->fT;
1885 break;
1886 }
1887 }
1888 testSpan = &lastSpan + 1;
1889 while (--testSpan >= &firstSpan) {
1890 if (testSpan->fLoop) {
1891 lastLoopT = testSpan->fT;
1892 break;
1893 }
1894 }
1895 SkASSERT((firstLoopT == -1) == (lastLoopT == -1));
1896 if (firstLoopT == -1) {
1897 return false;
1898 }
1899 SkASSERT(firstLoopT < lastLoopT);
1900 testSpan = &firstSpan - 1;
1901 smallCounts[0] = smallCounts[1] = 0;
1902 while (++testSpan <= &lastSpan) {
1903 SkASSERT(approximately_equal(testSpan->fT, firstLoopT) +
1904 approximately_equal(testSpan->fT, lastLoopT) == 1);
1905 smallCounts[approximately_equal(testSpan->fT, lastLoopT)]++;
1906 }
1907 return true;
1908}
1909
caryclarkdac1d172014-06-17 05:15:38 -07001910double SkOpSegment::calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
1911 double hiEnd, const SkOpSegment* other, int thisStart) {
1912 if (max >= hiEnd) {
1913 return -1;
1914 }
1915 int end = findOtherT(hiEnd, ref);
1916 if (end < 0) {
1917 return -1;
1918 }
1919 double tHi = span(end).fT;
1920 double tLo, refLo;
1921 if (thisStart >= 0) {
1922 tLo = span(thisStart).fT;
1923 refLo = min;
1924 } else {
1925 int start1 = findOtherT(loEnd, ref);
1926 SkASSERT(start1 >= 0);
1927 tLo = span(start1).fT;
1928 refLo = loEnd;
1929 }
1930 double missingT = (max - refLo) / (hiEnd - refLo);
1931 missingT = tLo + missingT * (tHi - tLo);
1932 return missingT;
1933}
1934
1935double SkOpSegment::calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
1936 double hiEnd, const SkOpSegment* other, int thisEnd) {
1937 if (min <= loEnd) {
1938 return -1;
1939 }
1940 int start = findOtherT(loEnd, ref);
1941 if (start < 0) {
1942 return -1;
1943 }
1944 double tLo = span(start).fT;
1945 double tHi, refHi;
1946 if (thisEnd >= 0) {
1947 tHi = span(thisEnd).fT;
1948 refHi = max;
1949 } else {
1950 int end1 = findOtherT(hiEnd, ref);
1951 if (end1 < 0) {
1952 return -1;
1953 }
1954 tHi = span(end1).fT;
1955 refHi = hiEnd;
1956 }
1957 double missingT = (min - loEnd) / (refHi - loEnd);
1958 missingT = tLo + missingT * (tHi - tLo);
1959 return missingT;
1960}
1961
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001962// see if spans with two or more intersections have the same number on the other end
1963void SkOpSegment::checkDuplicates() {
1964 debugValidate();
1965 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
1966 int index;
1967 int endIndex = 0;
1968 bool endFound;
1969 do {
1970 index = endIndex;
1971 endIndex = nextExactSpan(index, 1);
1972 if ((endFound = endIndex < 0)) {
1973 endIndex = count();
1974 }
1975 int dupCount = endIndex - index;
1976 if (dupCount < 2) {
1977 continue;
1978 }
1979 do {
1980 const SkOpSpan* thisSpan = &fTs[index];
caryclarkdac1d172014-06-17 05:15:38 -07001981 if (thisSpan->fNear) {
1982 continue;
1983 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001984 SkOpSegment* other = thisSpan->fOther;
1985 int oIndex = thisSpan->fOtherIndex;
1986 int oStart = other->nextExactSpan(oIndex, -1) + 1;
1987 int oEnd = other->nextExactSpan(oIndex, 1);
1988 if (oEnd < 0) {
1989 oEnd = other->count();
1990 }
1991 int oCount = oEnd - oStart;
1992 // force the other to match its t and this pt if not on an end point
1993 if (oCount != dupCount) {
1994 MissingSpan& missing = missingSpans.push_back();
1995 missing.fOther = NULL;
1996 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
1997 missing.fPt = thisSpan->fPt;
1998 const SkOpSpan& oSpan = other->span(oIndex);
1999 if (oCount > dupCount) {
2000 missing.fSegment = this;
2001 missing.fT = thisSpan->fT;
2002 other->checkLinks(&oSpan, &missingSpans);
2003 } else {
2004 missing.fSegment = other;
2005 missing.fT = oSpan.fT;
2006 checkLinks(thisSpan, &missingSpans);
2007 }
2008 if (!missingSpans.back().fOther) {
2009 missingSpans.pop_back();
2010 }
2011 }
2012 } while (++index < endIndex);
2013 } while (!endFound);
2014 int missingCount = missingSpans.count();
2015 if (missingCount == 0) {
2016 return;
2017 }
2018 SkSTArray<kMissingSpanCount, MissingSpan, true> missingCoincidence;
2019 for (index = 0; index < missingCount; ++index) {
2020 MissingSpan& missing = missingSpans[index];
2021 SkOpSegment* missingOther = missing.fOther;
2022 if (missing.fSegment == missing.fOther) {
2023 continue;
2024 }
caryclarkdac1d172014-06-17 05:15:38 -07002025#if 0 // FIXME: this eliminates spurious data from skpwww_argus_presse_fr_41 but breaks
2026 // skpwww_fashionscandal_com_94 -- calcAngles complains, but I don't understand why
2027 if (missing.fSegment->containsT(missing.fT, missing.fOther, missing.fOtherT)) {
2028#if DEBUG_DUPLICATES
2029 SkDebugf("skip 1 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fSegment->fID,
2030 missing.fT, missing.fOther->fID, missing.fOtherT);
2031#endif
2032 continue;
2033 }
2034 if (missing.fOther->containsT(missing.fOtherT, missing.fSegment, missing.fT)) {
2035#if DEBUG_DUPLICATES
2036 SkDebugf("skip 2 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fOther->fID,
2037 missing.fOtherT, missing.fSegment->fID, missing.fT);
2038#endif
2039 continue;
2040 }
2041#endif
2042 // skip if adding would insert point into an existing coincindent span
2043 if (missing.fSegment->inCoincidentSpan(missing.fT, missingOther)
2044 && missingOther->inCoincidentSpan(missing.fOtherT, this)) {
2045 continue;
2046 }
2047 // skip if the created coincident spans are small
2048 if (missing.fSegment->coincidentSmall(missing.fPt, missing.fT, missingOther)
2049 && missingOther->coincidentSmall(missing.fPt, missing.fOtherT, missing.fSegment)) {
2050 continue;
2051 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002052 const SkOpSpan* added = missing.fSegment->addTPair(missing.fT, missingOther,
2053 missing.fOtherT, false, missing.fPt);
2054 if (added && added->fSmall) {
2055 missing.fSegment->checkSmallCoincidence(*added, &missingCoincidence);
2056 }
2057 }
2058 for (index = 0; index < missingCount; ++index) {
2059 MissingSpan& missing = missingSpans[index];
2060 missing.fSegment->fixOtherTIndex();
2061 missing.fOther->fixOtherTIndex();
2062 }
2063 for (index = 0; index < missingCoincidence.count(); ++index) {
2064 MissingSpan& missing = missingCoincidence[index];
2065 missing.fSegment->fixOtherTIndex();
2066 }
2067 debugValidate();
2068}
2069
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002070// look to see if the curve end intersects an intermediary that intersects the other
2071void SkOpSegment::checkEnds() {
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002072 debugValidate();
caryclark@google.com570863f2013-09-16 15:55:01 +00002073 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002074 int count = fTs.count();
2075 for (int index = 0; index < count; ++index) {
2076 const SkOpSpan& span = fTs[index];
caryclark@google.com570863f2013-09-16 15:55:01 +00002077 double otherT = span.fOtherT;
2078 if (otherT != 0 && otherT != 1) { // only check ends
2079 continue;
2080 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002081 const SkOpSegment* other = span.fOther;
caryclark@google.com570863f2013-09-16 15:55:01 +00002082 // peek start/last describe the range of spans that match the other t of this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002083 int peekStart = span.fOtherIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00002084 while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT)
2085 ;
2086 int otherCount = other->fTs.count();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002087 int peekLast = span.fOtherIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00002088 while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT)
2089 ;
2090 if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002091 continue;
2092 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002093 // t start/last describe the range of spans that match the t of this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002094 double t = span.fT;
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002095 double tBottom = -1;
2096 int tStart = -1;
2097 int tLast = count;
2098 bool lastSmall = false;
2099 double afterT = t;
2100 for (int inner = 0; inner < count; ++inner) {
2101 double innerT = fTs[inner].fT;
2102 if (innerT <= t && innerT > tBottom) {
2103 if (innerT < t || !lastSmall) {
2104 tStart = inner - 1;
2105 }
2106 tBottom = innerT;
2107 }
2108 if (innerT > afterT) {
2109 if (t == afterT && lastSmall) {
2110 afterT = innerT;
2111 } else {
2112 tLast = inner;
2113 break;
2114 }
2115 }
2116 lastSmall = innerT <= t ? fTs[inner].fSmall : false;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002117 }
2118 for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) {
caryclark@google.com570863f2013-09-16 15:55:01 +00002119 if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002120 continue;
2121 }
2122 const SkOpSpan& peekSpan = other->fTs[peekIndex];
2123 SkOpSegment* match = peekSpan.fOther;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002124 if (match->done()) {
2125 continue; // if the edge has already been eaten (likely coincidence), ignore it
2126 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002127 const double matchT = peekSpan.fOtherT;
caryclark@google.com570863f2013-09-16 15:55:01 +00002128 // see if any of the spans match the other spans
2129 for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002130 const SkOpSpan& tSpan = fTs[tIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +00002131 if (tSpan.fOther == match) {
2132 if (tSpan.fOtherT == matchT) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002133 goto nextPeekIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00002134 }
2135 double midT = (tSpan.fOtherT + matchT) / 2;
2136 if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002137 goto nextPeekIndex;
caryclark@google.com570863f2013-09-16 15:55:01 +00002138 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002139 }
2140 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002141 if (missingSpans.count() > 0) {
2142 const MissingSpan& lastMissing = missingSpans.back();
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00002143 if (lastMissing.fT == t
caryclark@google.com570863f2013-09-16 15:55:01 +00002144 && lastMissing.fOther == match
2145 && lastMissing.fOtherT == matchT) {
2146 SkASSERT(lastMissing.fPt == peekSpan.fPt);
2147 continue;
2148 }
2149 }
2150#if DEBUG_CHECK_ENDS
2151 SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n",
2152 __FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY);
2153#endif
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002154 // this segment is missing a entry that the other contains
2155 // remember so we can add the missing one and recompute the indices
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002156 {
2157 MissingSpan& missing = missingSpans.push_back();
2158 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002159 missing.fT = t;
2160 missing.fOther = match;
2161 missing.fOtherT = matchT;
2162 missing.fPt = peekSpan.fPt;
2163 }
2164 break;
2165nextPeekIndex:
2166 ;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002167 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002168 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002169 if (missingSpans.count() == 0) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00002170 debugValidate();
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002171 return;
2172 }
2173 debugValidate();
caryclark@google.com570863f2013-09-16 15:55:01 +00002174 int missingCount = missingSpans.count();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002175 for (int index = 0; index < missingCount; ++index) {
caryclark@google.com570863f2013-09-16 15:55:01 +00002176 MissingSpan& missing = missingSpans[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002177 if (this != missing.fOther) {
2178 addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
2179 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002180 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002181 fixOtherTIndex();
caryclark@google.com570863f2013-09-16 15:55:01 +00002182 // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
2183 // avoid this
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002184 for (int index = 0; index < missingCount; ++index) {
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00002185 missingSpans[index].fOther->fixOtherTIndex();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002186 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00002187 debugValidate();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +00002188}
2189
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002190void SkOpSegment::checkLinks(const SkOpSpan* base,
2191 SkTArray<MissingSpan, true>* missingSpans) const {
2192 const SkOpSpan* first = fTs.begin();
2193 const SkOpSpan* last = fTs.end() - 1;
2194 SkASSERT(base >= first && last >= base);
2195 const SkOpSegment* other = base->fOther;
2196 const SkOpSpan* oFirst = other->fTs.begin();
2197 const SkOpSpan* oLast = other->fTs.end() - 1;
2198 const SkOpSpan* oSpan = &other->fTs[base->fOtherIndex];
2199 const SkOpSpan* test = base;
2200 const SkOpSpan* missing = NULL;
2201 while (test > first && (--test)->fPt == base->fPt) {
2202 CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
2203 }
2204 test = base;
2205 while (test < last && (++test)->fPt == base->fPt) {
2206 CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
2207 }
2208}
2209
2210// see if spans with two or more intersections all agree on common t and point values
2211void SkOpSegment::checkMultiples() {
2212 debugValidate();
2213 int index;
2214 int end = 0;
2215 while (fTs[++end].fT == 0)
2216 ;
2217 while (fTs[end].fT < 1) {
2218 int start = index = end;
2219 end = nextExactSpan(index, 1);
2220 if (end <= index) {
2221 return; // buffer overflow example triggers this
2222 }
2223 if (index + 1 == end) {
2224 continue;
2225 }
2226 // force the duplicates to agree on t and pt if not on the end
caryclarkdac1d172014-06-17 05:15:38 -07002227 SkOpSpan& span = fTs[index];
2228 double thisT = span.fT;
2229 const SkPoint& thisPt = span.fPt;
2230 span.fMultiple = true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002231 bool aligned = false;
2232 while (++index < end) {
2233 aligned |= alignSpan(index, thisT, thisPt);
2234 }
2235 if (aligned) {
2236 alignSpanState(start, end);
2237 }
caryclarkdac1d172014-06-17 05:15:38 -07002238 fMultiples = true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002239 }
2240 debugValidate();
2241}
2242
2243void SkOpSegment::CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
2244 const SkOpSpan* oFirst, const SkOpSpan* oLast, const SkOpSpan** missingPtr,
2245 SkTArray<MissingSpan, true>* missingSpans) {
2246 SkASSERT(oSpan->fPt == test->fPt);
2247 const SkOpSpan* oTest = oSpan;
2248 while (oTest > oFirst && (--oTest)->fPt == test->fPt) {
2249 if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
2250 return;
2251 }
2252 }
2253 oTest = oSpan;
2254 while (oTest < oLast && (++oTest)->fPt == test->fPt) {
2255 if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
2256 return;
2257 }
2258 }
2259 if (*missingPtr) {
2260 missingSpans->push_back();
2261 }
2262 MissingSpan& lastMissing = missingSpans->back();
2263 if (*missingPtr) {
2264 lastMissing = missingSpans->end()[-2];
2265 }
2266 *missingPtr = test;
2267 lastMissing.fOther = test->fOther;
2268 lastMissing.fOtherT = test->fOtherT;
2269}
2270
caryclark@google.com570863f2013-09-16 15:55:01 +00002271bool SkOpSegment::checkSmall(int index) const {
2272 if (fTs[index].fSmall) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002273 return true;
2274 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002275 double tBase = fTs[index].fT;
2276 while (index > 0 && precisely_negative(tBase - fTs[--index].fT))
2277 ;
2278 return fTs[index].fSmall;
2279}
2280
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002281// a pair of curves may turn into coincident lines -- small may be a hint that that happened
2282// if a cubic contains a loop, the counts must be adjusted
2283void SkOpSegment::checkSmall() {
2284 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
2285 const SkOpSpan* beginSpan = fTs.begin();
2286 const SkOpSpan* thisSpan = beginSpan - 1;
2287 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be small
2288 while (++thisSpan < endSpan) {
2289 if (!thisSpan->fSmall) {
2290 continue;
2291 }
2292 if (!thisSpan->fWindValue) {
2293 continue;
2294 }
2295 const SkOpSpan& firstSpan = this->firstSpan(*thisSpan);
2296 const SkOpSpan& lastSpan = this->lastSpan(*thisSpan);
caryclark5e27e0e2014-08-12 07:46:33 -07002297 const SkOpSpan* nextSpan = &firstSpan + 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002298 ptrdiff_t smallCount = &lastSpan - &firstSpan + 1;
2299 SkASSERT(1 <= smallCount && smallCount < count());
caryclark5e27e0e2014-08-12 07:46:33 -07002300 if (smallCount <= 1 && !nextSpan->fSmall) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002301 SkASSERT(1 == smallCount);
2302 checkSmallCoincidence(firstSpan, NULL);
2303 continue;
2304 }
2305 // at this point, check for missing computed intersections
2306 const SkPoint& testPt = firstSpan.fPt;
2307 thisSpan = &firstSpan - 1;
2308 SkOpSegment* other = NULL;
2309 while (++thisSpan <= &lastSpan) {
2310 other = thisSpan->fOther;
2311 if (other != this) {
2312 break;
2313 }
2314 }
2315 SkASSERT(other != this);
2316 int oIndex = thisSpan->fOtherIndex;
2317 const SkOpSpan& oSpan = other->span(oIndex);
2318 const SkOpSpan& oFirstSpan = other->firstSpan(oSpan);
2319 const SkOpSpan& oLastSpan = other->lastSpan(oSpan);
2320 ptrdiff_t oCount = &oLastSpan - &oFirstSpan + 1;
2321 if (fLoop) {
2322 int smallCounts[2];
2323 SkASSERT(!other->fLoop); // FIXME: we need more complicated logic for pair of loops
2324 if (calcLoopSpanCount(*thisSpan, smallCounts)) {
2325 if (smallCounts[0] && oCount != smallCounts[0]) {
2326 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2327 }
2328 if (smallCounts[1] && oCount != smallCounts[1]) {
2329 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2330 }
2331 goto nextSmallCheck;
2332 }
2333 }
2334 if (other->fLoop) {
2335 int otherCounts[2];
2336 if (other->calcLoopSpanCount(other->span(oIndex), otherCounts)) {
2337 if (otherCounts[0] && otherCounts[0] != smallCount) {
2338 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2339 }
2340 if (otherCounts[1] && otherCounts[1] != smallCount) {
2341 SkASSERT(0); // FIXME: need a working test case to properly code & debug
2342 }
2343 goto nextSmallCheck;
2344 }
2345 }
2346 if (oCount != smallCount) { // check if number of pts in this match other
2347 MissingSpan& missing = missingSpans.push_back();
2348 missing.fOther = NULL;
2349 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
2350 missing.fPt = testPt;
2351 const SkOpSpan& oSpan = other->span(oIndex);
2352 if (oCount > smallCount) {
2353 missing.fSegment = this;
2354 missing.fT = thisSpan->fT;
2355 other->checkLinks(&oSpan, &missingSpans);
2356 } else {
2357 missing.fSegment = other;
2358 missing.fT = oSpan.fT;
2359 checkLinks(thisSpan, &missingSpans);
2360 }
2361 if (!missingSpans.back().fOther || missing.fSegment->done()) {
2362 missingSpans.pop_back();
2363 }
2364 }
2365nextSmallCheck:
2366 thisSpan = &lastSpan;
2367 }
2368 int missingCount = missingSpans.count();
2369 for (int index = 0; index < missingCount; ++index) {
2370 MissingSpan& missing = missingSpans[index];
2371 SkOpSegment* missingOther = missing.fOther;
2372 // note that add t pair may edit span arrays, so prior pointers to spans are no longer valid
2373 if (!missing.fSegment->addTPair(missing.fT, missingOther, missing.fOtherT, false,
2374 missing.fPt)) {
2375 continue;
2376 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002377 int otherTIndex = missingOther->findT(missing.fOtherT, missing.fPt, missing.fSegment);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002378 const SkOpSpan& otherSpan = missingOther->span(otherTIndex);
2379 if (otherSpan.fSmall) {
2380 const SkOpSpan* nextSpan = &otherSpan;
2381 do {
2382 ++nextSpan;
2383 } while (nextSpan->fSmall);
2384 missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt, nextSpan->fT,
2385 missingOther);
2386 } else if (otherSpan.fT > 0) {
2387 const SkOpSpan* priorSpan = &otherSpan;
2388 do {
2389 --priorSpan;
2390 } while (priorSpan->fT == otherSpan.fT);
2391 if (priorSpan->fSmall) {
2392 missing.fSegment->addTCancel(missing.fPt, priorSpan->fPt, missingOther);
2393 }
2394 }
2395 }
2396 // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
2397 // avoid this
2398 for (int index = 0; index < missingCount; ++index) {
2399 MissingSpan& missing = missingSpans[index];
2400 missing.fSegment->fixOtherTIndex();
2401 missing.fOther->fixOtherTIndex();
2402 }
2403 debugValidate();
2404}
2405
2406void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span,
2407 SkTArray<MissingSpan, true>* checkMultiple) {
2408 SkASSERT(span.fSmall);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002409 if (0 && !span.fWindValue) {
2410 return;
2411 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002412 SkASSERT(&span < fTs.end() - 1);
2413 const SkOpSpan* next = &span + 1;
2414 SkASSERT(!next->fSmall || checkMultiple);
2415 if (checkMultiple) {
2416 while (next->fSmall) {
2417 ++next;
2418 SkASSERT(next < fTs.end());
2419 }
2420 }
2421 SkOpSegment* other = span.fOther;
2422 while (other != next->fOther) {
2423 if (!checkMultiple) {
2424 return;
2425 }
2426 const SkOpSpan* test = next + 1;
2427 if (test == fTs.end()) {
2428 return;
2429 }
2430 if (test->fPt != next->fPt || !precisely_equal(test->fT, next->fT)) {
2431 return;
2432 }
2433 next = test;
2434 }
2435 SkASSERT(span.fT < next->fT);
2436 int oStartIndex = other->findExactT(span.fOtherT, this);
2437 int oEndIndex = other->findExactT(next->fOtherT, this);
2438 // FIXME: be overly conservative by limiting this to the caller that allows multiple smalls
2439 if (!checkMultiple || fVerb != SkPath::kLine_Verb || other->fVerb != SkPath::kLine_Verb) {
2440 SkPoint mid = ptAtT((span.fT + next->fT) / 2);
2441 const SkOpSpan& oSpanStart = other->fTs[oStartIndex];
2442 const SkOpSpan& oSpanEnd = other->fTs[oEndIndex];
2443 SkPoint oMid = other->ptAtT((oSpanStart.fT + oSpanEnd.fT) / 2);
2444 if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
2445 return;
2446 }
2447 }
2448 // FIXME: again, be overly conservative to avoid breaking existing tests
2449 const SkOpSpan& oSpan = oStartIndex < oEndIndex ? other->fTs[oStartIndex]
2450 : other->fTs[oEndIndex];
2451 if (checkMultiple && !oSpan.fSmall) {
2452 return;
2453 }
2454 SkASSERT(oSpan.fSmall);
2455 if (oStartIndex < oEndIndex) {
2456 addTCoincident(span.fPt, next->fPt, next->fT, other);
2457 } else {
2458 addTCancel(span.fPt, next->fPt, other);
2459 }
2460 if (!checkMultiple) {
2461 return;
2462 }
2463 // check to see if either segment is coincident with a third segment -- if it is, and if
2464 // the opposite segment is not already coincident with the third, make it so
2465 // OPTIMIZE: to make this check easier, add coincident and cancel could set a coincident bit
2466 if (span.fWindValue != 1 || span.fOppValue != 0) {
2467// start here;
2468 // iterate through the spans, looking for the third coincident case
2469 // if we find one, we need to return state to the caller so that the indices can be fixed
2470 // this also suggests that all of this function is fragile since it relies on a valid index
2471 }
2472 // probably should make this a common function rather than copy/paste code
2473 if (oSpan.fWindValue != 1 || oSpan.fOppValue != 0) {
2474 const SkOpSpan* oTest = &oSpan;
2475 while (--oTest >= other->fTs.begin()) {
2476 if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
2477 break;
2478 }
2479 SkOpSegment* testOther = oTest->fOther;
2480 SkASSERT(testOther != this);
2481 // look in both directions to see if there is a coincident span
2482 const SkOpSpan* tTest = testOther->fTs.begin();
2483 for (int testIndex = 0; testIndex < testOther->count(); ++testIndex) {
2484 if (tTest->fPt != span.fPt) {
2485 ++tTest;
2486 continue;
2487 }
2488 if (testOther->verb() != SkPath::kLine_Verb
2489 || other->verb() != SkPath::kLine_Verb) {
2490 SkPoint mid = ptAtT((span.fT + next->fT) / 2);
2491 SkPoint oMid = other->ptAtT((oTest->fOtherT + tTest->fT) / 2);
2492 if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
2493 continue;
2494 }
skia.committer@gmail.coma1ed7ae2014-04-15 03:04:18 +00002495 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002496#if DEBUG_CONCIDENT
2497 SkDebugf("%s coincident found=%d %1.9g %1.9g\n", __FUNCTION__, testOther->fID,
2498 oTest->fOtherT, tTest->fT);
2499#endif
2500 if (tTest->fT < oTest->fOtherT) {
2501 addTCoincident(span.fPt, next->fPt, next->fT, testOther);
2502 } else {
2503 addTCancel(span.fPt, next->fPt, testOther);
2504 }
2505 MissingSpan missing;
2506 missing.fSegment = testOther;
2507 checkMultiple->push_back(missing);
2508 break;
2509 }
2510 }
2511 oTest = &oSpan;
2512 while (++oTest < other->fTs.end()) {
2513 if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
2514 break;
2515 }
2516
2517 }
2518 }
2519}
2520
caryclark@google.com570863f2013-09-16 15:55:01 +00002521// if pair of spans on either side of tiny have the same end point and mid point, mark
2522// them as parallel
caryclark@google.com570863f2013-09-16 15:55:01 +00002523void SkOpSegment::checkTiny() {
2524 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
2525 SkOpSpan* thisSpan = fTs.begin() - 1;
2526 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be tiny
2527 while (++thisSpan < endSpan) {
2528 if (!thisSpan->fTiny) {
2529 continue;
2530 }
2531 SkOpSpan* nextSpan = thisSpan + 1;
2532 double thisT = thisSpan->fT;
2533 double nextT = nextSpan->fT;
2534 if (thisT == nextT) {
2535 continue;
2536 }
2537 SkASSERT(thisT < nextT);
2538 SkASSERT(thisSpan->fPt == nextSpan->fPt);
2539 SkOpSegment* thisOther = thisSpan->fOther;
2540 SkOpSegment* nextOther = nextSpan->fOther;
2541 int oIndex = thisSpan->fOtherIndex;
2542 for (int oStep = -1; oStep <= 1; oStep += 2) {
2543 int oEnd = thisOther->nextExactSpan(oIndex, oStep);
2544 if (oEnd < 0) {
2545 continue;
2546 }
2547 const SkOpSpan& oSpan = thisOther->span(oEnd);
2548 int nIndex = nextSpan->fOtherIndex;
2549 for (int nStep = -1; nStep <= 1; nStep += 2) {
2550 int nEnd = nextOther->nextExactSpan(nIndex, nStep);
2551 if (nEnd < 0) {
2552 continue;
2553 }
2554 const SkOpSpan& nSpan = nextOther->span(nEnd);
2555 if (oSpan.fPt != nSpan.fPt) {
2556 continue;
2557 }
2558 double oMidT = (thisSpan->fOtherT + oSpan.fT) / 2;
2559 const SkPoint& oPt = thisOther->ptAtT(oMidT);
2560 double nMidT = (nextSpan->fOtherT + nSpan.fT) / 2;
2561 const SkPoint& nPt = nextOther->ptAtT(nMidT);
2562 if (!AlmostEqualUlps(oPt, nPt)) {
2563 continue;
2564 }
2565#if DEBUG_CHECK_TINY
2566 SkDebugf("%s [%d] add coincidence [%d] [%d]\n", __FUNCTION__, fID,
2567 thisOther->fID, nextOther->fID);
2568#endif
2569 // this segment is missing a entry that the other contains
2570 // remember so we can add the missing one and recompute the indices
2571 MissingSpan& missing = missingSpans.push_back();
2572 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
caryclark@google.com570863f2013-09-16 15:55:01 +00002573 missing.fSegment = thisOther;
2574 missing.fT = thisSpan->fOtherT;
2575 missing.fOther = nextOther;
2576 missing.fOtherT = nextSpan->fOtherT;
2577 missing.fPt = thisSpan->fPt;
2578 }
2579 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002580 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002581 int missingCount = missingSpans.count();
2582 if (!missingCount) {
2583 return;
2584 }
2585 for (int index = 0; index < missingCount; ++index) {
2586 MissingSpan& missing = missingSpans[index];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002587 if (missing.fSegment != missing.fOther) {
2588 missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false,
2589 missing.fPt);
2590 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002591 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002592 // OPTIMIZE: consolidate to avoid multiple calls to fix index
caryclark@google.com570863f2013-09-16 15:55:01 +00002593 for (int index = 0; index < missingCount; ++index) {
2594 MissingSpan& missing = missingSpans[index];
2595 missing.fSegment->fixOtherTIndex();
2596 missing.fOther->fixOtherTIndex();
2597 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002598}
2599
caryclarkdac1d172014-06-17 05:15:38 -07002600bool SkOpSegment::coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const {
2601 int count = this->count();
2602 for (int index = 0; index < count; ++index) {
2603 const SkOpSpan& span = this->span(index);
2604 if (span.fOther != other) {
2605 continue;
2606 }
2607 if (span.fPt == pt) {
2608 continue;
2609 }
2610 if (!AlmostEqualUlps(span.fPt, pt)) {
2611 continue;
2612 }
2613 if (fVerb != SkPath::kCubic_Verb) {
2614 return true;
2615 }
2616 double tInterval = t - span.fT;
2617 double tMid = t - tInterval / 2;
2618 SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
2619 return midPt.approximatelyEqual(xyAtT(t));
2620 }
2621 return false;
2622}
2623
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00002624bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart,
2625 int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const {
2626 SkASSERT(span->fT == 0 || span->fT == 1);
2627 SkASSERT(span->fOtherT == 0 || span->fOtherT == 1);
2628 const SkOpSpan* otherSpan = &other->span(oEnd);
2629 double refT = otherSpan->fT;
2630 const SkPoint& refPt = otherSpan->fPt;
2631 const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0);
2632 do {
2633 const SkOpSegment* match = span->fOther;
2634 if (match == otherSpan->fOther) {
2635 // find start of respective spans and see if both have winding
2636 int startIndex, endIndex;
2637 if (span->fOtherT == 1) {
2638 endIndex = span->fOtherIndex;
2639 startIndex = match->nextExactSpan(endIndex, -1);
2640 } else {
2641 startIndex = span->fOtherIndex;
2642 endIndex = match->nextExactSpan(startIndex, 1);
2643 }
2644 const SkOpSpan& startSpan = match->span(startIndex);
2645 if (startSpan.fWindValue != 0) {
2646 // draw ray from endSpan.fPt perpendicular to end tangent and measure distance
2647 // to other segment.
2648 const SkOpSpan& endSpan = match->span(endIndex);
2649 SkDLine ray;
2650 SkVector dxdy;
2651 if (span->fOtherT == 1) {
2652 ray.fPts[0].set(startSpan.fPt);
2653 dxdy = match->dxdy(startIndex);
2654 } else {
2655 ray.fPts[0].set(endSpan.fPt);
2656 dxdy = match->dxdy(endIndex);
2657 }
2658 ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY;
2659 ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX;
2660 SkIntersections i;
2661 int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray);
2662 for (int index = 0; index < roots; ++index) {
2663 if (ray.fPts[0].approximatelyEqual(i.pt(index))) {
2664 double matchMidT = (match->span(startIndex).fT
2665 + match->span(endIndex).fT) / 2;
2666 SkPoint matchMidPt = match->ptAtT(matchMidT);
2667 double otherMidT = (i[0][index] + other->span(oStart).fT) / 2;
2668 SkPoint otherMidPt = other->ptAtT(otherMidT);
2669 if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) {
2670 *startPt = startSpan.fPt;
2671 *endPt = endSpan.fPt;
2672 *endT = endSpan.fT;
2673 return true;
2674 }
2675 }
2676 }
2677 }
2678 return false;
2679 }
2680 if (otherSpan == lastSpan) {
2681 break;
2682 }
2683 otherSpan += step;
2684 } while (otherSpan->fT == refT || otherSpan->fPt == refPt);
2685 return false;
2686}
2687
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002688int SkOpSegment::findEndSpan(int endIndex) const {
2689 const SkOpSpan* span = &fTs[--endIndex];
2690 const SkPoint& lastPt = span->fPt;
2691 double endT = span->fT;
2692 do {
2693 span = &fTs[--endIndex];
2694 } while (SkDPoint::ApproximatelyEqual(span->fPt, lastPt) && (span->fT == endT || span->fTiny));
2695 return endIndex + 1;
2696}
2697
caryclark@google.com07393ca2013-04-08 11:47:37 +00002698/*
2699 The M and S variable name parts stand for the operators.
2700 Mi stands for Minuend (see wiki subtraction, analogous to difference)
2701 Su stands for Subtrahend
2702 The Opp variable name part designates that the value is for the Opposite operator.
2703 Opposite values result from combining coincident spans.
2704 */
2705SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
2706 bool* unsortable, SkPathOp op, const int xorMiMask,
2707 const int xorSuMask) {
2708 const int startIndex = *nextStart;
2709 const int endIndex = *nextEnd;
2710 SkASSERT(startIndex != endIndex);
2711 SkDEBUGCODE(const int count = fTs.count());
2712 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
caryclarkdac1d172014-06-17 05:15:38 -07002713 int step = SkSign32(endIndex - startIndex);
2714 *nextStart = startIndex;
2715 SkOpSegment* other = isSimple(nextStart, &step);
2716 if (other)
2717 {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002718 // mark the smaller of startIndex, endIndex done, and all adjacent
2719 // spans with the same T value (but not 'other' spans)
2720#if DEBUG_WINDING
2721 SkDebugf("%s simple\n", __FUNCTION__);
2722#endif
2723 int min = SkMin32(startIndex, endIndex);
2724 if (fTs[min].fDone) {
2725 return NULL;
2726 }
2727 markDoneBinary(min);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002728 double startT = other->fTs[*nextStart].fT;
2729 *nextEnd = *nextStart;
2730 do {
2731 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00002732 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002733 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002734 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +00002735 *unsortable = true;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002736 return NULL;
2737 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002738 return other;
2739 }
caryclarkdac1d172014-06-17 05:15:38 -07002740 const int end = nextExactSpan(startIndex, step);
2741 SkASSERT(end >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002742 SkASSERT(startIndex - endIndex != 0);
2743 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002744 // more than one viable candidate -- measure angles to find best
2745
2746 int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp);
caryclark@google.com570863f2013-09-16 15:55:01 +00002747 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002748 if (!sortable) {
2749 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002750 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002751 return NULL;
2752 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002753 SkOpAngle* angle = spanToAngle(end, startIndex);
2754 if (angle->unorderable()) {
2755 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002756 markDoneBinary(SkMin32(startIndex, endIndex));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002757 return NULL;
2758 }
2759#if DEBUG_SORT
2760 SkDebugf("%s\n", __FUNCTION__);
2761 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002762#endif
2763 int sumMiWinding = updateWinding(endIndex, startIndex);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002764 if (sumMiWinding == SK_MinS32) {
2765 *unsortable = true;
2766 markDoneBinary(SkMin32(startIndex, endIndex));
2767 return NULL;
2768 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002769 int sumSuWinding = updateOppWinding(endIndex, startIndex);
2770 if (operand()) {
2771 SkTSwap<int>(sumMiWinding, sumSuWinding);
2772 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002773 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002774 const SkOpAngle* foundAngle = NULL;
2775 bool foundDone = false;
2776 // iterate through the angle, and compute everyone's winding
2777 SkOpSegment* nextSegment;
2778 int activeCount = 0;
2779 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002780 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002781 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002782 nextAngle->end(), op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002783 if (activeAngle) {
2784 ++activeCount;
2785 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002786 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002787 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002788 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002789 return NULL;
2790 }
2791 foundAngle = nextAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +00002792 foundDone = nextSegment->done(nextAngle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002793 }
2794 }
2795 if (nextSegment->done()) {
2796 continue;
2797 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002798 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002799 continue;
2800 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002801 if (!activeAngle) {
caryclarkdac1d172014-06-17 05:15:38 -07002802 (void) nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
caryclark@google.com570863f2013-09-16 15:55:01 +00002803 }
2804 SkOpSpan* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002805 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002806 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002807 *chase->append() = last;
2808#if DEBUG_WINDING
caryclark@google.com570863f2013-09-16 15:55:01 +00002809 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
2810 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
2811 last->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002812#endif
2813 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002814 } while ((nextAngle = nextAngle->next()) != angle);
2815#if DEBUG_ANGLE
2816 if (foundAngle) {
2817 foundAngle->debugSameAs(foundAngle);
2818 }
2819#endif
2820
caryclark@google.com07393ca2013-04-08 11:47:37 +00002821 markDoneBinary(SkMin32(startIndex, endIndex));
2822 if (!foundAngle) {
2823 return NULL;
2824 }
2825 *nextStart = foundAngle->start();
2826 *nextEnd = foundAngle->end();
2827 nextSegment = foundAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002828#if DEBUG_WINDING
2829 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2830 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
2831 #endif
2832 return nextSegment;
2833}
2834
2835SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart,
2836 int* nextEnd, bool* unsortable) {
2837 const int startIndex = *nextStart;
2838 const int endIndex = *nextEnd;
2839 SkASSERT(startIndex != endIndex);
2840 SkDEBUGCODE(const int count = fTs.count());
2841 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
caryclarkdac1d172014-06-17 05:15:38 -07002842 int step = SkSign32(endIndex - startIndex);
2843 *nextStart = startIndex;
2844 SkOpSegment* other = isSimple(nextStart, &step);
2845 if (other)
2846 {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002847 // mark the smaller of startIndex, endIndex done, and all adjacent
2848 // spans with the same T value (but not 'other' spans)
2849#if DEBUG_WINDING
2850 SkDebugf("%s simple\n", __FUNCTION__);
2851#endif
2852 int min = SkMin32(startIndex, endIndex);
2853 if (fTs[min].fDone) {
2854 return NULL;
2855 }
2856 markDoneUnary(min);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002857 double startT = other->fTs[*nextStart].fT;
2858 *nextEnd = *nextStart;
2859 do {
2860 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00002861 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002862 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
caryclark@google.com570863f2013-09-16 15:55:01 +00002863 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
2864 *unsortable = true;
2865 return NULL;
2866 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002867 return other;
2868 }
caryclarkdac1d172014-06-17 05:15:38 -07002869 const int end = nextExactSpan(startIndex, step);
2870 SkASSERT(end >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002871 SkASSERT(startIndex - endIndex != 0);
2872 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002873 // more than one viable candidate -- measure angles to find best
2874
2875 int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00002876 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002877 if (!sortable) {
2878 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002879 markDoneUnary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002880 return NULL;
2881 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002882 SkOpAngle* angle = spanToAngle(end, startIndex);
2883#if DEBUG_SORT
2884 SkDebugf("%s\n", __FUNCTION__);
2885 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002886#endif
2887 int sumWinding = updateWinding(endIndex, startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002888 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002889 const SkOpAngle* foundAngle = NULL;
2890 bool foundDone = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002891 SkOpSegment* nextSegment;
2892 int activeCount = 0;
2893 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002894 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002895 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002896 &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002897 if (activeAngle) {
2898 ++activeCount;
2899 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002900 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002901 *unsortable = true;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002902 markDoneUnary(SkMin32(startIndex, endIndex));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002903 return NULL;
2904 }
2905 foundAngle = nextAngle;
2906 foundDone = nextSegment->done(nextAngle);
2907 }
2908 }
2909 if (nextSegment->done()) {
2910 continue;
2911 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002912 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002913 continue;
2914 }
caryclark@google.com570863f2013-09-16 15:55:01 +00002915 if (!activeAngle) {
2916 nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
2917 }
2918 SkOpSpan* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002919 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002920 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002921 *chase->append() = last;
2922#if DEBUG_WINDING
caryclark@google.com570863f2013-09-16 15:55:01 +00002923 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
2924 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
2925 last->fSmall);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002926#endif
2927 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002928 } while ((nextAngle = nextAngle->next()) != angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002929 markDoneUnary(SkMin32(startIndex, endIndex));
2930 if (!foundAngle) {
2931 return NULL;
2932 }
2933 *nextStart = foundAngle->start();
2934 *nextEnd = foundAngle->end();
2935 nextSegment = foundAngle->segment();
2936#if DEBUG_WINDING
2937 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2938 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
2939 #endif
2940 return nextSegment;
2941}
2942
2943SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsortable) {
2944 const int startIndex = *nextStart;
2945 const int endIndex = *nextEnd;
2946 SkASSERT(startIndex != endIndex);
2947 SkDEBUGCODE(int count = fTs.count());
caryclark@google.com570863f2013-09-16 15:55:01 +00002948 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002949 int step = SkSign32(endIndex - startIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002950// Detect cases where all the ends canceled out (e.g.,
caryclarkdac1d172014-06-17 05:15:38 -07002951// there is no angle) and therefore there's only one valid connection
2952 *nextStart = startIndex;
2953 SkOpSegment* other = isSimple(nextStart, &step);
2954 if (other)
2955 {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002956#if DEBUG_WINDING
2957 SkDebugf("%s simple\n", __FUNCTION__);
2958#endif
2959 int min = SkMin32(startIndex, endIndex);
2960 if (fTs[min].fDone) {
2961 return NULL;
2962 }
2963 markDone(min, 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002964 double startT = other->fTs[*nextStart].fT;
2965 // FIXME: I don't know why the logic here is difference from the winding case
2966 SkDEBUGCODE(bool firstLoop = true;)
2967 if ((approximately_less_than_zero(startT) && step < 0)
2968 || (approximately_greater_than_one(startT) && step > 0)) {
2969 step = -step;
2970 SkDEBUGCODE(firstLoop = false;)
2971 }
2972 do {
2973 *nextEnd = *nextStart;
2974 do {
2975 *nextEnd += step;
caryclark@google.com570863f2013-09-16 15:55:01 +00002976 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
caryclark@google.com07393ca2013-04-08 11:47:37 +00002977 if (other->fTs[SkMin32(*nextStart, *nextEnd)].fWindValue) {
2978 break;
2979 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002980 SkASSERT(firstLoop);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002981 SkDEBUGCODE(firstLoop = false;)
2982 step = -step;
2983 } while (true);
2984 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
2985 return other;
2986 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002987 SkASSERT(startIndex - endIndex != 0);
2988 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002989 // parallel block above with presorted version
caryclarkdac1d172014-06-17 05:15:38 -07002990 int end = nextExactSpan(startIndex, step);
2991 SkASSERT(end >= 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002992 SkOpAngle* angle = spanToAngle(end, startIndex);
2993 SkASSERT(angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002994#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002995 SkDebugf("%s\n", __FUNCTION__);
2996 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002997#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00002998 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002999 const SkOpAngle* foundAngle = NULL;
3000 bool foundDone = false;
3001 SkOpSegment* nextSegment;
3002 int activeCount = 0;
3003 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003004 nextSegment = nextAngle->segment();
3005 ++activeCount;
3006 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003007 if (nextSegment->isTiny(nextAngle)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003008 *unsortable = true;
3009 return NULL;
3010 }
3011 foundAngle = nextAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003012 if (!(foundDone = nextSegment->done(nextAngle))) {
3013 break;
3014 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003015 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003016 nextAngle = nextAngle->next();
3017 } while (nextAngle != angle);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003018 markDone(SkMin32(startIndex, endIndex), 1);
3019 if (!foundAngle) {
3020 return NULL;
3021 }
3022 *nextStart = foundAngle->start();
3023 *nextEnd = foundAngle->end();
3024 nextSegment = foundAngle->segment();
3025#if DEBUG_WINDING
3026 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
3027 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
3028 #endif
3029 return nextSegment;
3030}
3031
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003032int SkOpSegment::findStartSpan(int startIndex) const {
3033 int index = startIndex;
3034 const SkOpSpan* span = &fTs[index];
3035 const SkPoint& firstPt = span->fPt;
3036 double firstT = span->fT;
caryclarkdac1d172014-06-17 05:15:38 -07003037 const SkOpSpan* prior;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003038 do {
caryclarkdac1d172014-06-17 05:15:38 -07003039 prior = span;
3040 span = &fTs[++index];
3041 } while (SkDPoint::ApproximatelyEqual(span->fPt, firstPt)
3042 && (span->fT == firstT || prior->fTiny));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003043 return index;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003044}
3045
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003046int SkOpSegment::findExactT(double t, const SkOpSegment* match) const {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003047 int count = this->count();
3048 for (int index = 0; index < count; ++index) {
3049 const SkOpSpan& span = fTs[index];
3050 if (span.fT == t && span.fOther == match) {
3051 return index;
3052 }
3053 }
3054 SkASSERT(0);
3055 return -1;
3056}
3057
caryclarkdac1d172014-06-17 05:15:38 -07003058int SkOpSegment::findOtherT(double t, const SkOpSegment* match) const {
3059 int count = this->count();
3060 for (int index = 0; index < count; ++index) {
3061 const SkOpSpan& span = fTs[index];
3062 if (span.fOtherT == t && span.fOther == match) {
3063 return index;
3064 }
3065 }
3066 return -1;
3067}
3068
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003069int SkOpSegment::findT(double t, const SkPoint& pt, const SkOpSegment* match) const {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003070 int count = this->count();
caryclark5e27e0e2014-08-12 07:46:33 -07003071 // prefer exact matches over approximate matches
3072 for (int index = 0; index < count; ++index) {
3073 const SkOpSpan& span = fTs[index];
3074 if (span.fT == t && span.fOther == match) {
3075 return index;
3076 }
3077 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003078 for (int index = 0; index < count; ++index) {
3079 const SkOpSpan& span = fTs[index];
3080 if (approximately_equal_orderable(span.fT, t) && span.fOther == match) {
3081 return index;
3082 }
3083 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003084 // Usually, the pair of ts are an exact match. It's possible that the t values have
3085 // been adjusted to make multiple intersections align. In this rare case, look for a
3086 // matching point / match pair instead.
3087 for (int index = 0; index < count; ++index) {
3088 const SkOpSpan& span = fTs[index];
3089 if (span.fPt == pt && span.fOther == match) {
3090 return index;
3091 }
3092 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003093 SkASSERT(0);
3094 return -1;
3095}
caryclark@google.com07393ca2013-04-08 11:47:37 +00003096
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003097SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable,
3098 bool firstPass) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003099 // iterate through T intersections and return topmost
3100 // topmost tangent from y-min to first pt is closer to horizontal
3101 SkASSERT(!done());
3102 int firstT = -1;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003103 /* SkPoint topPt = */ activeLeftTop(&firstT);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003104 if (firstT < 0) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003105 *unsortable = !firstPass;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003106 firstT = 0;
3107 while (fTs[firstT].fDone) {
3108 SkASSERT(firstT < fTs.count());
3109 ++firstT;
3110 }
3111 *tIndexPtr = firstT;
3112 *endIndexPtr = nextExactSpan(firstT, 1);
3113 return this;
3114 }
3115 // sort the edges to find the leftmost
3116 int step = 1;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003117 int end;
3118 if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003119 step = -1;
3120 end = nextSpan(firstT, step);
3121 SkASSERT(end != -1);
3122 }
3123 // if the topmost T is not on end, or is three-way or more, find left
3124 // look for left-ness from tLeft to firstT (matching y of other)
caryclark@google.com07393ca2013-04-08 11:47:37 +00003125 SkASSERT(firstT - end != 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003126 SkOpAngle* markAngle = spanToAngle(firstT, end);
3127 if (!markAngle) {
caryclarkdac1d172014-06-17 05:15:38 -07003128 markAngle = addSingletonAngles(step);
caryclark@google.com570863f2013-09-16 15:55:01 +00003129 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003130 markAngle->markStops();
caryclarke4097e32014-06-18 07:24:19 -07003131 const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
3132 : markAngle->findFirst();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003133 if (!baseAngle) {
3134 return NULL; // nothing to do
caryclark@google.com570863f2013-09-16 15:55:01 +00003135 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003136 SkScalar top = SK_ScalarMax;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003137 const SkOpAngle* firstAngle = NULL;
3138 const SkOpAngle* angle = baseAngle;
3139 do {
3140 if (!angle->unorderable()) {
3141 SkOpSegment* next = angle->segment();
3142 SkPathOpsBounds bounds;
3143 next->subDivideBounds(angle->end(), angle->start(), &bounds);
3144 if (approximately_greater(top, bounds.fTop)) {
3145 top = bounds.fTop;
3146 firstAngle = angle;
3147 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003148 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003149 angle = angle->next();
3150 } while (angle != baseAngle);
3151 SkASSERT(firstAngle);
3152#if DEBUG_SORT
3153 SkDebugf("%s\n", __FUNCTION__);
3154 firstAngle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003155#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003156 // skip edges that have already been processed
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003157 angle = firstAngle;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003158 SkOpSegment* leftSegment = NULL;
3159 bool looped = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003160 do {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003161 *unsortable = angle->unorderable();
3162 if (firstPass || !*unsortable) {
3163 leftSegment = angle->segment();
3164 *tIndexPtr = angle->end();
3165 *endIndexPtr = angle->start();
3166 if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
3167 break;
3168 }
3169 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003170 angle = angle->next();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003171 looped = true;
3172 } while (angle != firstAngle);
3173 if (angle == firstAngle && looped) {
3174 return NULL;
3175 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003176 if (leftSegment->verb() >= SkPath::kQuad_Verb) {
3177 const int tIndex = *tIndexPtr;
3178 const int endIndex = *endIndexPtr;
caryclarke4097e32014-06-18 07:24:19 -07003179 bool swap;
3180 if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003181 #if DEBUG_SWAP_TOP
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003182 SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
3183 __FUNCTION__,
3184 swap, leftSegment->debugInflections(tIndex, endIndex),
caryclark@google.com07393ca2013-04-08 11:47:37 +00003185 leftSegment->serpentine(tIndex, endIndex),
3186 leftSegment->controlsContainedByEnds(tIndex, endIndex),
3187 leftSegment->monotonicInY(tIndex, endIndex));
3188 #endif
3189 if (swap) {
3190 // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
3191 // sorted but merely the first not already processed (i.e., not done)
3192 SkTSwap(*tIndexPtr, *endIndexPtr);
3193 }
3194 }
3195 }
3196 SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
3197 return leftSegment;
3198}
3199
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003200int SkOpSegment::firstActive(int tIndex) const {
3201 while (fTs[tIndex].fTiny) {
3202 SkASSERT(!isCanceled(tIndex));
3203 ++tIndex;
3204 }
3205 return tIndex;
3206}
3207
caryclark@google.com07393ca2013-04-08 11:47:37 +00003208// FIXME: not crazy about this
3209// when the intersections are performed, the other index is into an
3210// incomplete array. As the array grows, the indices become incorrect
3211// while the following fixes the indices up again, it isn't smart about
3212// skipping segments whose indices are already correct
3213// assuming we leave the code that wrote the index in the first place
caryclark@google.com570863f2013-09-16 15:55:01 +00003214// FIXME: if called after remove, this needs to correct tiny
caryclark@google.com07393ca2013-04-08 11:47:37 +00003215void SkOpSegment::fixOtherTIndex() {
3216 int iCount = fTs.count();
3217 for (int i = 0; i < iCount; ++i) {
3218 SkOpSpan& iSpan = fTs[i];
3219 double oT = iSpan.fOtherT;
3220 SkOpSegment* other = iSpan.fOther;
3221 int oCount = other->fTs.count();
caryclark@google.com4fdbb222013-07-23 15:27:41 +00003222 SkDEBUGCODE(iSpan.fOtherIndex = -1);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003223 for (int o = 0; o < oCount; ++o) {
3224 SkOpSpan& oSpan = other->fTs[o];
3225 if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) {
3226 iSpan.fOtherIndex = o;
caryclark@google.com4fdbb222013-07-23 15:27:41 +00003227 oSpan.fOtherIndex = i;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003228 break;
3229 }
3230 }
caryclark@google.com4fdbb222013-07-23 15:27:41 +00003231 SkASSERT(iSpan.fOtherIndex >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003232 }
3233}
3234
caryclarkdac1d172014-06-17 05:15:38 -07003235bool SkOpSegment::inCoincidentSpan(double t, const SkOpSegment* other) const {
3236 int foundEnds = 0;
3237 int count = this->count();
3238 for (int index = 0; index < count; ++index) {
3239 const SkOpSpan& span = this->span(index);
3240 if (span.fCoincident) {
3241 foundEnds |= (span.fOther == other) << ((t > span.fT) + (t >= span.fT));
3242 }
3243 }
3244 SkASSERT(foundEnds != 7);
3245 return foundEnds == 0x3 || foundEnds == 0x5 || foundEnds == 0x6; // two bits set
3246}
3247
caryclark@google.com07393ca2013-04-08 11:47:37 +00003248void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
3249 fDoneSpans = 0;
3250 fOperand = operand;
3251 fXor = evenOdd;
3252 fPts = pts;
3253 fVerb = verb;
caryclarkdac1d172014-06-17 05:15:38 -07003254 fLoop = fMultiples = fSmall = fTiny = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003255}
3256
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003257void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003258 int local = spanSign(start, end);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003259 if (angleIncludeType == SkOpAngle::kBinarySingle) {
3260 int oppLocal = oppSign(start, end);
3261 (void) markAndChaseWinding(start, end, local, oppLocal);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003262 // OPTIMIZATION: the reverse mark and chase could skip the first marking
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003263 (void) markAndChaseWinding(end, start, local, oppLocal);
3264 } else {
3265 (void) markAndChaseWinding(start, end, local);
3266 // OPTIMIZATION: the reverse mark and chase could skip the first marking
3267 (void) markAndChaseWinding(end, start, local);
3268 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003269}
3270
3271/*
3272when we start with a vertical intersect, we try to use the dx to determine if the edge is to
3273the left or the right of vertical. This determines if we need to add the span's
3274sign or not. However, this isn't enough.
3275If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
3276If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
3277from has the same x direction as this span, the winding should change. If the dx is opposite, then
3278the same winding is shared by both.
3279*/
3280void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
3281 int oppWind, SkScalar hitOppDx) {
3282 SkASSERT(hitDx || !winding);
reed@google.com277c3f82013-05-31 15:17:50 +00003283 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003284 SkASSERT(dx);
3285 int windVal = windValue(SkMin32(start, end));
3286#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07003287 SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), winding,
caryclark@google.com07393ca2013-04-08 11:47:37 +00003288 hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
3289#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003290 int sideWind = winding + (dx < 0 ? windVal : -windVal);
3291 if (abs(winding) < abs(sideWind)) {
3292 winding = sideWind;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003293 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003294 SkDEBUGCODE(int oppLocal = oppSign(start, end));
3295 SkASSERT(hitOppDx || !oppWind || !oppLocal);
3296 int oppWindVal = oppValue(SkMin32(start, end));
3297 if (!oppWind) {
3298 oppWind = dx < 0 ? oppWindVal : -oppWindVal;
3299 } else if (hitOppDx * dx >= 0) {
3300 int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
3301 if (abs(oppWind) < abs(oppSideWind)) {
3302 oppWind = oppSideWind;
3303 }
3304 }
caryclarkdac1d172014-06-17 05:15:38 -07003305#if DEBUG_WINDING_AT_T
3306 SkDebugf(" winding=%d oppWind=%d\n", winding, oppWind);
3307#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003308 (void) markAndChaseWinding(start, end, winding, oppWind);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003309 // OPTIMIZATION: the reverse mark and chase could skip the first marking
3310 (void) markAndChaseWinding(end, start, winding, oppWind);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003311}
3312
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003313bool SkOpSegment::inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const {
3314 if (!baseAngle->inLoop()) {
3315 return false;
3316 }
3317 int index = *indexPtr;
caryclarkdac1d172014-06-17 05:15:38 -07003318 SkOpAngle* from = fTs[index].fFromAngle;
3319 SkOpAngle* to = fTs[index].fToAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003320 while (++index < spanCount) {
caryclarkdac1d172014-06-17 05:15:38 -07003321 SkOpAngle* nextFrom = fTs[index].fFromAngle;
3322 SkOpAngle* nextTo = fTs[index].fToAngle;
3323 if (from != nextFrom || to != nextTo) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003324 break;
3325 }
3326 }
3327 *indexPtr = index;
3328 return true;
3329}
3330
caryclark@google.com07393ca2013-04-08 11:47:37 +00003331// OPTIMIZE: successive calls could start were the last leaves off
3332// or calls could specialize to walk forwards or backwards
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003333bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003334 int tCount = fTs.count();
3335 for (int index = 0; index < tCount; ++index) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003336 const SkOpSpan& span = fTs[index];
3337 if (approximately_zero(startT - span.fT) && pt == span.fPt) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003338 return false;
3339 }
3340 }
3341 return true;
3342}
3343
caryclarkdac1d172014-06-17 05:15:38 -07003344
3345SkOpSegment* SkOpSegment::isSimple(int* end, int* step) {
3346 return nextChase(end, step, NULL, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003347}
3348
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003349bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
3350 int start = angle->start();
3351 int end = angle->end();
3352 const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
3353 return mSpan.fTiny;
3354}
3355
3356bool SkOpSegment::isTiny(int index) const {
3357 return fTs[index].fTiny;
3358}
3359
3360// look pair of active edges going away from coincident edge
3361// one of them should be the continuation of other
3362// 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 +00003363// if at least one is a line, then make the pair coincident
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003364// if neither is a line, test for coincidence
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003365bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt,
3366 int step, bool cancel) {
3367 int otherTIndex = other->findT(otherT, otherPt, this);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003368 int next = other->nextExactSpan(otherTIndex, step);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003369 int otherMin = SkMin32(otherTIndex, next);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003370 int otherWind = other->span(otherMin).fWindValue;
3371 if (otherWind == 0) {
3372 return false;
3373 }
3374 SkASSERT(next >= 0);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003375 int tIndex = 0;
3376 do {
3377 SkOpSpan* test = &fTs[tIndex];
3378 SkASSERT(test->fT == 0);
3379 if (test->fOther == other || test->fOtherT != 1) {
3380 continue;
3381 }
3382 SkPoint startPt, endPt;
3383 double endT;
3384 if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) {
3385 SkOpSegment* match = test->fOther;
3386 if (cancel) {
3387 match->addTCancel(startPt, endPt, other);
3388 } else {
3389 match->addTCoincident(startPt, endPt, endT, other);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003390 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003391 return true;
3392 }
3393 } while (fTs[++tIndex].fT == 0);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00003394 return false;
3395}
3396
caryclark@google.com07393ca2013-04-08 11:47:37 +00003397// this span is excluded by the winding rule -- chase the ends
3398// as long as they are unambiguous to mark connections as done
3399// and give them the same winding value
caryclark@google.com07393ca2013-04-08 11:47:37 +00003400
3401SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
3402 int step = SkSign32(endIndex - index);
3403 int min = SkMin32(index, endIndex);
3404 markDoneBinary(min);
caryclarkdac1d172014-06-17 05:15:38 -07003405 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003406 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003407 while ((other = other->nextChase(&index, &step, &min, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003408 if (other->done()) {
caryclarkdac1d172014-06-17 05:15:38 -07003409 SkASSERT(!last);
3410 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003411 }
3412 other->markDoneBinary(min);
3413 }
3414 return last;
3415}
3416
3417SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
3418 int step = SkSign32(endIndex - index);
3419 int min = SkMin32(index, endIndex);
3420 markDoneUnary(min);
caryclarkdac1d172014-06-17 05:15:38 -07003421 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003422 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003423 while ((other = other->nextChase(&index, &step, &min, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003424 if (other->done()) {
caryclarkdac1d172014-06-17 05:15:38 -07003425 SkASSERT(!last);
3426 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003427 }
3428 other->markDoneUnary(min);
3429 }
3430 return last;
3431}
3432
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003433SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003434 int index = angle->start();
3435 int endIndex = angle->end();
3436 int step = SkSign32(endIndex - index);
3437 int min = SkMin32(index, endIndex);
3438 markWinding(min, winding);
caryclarkdac1d172014-06-17 05:15:38 -07003439 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003440 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003441 while ((other = other->nextChase(&index, &step, &min, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003442 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark19eb3b22014-07-18 05:08:14 -07003443// SkASSERT(other->fTs[min].fWindSum == winding);
caryclarkdac1d172014-06-17 05:15:38 -07003444 SkASSERT(!last);
3445 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003446 }
3447 other->markWinding(min, winding);
3448 }
3449 return last;
3450}
3451
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003452SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding) {
3453 int min = SkMin32(index, endIndex);
3454 int step = SkSign32(endIndex - index);
3455 markWinding(min, winding);
caryclarkdac1d172014-06-17 05:15:38 -07003456 SkOpSpan* last = NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003457 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003458 while ((other = other->nextChase(&index, &step, &min, &last))) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003459 if (other->fTs[min].fWindSum != SK_MinS32) {
3460 SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
caryclarkdac1d172014-06-17 05:15:38 -07003461 SkASSERT(!last);
3462 break;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003463 }
3464 other->markWinding(min, winding);
3465 }
3466 return last;
3467}
3468
caryclark@google.com07393ca2013-04-08 11:47:37 +00003469SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
3470 int min = SkMin32(index, endIndex);
3471 int step = SkSign32(endIndex - index);
3472 markWinding(min, winding, oppWinding);
caryclarkdac1d172014-06-17 05:15:38 -07003473 SkOpSpan* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003474 SkOpSegment* other = this;
caryclarkdac1d172014-06-17 05:15:38 -07003475 while ((other = other->nextChase(&index, &step, &min, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003476 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclarkda085e62014-06-17 05:41:12 -07003477#ifdef SK_DEBUG
caryclarkdac1d172014-06-17 05:15:38 -07003478 if (!other->fTs[min].fLoop) {
3479 if (fOperand == other->fOperand) {
3480// FIXME: this is probably a bug -- rects4 asserts here
3481// SkASSERT(other->fTs[min].fWindSum == winding);
3482// FIXME: this is probably a bug -- rects3 asserts here
3483// SkASSERT(other->fTs[min].fOppSum == oppWinding);
3484 } else {
3485 SkASSERT(other->fTs[min].fWindSum == oppWinding);
3486// FIXME: this is probably a bug -- skpwww_joomla_org_23 asserts here
3487// SkASSERT(other->fTs[min].fOppSum == winding);
3488 }
3489 }
3490 SkASSERT(!last);
3491#endif
3492 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003493 }
caryclarkdac1d172014-06-17 05:15:38 -07003494 if (fOperand == other->fOperand) {
3495 other->markWinding(min, winding, oppWinding);
3496 } else {
3497 other->markWinding(min, oppWinding, winding);
3498 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003499 }
3500 return last;
3501}
3502
3503SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding) {
3504 int start = angle->start();
3505 int end = angle->end();
3506 return markAndChaseWinding(start, end, winding, oppWinding);
3507}
3508
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003509SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003510 SkASSERT(angle->segment() == this);
3511 if (UseInnerWinding(maxWinding, sumWinding)) {
3512 maxWinding = sumWinding;
3513 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003514 SkOpSpan* last = markAndChaseWinding(angle, maxWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00003515#if DEBUG_WINDING
3516 if (last) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003517 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
3518 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
3519 SkPathOpsDebug::WindingPrintf(last->fWindSum);
3520 SkDebugf(" small=%d\n", last->fSmall);
caryclark@google.com570863f2013-09-16 15:55:01 +00003521 }
3522#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003523 return last;
3524}
3525
3526SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003527 int oppSumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003528 SkASSERT(angle->segment() == this);
3529 if (UseInnerWinding(maxWinding, sumWinding)) {
3530 maxWinding = sumWinding;
3531 }
3532 if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
3533 oppMaxWinding = oppSumWinding;
3534 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003535 SkOpSpan* last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +00003536#if DEBUG_WINDING
3537 if (last) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003538 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
3539 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
3540 SkPathOpsDebug::WindingPrintf(last->fWindSum);
3541 SkDebugf(" small=%d\n", last->fSmall);
caryclark@google.com570863f2013-09-16 15:55:01 +00003542 }
3543#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003544 return last;
3545}
3546
3547// FIXME: this should also mark spans with equal (x,y)
3548// This may be called when the segment is already marked done. While this
3549// wastes time, it shouldn't do any more than spin through the T spans.
3550// OPTIMIZATION: abort on first done found (assuming that this code is
3551// always called to mark segments done).
3552void SkOpSegment::markDone(int index, int winding) {
3553 // SkASSERT(!done());
3554 SkASSERT(winding);
3555 double referenceT = fTs[index].fT;
3556 int lesser = index;
3557 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3558 markOneDone(__FUNCTION__, lesser, winding);
3559 }
3560 do {
3561 markOneDone(__FUNCTION__, index, winding);
3562 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003563 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003564}
3565
caryclark@google.com07393ca2013-04-08 11:47:37 +00003566void SkOpSegment::markDoneBinary(int index) {
3567 double referenceT = fTs[index].fT;
3568 int lesser = index;
3569 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3570 markOneDoneBinary(__FUNCTION__, lesser);
3571 }
3572 do {
3573 markOneDoneBinary(__FUNCTION__, index);
3574 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003575 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003576}
3577
3578void SkOpSegment::markDoneUnary(int index) {
3579 double referenceT = fTs[index].fT;
3580 int lesser = index;
3581 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3582 markOneDoneUnary(__FUNCTION__, lesser);
3583 }
3584 do {
3585 markOneDoneUnary(__FUNCTION__, index);
3586 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003587 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003588}
3589
3590void SkOpSegment::markOneDone(const char* funName, int tIndex, int winding) {
3591 SkOpSpan* span = markOneWinding(funName, tIndex, winding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003592 if (!span || span->fDone) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003593 return;
3594 }
3595 span->fDone = true;
3596 fDoneSpans++;
3597}
3598
3599void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
3600 SkOpSpan* span = verifyOneWinding(funName, tIndex);
3601 if (!span) {
3602 return;
3603 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003604 SkASSERT(!span->fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003605 span->fDone = true;
3606 fDoneSpans++;
3607}
3608
caryclark@google.com07393ca2013-04-08 11:47:37 +00003609void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
3610 SkOpSpan* span = verifyOneWindingU(funName, tIndex);
3611 if (!span) {
3612 return;
3613 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003614 if (span->fWindSum == SK_MinS32) {
3615 SkDebugf("%s uncomputed\n", __FUNCTION__);
3616 }
3617 SkASSERT(!span->fDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003618 span->fDone = true;
3619 fDoneSpans++;
3620}
3621
3622SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding) {
3623 SkOpSpan& span = fTs[tIndex];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003624 if (span.fDone && !span.fSmall) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003625 return NULL;
3626 }
3627#if DEBUG_MARK_DONE
3628 debugShowNewWinding(funName, span, winding);
3629#endif
3630 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003631#if DEBUG_LIMIT_WIND_SUM
3632 SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
3633#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003634 span.fWindSum = winding;
3635 return &span;
3636}
3637
3638SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
3639 int oppWinding) {
3640 SkOpSpan& span = fTs[tIndex];
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00003641 if (span.fDone && !span.fSmall) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003642 return NULL;
3643 }
3644#if DEBUG_MARK_DONE
3645 debugShowNewWinding(funName, span, winding, oppWinding);
3646#endif
3647 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003648#if DEBUG_LIMIT_WIND_SUM
3649 SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
3650#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003651 span.fWindSum = winding;
3652 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003653#if DEBUG_LIMIT_WIND_SUM
3654 SkASSERT(abs(oppWinding) <= DEBUG_LIMIT_WIND_SUM);
3655#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00003656 span.fOppSum = oppWinding;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003657 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003658 return &span;
3659}
3660
3661// 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 -07003662bool SkOpSegment::clockwise(int tStart, int tEnd, bool* swap) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003663 SkASSERT(fVerb != SkPath::kLine_Verb);
3664 SkPoint edge[4];
3665 subDivide(tStart, tEnd, edge);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00003666 int points = SkPathOpsVerbToPoints(fVerb);
3667 double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
caryclarke4097e32014-06-18 07:24:19 -07003668 bool sumSet = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003669 if (fVerb == SkPath::kCubic_Verb) {
caryclarke4097e32014-06-18 07:24:19 -07003670 SkDCubic cubic;
3671 cubic.set(edge);
3672 double inflectionTs[2];
3673 int inflections = cubic.findInflections(inflectionTs);
3674 // FIXME: this fixes cubicOp114 and breaks cubicOp58d
3675 // the trouble is that cubics with inflections confuse whether the curve breaks towards
3676 // or away, which in turn is used to determine if it is on the far right or left.
3677 // Probably a totally different approach is in order. At one time I tried to project a
3678 // horizontal ray to determine winding, but was confused by how to map the vertically
3679 // oriented winding computation over.
3680 if (0 && inflections) {
3681 double tLo = this->span(tStart).fT;
3682 double tHi = this->span(tEnd).fT;
3683 double tLoStart = tLo;
3684 for (int index = 0; index < inflections; ++index) {
3685 if (between(tLo, inflectionTs[index], tHi)) {
3686 tLo = inflectionTs[index];
3687 }
3688 }
3689 if (tLo != tLoStart && tLo != tHi) {
3690 SkDPoint sub[2];
3691 sub[0] = cubic.ptAtT(tLo);
3692 sub[1].set(edge[3]);
3693 SkDPoint ctrl[2];
3694 SkDCubic::SubDivide(fPts, sub[0], sub[1], tLo, tHi, ctrl);
3695 edge[0] = sub[0].asSkPoint();
3696 edge[1] = ctrl[0].asSkPoint();
3697 edge[2] = ctrl[1].asSkPoint();
3698 sum = (edge[0].fX - edge[3].fX) * (edge[0].fY + edge[3].fY);
3699 }
3700 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003701 SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
3702 if (edge[1].fY < lesser && edge[2].fY < lesser) {
3703 SkDLine tangent1 = {{ {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} }};
3704 SkDLine tangent2 = {{ {edge[2].fX, edge[2].fY}, {edge[3].fX, edge[3].fY} }};
3705 if (SkIntersections::Test(tangent1, tangent2)) {
3706 SkPoint topPt = cubic_top(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3707 sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
3708 sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
caryclarke4097e32014-06-18 07:24:19 -07003709 sumSet = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003710 }
3711 }
3712 }
caryclarke4097e32014-06-18 07:24:19 -07003713 if (!sumSet) {
3714 for (int idx = 0; idx < points; ++idx){
3715 sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
3716 }
3717 }
3718 if (fVerb == SkPath::kCubic_Verb) {
3719 SkDCubic cubic;
3720 cubic.set(edge);
3721 *swap = sum > 0 && !cubic.monotonicInY() && !cubic.serpentine();
3722 } else {
3723 SkDQuad quad;
3724 quad.set(edge);
3725 *swap = sum > 0 && !quad.monotonicInY();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003726 }
3727 return sum <= 0;
3728}
3729
3730bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00003731 SkASSERT(fVerb != SkPath::kLine_Verb);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003732 if (fVerb == SkPath::kQuad_Verb) {
3733 SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3734 return dst.monotonicInY();
3735 }
3736 SkASSERT(fVerb == SkPath::kCubic_Verb);
3737 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3738 return dst.monotonicInY();
3739}
3740
3741bool SkOpSegment::serpentine(int tStart, int tEnd) const {
3742 if (fVerb != SkPath::kCubic_Verb) {
3743 return false;
3744 }
3745 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
3746 return dst.serpentine();
3747}
3748
3749SkOpSpan* SkOpSegment::verifyOneWinding(const char* funName, int tIndex) {
3750 SkOpSpan& span = fTs[tIndex];
3751 if (span.fDone) {
3752 return NULL;
3753 }
3754#if DEBUG_MARK_DONE
3755 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
3756#endif
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003757// If the prior angle in the sort is unorderable, the winding sum may not be computable.
3758// To enable the assert, the 'prior is unorderable' state could be
3759// piped down to this test, but not sure it's worth it.
3760// (Once the sort order is stored in the span, this test may be feasible.)
3761// SkASSERT(span.fWindSum != SK_MinS32);
3762// SkASSERT(span.fOppSum != SK_MinS32);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003763 return &span;
3764}
3765
3766SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
3767 SkOpSpan& span = fTs[tIndex];
3768 if (span.fDone) {
3769 return NULL;
3770 }
3771#if DEBUG_MARK_DONE
3772 debugShowNewWinding(funName, span, span.fWindSum);
3773#endif
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00003774// If the prior angle in the sort is unorderable, the winding sum may not be computable.
3775// To enable the assert, the 'prior is unorderable' state could be
3776// piped down to this test, but not sure it's worth it.
3777// (Once the sort order is stored in the span, this test may be feasible.)
3778// SkASSERT(span.fWindSum != SK_MinS32);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003779 return &span;
3780}
3781
caryclark@google.com07393ca2013-04-08 11:47:37 +00003782void SkOpSegment::markWinding(int index, int winding) {
3783// SkASSERT(!done());
3784 SkASSERT(winding);
3785 double referenceT = fTs[index].fT;
3786 int lesser = index;
3787 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3788 markOneWinding(__FUNCTION__, lesser, winding);
3789 }
3790 do {
3791 markOneWinding(__FUNCTION__, index, winding);
3792 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003793 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003794}
3795
3796void SkOpSegment::markWinding(int index, int winding, int oppWinding) {
3797// SkASSERT(!done());
3798 SkASSERT(winding || oppWinding);
3799 double referenceT = fTs[index].fT;
3800 int lesser = index;
3801 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3802 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
3803 }
3804 do {
3805 markOneWinding(__FUNCTION__, index, winding, oppWinding);
3806 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003807 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00003808}
3809
3810void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
3811 int nextDoorWind = SK_MaxS32;
3812 int nextOppWind = SK_MaxS32;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003813 // prefer exact matches
caryclark@google.com07393ca2013-04-08 11:47:37 +00003814 if (tIndex > 0) {
3815 const SkOpSpan& below = fTs[tIndex - 1];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00003816 if (below.fT == t) {
3817 nextDoorWind = below.fWindValue;
3818 nextOppWind = below.fOppValue;
3819 }
3820 }
3821 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3822 const SkOpSpan& above = fTs[tIndex + 1];
3823 if (above.fT == t) {
3824 nextDoorWind = above.fWindValue;
3825 nextOppWind = above.fOppValue;
3826 }
3827 }
3828 if (nextDoorWind == SK_MaxS32 && tIndex > 0) {
3829 const SkOpSpan& below = fTs[tIndex - 1];
caryclark@google.com07393ca2013-04-08 11:47:37 +00003830 if (approximately_negative(t - below.fT)) {
3831 nextDoorWind = below.fWindValue;
3832 nextOppWind = below.fOppValue;
3833 }
3834 }
3835 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3836 const SkOpSpan& above = fTs[tIndex + 1];
3837 if (approximately_negative(above.fT - t)) {
3838 nextDoorWind = above.fWindValue;
3839 nextOppWind = above.fOppValue;
3840 }
3841 }
3842 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
3843 const SkOpSpan& below = fTs[tIndex - 1];
3844 nextDoorWind = below.fWindValue;
3845 nextOppWind = below.fOppValue;
3846 }
3847 if (nextDoorWind != SK_MaxS32) {
3848 SkOpSpan& newSpan = fTs[tIndex];
3849 newSpan.fWindValue = nextDoorWind;
3850 newSpan.fOppValue = nextOppWind;
3851 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
3852 newSpan.fDone = true;
3853 ++fDoneSpans;
3854 }
3855 }
3856}
3857
caryclark@google.com07393ca2013-04-08 11:47:37 +00003858bool SkOpSegment::nextCandidate(int* start, int* end) const {
3859 while (fTs[*end].fDone) {
3860 if (fTs[*end].fT == 1) {
3861 return false;
3862 }
3863 ++(*end);
3864 }
3865 *start = *end;
3866 *end = nextExactSpan(*start, 1);
3867 return true;
3868}
3869
caryclarkdac1d172014-06-17 05:15:38 -07003870static SkOpSegment* set_last(SkOpSpan** last, SkOpSpan* endSpan) {
3871 if (last && !endSpan->fSmall) {
3872 *last = endSpan;
3873 }
3874 return NULL;
3875}
3876
3877SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr, SkOpSpan** last) {
3878 int origIndex = *indexPtr;
3879 int step = *stepPtr;
3880 int end = nextExactSpan(origIndex, step);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003881 SkASSERT(end >= 0);
caryclarkdac1d172014-06-17 05:15:38 -07003882 SkOpSpan& endSpan = fTs[end];
3883 SkOpAngle* angle = step > 0 ? endSpan.fFromAngle : endSpan.fToAngle;
3884 int foundIndex;
3885 int otherEnd;
3886 SkOpSegment* other;
3887 if (angle == NULL) {
3888 if (endSpan.fT != 0 && endSpan.fT != 1) {
3889 return NULL;
3890 }
3891 other = endSpan.fOther;
3892 foundIndex = endSpan.fOtherIndex;
3893 otherEnd = other->nextExactSpan(foundIndex, step);
3894 } else {
3895 int loopCount = angle->loopCount();
3896 if (loopCount > 2) {
3897 return set_last(last, &endSpan);
3898 }
3899 const SkOpAngle* next = angle->next();
3900 if (angle->sign() != next->sign()) {
3901#if DEBUG_WINDING
3902 SkDebugf("%s mismatched signs\n", __FUNCTION__);
3903#endif
3904 // return set_last(last, &endSpan);
3905 }
3906 other = next->segment();
3907 foundIndex = end = next->start();
3908 otherEnd = next->end();
caryclark@google.com570863f2013-09-16 15:55:01 +00003909 }
caryclarkdac1d172014-06-17 05:15:38 -07003910 int foundStep = foundIndex < otherEnd ? 1 : -1;
3911 if (*stepPtr != foundStep) {
3912 return set_last(last, &endSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003913 }
caryclarkdac1d172014-06-17 05:15:38 -07003914 SkASSERT(*indexPtr >= 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00003915 SkASSERT(otherEnd >= 0);
caryclarkdac1d172014-06-17 05:15:38 -07003916#if 1
3917 int origMin = origIndex + (step < 0 ? step : 0);
3918 const SkOpSpan& orig = this->span(origMin);
3919#endif
3920 int foundMin = SkMin32(foundIndex, otherEnd);
3921#if 1
3922 const SkOpSpan& found = other->span(foundMin);
3923 if (found.fWindValue != orig.fWindValue || found.fOppValue != orig.fOppValue) {
3924 return set_last(last, &endSpan);
3925 }
3926#endif
3927 *indexPtr = foundIndex;
3928 *stepPtr = foundStep;
3929 if (minPtr) {
3930 *minPtr = foundMin;
caryclark@google.coma5e55922013-05-07 18:51:31 +00003931 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003932 return other;
3933}
3934
3935// This has callers for two different situations: one establishes the end
3936// of the current span, and one establishes the beginning of the next span
3937// (thus the name). When this is looking for the end of the current span,
3938// coincidence is found when the beginning Ts contain -step and the end
3939// contains step. When it is looking for the beginning of the next, the
3940// first Ts found can be ignored and the last Ts should contain -step.
3941// OPTIMIZATION: probably should split into two functions
3942int SkOpSegment::nextSpan(int from, int step) const {
3943 const SkOpSpan& fromSpan = fTs[from];
3944 int count = fTs.count();
3945 int to = from;
3946 while (step > 0 ? ++to < count : --to >= 0) {
3947 const SkOpSpan& span = fTs[to];
3948 if (approximately_zero(span.fT - fromSpan.fT)) {
3949 continue;
3950 }
3951 return to;
3952 }
3953 return -1;
3954}
3955
3956// FIXME
3957// this returns at any difference in T, vs. a preset minimum. It may be
3958// that all callers to nextSpan should use this instead.
caryclark@google.com07393ca2013-04-08 11:47:37 +00003959int SkOpSegment::nextExactSpan(int from, int step) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00003960 int to = from;
caryclark@google.com570863f2013-09-16 15:55:01 +00003961 if (step < 0) {
3962 const SkOpSpan& fromSpan = fTs[from];
3963 while (--to >= 0) {
3964 const SkOpSpan& span = fTs[to];
3965 if (precisely_negative(fromSpan.fT - span.fT) || span.fTiny) {
3966 continue;
3967 }
3968 return to;
caryclark@google.com07393ca2013-04-08 11:47:37 +00003969 }
caryclark@google.com570863f2013-09-16 15:55:01 +00003970 } else {
3971 while (fTs[from].fTiny) {
3972 from++;
3973 }
3974 const SkOpSpan& fromSpan = fTs[from];
3975 int count = fTs.count();
3976 while (++to < count) {
3977 const SkOpSpan& span = fTs[to];
3978 if (precisely_negative(span.fT - fromSpan.fT)) {
3979 continue;
3980 }
3981 return to;
3982 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00003983 }
3984 return -1;
3985}
3986
caryclarkdac1d172014-06-17 05:15:38 -07003987void SkOpSegment::pinT(const SkPoint& pt, double* t) {
3988 if (pt == fPts[0]) {
3989 *t = 0;
3990 }
3991 int count = SkPathOpsVerbToPoints(fVerb);
3992 if (pt == fPts[count]) {
3993 *t = 1;
3994 }
3995}
3996
caryclark5e27e0e2014-08-12 07:46:33 -07003997bool SkOpSegment::reversePoints(const SkPoint& p1, const SkPoint& p2) const {
3998 SkASSERT(p1 != p2);
3999 int spanCount = count();
4000 int p1IndexMin = -1;
4001 int p2IndexMax = spanCount;
4002 for (int index = 0; index < spanCount; ++index) {
4003 const SkOpSpan& span = fTs[index];
4004 if (span.fPt == p1) {
4005 if (p1IndexMin < 0) {
4006 p1IndexMin = index;
4007 }
4008 } else if (span.fPt == p2) {
4009 p2IndexMax = index;
4010 }
4011 }
4012 return p1IndexMin > p2IndexMax;
4013}
4014
caryclarkdac1d172014-06-17 05:15:38 -07004015void SkOpSegment::setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt,
4016 SkOpSegment* other) {
4017 int count = this->count();
4018 for (int index = 0; index < count; ++index) {
4019 SkOpSpan &span = fTs[index];
4020 if ((startPt == span.fPt || endPt == span.fPt) && other == span.fOther) {
4021 span.fCoincident = true;
4022 }
4023 }
4024}
4025
caryclark@google.com07393ca2013-04-08 11:47:37 +00004026void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
4027 int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
4028 int deltaSum = spanSign(index, endIndex);
4029 int oppDeltaSum = oppSign(index, endIndex);
4030 if (operand()) {
4031 *maxWinding = *sumSuWinding;
4032 *sumWinding = *sumSuWinding -= deltaSum;
4033 *oppMaxWinding = *sumMiWinding;
4034 *oppSumWinding = *sumMiWinding -= oppDeltaSum;
4035 } else {
4036 *maxWinding = *sumMiWinding;
4037 *sumWinding = *sumMiWinding -= deltaSum;
4038 *oppMaxWinding = *sumSuWinding;
4039 *oppSumWinding = *sumSuWinding -= oppDeltaSum;
4040 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004041#if DEBUG_LIMIT_WIND_SUM
4042 SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
4043 SkASSERT(abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
4044#endif
caryclark@google.com570863f2013-09-16 15:55:01 +00004045}
4046
4047void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
4048 int* maxWinding, int* sumWinding) {
4049 int deltaSum = spanSign(index, endIndex);
4050 *maxWinding = *sumMiWinding;
4051 *sumWinding = *sumMiWinding -= deltaSum;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004052#if DEBUG_LIMIT_WIND_SUM
4053 SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
4054#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00004055}
4056
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004057void SkOpSegment::sortAngles() {
4058 int spanCount = fTs.count();
4059 if (spanCount <= 2) {
4060 return;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004061 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004062 int index = 0;
4063 do {
caryclarkdac1d172014-06-17 05:15:38 -07004064 SkOpAngle* fromAngle = fTs[index].fFromAngle;
4065 SkOpAngle* toAngle = fTs[index].fToAngle;
4066 if (!fromAngle && !toAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004067 index += 1;
4068 continue;
4069 }
4070 SkOpAngle* baseAngle = NULL;
caryclarkdac1d172014-06-17 05:15:38 -07004071 if (fromAngle) {
4072 baseAngle = fromAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004073 if (inLoop(baseAngle, spanCount, &index)) {
4074 continue;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004075 }
4076 }
caryclark@google.com570863f2013-09-16 15:55:01 +00004077#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004078 bool wroteAfterHeader = false;
caryclark@google.com570863f2013-09-16 15:55:01 +00004079#endif
caryclarkdac1d172014-06-17 05:15:38 -07004080 if (toAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004081 if (!baseAngle) {
4082 baseAngle = toAngle;
4083 if (inLoop(baseAngle, spanCount, &index)) {
4084 continue;
4085 }
4086 } else {
4087 SkDEBUGCODE(int newIndex = index);
4088 SkASSERT(!inLoop(baseAngle, spanCount, &newIndex) && newIndex == index);
4089#if DEBUG_ANGLE
4090 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
4091 index);
4092 wroteAfterHeader = true;
4093#endif
4094 baseAngle->insert(toAngle);
4095 }
4096 }
caryclarkdac1d172014-06-17 05:15:38 -07004097 SkOpAngle* nextFrom, * nextTo;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004098 int firstIndex = index;
4099 do {
4100 SkOpSpan& span = fTs[index];
4101 SkOpSegment* other = span.fOther;
4102 SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
caryclarkdac1d172014-06-17 05:15:38 -07004103 SkOpAngle* oAngle = oSpan.fFromAngle;
4104 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004105#if DEBUG_ANGLE
4106 if (!wroteAfterHeader) {
4107 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
4108 index);
4109 wroteAfterHeader = true;
4110 }
4111#endif
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004112 if (!oAngle->loopContains(*baseAngle)) {
4113 baseAngle->insert(oAngle);
4114 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004115 }
caryclarkdac1d172014-06-17 05:15:38 -07004116 oAngle = oSpan.fToAngle;
4117 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004118#if DEBUG_ANGLE
4119 if (!wroteAfterHeader) {
4120 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
4121 index);
4122 wroteAfterHeader = true;
4123 }
4124#endif
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004125 if (!oAngle->loopContains(*baseAngle)) {
4126 baseAngle->insert(oAngle);
4127 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004128 }
4129 if (++index == spanCount) {
4130 break;
4131 }
caryclarkdac1d172014-06-17 05:15:38 -07004132 nextFrom = fTs[index].fFromAngle;
4133 nextTo = fTs[index].fToAngle;
4134 } while (fromAngle == nextFrom && toAngle == nextTo);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004135 if (baseAngle && baseAngle->loopCount() == 1) {
4136 index = firstIndex;
4137 do {
4138 SkOpSpan& span = fTs[index];
caryclarkdac1d172014-06-17 05:15:38 -07004139 span.fFromAngle = span.fToAngle = NULL;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004140 if (++index == spanCount) {
4141 break;
4142 }
caryclarkdac1d172014-06-17 05:15:38 -07004143 nextFrom = fTs[index].fFromAngle;
4144 nextTo = fTs[index].fToAngle;
4145 } while (fromAngle == nextFrom && toAngle == nextTo);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004146 baseAngle = NULL;
4147 }
4148#if DEBUG_SORT
4149 SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
4150#endif
4151 } while (index < spanCount);
caryclark@google.com570863f2013-09-16 15:55:01 +00004152}
4153
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004154// return true if midpoints were computed
4155bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
4156 SkASSERT(start != end);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004157 edge[0] = fTs[start].fPt;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004158 int points = SkPathOpsVerbToPoints(fVerb);
4159 edge[points] = fTs[end].fPt;
4160 if (fVerb == SkPath::kLine_Verb) {
4161 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004162 }
caryclark@google.comcffbcc32013-06-04 17:59:42 +00004163 double startT = fTs[start].fT;
4164 double endT = fTs[end].fT;
4165 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
4166 // don't compute midpoints if we already have them
4167 if (fVerb == SkPath::kQuad_Verb) {
4168 edge[1] = fPts[1];
4169 return false;
4170 }
4171 SkASSERT(fVerb == SkPath::kCubic_Verb);
4172 if (start < end) {
4173 edge[1] = fPts[1];
4174 edge[2] = fPts[2];
4175 return false;
4176 }
4177 edge[1] = fPts[2];
4178 edge[2] = fPts[1];
4179 return false;
4180 }
4181 const SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[points].fX, edge[points].fY }};
4182 if (fVerb == SkPath::kQuad_Verb) {
4183 edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint();
4184 } else {
4185 SkASSERT(fVerb == SkPath::kCubic_Verb);
4186 SkDPoint ctrl[2];
4187 SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl);
4188 edge[1] = ctrl[0].asSkPoint();
4189 edge[2] = ctrl[1].asSkPoint();
4190 }
4191 return true;
4192}
4193
4194// return true if midpoints were computed
4195bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
4196 SkASSERT(start != end);
4197 (*result)[0].set(fTs[start].fPt);
4198 int points = SkPathOpsVerbToPoints(fVerb);
4199 (*result)[points].set(fTs[end].fPt);
4200 if (fVerb == SkPath::kLine_Verb) {
4201 return false;
4202 }
4203 double startT = fTs[start].fT;
4204 double endT = fTs[end].fT;
4205 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
4206 // don't compute midpoints if we already have them
4207 if (fVerb == SkPath::kQuad_Verb) {
4208 (*result)[1].set(fPts[1]);
4209 return false;
4210 }
4211 SkASSERT(fVerb == SkPath::kCubic_Verb);
4212 if (start < end) {
4213 (*result)[1].set(fPts[1]);
4214 (*result)[2].set(fPts[2]);
4215 return false;
4216 }
4217 (*result)[1].set(fPts[2]);
4218 (*result)[2].set(fPts[1]);
4219 return false;
4220 }
4221 if (fVerb == SkPath::kQuad_Verb) {
4222 (*result)[1] = SkDQuad::SubDivide(fPts, (*result)[0], (*result)[2], startT, endT);
4223 } else {
4224 SkASSERT(fVerb == SkPath::kCubic_Verb);
4225 SkDCubic::SubDivide(fPts, (*result)[0], (*result)[3], startT, endT, &(*result)[1]);
4226 }
4227 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004228}
4229
4230void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
4231 SkPoint edge[4];
4232 subDivide(start, end, edge);
reed@google.com277c3f82013-05-31 15:17:50 +00004233 (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004234}
4235
caryclark@google.com570863f2013-09-16 15:55:01 +00004236void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt,
4237 const SkPoint& startPt) {
4238 int outCount = outsidePts->count();
4239 if (outCount == 0 || endPt != (*outsidePts)[outCount - 2]) {
4240 outsidePts->push_back(endPt);
4241 outsidePts->push_back(startPt);
4242 }
4243}
4244
4245void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoint& startPt) {
4246 int outCount = outsidePts->count();
4247 if (outCount == 0 || startPt != (*outsidePts)[outCount - 1]) {
4248 outsidePts->push_back(startPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004249 }
4250}
4251
4252void SkOpSegment::undoneSpan(int* start, int* end) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00004253 int tCount = fTs.count();
4254 int index;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004255 for (index = 0; index < tCount; ++index) {
4256 if (!fTs[index].fDone) {
4257 break;
4258 }
4259 }
4260 SkASSERT(index < tCount - 1);
4261 *start = index;
4262 double startT = fTs[index].fT;
4263 while (approximately_negative(fTs[++index].fT - startT))
4264 SkASSERT(index < tCount);
4265 SkASSERT(index < tCount);
4266 *end = index;
4267}
4268
4269int SkOpSegment::updateOppWinding(int index, int endIndex) const {
4270 int lesser = SkMin32(index, endIndex);
4271 int oppWinding = oppSum(lesser);
4272 int oppSpanWinding = oppSign(index, endIndex);
4273 if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
4274 && oppWinding != SK_MaxS32) {
4275 oppWinding -= oppSpanWinding;
4276 }
4277 return oppWinding;
4278}
4279
4280int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
4281 int startIndex = angle->start();
4282 int endIndex = angle->end();
4283 return updateOppWinding(endIndex, startIndex);
4284}
4285
4286int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
4287 int startIndex = angle->start();
4288 int endIndex = angle->end();
4289 return updateOppWinding(startIndex, endIndex);
4290}
4291
4292int SkOpSegment::updateWinding(int index, int endIndex) const {
4293 int lesser = SkMin32(index, endIndex);
4294 int winding = windSum(lesser);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00004295 if (winding == SK_MinS32) {
4296 return winding;
4297 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004298 int spanWinding = spanSign(index, endIndex);
caryclark@google.com570863f2013-09-16 15:55:01 +00004299 if (winding && UseInnerWinding(winding - spanWinding, winding)
4300 && winding != SK_MaxS32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00004301 winding -= spanWinding;
4302 }
4303 return winding;
4304}
4305
4306int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
4307 int startIndex = angle->start();
4308 int endIndex = angle->end();
4309 return updateWinding(endIndex, startIndex);
4310}
4311
caryclark@google.com570863f2013-09-16 15:55:01 +00004312int SkOpSegment::updateWindingReverse(int index, int endIndex) const {
4313 int lesser = SkMin32(index, endIndex);
4314 int winding = windSum(lesser);
4315 int spanWinding = spanSign(endIndex, index);
4316 if (winding && UseInnerWindingReverse(winding - spanWinding, winding)
4317 && winding != SK_MaxS32) {
4318 winding -= spanWinding;
4319 }
4320 return winding;
4321}
4322
caryclark@google.com07393ca2013-04-08 11:47:37 +00004323int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
4324 int startIndex = angle->start();
4325 int endIndex = angle->end();
caryclark@google.com570863f2013-09-16 15:55:01 +00004326 return updateWindingReverse(endIndex, startIndex);
4327}
4328
4329// OPTIMIZATION: does the following also work, and is it any faster?
4330// return outerWinding * innerWinding > 0
4331// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
4332bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
4333 SkASSERT(outerWinding != SK_MaxS32);
4334 SkASSERT(innerWinding != SK_MaxS32);
4335 int absOut = abs(outerWinding);
4336 int absIn = abs(innerWinding);
4337 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
4338 return result;
4339}
4340
4341bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) {
4342 SkASSERT(outerWinding != SK_MaxS32);
4343 SkASSERT(innerWinding != SK_MaxS32);
4344 int absOut = abs(outerWinding);
4345 int absIn = abs(innerWinding);
4346 bool result = absOut == absIn ? true : absOut < absIn;
4347 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004348}
4349
4350int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const {
4351 if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
4352 return SK_MinS32;
4353 }
4354 int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
4355 SkASSERT(winding != SK_MinS32);
4356 int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
4357#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07004358 SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
4359 debugID(), crossOpp, tHit, t(tIndex), winding, windVal);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004360#endif
4361 // see if a + change in T results in a +/- change in X (compute x'(T))
reed@google.com277c3f82013-05-31 15:17:50 +00004362 *dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00004363 if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
4364 *dx = fPts[2].fX - fPts[1].fX - *dx;
4365 }
4366 if (*dx == 0) {
4367#if DEBUG_WINDING_AT_T
4368 SkDebugf(" dx=0 winding=SK_MinS32\n");
4369#endif
4370 return SK_MinS32;
4371 }
skia.committer@gmail.coma4aced42013-07-09 07:00:56 +00004372 if (windVal < 0) { // reverse sign if opp contour traveled in reverse
caryclark@google.com07e97fc2013-07-08 17:17:02 +00004373 *dx = -*dx;
4374 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004375 if (winding * *dx > 0) { // if same signs, result is negative
4376 winding += *dx > 0 ? -windVal : windVal;
4377 }
4378#if DEBUG_WINDING_AT_T
4379 SkDebugf(" dx=%c winding=%d\n", *dx > 0 ? '+' : '-', winding);
4380#endif
4381 return winding;
4382}
4383
4384int SkOpSegment::windSum(const SkOpAngle* angle) const {
4385 int start = angle->start();
4386 int end = angle->end();
4387 int index = SkMin32(start, end);
4388 return windSum(index);
4389}
4390
caryclark@google.com07393ca2013-04-08 11:47:37 +00004391void SkOpSegment::zeroSpan(SkOpSpan* span) {
caryclark@google.coma5e55922013-05-07 18:51:31 +00004392 SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
caryclark@google.com07393ca2013-04-08 11:47:37 +00004393 span->fWindValue = 0;
4394 span->fOppValue = 0;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00004395 if (span->fTiny || span->fSmall) {
4396 return;
4397 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00004398 SkASSERT(!span->fDone);
4399 span->fDone = true;
4400 ++fDoneSpans;
4401}