blob: 902f27374438b150739c125ec8e0d24760f442d2 [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 */
caryclarkccec0f92015-03-24 07:28:17 -07007#include "SkOpCoincidence.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"
caryclarkccec0f92015-03-24 07:28:17 -070011
12/*
13After computing raw intersections, post process all segments to:
14- find small collections of points that can be collapsed to a single point
15- find missing intersections to resolve differences caused by different algorithms
16
17Consider segments containing tiny or small intervals. Consider coincident segments
18because coincidence finds intersections through distance measurement that non-coincident
19intersection tests cannot.
20 */
caryclark@google.com07393ca2013-04-08 11:47:37 +000021
22#define F (false) // discard the edge
23#define T (true) // keep the edge
24
25static const bool gUnaryActiveEdge[2][2] = {
26// from=0 from=1
27// to=0,1 to=0,1
28 {F, T}, {T, F},
29};
30
caryclark@google.com07393ca2013-04-08 11:47:37 +000031static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
32// miFrom=0 miFrom=1
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000033// miTo=0 miTo=1 miTo=0 miTo=1
34// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
caryclark@google.com07393ca2013-04-08 11:47:37 +000035// suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1
36 {{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su
37 {{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su
38 {{{{F, T}, {T, F}}, {{T, T}, {F, F}}}, {{{T, F}, {T, F}}, {{F, F}, {F, F}}}}, // mi | su
39 {{{{F, T}, {T, F}}, {{T, F}, {F, T}}}, {{{T, F}, {F, T}}, {{F, T}, {T, F}}}}, // mi ^ su
40};
41
42#undef F
43#undef T
44
caryclarkccec0f92015-03-24 07:28:17 -070045SkOpAngle* SkOpSegment::activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr,
46 SkOpSpanBase** endPtr, bool* done, bool* sortable) {
47 if (SkOpAngle* result = activeAngleInner(start, startPtr, endPtr, done, sortable)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000048 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +000049 }
caryclarkccec0f92015-03-24 07:28:17 -070050 if (SkOpAngle* result = activeAngleOther(start, startPtr, endPtr, done, sortable)) {
51 return result;
caryclark@google.com07393ca2013-04-08 11:47:37 +000052 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000053 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +000054}
55
caryclarkccec0f92015-03-24 07:28:17 -070056SkOpAngle* SkOpSegment::activeAngleInner(SkOpSpanBase* start, SkOpSpanBase** startPtr,
57 SkOpSpanBase** endPtr, bool* done, bool* sortable) {
58 SkOpSpan* upSpan = start->upCastable();
59 if (upSpan) {
60 if (upSpan->windValue() || upSpan->oppValue()) {
61 SkOpSpanBase* next = upSpan->next();
62 if (!*endPtr) {
63 *startPtr = start;
64 *endPtr = next;
caryclark@google.com07393ca2013-04-08 11:47:37 +000065 }
caryclarkccec0f92015-03-24 07:28:17 -070066 if (!upSpan->done()) {
67 if (upSpan->windSum() != SK_MinS32) {
68 return spanToAngle(start, next);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000069 }
70 *done = false;
71 }
72 } else {
caryclarkccec0f92015-03-24 07:28:17 -070073 SkASSERT(upSpan->done());
caryclark@google.com07393ca2013-04-08 11:47:37 +000074 }
75 }
caryclarkccec0f92015-03-24 07:28:17 -070076 SkOpSpan* downSpan = start->prev();
caryclark@google.com07393ca2013-04-08 11:47:37 +000077 // edge leading into junction
caryclarkccec0f92015-03-24 07:28:17 -070078 if (downSpan) {
79 if (downSpan->windValue() || downSpan->oppValue()) {
80 if (!*endPtr) {
81 *startPtr = start;
82 *endPtr = downSpan;
caryclark@google.com07393ca2013-04-08 11:47:37 +000083 }
caryclarkccec0f92015-03-24 07:28:17 -070084 if (!downSpan->done()) {
85 if (downSpan->windSum() != SK_MinS32) {
86 return spanToAngle(start, downSpan);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000087 }
88 *done = false;
89 }
90 } else {
caryclarkccec0f92015-03-24 07:28:17 -070091 SkASSERT(downSpan->done());
caryclark@google.com07393ca2013-04-08 11:47:37 +000092 }
93 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000094 return NULL;
95}
96
caryclarkccec0f92015-03-24 07:28:17 -070097SkOpAngle* SkOpSegment::activeAngleOther(SkOpSpanBase* start, SkOpSpanBase** startPtr,
98 SkOpSpanBase** endPtr, bool* done, bool* sortable) {
99 SkOpPtT* oPtT = start->ptT()->next();
100 SkOpSegment* other = oPtT->segment();
101 SkOpSpanBase* oSpan = oPtT->span();
102 return other->activeAngleInner(oSpan, startPtr, endPtr, done, sortable);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000103}
104
caryclarkccec0f92015-03-24 07:28:17 -0700105SkPoint SkOpSegment::activeLeftTop(SkOpSpanBase** firstSpan) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000106 SkASSERT(!done());
107 SkPoint topPt = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.com07393ca2013-04-08 11:47:37 +0000108 // see if either end is not done since we want smaller Y of the pair
109 bool lastDone = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000110 double lastT = -1;
caryclarkccec0f92015-03-24 07:28:17 -0700111 SkOpSpanBase* span = &fHead;
112 do {
113 if (lastDone && (span->final() || span->upCast()->done())) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000114 goto next;
115 }
116 {
caryclarkccec0f92015-03-24 07:28:17 -0700117 const SkPoint& xy = span->pt();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000118 if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
119 topPt = xy;
caryclarkccec0f92015-03-24 07:28:17 -0700120 if (firstSpan) {
121 *firstSpan = span;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000122 }
123 }
124 if (fVerb != SkPath::kLine_Verb && !lastDone) {
caryclarkccec0f92015-03-24 07:28:17 -0700125 SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT,
126 span->t());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000127 if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
128 && topPt.fX > curveTop.fX)) {
129 topPt = curveTop;
caryclarkccec0f92015-03-24 07:28:17 -0700130 if (firstSpan) {
131 *firstSpan = span;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000132 }
133 }
134 }
caryclarkccec0f92015-03-24 07:28:17 -0700135 lastT = span->t();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000136 }
137next:
caryclarkccec0f92015-03-24 07:28:17 -0700138 if (span->final()) {
139 break;
140 }
141 lastDone = span->upCast()->done();
142 } while ((span = span->upCast()->next()));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000143 return topPt;
144}
145
caryclarkccec0f92015-03-24 07:28:17 -0700146bool SkOpSegment::activeOp(SkOpSpanBase* start, SkOpSpanBase* end, int xorMiMask, int xorSuMask,
147 SkPathOp op) {
148 int sumMiWinding = this->updateWinding(end, start);
149 int sumSuWinding = this->updateOppWinding(end, start);
caryclark65f55312014-11-13 06:58:52 -0800150#if DEBUG_LIMIT_WIND_SUM
151 SkASSERT(abs(sumMiWinding) <= DEBUG_LIMIT_WIND_SUM);
152 SkASSERT(abs(sumSuWinding) <= DEBUG_LIMIT_WIND_SUM);
153#endif
caryclarkccec0f92015-03-24 07:28:17 -0700154 if (this->operand()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000155 SkTSwap<int>(sumMiWinding, sumSuWinding);
156 }
caryclarkccec0f92015-03-24 07:28:17 -0700157 return this->activeOp(xorMiMask, xorSuMask, start, end, op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000158}
159
caryclarkccec0f92015-03-24 07:28:17 -0700160bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end,
161 SkPathOp op, int* sumMiWinding, int* sumSuWinding) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000162 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
caryclarkccec0f92015-03-24 07:28:17 -0700163 this->setUpWindings(start, end, sumMiWinding, sumSuWinding,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000164 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000165 bool miFrom;
166 bool miTo;
167 bool suFrom;
168 bool suTo;
169 if (operand()) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000170 miFrom = (oppMaxWinding & xorMiMask) != 0;
171 miTo = (oppSumWinding & xorMiMask) != 0;
172 suFrom = (maxWinding & xorSuMask) != 0;
173 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000174 } else {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000175 miFrom = (maxWinding & xorMiMask) != 0;
176 miTo = (sumWinding & xorMiMask) != 0;
177 suFrom = (oppMaxWinding & xorSuMask) != 0;
178 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000179 }
180 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
181#if DEBUG_ACTIVE_OP
caryclarkdac1d172014-06-17 05:15:38 -0700182 SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n",
caryclarkccec0f92015-03-24 07:28:17 -0700183 __FUNCTION__, debugID(), start->t(), end->t(),
caryclark@google.com570863f2013-09-16 15:55:01 +0000184 SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000185#endif
186 return result;
187}
188
caryclarkccec0f92015-03-24 07:28:17 -0700189bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end) {
190 int sumWinding = updateWinding(end, start);
191 return activeWinding(start, end, &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000192}
193
caryclarkccec0f92015-03-24 07:28:17 -0700194bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000195 int maxWinding;
caryclarkccec0f92015-03-24 07:28:17 -0700196 setUpWinding(start, end, &maxWinding, sumWinding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000197 bool from = maxWinding != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000198 bool to = *sumWinding != 0;
199 bool result = gUnaryActiveEdge[from][to];
200 return result;
201}
202
caryclarkccec0f92015-03-24 07:28:17 -0700203void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
204 SkPathWriter* path, bool active) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000205 SkPoint edge[4];
206 const SkPoint* ePtr;
caryclarkccec0f92015-03-24 07:28:17 -0700207 if ((start == &fHead && end == &fTail) || (start == &fTail && end == &fHead)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000208 ePtr = fPts;
209 } else {
210 // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
211 subDivide(start, end, edge);
212 ePtr = edge;
213 }
214 if (active) {
caryclarkccec0f92015-03-24 07:28:17 -0700215 bool reverse = ePtr == fPts && start != &fHead;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000216 if (reverse) {
reed@google.com277c3f82013-05-31 15:17:50 +0000217 path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000218 switch (fVerb) {
219 case SkPath::kLine_Verb:
220 path->deferredLine(ePtr[0]);
221 break;
222 case SkPath::kQuad_Verb:
223 path->quadTo(ePtr[1], ePtr[0]);
224 break;
225 case SkPath::kCubic_Verb:
226 path->cubicTo(ePtr[2], ePtr[1], ePtr[0]);
227 break;
228 default:
229 SkASSERT(0);
230 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000231 } else {
232 path->deferredMoveLine(ePtr[0]);
233 switch (fVerb) {
234 case SkPath::kLine_Verb:
235 path->deferredLine(ePtr[1]);
236 break;
237 case SkPath::kQuad_Verb:
238 path->quadTo(ePtr[1], ePtr[2]);
239 break;
240 case SkPath::kCubic_Verb:
241 path->cubicTo(ePtr[1], ePtr[2], ePtr[3]);
242 break;
243 default:
244 SkASSERT(0);
245 }
246 }
247 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000248}
249
caryclarkccec0f92015-03-24 07:28:17 -0700250SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) {
251 SkOpSpanBase* existing = NULL;
252 SkOpSpanBase* test = &fHead;
253 double testT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000254 do {
caryclarkccec0f92015-03-24 07:28:17 -0700255 if ((testT = test->ptT()->fT) >= t) {
256 if (testT == t) {
257 existing = test;
258 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000259 break;
260 }
caryclarkccec0f92015-03-24 07:28:17 -0700261 } while ((test = test->upCast()->next()));
262 SkOpPtT* result;
263 if (existing && existing->contains(opp)) {
264 result = existing->ptT();
265 } else {
266 result = this->addT(t, SkOpSegment::kNoAlias, allocator);
267 }
268 SkASSERT(result);
269 return result;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000270}
271
caryclarkccec0f92015-03-24 07:28:17 -0700272SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr,
273 SkChunkAlloc* allocator) {
274 SkOpSpan* startSpan = fTail.prev();
275 SkASSERT(startSpan);
276 SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
277 *anglePtr = angle;
278 angle->set(&fTail, startSpan);
279 fTail.setFromAngle(angle);
280 SkOpSegment* other = NULL; // these initializations silence a release build warning
281 SkOpSpan* oStartSpan = NULL;
282 SkOpSpanBase* oEndSpan = NULL;
283 SkOpPtT* ptT = fTail.ptT(), * startPtT = ptT;
284 while ((ptT = ptT->next()) != startPtT) {
285 other = ptT->segment();
286 oStartSpan = ptT->span()->upCastable();
287 if (oStartSpan && oStartSpan->windValue()) {
288 oEndSpan = oStartSpan->next();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000289 break;
290 }
caryclarkccec0f92015-03-24 07:28:17 -0700291 oEndSpan = ptT->span();
292 oStartSpan = oEndSpan->prev();
293 if (oStartSpan && oStartSpan->windValue()) {
294 break;
295 }
296 }
297 SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
298 oAngle->set(oStartSpan, oEndSpan);
299 oStartSpan->setToAngle(oAngle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000300 *otherPtr = other;
caryclarkccec0f92015-03-24 07:28:17 -0700301 return oAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000302}
303
caryclarkccec0f92015-03-24 07:28:17 -0700304SkOpAngle* SkOpSegment::addSingletonAngles(int step, SkChunkAlloc* allocator) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000305 SkOpSegment* other;
caryclarkdac1d172014-06-17 05:15:38 -0700306 SkOpAngle* angle, * otherAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000307 if (step > 0) {
caryclarkccec0f92015-03-24 07:28:17 -0700308 otherAngle = addSingletonAngleUp(&other, &angle, allocator);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000309 } else {
caryclarkccec0f92015-03-24 07:28:17 -0700310 otherAngle = addSingletonAngleDown(&other, &angle, allocator);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000311 }
caryclarkdac1d172014-06-17 05:15:38 -0700312 angle->insert(otherAngle);
313 return angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000314}
315
caryclarkccec0f92015-03-24 07:28:17 -0700316SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr,
317 SkChunkAlloc* allocator) {
318 SkOpSpanBase* endSpan = fHead.next();
319 SkASSERT(endSpan);
320 SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
321 *anglePtr = angle;
322 angle->set(&fHead, endSpan);
323 fHead.setToAngle(angle);
324 SkOpSegment* other = NULL; // these initializations silence a release build warning
325 SkOpSpan* oStartSpan = NULL;
326 SkOpSpanBase* oEndSpan = NULL;
327 SkOpPtT* ptT = fHead.ptT(), * startPtT = ptT;
328 while ((ptT = ptT->next()) != startPtT) {
329 other = ptT->segment();
330 oEndSpan = ptT->span();
331 oStartSpan = oEndSpan->prev();
332 if (oStartSpan && oStartSpan->windValue()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000333 break;
334 }
caryclarkccec0f92015-03-24 07:28:17 -0700335 oStartSpan = oEndSpan->upCastable();
336 if (oStartSpan && oStartSpan->windValue()) {
337 oEndSpan = oStartSpan->next();
caryclark19eb3b22014-07-18 05:08:14 -0700338 break;
339 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000340 }
caryclarkccec0f92015-03-24 07:28:17 -0700341 SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
342 oAngle->set(oEndSpan, oStartSpan);
343 oEndSpan->setFromAngle(oAngle);
344 *otherPtr = other;
345 return oAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000346}
347
caryclarkccec0f92015-03-24 07:28:17 -0700348SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
caryclarkdac1d172014-06-17 05:15:38 -0700349 debugValidate();
caryclarkccec0f92015-03-24 07:28:17 -0700350 SkPoint pt = this->ptAtT(t);
351 SkOpSpanBase* span = &fHead;
352 do {
353 SkOpPtT* result = span->ptT();
354 if (t == result->fT) {
355 return result;
caryclarkdac1d172014-06-17 05:15:38 -0700356 }
caryclarkccec0f92015-03-24 07:28:17 -0700357 if (this->match(result, this, t, pt)) {
358 // see if any existing alias matches segment, pt, and t
359 SkOpPtT* loop = result->next();
360 bool duplicatePt = false;
361 while (loop != result) {
362 bool ptMatch = loop->fPt == pt;
363 if (loop->segment() == this && loop->fT == t && ptMatch) {
364 return result;
caryclarkdac1d172014-06-17 05:15:38 -0700365 }
caryclarkccec0f92015-03-24 07:28:17 -0700366 duplicatePt |= ptMatch;
367 loop = loop->next();
caryclarkdac1d172014-06-17 05:15:38 -0700368 }
caryclarkccec0f92015-03-24 07:28:17 -0700369 if (kNoAlias == allowAlias) {
370 return result;
371 }
372 SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator);
373 alias->init(result->span(), t, pt, duplicatePt);
374 result->insert(alias);
375 result->span()->unaligned();
376 this->debugValidate();
377#if DEBUG_ADD_T
378 SkDebugf("%s alias t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
379 alias->segment()->debugID(), alias->span()->debugID());
380#endif
381 return alias;
caryclarkdac1d172014-06-17 05:15:38 -0700382 }
caryclarkccec0f92015-03-24 07:28:17 -0700383 if (t < result->fT) {
384 SkOpSpan* prev = result->span()->prev();
385 SkOpSpan* span = insert(prev, allocator);
386 span->init(this, prev, t, pt);
387 this->debugValidate();
388#if DEBUG_ADD_T
389 SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
390 span->segment()->debugID(), span->debugID());
391#endif
392 return span->ptT();
393 }
394 SkASSERT(span != &fTail);
395 } while ((span = span->upCast()->next()));
396 SkASSERT(0);
397 return NULL;
398}
399
400// choose a solitary t and pt value; remove aliases; align the opposite ends
401void SkOpSegment::align() {
402 debugValidate();
403 SkOpSpanBase* span = &fHead;
404 if (!span->aligned()) {
405 span->alignEnd(0, fPts[0]);
406 }
407 while ((span = span->upCast()->next())) {
408 if (span == &fTail) {
409 break;
410 }
411 span->align();
412 }
413 if (!span->aligned()) {
414 span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
caryclarkdac1d172014-06-17 05:15:38 -0700415 }
416 debugValidate();
417}
418
caryclarkccec0f92015-03-24 07:28:17 -0700419bool SkOpSegment::BetweenTs(const SkOpSpanBase* lesser, double testT,
420 const SkOpSpanBase* greater) {
421 if (lesser->t() > greater->t()) {
422 SkTSwap<const SkOpSpanBase*>(lesser, greater);
caryclark65f55312014-11-13 06:58:52 -0800423 }
caryclarkccec0f92015-03-24 07:28:17 -0700424 return approximately_between(lesser->t(), testT, greater->t());
caryclark65f55312014-11-13 06:58:52 -0800425}
426
caryclarkccec0f92015-03-24 07:28:17 -0700427void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
428 bool activePrior = !fHead.isCanceled();
429 if (activePrior && !fHead.simple()) {
430 addStartSpan(allocator);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000431 }
caryclarkccec0f92015-03-24 07:28:17 -0700432 SkOpSpan* prior = &fHead;
433 SkOpSpanBase* spanBase = fHead.next();
434 while (spanBase != &fTail) {
435 if (activePrior) {
436 SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
437 priorAngle->set(spanBase, prior);
438 spanBase->setFromAngle(priorAngle);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000439 }
caryclarkccec0f92015-03-24 07:28:17 -0700440 SkOpSpan* span = spanBase->upCast();
441 bool active = !span->isCanceled();
442 SkOpSpanBase* next = span->next();
443 if (active) {
444 SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
445 angle->set(span, next);
446 span->setToAngle(angle);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000447 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000448 activePrior = active;
caryclarkccec0f92015-03-24 07:28:17 -0700449 prior = span;
450 spanBase = next;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000451 }
caryclarkccec0f92015-03-24 07:28:17 -0700452 if (activePrior && !fTail.simple()) {
453 addEndSpan(allocator);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000454 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000455}
456
caryclarkccec0f92015-03-24 07:28:17 -0700457void SkOpSegment::checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
458 SkOpSpanBase* base = &fHead;
459 SkOpSpan* span;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000460 do {
caryclarkccec0f92015-03-24 07:28:17 -0700461 SkOpAngle* angle = base->fromAngle();
462 if (angle && angle->fCheckCoincidence) {
463 angle->checkNearCoincidence();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000464 }
caryclarkccec0f92015-03-24 07:28:17 -0700465 if (base->final()) {
466 break;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000467 }
caryclarkccec0f92015-03-24 07:28:17 -0700468 span = base->upCast();
469 angle = span->toAngle();
470 if (angle && angle->fCheckCoincidence) {
471 angle->checkNearCoincidence();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000472 }
caryclarkccec0f92015-03-24 07:28:17 -0700473 } while ((base = span->next()));
474}
475
476// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
477bool SkOpSegment::clockwise(const SkOpSpanBase* start, const SkOpSpanBase* end, bool* swap) const {
478 SkASSERT(fVerb != SkPath::kLine_Verb);
479 SkPoint edge[4];
480 if (fVerb == SkPath::kCubic_Verb) {
481 double startT = start->t();
482 double endT = end->t();
483 bool flip = startT > endT;
484 SkDCubic cubic;
485 cubic.set(fPts);
486 double inflectionTs[2];
487 int inflections = cubic.findInflections(inflectionTs);
488 for (int index = 0; index < inflections; ++index) {
489 double inflectionT = inflectionTs[index];
490 if (between(startT, inflectionT, endT)) {
491 if (flip) {
492 if (inflectionT != endT) {
493 startT = inflectionT;
494 }
495 } else {
496 if (inflectionT != startT) {
497 endT = inflectionT;
498 }
499 }
500 }
501 }
502 SkDCubic part = cubic.subDivide(startT, endT);
503 for (int index = 0; index < 4; ++index) {
504 edge[index] = part[index].asSkPoint();
505 }
506 } else {
507 subDivide(start, end, edge);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000508 }
caryclarkccec0f92015-03-24 07:28:17 -0700509 bool sumSet = false;
510 int points = SkPathOpsVerbToPoints(fVerb);
511 double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
512 if (!sumSet) {
513 for (int idx = 0; idx < points; ++idx){
514 sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
515 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000516 }
caryclarkccec0f92015-03-24 07:28:17 -0700517 if (fVerb == SkPath::kCubic_Verb) {
518 SkDCubic cubic;
519 cubic.set(edge);
520 *swap = sum > 0 && !cubic.monotonicInY();
521 } else {
522 SkDQuad quad;
523 quad.set(edge);
524 *swap = sum > 0 && !quad.monotonicInY();
525 }
526 return sum <= 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000527}
528
caryclark@google.com570863f2013-09-16 15:55:01 +0000529void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
530 SkOpAngle::IncludeType includeType) {
531 const SkOpSegment* baseSegment = baseAngle->segment();
532 int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
533 int sumSuWinding;
534 bool binary = includeType >= SkOpAngle::kBinarySingle;
535 if (binary) {
536 sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
537 if (baseSegment->operand()) {
538 SkTSwap<int>(sumMiWinding, sumSuWinding);
539 }
540 }
541 SkOpSegment* nextSegment = nextAngle->segment();
542 int maxWinding, sumWinding;
caryclarkccec0f92015-03-24 07:28:17 -0700543 SkOpSpanBase* last;
caryclark@google.com570863f2013-09-16 15:55:01 +0000544 if (binary) {
545 int oppMaxWinding, oppSumWinding;
546 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
547 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
548 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000549 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +0000550 } else {
551 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
552 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000553 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +0000554 }
555 nextAngle->setLastMarked(last);
556}
557
558void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
559 SkOpAngle::IncludeType includeType) {
560 const SkOpSegment* baseSegment = baseAngle->segment();
561 int sumMiWinding = baseSegment->updateWinding(baseAngle);
562 int sumSuWinding;
563 bool binary = includeType >= SkOpAngle::kBinarySingle;
564 if (binary) {
565 sumSuWinding = baseSegment->updateOppWinding(baseAngle);
566 if (baseSegment->operand()) {
567 SkTSwap<int>(sumMiWinding, sumSuWinding);
568 }
569 }
570 SkOpSegment* nextSegment = nextAngle->segment();
571 int maxWinding, sumWinding;
caryclarkccec0f92015-03-24 07:28:17 -0700572 SkOpSpanBase* last;
caryclark@google.com570863f2013-09-16 15:55:01 +0000573 if (binary) {
574 int oppMaxWinding, oppSumWinding;
575 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
576 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
577 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000578 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +0000579 } else {
580 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
581 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000582 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +0000583 }
584 nextAngle->setLastMarked(last);
585}
586
caryclarkccec0f92015-03-24 07:28:17 -0700587// at this point, the span is already ordered, or unorderable
588int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
589 SkOpAngle::IncludeType includeType) {
590 SkASSERT(includeType != SkOpAngle::kUnaryXor);
591 SkOpAngle* firstAngle = this->spanToAngle(end, start);
592 if (NULL == firstAngle || NULL == firstAngle->next()) {
593 return SK_NaN32;
594 }
595 // if all angles have a computed winding,
596 // or if no adjacent angles are orderable,
597 // or if adjacent orderable angles have no computed winding,
598 // there's nothing to do
599 // if two orderable angles are adjacent, and both are next to orderable angles,
600 // and one has winding computed, transfer to the other
601 SkOpAngle* baseAngle = NULL;
602 bool tryReverse = false;
603 // look for counterclockwise transfers
604 SkOpAngle* angle = firstAngle->previous();
605 SkOpAngle* next = angle->next();
606 firstAngle = next;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000607 do {
caryclarkccec0f92015-03-24 07:28:17 -0700608 SkOpAngle* prior = angle;
609 angle = next;
610 next = angle->next();
611 SkASSERT(prior->next() == angle);
612 SkASSERT(angle->next() == next);
613 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
614 baseAngle = NULL;
615 continue;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000616 }
caryclarkccec0f92015-03-24 07:28:17 -0700617 int testWinding = angle->starter()->windSum();
618 if (SK_MinS32 != testWinding) {
619 baseAngle = angle;
620 tryReverse = true;
621 continue;
caryclarkdac1d172014-06-17 05:15:38 -0700622 }
caryclarkccec0f92015-03-24 07:28:17 -0700623 if (baseAngle) {
624 ComputeOneSum(baseAngle, angle, includeType);
625 baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
626 }
627 } while (next != firstAngle);
628 if (baseAngle && SK_MinS32 == firstAngle->starter()->windSum()) {
629 firstAngle = baseAngle;
630 tryReverse = true;
631 }
632 if (tryReverse) {
633 baseAngle = NULL;
634 SkOpAngle* prior = firstAngle;
635 do {
636 angle = prior;
637 prior = angle->previous();
638 SkASSERT(prior->next() == angle);
639 next = angle->next();
640 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
641 baseAngle = NULL;
caryclarkdac1d172014-06-17 05:15:38 -0700642 continue;
643 }
caryclarkccec0f92015-03-24 07:28:17 -0700644 int testWinding = angle->starter()->windSum();
645 if (SK_MinS32 != testWinding) {
646 baseAngle = angle;
647 continue;
caryclarkdac1d172014-06-17 05:15:38 -0700648 }
caryclarkccec0f92015-03-24 07:28:17 -0700649 if (baseAngle) {
650 ComputeOneSumReverse(baseAngle, angle, includeType);
651 baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
caryclarkdac1d172014-06-17 05:15:38 -0700652 }
caryclarkccec0f92015-03-24 07:28:17 -0700653 } while (prior != firstAngle);
caryclarkdac1d172014-06-17 05:15:38 -0700654 }
caryclarkccec0f92015-03-24 07:28:17 -0700655 return start->starter(end)->windSum();
caryclarkdac1d172014-06-17 05:15:38 -0700656}
657
caryclarkccec0f92015-03-24 07:28:17 -0700658SkOpSpan* SkOpSegment::crossedSpanY(const SkPoint& basePt, double mid, bool opp, bool current,
659 SkScalar* bestY, double* hitT, bool* hitSomething, bool* vertical) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000660 SkScalar bottom = fBounds.fBottom;
caryclarkccec0f92015-03-24 07:28:17 -0700661 *vertical = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000662 if (bottom <= *bestY) {
caryclarkccec0f92015-03-24 07:28:17 -0700663 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000664 }
665 SkScalar top = fBounds.fTop;
666 if (top >= basePt.fY) {
caryclarkccec0f92015-03-24 07:28:17 -0700667 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000668 }
669 if (fBounds.fLeft > basePt.fX) {
caryclarkccec0f92015-03-24 07:28:17 -0700670 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000671 }
672 if (fBounds.fRight < basePt.fX) {
caryclarkccec0f92015-03-24 07:28:17 -0700673 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000674 }
675 if (fBounds.fLeft == fBounds.fRight) {
676 // if vertical, and directly above test point, wait for another one
caryclarkccec0f92015-03-24 07:28:17 -0700677 *vertical = AlmostEqualUlps(basePt.fX, fBounds.fLeft);
678 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000679 }
680 // intersect ray starting at basePt with edge
681 SkIntersections intersections;
682 // OPTIMIZE: use specialty function that intersects ray with curve,
683 // returning t values only for curve (we don't care about t on ray)
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000684 intersections.allowNear(false);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000685 int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
686 (fPts, top, bottom, basePt.fX, false);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000687 if (pts == 0 || (current && pts == 1)) {
caryclarkccec0f92015-03-24 07:28:17 -0700688 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000689 }
690 if (current) {
691 SkASSERT(pts > 1);
692 int closestIdx = 0;
693 double closest = fabs(intersections[0][0] - mid);
694 for (int idx = 1; idx < pts; ++idx) {
695 double test = fabs(intersections[0][idx] - mid);
696 if (closest > test) {
697 closestIdx = idx;
698 closest = test;
699 }
700 }
701 intersections.quickRemoveOne(closestIdx, --pts);
702 }
703 double bestT = -1;
704 for (int index = 0; index < pts; ++index) {
705 double foundT = intersections[0][index];
706 if (approximately_less_than_zero(foundT)
707 || approximately_greater_than_one(foundT)) {
708 continue;
709 }
reed@google.com277c3f82013-05-31 15:17:50 +0000710 SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000711 if (approximately_negative(testY - *bestY)
712 || approximately_negative(basePt.fY - testY)) {
713 continue;
714 }
715 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
caryclarkccec0f92015-03-24 07:28:17 -0700716 *vertical = true;
717 return NULL; // if the intersection is edge on, wait for another one
caryclark@google.com07393ca2013-04-08 11:47:37 +0000718 }
719 if (fVerb > SkPath::kLine_Verb) {
reed@google.com277c3f82013-05-31 15:17:50 +0000720 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000721 if (approximately_zero(dx)) {
caryclarkccec0f92015-03-24 07:28:17 -0700722 *vertical = true;
723 return NULL; // hit vertical, wait for another one
caryclark@google.com07393ca2013-04-08 11:47:37 +0000724 }
725 }
726 *bestY = testY;
727 bestT = foundT;
728 }
729 if (bestT < 0) {
caryclarkccec0f92015-03-24 07:28:17 -0700730 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000731 }
732 SkASSERT(bestT >= 0);
caryclarkccec0f92015-03-24 07:28:17 -0700733 SkASSERT(bestT < 1);
734 SkOpSpanBase* testTSpanBase = &this->fHead;
735 SkOpSpanBase* nextTSpan;
736 double endT = 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000737 do {
caryclarkccec0f92015-03-24 07:28:17 -0700738 nextTSpan = testTSpanBase->upCast()->next();
739 endT = nextTSpan->t();
740 if (endT >= bestT) {
741 break;
742 }
743 testTSpanBase = nextTSpan;
744 } while (testTSpanBase);
745 SkOpSpan* bestTSpan = NULL;
746 SkOpSpan* testTSpan = testTSpanBase->upCast();
747 if (!testTSpan->isCanceled()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000748 *hitT = bestT;
caryclarkccec0f92015-03-24 07:28:17 -0700749 bestTSpan = testTSpan;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000750 *hitSomething = true;
751 }
caryclarkccec0f92015-03-24 07:28:17 -0700752 return bestTSpan;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000753}
754
caryclarkccec0f92015-03-24 07:28:17 -0700755void SkOpSegment::detach(const SkOpSpan* span) {
756 if (span->done()) {
757 --this->fDoneCount;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000758 }
caryclarkccec0f92015-03-24 07:28:17 -0700759 --this->fCount;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000760}
761
caryclarkccec0f92015-03-24 07:28:17 -0700762double SkOpSegment::distSq(double t, SkOpAngle* oppAngle) {
763 SkDPoint testPt = this->dPtAtT(t);
764 SkDLine testPerp = {{ testPt, testPt }};
765 SkDVector slope = this->dSlopeAtT(t);
766 testPerp[1].fX += slope.fY;
767 testPerp[1].fY -= slope.fX;
768 SkIntersections i;
769 SkOpSegment* oppSegment = oppAngle->segment();
770 int oppPtCount = SkPathOpsVerbToPoints(oppSegment->verb());
771 (*CurveIntersectRay[oppPtCount])(oppSegment->pts(), testPerp, &i);
772 double closestDistSq = SK_ScalarInfinity;
773 for (int index = 0; index < i.used(); ++index) {
774 if (!between(oppAngle->start()->t(), i[0][index], oppAngle->end()->t())) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000775 continue;
776 }
caryclarkccec0f92015-03-24 07:28:17 -0700777 double testDistSq = testPt.distanceSquared(i.pt(index));
778 if (closestDistSq > testDistSq) {
779 closestDistSq = testDistSq;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000780 }
781 }
caryclarkccec0f92015-03-24 07:28:17 -0700782 return closestDistSq;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000783}
784
caryclark@google.com07393ca2013-04-08 11:47:37 +0000785/*
786 The M and S variable name parts stand for the operators.
787 Mi stands for Minuend (see wiki subtraction, analogous to difference)
788 Su stands for Subtrahend
789 The Opp variable name part designates that the value is for the Opposite operator.
790 Opposite values result from combining coincident spans.
791 */
caryclarkccec0f92015-03-24 07:28:17 -0700792SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
793 SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op, int xorMiMask, int xorSuMask) {
794 SkOpSpanBase* start = *nextStart;
795 SkOpSpanBase* end = *nextEnd;
796 SkASSERT(start != end);
797 int step = start->step(end);
798 SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
799 if (other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000800 // mark the smaller of startIndex, endIndex done, and all adjacent
801 // spans with the same T value (but not 'other' spans)
802#if DEBUG_WINDING
803 SkDebugf("%s simple\n", __FUNCTION__);
804#endif
caryclarkccec0f92015-03-24 07:28:17 -0700805 SkOpSpan* startSpan = start->starter(end);
806 if (startSpan->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000807 return NULL;
808 }
caryclarkccec0f92015-03-24 07:28:17 -0700809 markDone(startSpan);
810 *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000811 return other;
812 }
caryclarkccec0f92015-03-24 07:28:17 -0700813 SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
814 SkASSERT(endNear == end); // is this ever not end?
815 SkASSERT(endNear);
816 SkASSERT(start != endNear);
817 SkASSERT((start->t() < endNear->t()) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000818 // more than one viable candidate -- measure angles to find best
caryclarkccec0f92015-03-24 07:28:17 -0700819 int calcWinding = computeSum(start, endNear, SkOpAngle::kBinaryOpp);
caryclark@google.com570863f2013-09-16 15:55:01 +0000820 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000821 if (!sortable) {
822 *unsortable = true;
caryclarkccec0f92015-03-24 07:28:17 -0700823 markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000824 return NULL;
825 }
caryclarkccec0f92015-03-24 07:28:17 -0700826 SkOpAngle* angle = this->spanToAngle(end, start);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000827 if (angle->unorderable()) {
828 *unsortable = true;
caryclarkccec0f92015-03-24 07:28:17 -0700829 markDone(start->starter(end));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000830 return NULL;
831 }
832#if DEBUG_SORT
833 SkDebugf("%s\n", __FUNCTION__);
834 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000835#endif
caryclarkccec0f92015-03-24 07:28:17 -0700836 int sumMiWinding = updateWinding(end, start);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000837 if (sumMiWinding == SK_MinS32) {
838 *unsortable = true;
caryclarkccec0f92015-03-24 07:28:17 -0700839 markDone(start->starter(end));
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000840 return NULL;
841 }
caryclarkccec0f92015-03-24 07:28:17 -0700842 int sumSuWinding = updateOppWinding(end, start);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000843 if (operand()) {
844 SkTSwap<int>(sumMiWinding, sumSuWinding);
845 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000846 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000847 const SkOpAngle* foundAngle = NULL;
848 bool foundDone = false;
849 // iterate through the angle, and compute everyone's winding
850 SkOpSegment* nextSegment;
851 int activeCount = 0;
852 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000853 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000854 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000855 nextAngle->end(), op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000856 if (activeAngle) {
857 ++activeCount;
858 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000859 foundAngle = nextAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +0000860 foundDone = nextSegment->done(nextAngle);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000861 }
862 }
863 if (nextSegment->done()) {
864 continue;
865 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000866 if (!activeAngle) {
caryclarkccec0f92015-03-24 07:28:17 -0700867 (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
caryclark@google.com570863f2013-09-16 15:55:01 +0000868 }
caryclarkccec0f92015-03-24 07:28:17 -0700869 SkOpSpanBase* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000870 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000871 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000872 *chase->append() = last;
873#if DEBUG_WINDING
caryclarkccec0f92015-03-24 07:28:17 -0700874 SkDebugf("%s chase.append segment=%d span=%d", __FUNCTION__,
875 last->segment()->debugID(), last->debugID());
876 if (!last->final()) {
877 SkDebugf(" windSum=%d", last->upCast()->windSum());
878 }
879 SkDebugf("\n");
caryclark@google.com07393ca2013-04-08 11:47:37 +0000880#endif
881 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000882 } while ((nextAngle = nextAngle->next()) != angle);
caryclarkccec0f92015-03-24 07:28:17 -0700883 start->segment()->markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000884 if (!foundAngle) {
885 return NULL;
886 }
887 *nextStart = foundAngle->start();
888 *nextEnd = foundAngle->end();
889 nextSegment = foundAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000890#if DEBUG_WINDING
891 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
892 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
893 #endif
894 return nextSegment;
895}
896
caryclarkccec0f92015-03-24 07:28:17 -0700897SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
898 SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable) {
899 SkOpSpanBase* start = *nextStart;
900 SkOpSpanBase* end = *nextEnd;
901 SkASSERT(start != end);
902 int step = start->step(end);
903 SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
904 if (other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000905 // mark the smaller of startIndex, endIndex done, and all adjacent
906 // spans with the same T value (but not 'other' spans)
907#if DEBUG_WINDING
908 SkDebugf("%s simple\n", __FUNCTION__);
909#endif
caryclarkccec0f92015-03-24 07:28:17 -0700910 SkOpSpan* startSpan = start->starter(end);
911 if (startSpan->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000912 return NULL;
913 }
caryclarkccec0f92015-03-24 07:28:17 -0700914 markDone(startSpan);
915 *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000916 return other;
917 }
caryclarkccec0f92015-03-24 07:28:17 -0700918 SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
919 SkASSERT(endNear == end); // is this ever not end?
920 SkASSERT(endNear);
921 SkASSERT(start != endNear);
922 SkASSERT((start->t() < endNear->t()) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000923 // more than one viable candidate -- measure angles to find best
caryclarkccec0f92015-03-24 07:28:17 -0700924 int calcWinding = computeSum(start, endNear, SkOpAngle::kUnaryWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +0000925 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000926 if (!sortable) {
927 *unsortable = true;
caryclarkccec0f92015-03-24 07:28:17 -0700928 markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000929 return NULL;
930 }
caryclarkccec0f92015-03-24 07:28:17 -0700931 SkOpAngle* angle = this->spanToAngle(end, start);
932 if (angle->unorderable()) {
933 *unsortable = true;
934 markDone(start->starter(end));
935 return NULL;
936 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000937#if DEBUG_SORT
938 SkDebugf("%s\n", __FUNCTION__);
939 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000940#endif
caryclarkccec0f92015-03-24 07:28:17 -0700941 int sumWinding = updateWinding(end, start);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000942 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000943 const SkOpAngle* foundAngle = NULL;
944 bool foundDone = false;
caryclarkccec0f92015-03-24 07:28:17 -0700945 // iterate through the angle, and compute everyone's winding
caryclark@google.com07393ca2013-04-08 11:47:37 +0000946 SkOpSegment* nextSegment;
947 int activeCount = 0;
948 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000949 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000950 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000951 &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000952 if (activeAngle) {
953 ++activeCount;
954 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000955 foundAngle = nextAngle;
956 foundDone = nextSegment->done(nextAngle);
957 }
958 }
959 if (nextSegment->done()) {
960 continue;
961 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000962 if (!activeAngle) {
caryclarkccec0f92015-03-24 07:28:17 -0700963 (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
caryclark@google.com570863f2013-09-16 15:55:01 +0000964 }
caryclarkccec0f92015-03-24 07:28:17 -0700965 SkOpSpanBase* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000966 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000967 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000968 *chase->append() = last;
969#if DEBUG_WINDING
caryclarkccec0f92015-03-24 07:28:17 -0700970 SkDebugf("%s chase.append segment=%d span=%d", __FUNCTION__,
971 last->segment()->debugID(), last->debugID());
972 if (!last->final()) {
973 SkDebugf(" windSum=%d", last->upCast()->windSum());
974 }
975 SkDebugf("\n");
caryclark@google.com07393ca2013-04-08 11:47:37 +0000976#endif
977 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000978 } while ((nextAngle = nextAngle->next()) != angle);
caryclarkccec0f92015-03-24 07:28:17 -0700979 start->segment()->markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000980 if (!foundAngle) {
981 return NULL;
982 }
983 *nextStart = foundAngle->start();
984 *nextEnd = foundAngle->end();
985 nextSegment = foundAngle->segment();
986#if DEBUG_WINDING
987 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
988 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
989 #endif
990 return nextSegment;
991}
992
caryclarkccec0f92015-03-24 07:28:17 -0700993SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd,
994 bool* unsortable) {
995 SkOpSpanBase* start = *nextStart;
996 SkOpSpanBase* end = *nextEnd;
997 SkASSERT(start != end);
998 int step = start->step(end);
999 SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
1000 if (other) {
1001 // mark the smaller of startIndex, endIndex done, and all adjacent
1002 // spans with the same T value (but not 'other' spans)
caryclark@google.com07393ca2013-04-08 11:47:37 +00001003#if DEBUG_WINDING
1004 SkDebugf("%s simple\n", __FUNCTION__);
1005#endif
caryclarkccec0f92015-03-24 07:28:17 -07001006 SkOpSpan* startSpan = start->starter(end);
1007 if (startSpan->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001008 return NULL;
1009 }
caryclarkccec0f92015-03-24 07:28:17 -07001010 markDone(startSpan);
1011 *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001012 return other;
1013 }
caryclarkccec0f92015-03-24 07:28:17 -07001014 SkDEBUGCODE(SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() \
1015 : (*nextStart)->prev());
1016 SkASSERT(endNear == end); // is this ever not end?
1017 SkASSERT(endNear);
1018 SkASSERT(start != endNear);
1019 SkASSERT((start->t() < endNear->t()) ^ (step < 0));
1020 SkOpAngle* angle = this->spanToAngle(end, start);
1021 if (angle->unorderable()) {
1022 *unsortable = true;
1023 markDone(start->starter(end));
1024 return NULL;
1025 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001026#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001027 SkDebugf("%s\n", __FUNCTION__);
1028 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001029#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001030 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001031 const SkOpAngle* foundAngle = NULL;
1032 bool foundDone = false;
caryclarkccec0f92015-03-24 07:28:17 -07001033 // iterate through the angle, and compute everyone's winding
caryclark@google.com07393ca2013-04-08 11:47:37 +00001034 SkOpSegment* nextSegment;
1035 int activeCount = 0;
1036 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001037 nextSegment = nextAngle->segment();
1038 ++activeCount;
1039 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001040 foundAngle = nextAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001041 if (!(foundDone = nextSegment->done(nextAngle))) {
1042 break;
1043 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001044 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001045 nextAngle = nextAngle->next();
1046 } while (nextAngle != angle);
caryclarkccec0f92015-03-24 07:28:17 -07001047 start->segment()->markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001048 if (!foundAngle) {
1049 return NULL;
1050 }
1051 *nextStart = foundAngle->start();
1052 *nextEnd = foundAngle->end();
1053 nextSegment = foundAngle->segment();
1054#if DEBUG_WINDING
1055 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
1056 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
1057 #endif
1058 return nextSegment;
1059}
1060
caryclarkccec0f92015-03-24 07:28:17 -07001061SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
1062 bool* unsortable, SkChunkAlloc* allocator) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001063 // iterate through T intersections and return topmost
1064 // topmost tangent from y-min to first pt is closer to horizontal
1065 SkASSERT(!done());
caryclarkccec0f92015-03-24 07:28:17 -07001066 SkOpSpanBase* firstT = NULL;
1067 (void) this->activeLeftTop(&firstT);
1068 if (!firstT) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001069 *unsortable = !firstPass;
caryclarkccec0f92015-03-24 07:28:17 -07001070 firstT = &fHead;
1071 while (firstT->upCast()->done()) {
1072 firstT = firstT->upCast()->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001073 }
caryclarkccec0f92015-03-24 07:28:17 -07001074 *startPtr = firstT;
1075 *endPtr = firstT->upCast()->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001076 return this;
1077 }
1078 // sort the edges to find the leftmost
1079 int step = 1;
caryclarkccec0f92015-03-24 07:28:17 -07001080 SkOpSpanBase* end;
1081 if (firstT->final() || firstT->upCast()->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001082 step = -1;
caryclarkccec0f92015-03-24 07:28:17 -07001083 end = firstT->prev();
1084 SkASSERT(end);
1085 } else {
1086 end = firstT->upCast()->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001087 }
1088 // if the topmost T is not on end, or is three-way or more, find left
1089 // look for left-ness from tLeft to firstT (matching y of other)
caryclarkccec0f92015-03-24 07:28:17 -07001090 SkASSERT(firstT != end);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001091 SkOpAngle* markAngle = spanToAngle(firstT, end);
1092 if (!markAngle) {
caryclarkccec0f92015-03-24 07:28:17 -07001093 markAngle = addSingletonAngles(step, allocator);
caryclark@google.com570863f2013-09-16 15:55:01 +00001094 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001095 markAngle->markStops();
caryclarke4097e32014-06-18 07:24:19 -07001096 const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
1097 : markAngle->findFirst();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001098 if (!baseAngle) {
1099 return NULL; // nothing to do
caryclark@google.com570863f2013-09-16 15:55:01 +00001100 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001101 SkScalar top = SK_ScalarMax;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001102 const SkOpAngle* firstAngle = NULL;
1103 const SkOpAngle* angle = baseAngle;
1104 do {
1105 if (!angle->unorderable()) {
caryclarkccec0f92015-03-24 07:28:17 -07001106 const SkOpSegment* next = angle->segment();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001107 SkPathOpsBounds bounds;
1108 next->subDivideBounds(angle->end(), angle->start(), &bounds);
caryclark65f55312014-11-13 06:58:52 -08001109 bool nearSame = AlmostEqualUlps(top, bounds.top());
1110 bool lowerSector = !firstAngle || angle->sectorEnd() < firstAngle->sectorStart();
1111 bool lesserSector = top > bounds.fTop;
1112 if (lesserSector && (!nearSame || lowerSector)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001113 top = bounds.fTop;
1114 firstAngle = angle;
1115 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001116 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001117 angle = angle->next();
1118 } while (angle != baseAngle);
caryclark65f55312014-11-13 06:58:52 -08001119 if (!firstAngle) {
1120 return NULL; // if all are unorderable, give up
1121 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001122#if DEBUG_SORT
1123 SkDebugf("%s\n", __FUNCTION__);
1124 firstAngle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001125#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00001126 // skip edges that have already been processed
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001127 angle = firstAngle;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001128 SkOpSegment* leftSegment = NULL;
1129 bool looped = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001130 do {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001131 *unsortable = angle->unorderable();
1132 if (firstPass || !*unsortable) {
1133 leftSegment = angle->segment();
caryclarkccec0f92015-03-24 07:28:17 -07001134 *startPtr = angle->end();
1135 *endPtr = angle->start();
1136 const SkOpSpan* firstSpan = (*startPtr)->starter(*endPtr);
1137 if (!firstSpan->done()) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001138 break;
1139 }
1140 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001141 angle = angle->next();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001142 looped = true;
1143 } while (angle != firstAngle);
1144 if (angle == firstAngle && looped) {
1145 return NULL;
1146 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001147 if (leftSegment->verb() >= SkPath::kQuad_Verb) {
caryclarkccec0f92015-03-24 07:28:17 -07001148 SkOpSpanBase* start = *startPtr;
1149 SkOpSpanBase* end = *endPtr;
caryclarke4097e32014-06-18 07:24:19 -07001150 bool swap;
caryclarkccec0f92015-03-24 07:28:17 -07001151 if (!leftSegment->clockwise(start, end, &swap)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001152 #if DEBUG_SWAP_TOP
caryclarkccec0f92015-03-24 07:28:17 -07001153 SkDebugf("%s swap=%d inflections=%d monotonic=%d\n",
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001154 __FUNCTION__,
caryclarkccec0f92015-03-24 07:28:17 -07001155 swap, leftSegment->debugInflections(start, end),
1156 leftSegment->monotonicInY(start, end));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001157 #endif
1158 if (swap) {
1159 // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
1160 // sorted but merely the first not already processed (i.e., not done)
caryclarkccec0f92015-03-24 07:28:17 -07001161 SkTSwap(*startPtr, *endPtr);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001162 }
1163 }
1164 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001165 return leftSegment;
1166}
1167
caryclarkccec0f92015-03-24 07:28:17 -07001168SkOpGlobalState* SkOpSegment::globalState() const {
1169 return contour()->globalState();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001170}
1171
caryclarkccec0f92015-03-24 07:28:17 -07001172void SkOpSegment::init(SkPoint pts[], SkOpContour* contour, SkPath::Verb verb) {
1173 fContour = contour;
1174 fNext = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001175 fPts = pts;
1176 fVerb = verb;
caryclarkccec0f92015-03-24 07:28:17 -07001177 fCount = 0;
1178 fDoneCount = 0;
1179 fVisited = false;
1180 SkOpSpan* zeroSpan = &fHead;
1181 zeroSpan->init(this, NULL, 0, fPts[0]);
1182 SkOpSpanBase* oneSpan = &fTail;
1183 zeroSpan->setNext(oneSpan);
1184 oneSpan->initBase(this, zeroSpan, 1, fPts[SkPathOpsVerbToPoints(fVerb)]);
1185 PATH_OPS_DEBUG_CODE(fID = globalState()->nextSegmentID());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001186}
1187
caryclarkccec0f92015-03-24 07:28:17 -07001188void SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end,
1189 SkOpAngle::IncludeType angleIncludeType) {
1190 int local = SkOpSegment::SpanSign(start, end);
caryclark65f55312014-11-13 06:58:52 -08001191 SkDEBUGCODE(bool success);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001192 if (angleIncludeType == SkOpAngle::kBinarySingle) {
caryclarkccec0f92015-03-24 07:28:17 -07001193 int oppLocal = SkOpSegment::OppSign(start, end);
caryclark65f55312014-11-13 06:58:52 -08001194 SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, oppLocal, NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001195 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08001196 SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, oppLocal, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001197 } else {
caryclark65f55312014-11-13 06:58:52 -08001198 SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001199 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08001200 SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001201 }
caryclark65f55312014-11-13 06:58:52 -08001202 SkASSERT(success);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001203}
1204
1205/*
1206when we start with a vertical intersect, we try to use the dx to determine if the edge is to
1207the left or the right of vertical. This determines if we need to add the span's
1208sign or not. However, this isn't enough.
1209If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
1210If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
1211from has the same x direction as this span, the winding should change. If the dx is opposite, then
1212the same winding is shared by both.
1213*/
caryclarkccec0f92015-03-24 07:28:17 -07001214bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHit,
1215 int winding, SkScalar hitDx, int oppWind, SkScalar hitOppDx) {
1216 SkASSERT(this == start->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001217 SkASSERT(hitDx || !winding);
reed@google.com277c3f82013-05-31 15:17:50 +00001218 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclarkccec0f92015-03-24 07:28:17 -07001219// SkASSERT(dx);
1220 int windVal = start->starter(end)->windValue();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001221#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07001222 SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), winding,
caryclark@google.com07393ca2013-04-08 11:47:37 +00001223 hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
1224#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001225 int sideWind = winding + (dx < 0 ? windVal : -windVal);
1226 if (abs(winding) < abs(sideWind)) {
1227 winding = sideWind;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001228 }
caryclarkccec0f92015-03-24 07:28:17 -07001229 SkDEBUGCODE(int oppLocal = SkOpSegment::OppSign(start, end));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001230 SkASSERT(hitOppDx || !oppWind || !oppLocal);
caryclarkccec0f92015-03-24 07:28:17 -07001231 int oppWindVal = start->starter(end)->oppValue();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001232 if (!oppWind) {
1233 oppWind = dx < 0 ? oppWindVal : -oppWindVal;
1234 } else if (hitOppDx * dx >= 0) {
1235 int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
1236 if (abs(oppWind) < abs(oppSideWind)) {
1237 oppWind = oppSideWind;
1238 }
1239 }
caryclarkdac1d172014-06-17 05:15:38 -07001240#if DEBUG_WINDING_AT_T
1241 SkDebugf(" winding=%d oppWind=%d\n", winding, oppWind);
1242#endif
caryclark65f55312014-11-13 06:58:52 -08001243 // if this fails to mark (because the edges are too small) inform caller to try again
1244 bool success = markAndChaseWinding(start, end, winding, oppWind, NULL);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001245 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08001246 success |= markAndChaseWinding(end, start, winding, oppWind, NULL);
1247 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001248}
1249
caryclarkccec0f92015-03-24 07:28:17 -07001250bool SkOpSegment::isClose(double t, const SkOpSegment* opp) const {
1251 SkDPoint cPt = this->dPtAtT(t);
1252 int pts = SkPathOpsVerbToPoints(this->verb());
1253 SkDVector dxdy = (*CurveDSlopeAtT[pts])(this->pts(), t);
1254 SkDLine perp = {{ cPt, {cPt.fX + dxdy.fY, cPt.fY - dxdy.fX} }};
1255 SkIntersections i;
1256 int oppPts = SkPathOpsVerbToPoints(opp->verb());
1257 (*CurveIntersectRay[oppPts])(opp->pts(), perp, &i);
1258 int used = i.used();
1259 for (int index = 0; index < used; ++index) {
1260 if (cPt.roughlyEqual(i.pt(index))) {
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +00001261 return true;
1262 }
caryclarkccec0f92015-03-24 07:28:17 -07001263 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00001264 return false;
1265}
1266
caryclarkccec0f92015-03-24 07:28:17 -07001267bool SkOpSegment::isXor() const {
1268 return fContour->isXor();
1269}
caryclark@google.com07393ca2013-04-08 11:47:37 +00001270
caryclarkccec0f92015-03-24 07:28:17 -07001271SkOpSpanBase* SkOpSegment::markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end) {
1272 int step = start->step(end);
1273 SkOpSpan* minSpan = start->starter(end);
1274 markDone(minSpan);
1275 SkOpSpanBase* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001276 SkOpSegment* other = this;
caryclarkccec0f92015-03-24 07:28:17 -07001277 while ((other = other->nextChase(&start, &step, &minSpan, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001278 if (other->done()) {
caryclarkdac1d172014-06-17 05:15:38 -07001279 SkASSERT(!last);
1280 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001281 }
caryclarkccec0f92015-03-24 07:28:17 -07001282 other->markDone(minSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001283 }
1284 return last;
1285}
1286
caryclarkccec0f92015-03-24 07:28:17 -07001287bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
1288 SkOpSpanBase** lastPtr) {
1289 SkOpSpan* spanStart = start->starter(end);
1290 int step = start->step(end);
1291 bool success = markWinding(spanStart, winding);
1292 SkOpSpanBase* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001293 SkOpSegment* other = this;
caryclarkccec0f92015-03-24 07:28:17 -07001294 while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
1295 if (spanStart->windSum() != SK_MinS32) {
1296 SkASSERT(spanStart->windSum() == winding);
caryclarkdac1d172014-06-17 05:15:38 -07001297 SkASSERT(!last);
1298 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001299 }
caryclarkccec0f92015-03-24 07:28:17 -07001300 (void) other->markWinding(spanStart, winding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001301 }
caryclark65f55312014-11-13 06:58:52 -08001302 if (lastPtr) {
1303 *lastPtr = last;
1304 }
1305 return success;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001306}
1307
caryclarkccec0f92015-03-24 07:28:17 -07001308bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end,
1309 int winding, int oppWinding, SkOpSpanBase** lastPtr) {
1310 SkOpSpan* spanStart = start->starter(end);
1311 int step = start->step(end);
1312 bool success = markWinding(spanStart, winding, oppWinding);
1313 SkOpSpanBase* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001314 SkOpSegment* other = this;
caryclarkccec0f92015-03-24 07:28:17 -07001315 while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
1316 if (spanStart->windSum() != SK_MinS32) {
1317 if (this->operand() == other->operand()) {
1318 SkASSERT(spanStart->windSum() == winding);
1319 if (spanStart->oppSum() != oppWinding) {
1320 this->globalState()->setWindingFailed();
1321 return false;
caryclarkdac1d172014-06-17 05:15:38 -07001322 }
caryclarkccec0f92015-03-24 07:28:17 -07001323 } else {
1324 SkASSERT(spanStart->windSum() == oppWinding);
1325 SkASSERT(spanStart->oppSum() == winding);
caryclarkdac1d172014-06-17 05:15:38 -07001326 }
1327 SkASSERT(!last);
caryclarkdac1d172014-06-17 05:15:38 -07001328 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001329 }
caryclarkccec0f92015-03-24 07:28:17 -07001330 if (this->operand() == other->operand()) {
1331 (void) other->markWinding(spanStart, winding, oppWinding);
caryclarkdac1d172014-06-17 05:15:38 -07001332 } else {
caryclarkccec0f92015-03-24 07:28:17 -07001333 (void) other->markWinding(spanStart, oppWinding, winding);
caryclarkdac1d172014-06-17 05:15:38 -07001334 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001335 }
caryclark65f55312014-11-13 06:58:52 -08001336 if (lastPtr) {
1337 *lastPtr = last;
1338 }
1339 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001340}
1341
caryclarkccec0f92015-03-24 07:28:17 -07001342SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001343 SkASSERT(angle->segment() == this);
1344 if (UseInnerWinding(maxWinding, sumWinding)) {
1345 maxWinding = sumWinding;
1346 }
caryclarkccec0f92015-03-24 07:28:17 -07001347 SkOpSpanBase* last;
1348 (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, &last);
caryclark@google.com570863f2013-09-16 15:55:01 +00001349#if DEBUG_WINDING
1350 if (last) {
caryclarkccec0f92015-03-24 07:28:17 -07001351 SkDebugf("%s last seg=%d span=%d", __FUNCTION__,
1352 last->segment()->debugID(), last->debugID());
1353 if (!last->final()) {
1354 SkDebugf(" windSum=");
1355 SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
1356 }
1357 SkDebugf("\n");
caryclark@google.com570863f2013-09-16 15:55:01 +00001358 }
1359#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00001360 return last;
1361}
1362
caryclarkccec0f92015-03-24 07:28:17 -07001363SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
1364 int oppSumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001365 SkASSERT(angle->segment() == this);
1366 if (UseInnerWinding(maxWinding, sumWinding)) {
1367 maxWinding = sumWinding;
1368 }
1369 if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
1370 oppMaxWinding = oppSumWinding;
1371 }
caryclarkccec0f92015-03-24 07:28:17 -07001372 SkOpSpanBase* last = NULL;
caryclark65f55312014-11-13 06:58:52 -08001373 // caller doesn't require that this marks anything
caryclarkccec0f92015-03-24 07:28:17 -07001374 (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, oppMaxWinding, &last);
caryclark@google.com570863f2013-09-16 15:55:01 +00001375#if DEBUG_WINDING
1376 if (last) {
caryclarkccec0f92015-03-24 07:28:17 -07001377 SkDebugf("%s last segment=%d span=%d", __FUNCTION__,
1378 last->segment()->debugID(), last->debugID());
1379 if (!last->final()) {
1380 SkDebugf(" windSum=");
1381 SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
1382 }
1383 SkDebugf(" \n");
caryclark@google.com570863f2013-09-16 15:55:01 +00001384 }
1385#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00001386 return last;
1387}
1388
caryclarkccec0f92015-03-24 07:28:17 -07001389void SkOpSegment::markDone(SkOpSpan* span) {
1390 SkASSERT(this == span->segment());
1391 if (span->done()) {
1392 return;
1393 }
1394#if DEBUG_MARK_DONE
1395 debugShowNewWinding(__FUNCTION__, span, span->windSum(), span->oppSum());
1396#endif
1397 span->setDone(true);
1398 ++fDoneCount;
1399 debugValidate();
1400}
1401
1402bool SkOpSegment::markWinding(SkOpSpan* span, int winding) {
1403 SkASSERT(this == span->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001404 SkASSERT(winding);
caryclarkccec0f92015-03-24 07:28:17 -07001405 if (span->done()) {
caryclark65f55312014-11-13 06:58:52 -08001406 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001407 }
1408#if DEBUG_MARK_DONE
caryclarkccec0f92015-03-24 07:28:17 -07001409 debugShowNewWinding(__FUNCTION__, span, winding);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001410#endif
caryclarkccec0f92015-03-24 07:28:17 -07001411 span->setWindSum(winding);
1412 debugValidate();
caryclark65f55312014-11-13 06:58:52 -08001413 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001414}
1415
caryclarkccec0f92015-03-24 07:28:17 -07001416bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
1417 SkASSERT(this == span->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001418 SkASSERT(winding || oppWinding);
caryclarkccec0f92015-03-24 07:28:17 -07001419 if (span->done()) {
1420 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001421 }
caryclarkccec0f92015-03-24 07:28:17 -07001422#if DEBUG_MARK_DONE
1423 debugShowNewWinding(__FUNCTION__, span, winding, oppWinding);
1424#endif
1425 span->setWindSum(winding);
1426 span->setOppSum(oppWinding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001427 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001428 return true;
1429}
1430
caryclarkccec0f92015-03-24 07:28:17 -07001431bool SkOpSegment::match(const SkOpPtT* base, const SkOpSegment* testParent, double testT,
1432 const SkPoint& testPt) const {
1433 const SkOpSegment* baseParent = base->segment();
1434 if (this == baseParent && this == testParent && precisely_equal(base->fT, testT)) {
1435 return true;
1436 }
1437 if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
1438 return false;
1439 }
1440 return !ptsDisjoint(base->fT, base->fPt, testT, testPt);
1441}
1442
1443static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
1444 if (last) {
1445 *last = endSpan;
caryclarkdac1d172014-06-17 05:15:38 -07001446 }
1447 return NULL;
1448}
1449
caryclarkccec0f92015-03-24 07:28:17 -07001450bool SkOpSegment::monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
1451 SkASSERT(fVerb != SkPath::kLine_Verb);
1452 if (fVerb == SkPath::kQuad_Verb) {
1453 SkDQuad dst = SkDQuad::SubDivide(fPts, start->t(), end->t());
1454 return dst.monotonicInY();
1455 }
1456 SkASSERT(fVerb == SkPath::kCubic_Verb);
1457 SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
1458 return dst.monotonicInY();
1459}
1460
1461bool SkOpSegment::NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start,
1462 SkOpSpanBase** end) {
1463 while (span->final() || span->upCast()->done()) {
1464 if (span->final()) {
1465 return false;
1466 }
1467 span = span->upCast()->next();
1468 }
1469 *start = span;
1470 *end = span->upCast()->next();
1471 return true;
1472}
1473
1474SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpSpan** minPtr,
1475 SkOpSpanBase** last) const {
1476 SkOpSpanBase* origStart = *startPtr;
caryclarkdac1d172014-06-17 05:15:38 -07001477 int step = *stepPtr;
caryclarkccec0f92015-03-24 07:28:17 -07001478 SkOpSpanBase* endSpan = step > 0 ? origStart->upCast()->next() : origStart->prev();
1479 SkASSERT(endSpan);
1480 SkOpAngle* angle = step > 0 ? endSpan->fromAngle() : endSpan->upCast()->toAngle();
1481 SkOpSpanBase* foundSpan;
1482 SkOpSpanBase* otherEnd;
caryclarkdac1d172014-06-17 05:15:38 -07001483 SkOpSegment* other;
1484 if (angle == NULL) {
caryclarkccec0f92015-03-24 07:28:17 -07001485 if (endSpan->t() != 0 && endSpan->t() != 1) {
caryclarkdac1d172014-06-17 05:15:38 -07001486 return NULL;
1487 }
caryclarkccec0f92015-03-24 07:28:17 -07001488 SkOpPtT* otherPtT = endSpan->ptT()->next();
1489 other = otherPtT->segment();
1490 foundSpan = otherPtT->span();
1491 otherEnd = step > 0 ? foundSpan->upCast()->next() : foundSpan->prev();
caryclarkdac1d172014-06-17 05:15:38 -07001492 } else {
1493 int loopCount = angle->loopCount();
1494 if (loopCount > 2) {
caryclarkccec0f92015-03-24 07:28:17 -07001495 return set_last(last, endSpan);
caryclarkdac1d172014-06-17 05:15:38 -07001496 }
1497 const SkOpAngle* next = angle->next();
caryclark65b427c2014-09-18 10:32:57 -07001498 if (NULL == next) {
1499 return NULL;
1500 }
caryclarkdac1d172014-06-17 05:15:38 -07001501#if DEBUG_WINDING
caryclarkccec0f92015-03-24 07:28:17 -07001502 if (angle->sign() != next->sign() && !angle->segment()->contour()->isXor()
1503 && !next->segment()->contour()->isXor()) {
caryclarkdac1d172014-06-17 05:15:38 -07001504 SkDebugf("%s mismatched signs\n", __FUNCTION__);
caryclarkdac1d172014-06-17 05:15:38 -07001505 }
caryclarkccec0f92015-03-24 07:28:17 -07001506#endif
caryclarkdac1d172014-06-17 05:15:38 -07001507 other = next->segment();
caryclarkccec0f92015-03-24 07:28:17 -07001508 foundSpan = endSpan = next->start();
caryclarkdac1d172014-06-17 05:15:38 -07001509 otherEnd = next->end();
caryclark@google.com570863f2013-09-16 15:55:01 +00001510 }
caryclarkccec0f92015-03-24 07:28:17 -07001511 int foundStep = foundSpan->step(otherEnd);
caryclarkdac1d172014-06-17 05:15:38 -07001512 if (*stepPtr != foundStep) {
caryclarkccec0f92015-03-24 07:28:17 -07001513 return set_last(last, endSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001514 }
caryclarkccec0f92015-03-24 07:28:17 -07001515 SkASSERT(*startPtr);
1516 if (!otherEnd) {
caryclark65b427c2014-09-18 10:32:57 -07001517 return NULL;
1518 }
1519// SkASSERT(otherEnd >= 0);
caryclarkccec0f92015-03-24 07:28:17 -07001520 SkOpSpan* origMin = step < 0 ? origStart->prev() : origStart->upCast();
1521 SkOpSpan* foundMin = foundSpan->starter(otherEnd);
1522 if (foundMin->windValue() != origMin->windValue()
1523 || foundMin->oppValue() != origMin->oppValue()) {
1524 return set_last(last, endSpan);
caryclarkdac1d172014-06-17 05:15:38 -07001525 }
caryclarkccec0f92015-03-24 07:28:17 -07001526 *startPtr = foundSpan;
caryclarkdac1d172014-06-17 05:15:38 -07001527 *stepPtr = foundStep;
1528 if (minPtr) {
1529 *minPtr = foundMin;
caryclark@google.coma5e55922013-05-07 18:51:31 +00001530 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001531 return other;
1532}
1533
caryclarkccec0f92015-03-24 07:28:17 -07001534static void clear_visited(SkOpSpan* span) {
1535 // reset visited flag back to false
1536 do {
1537 SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
1538 while ((ptT = ptT->next()) != stopPtT) {
1539 SkOpSegment* opp = ptT->segment();
1540 opp->resetVisited();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001541 }
caryclarkccec0f92015-03-24 07:28:17 -07001542 } while ((span = span->next()->upCastable()));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001543}
1544
caryclarkccec0f92015-03-24 07:28:17 -07001545// look for pairs of undetected coincident curves
1546// assumes that segments going in have visited flag clear
1547// curve/curve intersection should now do a pretty good job of finding coincident runs so
1548// this may be only be necessary for line/curve pairs -- so skip unless this is a line and the
1549// the opp is not a line
1550void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
1551 if (this->verb() != SkPath::kLine_Verb) {
1552 return;
1553 }
1554 SkOpSpan* prior = NULL;
1555 SkOpSpan* span = &fHead;
1556 do {
1557 SkOpPtT* ptT = span->ptT(), * spanStopPtT = ptT;
1558 SkASSERT(ptT->span() == span);
1559 while ((ptT = ptT->next()) != spanStopPtT) {
1560 SkOpSegment* opp = ptT->span()->segment();
1561 if (opp->setVisited()) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001562 continue;
1563 }
caryclarkccec0f92015-03-24 07:28:17 -07001564 if (opp->verb() == SkPath::kLine_Verb) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001565 continue;
1566 }
caryclarkccec0f92015-03-24 07:28:17 -07001567 if (span->containsCoincidence(opp)) { // FIXME: this assumes that if the opposite
1568 // segment is coincident then no more coincidence
1569 // needs to be detected. This may not be true.
1570 continue;
caryclark5e27e0e2014-08-12 07:46:33 -07001571 }
caryclarkccec0f92015-03-24 07:28:17 -07001572 if (span->containsCoinEnd(opp)) {
1573 continue;
1574 }
1575 // if already visited and visited again, check for coin
1576 if (span == &fHead) {
1577 continue;
1578 }
1579 SkOpPtT* priorPtT = NULL, * priorStopPtT;
1580 // find prior span containing opp segment
1581 SkOpSegment* priorOpp = NULL;
1582 prior = span;
1583 while (!priorOpp && (prior = prior->prev())) {
1584 priorStopPtT = priorPtT = prior->ptT();
1585 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
1586 SkOpSegment* segment = priorPtT->span()->segment();
1587 if (segment == opp) {
1588 priorOpp = opp;
1589 break;
1590 }
1591 }
1592 }
1593 if (!priorOpp) {
1594 continue;
1595 }
1596 SkOpPtT* oppStart = prior->ptT();
1597 SkOpPtT* oppEnd = span->ptT();
1598 bool swapped = priorPtT->fT > ptT->fT;
1599 if (swapped) {
1600 SkTSwap(priorPtT, ptT);
1601 SkTSwap(oppStart, oppEnd);
1602 }
1603 bool flipped = oppStart->fT > oppEnd->fT;
1604 bool coincident;
1605 if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
1606 goto swapBack;
1607 }
1608 {
1609 // average t, find mid pt
1610 double midT = (prior->t() + span->t()) / 2;
1611 SkPoint midPt = this->ptAtT(midT);
1612 coincident = true;
1613 // if the mid pt is not near either end pt, project perpendicular through opp seg
1614 if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt)
1615 && !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) {
1616 coincident = false;
1617 SkIntersections i;
1618 int ptCount = SkPathOpsVerbToPoints(this->verb());
1619 SkVector dxdy = (*CurveSlopeAtT[ptCount])(pts(), midT);
1620 SkDLine ray = {{{midPt.fX, midPt.fY},
1621 {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}};
1622 int oppPtCount = SkPathOpsVerbToPoints(opp->verb());
1623 (*CurveIntersectRay[oppPtCount])(opp->pts(), ray, &i);
1624 // measure distance and see if it's small enough to denote coincidence
1625 for (int index = 0; index < i.used(); ++index) {
1626 SkDPoint oppPt = i.pt(index);
1627 if (oppPt.approximatelyEqual(midPt)) {
1628 SkVector oppDxdy = (*CurveSlopeAtT[oppPtCount])(opp->pts(),
1629 i[index][0]);
1630 oppDxdy.normalize();
1631 dxdy.normalize();
1632 SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON);
1633 coincident |= flatness < 5000; // FIXME: replace with tuned value
1634 }
1635 }
1636 }
1637 }
1638 if (coincident) {
1639 // mark coincidence
1640 coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator);
1641 clear_visited(&fHead);
1642 missingCoincidence(coincidences, allocator);
1643 return;
1644 }
1645 swapBack:
1646 if (swapped) {
1647 SkTSwap(priorPtT, ptT);
1648 }
caryclark5e27e0e2014-08-12 07:46:33 -07001649 }
caryclarkccec0f92015-03-24 07:28:17 -07001650 } while ((span = span->next()->upCastable()));
1651 clear_visited(&fHead);
caryclark5e27e0e2014-08-12 07:46:33 -07001652}
1653
caryclarkccec0f92015-03-24 07:28:17 -07001654// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
1655bool SkOpSegment::moveNearby() {
1656 debugValidate();
1657 SkOpSpanBase* spanS = &fHead;
1658 do {
1659 SkOpSpanBase* test = spanS->upCast()->next();
1660 SkOpSpanBase* next;
1661 if (spanS->contains(test)) {
1662 if (!test->final()) {
1663 test->upCast()->detach(spanS->ptT());
1664 continue;
1665 } else if (spanS != &fHead) {
1666 spanS->upCast()->detach(test->ptT());
1667 spanS = test;
1668 continue;
1669 }
caryclarkdac1d172014-06-17 05:15:38 -07001670 }
caryclarkccec0f92015-03-24 07:28:17 -07001671 do { // iterate through all spans associated with start
1672 SkOpPtT* startBase = spanS->ptT();
1673 next = test->final() ? NULL : test->upCast()->next();
1674 do {
1675 SkOpPtT* testBase = test->ptT();
1676 do {
1677 if (startBase == testBase) {
1678 goto checkNextSpan;
1679 }
1680 if (testBase->duplicate()) {
1681 continue;
1682 }
1683 if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) {
1684 if (test == &this->fTail) {
1685 if (spanS == &fHead) {
1686 debugValidate();
1687 return true; // if this span has collapsed, remove it from parent
1688 }
1689 this->fTail.merge(spanS->upCast());
1690 debugValidate();
1691 return true;
1692 }
1693 spanS->merge(test->upCast());
1694 spanS->upCast()->setNext(next);
1695 goto checkNextSpan;
1696 }
1697 } while ((testBase = testBase->next()) != test->ptT());
1698 } while ((startBase = startBase->next()) != spanS->ptT());
1699 checkNextSpan:
1700 ;
1701 } while ((test = next));
1702 spanS = spanS->upCast()->next();
1703 } while (!spanS->final());
1704 debugValidate();
1705 return true;
caryclarkdac1d172014-06-17 05:15:38 -07001706}
1707
caryclarkccec0f92015-03-24 07:28:17 -07001708bool SkOpSegment::operand() const {
1709 return fContour->operand();
1710}
1711
1712bool SkOpSegment::oppXor() const {
1713 return fContour->oppXor();
1714}
1715
1716bool SkOpSegment::ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const {
1717 if (fVerb == SkPath::kLine_Verb) {
1718 return false;
1719 }
1720 // quads (and cubics) can loop back to nearly a line so that an opposite curve
1721 // hits in two places with very different t values.
1722 // OPTIMIZATION: curves could be preflighted so that, for example, something like
1723 // 'controls contained by ends' could avoid this check for common curves
1724 // 'ends are extremes in x or y' is cheaper to compute and real-world common
1725 // on the other hand, the below check is relatively inexpensive
1726 double midT = (t1 + t2) / 2;
1727 SkPoint midPt = this->ptAtT(midT);
1728 double seDistSq = SkTMax(pt1.distanceToSqd(pt2) * 2, FLT_EPSILON * 2);
1729 return midPt.distanceToSqd(pt1) > seDistSq || midPt.distanceToSqd(pt2) > seDistSq;
1730}
1731
1732void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
1733 int* maxWinding, int* sumWinding) {
1734 int deltaSum = SpanSign(start, end);
1735 *maxWinding = *sumMiWinding;
1736 *sumWinding = *sumMiWinding -= deltaSum;
1737 SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
1738}
1739
1740void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
1741 int* sumSuWinding, int* maxWinding, int* sumWinding, int* oppMaxWinding,
1742 int* oppSumWinding) {
1743 int deltaSum = SpanSign(start, end);
1744 int oppDeltaSum = OppSign(start, end);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001745 if (operand()) {
1746 *maxWinding = *sumSuWinding;
1747 *sumWinding = *sumSuWinding -= deltaSum;
1748 *oppMaxWinding = *sumMiWinding;
1749 *oppSumWinding = *sumMiWinding -= oppDeltaSum;
1750 } else {
1751 *maxWinding = *sumMiWinding;
1752 *sumWinding = *sumMiWinding -= deltaSum;
1753 *oppMaxWinding = *sumSuWinding;
1754 *oppSumWinding = *sumSuWinding -= oppDeltaSum;
1755 }
caryclarkccec0f92015-03-24 07:28:17 -07001756 SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
1757 SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001758}
1759
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001760void SkOpSegment::sortAngles() {
caryclarkccec0f92015-03-24 07:28:17 -07001761 SkOpSpanBase* span = &this->fHead;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001762 do {
caryclarkccec0f92015-03-24 07:28:17 -07001763 SkOpAngle* fromAngle = span->fromAngle();
1764 SkOpAngle* toAngle = span->final() ? NULL : span->upCast()->toAngle();
caryclarkdac1d172014-06-17 05:15:38 -07001765 if (!fromAngle && !toAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001766 continue;
1767 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001768#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001769 bool wroteAfterHeader = false;
caryclark@google.com570863f2013-09-16 15:55:01 +00001770#endif
caryclarkccec0f92015-03-24 07:28:17 -07001771 SkOpAngle* baseAngle = fromAngle;
1772 if (fromAngle && toAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001773#if DEBUG_ANGLE
caryclarkccec0f92015-03-24 07:28:17 -07001774 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), span->t(),
1775 span->debugID());
1776 wroteAfterHeader = true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001777#endif
caryclarkccec0f92015-03-24 07:28:17 -07001778 fromAngle->insert(toAngle);
1779 } else if (!fromAngle) {
1780 baseAngle = toAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001781 }
caryclarkccec0f92015-03-24 07:28:17 -07001782 SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001783 do {
caryclarkccec0f92015-03-24 07:28:17 -07001784 SkOpSpanBase* oSpan = ptT->span();
1785 if (oSpan == span) {
1786 continue;
1787 }
1788 SkOpAngle* oAngle = oSpan->fromAngle();
caryclarkdac1d172014-06-17 05:15:38 -07001789 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001790#if DEBUG_ANGLE
1791 if (!wroteAfterHeader) {
caryclarkccec0f92015-03-24 07:28:17 -07001792 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
1793 span->t(), span->debugID());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001794 wroteAfterHeader = true;
1795 }
1796#endif
caryclarkccec0f92015-03-24 07:28:17 -07001797 if (!oAngle->loopContains(baseAngle)) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001798 baseAngle->insert(oAngle);
1799 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001800 }
caryclarkccec0f92015-03-24 07:28:17 -07001801 if (!oSpan->final()) {
1802 oAngle = oSpan->upCast()->toAngle();
1803 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001804#if DEBUG_ANGLE
caryclarkccec0f92015-03-24 07:28:17 -07001805 if (!wroteAfterHeader) {
1806 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
1807 span->t(), span->debugID());
1808 wroteAfterHeader = true;
1809 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001810#endif
caryclarkccec0f92015-03-24 07:28:17 -07001811 if (!oAngle->loopContains(baseAngle)) {
1812 baseAngle->insert(oAngle);
1813 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001814 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001815 }
caryclarkccec0f92015-03-24 07:28:17 -07001816 } while ((ptT = ptT->next()) != stopPtT);
1817 if (baseAngle->loopCount() == 1) {
1818 span->setFromAngle(NULL);
1819 if (toAngle) {
1820 span->upCast()->setToAngle(NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001821 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001822 baseAngle = NULL;
1823 }
1824#if DEBUG_SORT
1825 SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
1826#endif
caryclarkccec0f92015-03-24 07:28:17 -07001827 } while (!span->final() && (span = span->upCast()->next()));
caryclark@google.com570863f2013-09-16 15:55:01 +00001828}
1829
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001830// return true if midpoints were computed
caryclarkccec0f92015-03-24 07:28:17 -07001831bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
1832 SkPoint edge[4]) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001833 SkASSERT(start != end);
caryclarkccec0f92015-03-24 07:28:17 -07001834 const SkOpPtT& startPtT = *start->ptT();
1835 const SkOpPtT& endPtT = *end->ptT();
1836 edge[0] = startPtT.fPt;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001837 int points = SkPathOpsVerbToPoints(fVerb);
caryclarkccec0f92015-03-24 07:28:17 -07001838 edge[points] = endPtT.fPt;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001839 if (fVerb == SkPath::kLine_Verb) {
1840 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001841 }
caryclarkccec0f92015-03-24 07:28:17 -07001842 double startT = startPtT.fT;
1843 double endT = endPtT.fT;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001844 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
1845 // don't compute midpoints if we already have them
1846 if (fVerb == SkPath::kQuad_Verb) {
1847 edge[1] = fPts[1];
1848 return false;
1849 }
1850 SkASSERT(fVerb == SkPath::kCubic_Verb);
1851 if (start < end) {
1852 edge[1] = fPts[1];
1853 edge[2] = fPts[2];
1854 return false;
1855 }
1856 edge[1] = fPts[2];
1857 edge[2] = fPts[1];
1858 return false;
1859 }
1860 const SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[points].fX, edge[points].fY }};
1861 if (fVerb == SkPath::kQuad_Verb) {
1862 edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint();
1863 } else {
1864 SkASSERT(fVerb == SkPath::kCubic_Verb);
1865 SkDPoint ctrl[2];
1866 SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl);
1867 edge[1] = ctrl[0].asSkPoint();
1868 edge[2] = ctrl[1].asSkPoint();
1869 }
1870 return true;
1871}
1872
caryclarkccec0f92015-03-24 07:28:17 -07001873bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
1874 SkDCubic* result) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001875 SkASSERT(start != end);
caryclarkccec0f92015-03-24 07:28:17 -07001876 const SkOpPtT& startPtT = *start->ptT();
1877 const SkOpPtT& endPtT = *end->ptT();
1878 (*result)[0].set(startPtT.fPt);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001879 int points = SkPathOpsVerbToPoints(fVerb);
caryclarkccec0f92015-03-24 07:28:17 -07001880 (*result)[points].set(endPtT.fPt);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001881 if (fVerb == SkPath::kLine_Verb) {
1882 return false;
1883 }
caryclarkccec0f92015-03-24 07:28:17 -07001884 double startT = startPtT.fT;
1885 double endT = endPtT.fT;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001886 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
1887 // don't compute midpoints if we already have them
1888 if (fVerb == SkPath::kQuad_Verb) {
1889 (*result)[1].set(fPts[1]);
1890 return false;
1891 }
1892 SkASSERT(fVerb == SkPath::kCubic_Verb);
caryclarkccec0f92015-03-24 07:28:17 -07001893 if (startT == 0) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001894 (*result)[1].set(fPts[1]);
1895 (*result)[2].set(fPts[2]);
1896 return false;
1897 }
1898 (*result)[1].set(fPts[2]);
1899 (*result)[2].set(fPts[1]);
1900 return false;
1901 }
1902 if (fVerb == SkPath::kQuad_Verb) {
1903 (*result)[1] = SkDQuad::SubDivide(fPts, (*result)[0], (*result)[2], startT, endT);
1904 } else {
1905 SkASSERT(fVerb == SkPath::kCubic_Verb);
1906 SkDCubic::SubDivide(fPts, (*result)[0], (*result)[3], startT, endT, &(*result)[1]);
1907 }
1908 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001909}
1910
caryclarkccec0f92015-03-24 07:28:17 -07001911void SkOpSegment::subDivideBounds(const SkOpSpanBase* start, const SkOpSpanBase* end,
1912 SkPathOpsBounds* bounds) const {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001913 SkPoint edge[4];
1914 subDivide(start, end, edge);
reed@google.com277c3f82013-05-31 15:17:50 +00001915 (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001916}
1917
caryclarkccec0f92015-03-24 07:28:17 -07001918void SkOpSegment::undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end) {
1919 SkOpSpan* span = this->head();
1920 do {
1921 if (!span->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001922 break;
1923 }
caryclarkccec0f92015-03-24 07:28:17 -07001924 } while ((span = span->next()->upCastable()));
1925 SkASSERT(span);
1926 *start = span;
1927 *end = span->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001928}
1929
caryclarkccec0f92015-03-24 07:28:17 -07001930int SkOpSegment::updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
1931 const SkOpSpan* lesser = start->starter(end);
1932 int oppWinding = lesser->oppSum();
1933 int oppSpanWinding = SkOpSegment::OppSign(start, end);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001934 if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
1935 && oppWinding != SK_MaxS32) {
1936 oppWinding -= oppSpanWinding;
1937 }
1938 return oppWinding;
1939}
1940
1941int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
caryclarkccec0f92015-03-24 07:28:17 -07001942 const SkOpSpanBase* startSpan = angle->start();
1943 const SkOpSpanBase* endSpan = angle->end();
1944 return updateOppWinding(endSpan, startSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001945}
1946
1947int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
caryclarkccec0f92015-03-24 07:28:17 -07001948 const SkOpSpanBase* startSpan = angle->start();
1949 const SkOpSpanBase* endSpan = angle->end();
1950 return updateOppWinding(startSpan, endSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001951}
1952
caryclarkccec0f92015-03-24 07:28:17 -07001953int SkOpSegment::updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
1954 const SkOpSpan* lesser = start->starter(end);
1955 int winding = lesser->windSum();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001956 if (winding == SK_MinS32) {
1957 return winding;
1958 }
caryclarkccec0f92015-03-24 07:28:17 -07001959 int spanWinding = SkOpSegment::SpanSign(start, end);
caryclark@google.com570863f2013-09-16 15:55:01 +00001960 if (winding && UseInnerWinding(winding - spanWinding, winding)
1961 && winding != SK_MaxS32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001962 winding -= spanWinding;
1963 }
1964 return winding;
1965}
1966
1967int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
caryclarkccec0f92015-03-24 07:28:17 -07001968 const SkOpSpanBase* startSpan = angle->start();
1969 const SkOpSpanBase* endSpan = angle->end();
1970 return updateWinding(endSpan, startSpan);
caryclark@google.com570863f2013-09-16 15:55:01 +00001971}
1972
caryclark@google.com07393ca2013-04-08 11:47:37 +00001973int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
caryclarkccec0f92015-03-24 07:28:17 -07001974 const SkOpSpanBase* startSpan = angle->start();
1975 const SkOpSpanBase* endSpan = angle->end();
1976 return updateWinding(startSpan, endSpan);
caryclark@google.com570863f2013-09-16 15:55:01 +00001977}
1978
1979// OPTIMIZATION: does the following also work, and is it any faster?
1980// return outerWinding * innerWinding > 0
1981// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
1982bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
1983 SkASSERT(outerWinding != SK_MaxS32);
1984 SkASSERT(innerWinding != SK_MaxS32);
1985 int absOut = abs(outerWinding);
1986 int absIn = abs(innerWinding);
1987 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
1988 return result;
1989}
1990
caryclarkccec0f92015-03-24 07:28:17 -07001991int SkOpSegment::windingAtT(double tHit, const SkOpSpan* span, bool crossOpp,
1992 SkScalar* dx) const {
1993 if (approximately_zero(tHit - span->t())) { // if we hit the end of a span, disregard
caryclark@google.com07393ca2013-04-08 11:47:37 +00001994 return SK_MinS32;
1995 }
caryclarkccec0f92015-03-24 07:28:17 -07001996 int winding = crossOpp ? span->oppSum() : span->windSum();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001997 SkASSERT(winding != SK_MinS32);
caryclarkccec0f92015-03-24 07:28:17 -07001998 int windVal = crossOpp ? span->oppValue() : span->windValue();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001999#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07002000 SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
caryclarkccec0f92015-03-24 07:28:17 -07002001 debugID(), crossOpp, tHit, span->t(), winding, windVal);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002002#endif
2003 // see if a + change in T results in a +/- change in X (compute x'(T))
reed@google.com277c3f82013-05-31 15:17:50 +00002004 *dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002005 if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
2006 *dx = fPts[2].fX - fPts[1].fX - *dx;
2007 }
2008 if (*dx == 0) {
2009#if DEBUG_WINDING_AT_T
2010 SkDebugf(" dx=0 winding=SK_MinS32\n");
2011#endif
2012 return SK_MinS32;
2013 }
skia.committer@gmail.coma4aced42013-07-09 07:00:56 +00002014 if (windVal < 0) { // reverse sign if opp contour traveled in reverse
caryclark@google.com07e97fc2013-07-08 17:17:02 +00002015 *dx = -*dx;
2016 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002017 if (winding * *dx > 0) { // if same signs, result is negative
2018 winding += *dx > 0 ? -windVal : windVal;
2019 }
2020#if DEBUG_WINDING_AT_T
2021 SkDebugf(" dx=%c winding=%d\n", *dx > 0 ? '+' : '-', winding);
2022#endif
2023 return winding;
2024}
2025
2026int SkOpSegment::windSum(const SkOpAngle* angle) const {
caryclarkccec0f92015-03-24 07:28:17 -07002027 const SkOpSpan* minSpan = angle->start()->starter(angle->end());
2028 return minSpan->windSum();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002029}