blob: b14be78d06fed2d17cbc9bacf26090314f6972f8 [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 */
caryclark54359292015-03-26 07:52:43 -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"
caryclark54359292015-03-26 07:52:43 -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
caryclark54359292015-03-26 07:52:43 -070031static const bool gActiveEdge[kXOR_SkPathOp + 1][2][2][2][2] = {
caryclark@google.com07393ca2013-04-08 11:47:37 +000032// 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
caryclark54359292015-03-26 07:52:43 -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 }
caryclark54359292015-03-26 07:52:43 -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
caryclark54359292015-03-26 07:52:43 -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 }
caryclark54359292015-03-26 07:52:43 -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 {
caryclark54359292015-03-26 07:52:43 -070073 SkASSERT(upSpan->done());
caryclark@google.com07393ca2013-04-08 11:47:37 +000074 }
75 }
caryclark54359292015-03-26 07:52:43 -070076 SkOpSpan* downSpan = start->prev();
caryclark@google.com07393ca2013-04-08 11:47:37 +000077 // edge leading into junction
caryclark54359292015-03-26 07:52:43 -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 }
caryclark54359292015-03-26 07:52:43 -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 {
caryclark54359292015-03-26 07:52:43 -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
caryclark54359292015-03-26 07:52:43 -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
caryclark54359292015-03-26 07:52:43 -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;
caryclark54359292015-03-26 07:52:43 -0700110 SkOpSpanBase* span = &fHead;
caryclark03b03ca2015-04-23 09:13:37 -0700111 SkOpSpanBase* lastSpan = NULL;
caryclark54359292015-03-26 07:52:43 -0700112 do {
caryclark03b03ca2015-04-23 09:13:37 -0700113 if (!lastDone || (!span->final() && !span->upCast()->done())) {
caryclark54359292015-03-26 07:52:43 -0700114 const SkPoint& xy = span->pt();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000115 if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
116 topPt = xy;
caryclark54359292015-03-26 07:52:43 -0700117 if (firstSpan) {
118 *firstSpan = span;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000119 }
120 }
caryclark03b03ca2015-04-23 09:13:37 -0700121 if (fVerb != SkPath::kLine_Verb && !lastDone
122 && fCubicType != SkDCubic::kSplitAtMaxCurvature_SkDCubicType) {
123 double curveTopT;
124 SkPoint curveTop = (*CurveTop[fVerb])(fPts, fWeight, lastSpan->t(), span->t(),
125 &curveTopT);
126 if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY && topPt.fX > curveTop.fX)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000127 topPt = curveTop;
caryclark54359292015-03-26 07:52:43 -0700128 if (firstSpan) {
caryclark03b03ca2015-04-23 09:13:37 -0700129 const SkPoint& lastXY = lastSpan->pt();
130 *firstSpan = lastXY.fY > xy.fY || (lastXY.fY == xy.fY && lastXY.fX > xy.fX)
131 ? span : lastSpan;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000132 }
133 }
134 }
caryclark03b03ca2015-04-23 09:13:37 -0700135 lastSpan = span;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000136 }
caryclark54359292015-03-26 07:52:43 -0700137 if (span->final()) {
138 break;
139 }
140 lastDone = span->upCast()->done();
141 } while ((span = span->upCast()->next()));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000142 return topPt;
143}
144
caryclark54359292015-03-26 07:52:43 -0700145bool SkOpSegment::activeOp(SkOpSpanBase* start, SkOpSpanBase* end, int xorMiMask, int xorSuMask,
146 SkPathOp op) {
147 int sumMiWinding = this->updateWinding(end, start);
148 int sumSuWinding = this->updateOppWinding(end, start);
caryclark65f55312014-11-13 06:58:52 -0800149#if DEBUG_LIMIT_WIND_SUM
150 SkASSERT(abs(sumMiWinding) <= DEBUG_LIMIT_WIND_SUM);
151 SkASSERT(abs(sumSuWinding) <= DEBUG_LIMIT_WIND_SUM);
152#endif
caryclark54359292015-03-26 07:52:43 -0700153 if (this->operand()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000154 SkTSwap<int>(sumMiWinding, sumSuWinding);
155 }
caryclark54359292015-03-26 07:52:43 -0700156 return this->activeOp(xorMiMask, xorSuMask, start, end, op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000157}
158
caryclark54359292015-03-26 07:52:43 -0700159bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end,
160 SkPathOp op, int* sumMiWinding, int* sumSuWinding) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000161 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
caryclark54359292015-03-26 07:52:43 -0700162 this->setUpWindings(start, end, sumMiWinding, sumSuWinding,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000163 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000164 bool miFrom;
165 bool miTo;
166 bool suFrom;
167 bool suTo;
168 if (operand()) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000169 miFrom = (oppMaxWinding & xorMiMask) != 0;
170 miTo = (oppSumWinding & xorMiMask) != 0;
171 suFrom = (maxWinding & xorSuMask) != 0;
172 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000173 } else {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000174 miFrom = (maxWinding & xorMiMask) != 0;
175 miTo = (sumWinding & xorMiMask) != 0;
176 suFrom = (oppMaxWinding & xorSuMask) != 0;
177 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000178 }
179 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
180#if DEBUG_ACTIVE_OP
caryclarkdac1d172014-06-17 05:15:38 -0700181 SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n",
caryclark54359292015-03-26 07:52:43 -0700182 __FUNCTION__, debugID(), start->t(), end->t(),
caryclark@google.com570863f2013-09-16 15:55:01 +0000183 SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000184#endif
185 return result;
186}
187
caryclark54359292015-03-26 07:52:43 -0700188bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end) {
189 int sumWinding = updateWinding(end, start);
190 return activeWinding(start, end, &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000191}
192
caryclark54359292015-03-26 07:52:43 -0700193bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000194 int maxWinding;
caryclark54359292015-03-26 07:52:43 -0700195 setUpWinding(start, end, &maxWinding, sumWinding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000196 bool from = maxWinding != 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000197 bool to = *sumWinding != 0;
198 bool result = gUnaryActiveEdge[from][to];
199 return result;
200}
201
caryclark54359292015-03-26 07:52:43 -0700202void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
203 SkPathWriter* path, bool active) const {
caryclark1049f122015-04-20 08:31:59 -0700204 SkOpCurve edge;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000205 const SkPoint* ePtr;
caryclark1049f122015-04-20 08:31:59 -0700206 SkScalar eWeight;
caryclark54359292015-03-26 07:52:43 -0700207 if ((start == &fHead && end == &fTail) || (start == &fTail && end == &fHead)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000208 ePtr = fPts;
caryclark1049f122015-04-20 08:31:59 -0700209 eWeight = fWeight;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000210 } else {
211 // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
caryclark1049f122015-04-20 08:31:59 -0700212 subDivide(start, end, &edge);
213 ePtr = edge.fPts;
214 eWeight = edge.fWeight;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000215 }
216 if (active) {
caryclark54359292015-03-26 07:52:43 -0700217 bool reverse = ePtr == fPts && start != &fHead;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000218 if (reverse) {
reed@google.com277c3f82013-05-31 15:17:50 +0000219 path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000220 switch (fVerb) {
221 case SkPath::kLine_Verb:
222 path->deferredLine(ePtr[0]);
223 break;
224 case SkPath::kQuad_Verb:
225 path->quadTo(ePtr[1], ePtr[0]);
226 break;
caryclark1049f122015-04-20 08:31:59 -0700227 case SkPath::kConic_Verb:
228 path->conicTo(ePtr[1], ePtr[0], eWeight);
229 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000230 case SkPath::kCubic_Verb:
231 path->cubicTo(ePtr[2], ePtr[1], ePtr[0]);
232 break;
233 default:
234 SkASSERT(0);
235 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000236 } else {
237 path->deferredMoveLine(ePtr[0]);
238 switch (fVerb) {
239 case SkPath::kLine_Verb:
240 path->deferredLine(ePtr[1]);
241 break;
242 case SkPath::kQuad_Verb:
243 path->quadTo(ePtr[1], ePtr[2]);
244 break;
caryclark1049f122015-04-20 08:31:59 -0700245 case SkPath::kConic_Verb:
246 path->conicTo(ePtr[1], ePtr[2], eWeight);
247 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000248 case SkPath::kCubic_Verb:
249 path->cubicTo(ePtr[1], ePtr[2], ePtr[3]);
250 break;
251 default:
252 SkASSERT(0);
253 }
254 }
255 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000256}
257
caryclark54359292015-03-26 07:52:43 -0700258SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) {
259 SkOpSpanBase* existing = NULL;
260 SkOpSpanBase* test = &fHead;
261 double testT;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000262 do {
caryclark54359292015-03-26 07:52:43 -0700263 if ((testT = test->ptT()->fT) >= t) {
264 if (testT == t) {
265 existing = test;
266 }
reed0dc4dd62015-03-24 13:55:33 -0700267 break;
268 }
caryclark54359292015-03-26 07:52:43 -0700269 } while ((test = test->upCast()->next()));
270 SkOpPtT* result;
271 if (existing && existing->contains(opp)) {
272 result = existing->ptT();
273 } else {
274 result = this->addT(t, SkOpSegment::kNoAlias, allocator);
275 }
276 SkASSERT(result);
277 return result;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000278}
279
caryclark54359292015-03-26 07:52:43 -0700280SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr,
281 SkChunkAlloc* allocator) {
282 SkOpSpan* startSpan = fTail.prev();
283 SkASSERT(startSpan);
284 SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
285 *anglePtr = angle;
286 angle->set(&fTail, startSpan);
287 fTail.setFromAngle(angle);
288 SkOpSegment* other = NULL; // these initializations silence a release build warning
289 SkOpSpan* oStartSpan = NULL;
290 SkOpSpanBase* oEndSpan = NULL;
291 SkOpPtT* ptT = fTail.ptT(), * startPtT = ptT;
292 while ((ptT = ptT->next()) != startPtT) {
293 other = ptT->segment();
294 oStartSpan = ptT->span()->upCastable();
295 if (oStartSpan && oStartSpan->windValue()) {
296 oEndSpan = oStartSpan->next();
reed0dc4dd62015-03-24 13:55:33 -0700297 break;
298 }
caryclark54359292015-03-26 07:52:43 -0700299 oEndSpan = ptT->span();
300 oStartSpan = oEndSpan->prev();
301 if (oStartSpan && oStartSpan->windValue()) {
302 break;
303 }
304 }
caryclark1049f122015-04-20 08:31:59 -0700305 if (!oStartSpan) {
306 return NULL;
307 }
caryclark54359292015-03-26 07:52:43 -0700308 SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
309 oAngle->set(oStartSpan, oEndSpan);
310 oStartSpan->setToAngle(oAngle);
reed0dc4dd62015-03-24 13:55:33 -0700311 *otherPtr = other;
caryclark54359292015-03-26 07:52:43 -0700312 return oAngle;
reed0dc4dd62015-03-24 13:55:33 -0700313}
314
caryclark54359292015-03-26 07:52:43 -0700315SkOpAngle* SkOpSegment::addSingletonAngles(int step, SkChunkAlloc* allocator) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000316 SkOpSegment* other;
caryclarkdac1d172014-06-17 05:15:38 -0700317 SkOpAngle* angle, * otherAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000318 if (step > 0) {
caryclark54359292015-03-26 07:52:43 -0700319 otherAngle = addSingletonAngleUp(&other, &angle, allocator);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000320 } else {
caryclark54359292015-03-26 07:52:43 -0700321 otherAngle = addSingletonAngleDown(&other, &angle, allocator);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000322 }
caryclark1049f122015-04-20 08:31:59 -0700323 if (!otherAngle) {
324 return NULL;
325 }
caryclarkdac1d172014-06-17 05:15:38 -0700326 angle->insert(otherAngle);
327 return angle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000328}
329
caryclark54359292015-03-26 07:52:43 -0700330SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr,
331 SkChunkAlloc* allocator) {
332 SkOpSpanBase* endSpan = fHead.next();
333 SkASSERT(endSpan);
334 SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
335 *anglePtr = angle;
336 angle->set(&fHead, endSpan);
337 fHead.setToAngle(angle);
338 SkOpSegment* other = NULL; // these initializations silence a release build warning
339 SkOpSpan* oStartSpan = NULL;
340 SkOpSpanBase* oEndSpan = NULL;
341 SkOpPtT* ptT = fHead.ptT(), * startPtT = ptT;
342 while ((ptT = ptT->next()) != startPtT) {
343 other = ptT->segment();
344 oEndSpan = ptT->span();
345 oStartSpan = oEndSpan->prev();
346 if (oStartSpan && oStartSpan->windValue()) {
caryclarkccec0f92015-03-24 07:28:17 -0700347 break;
348 }
caryclark54359292015-03-26 07:52:43 -0700349 oStartSpan = oEndSpan->upCastable();
350 if (oStartSpan && oStartSpan->windValue()) {
351 oEndSpan = oStartSpan->next();
reed0dc4dd62015-03-24 13:55:33 -0700352 break;
353 }
reed0dc4dd62015-03-24 13:55:33 -0700354 }
caryclark54359292015-03-26 07:52:43 -0700355 SkOpAngle* oAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
356 oAngle->set(oEndSpan, oStartSpan);
357 oEndSpan->setFromAngle(oAngle);
358 *otherPtr = other;
359 return oAngle;
reed0dc4dd62015-03-24 13:55:33 -0700360}
361
caryclark54359292015-03-26 07:52:43 -0700362SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
reed0dc4dd62015-03-24 13:55:33 -0700363 debugValidate();
caryclark54359292015-03-26 07:52:43 -0700364 SkPoint pt = this->ptAtT(t);
365 SkOpSpanBase* span = &fHead;
366 do {
367 SkOpPtT* result = span->ptT();
368 if (t == result->fT) {
369 return result;
reed0dc4dd62015-03-24 13:55:33 -0700370 }
caryclark54359292015-03-26 07:52:43 -0700371 if (this->match(result, this, t, pt)) {
372 // see if any existing alias matches segment, pt, and t
373 SkOpPtT* loop = result->next();
374 bool duplicatePt = false;
375 while (loop != result) {
376 bool ptMatch = loop->fPt == pt;
377 if (loop->segment() == this && loop->fT == t && ptMatch) {
378 return result;
reed0dc4dd62015-03-24 13:55:33 -0700379 }
caryclark54359292015-03-26 07:52:43 -0700380 duplicatePt |= ptMatch;
381 loop = loop->next();
reed0dc4dd62015-03-24 13:55:33 -0700382 }
caryclark54359292015-03-26 07:52:43 -0700383 if (kNoAlias == allowAlias) {
384 return result;
385 }
386 SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator);
387 alias->init(result->span(), t, pt, duplicatePt);
388 result->insert(alias);
389 result->span()->unaligned();
390 this->debugValidate();
391#if DEBUG_ADD_T
392 SkDebugf("%s alias t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
393 alias->segment()->debugID(), alias->span()->debugID());
394#endif
395 return alias;
reed0dc4dd62015-03-24 13:55:33 -0700396 }
caryclark54359292015-03-26 07:52:43 -0700397 if (t < result->fT) {
398 SkOpSpan* prev = result->span()->prev();
399 SkOpSpan* span = insert(prev, allocator);
400 span->init(this, prev, t, pt);
401 this->debugValidate();
402#if DEBUG_ADD_T
403 SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
404 span->segment()->debugID(), span->debugID());
405#endif
406 return span->ptT();
407 }
408 SkASSERT(span != &fTail);
409 } while ((span = span->upCast()->next()));
410 SkASSERT(0);
411 return NULL;
412}
413
414// choose a solitary t and pt value; remove aliases; align the opposite ends
415void SkOpSegment::align() {
416 debugValidate();
417 SkOpSpanBase* span = &fHead;
418 if (!span->aligned()) {
419 span->alignEnd(0, fPts[0]);
420 }
421 while ((span = span->upCast()->next())) {
422 if (span == &fTail) {
423 break;
424 }
425 span->align();
426 }
427 if (!span->aligned()) {
428 span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
reed0dc4dd62015-03-24 13:55:33 -0700429 }
430 debugValidate();
431}
432
caryclark54359292015-03-26 07:52:43 -0700433bool SkOpSegment::BetweenTs(const SkOpSpanBase* lesser, double testT,
434 const SkOpSpanBase* greater) {
435 if (lesser->t() > greater->t()) {
436 SkTSwap<const SkOpSpanBase*>(lesser, greater);
reed0dc4dd62015-03-24 13:55:33 -0700437 }
caryclark54359292015-03-26 07:52:43 -0700438 return approximately_between(lesser->t(), testT, greater->t());
reed0dc4dd62015-03-24 13:55:33 -0700439}
440
caryclark54359292015-03-26 07:52:43 -0700441void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
442 bool activePrior = !fHead.isCanceled();
443 if (activePrior && !fHead.simple()) {
444 addStartSpan(allocator);
reed0dc4dd62015-03-24 13:55:33 -0700445 }
caryclark54359292015-03-26 07:52:43 -0700446 SkOpSpan* prior = &fHead;
447 SkOpSpanBase* spanBase = fHead.next();
448 while (spanBase != &fTail) {
449 if (activePrior) {
450 SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
451 priorAngle->set(spanBase, prior);
452 spanBase->setFromAngle(priorAngle);
reed0dc4dd62015-03-24 13:55:33 -0700453 }
caryclark54359292015-03-26 07:52:43 -0700454 SkOpSpan* span = spanBase->upCast();
455 bool active = !span->isCanceled();
456 SkOpSpanBase* next = span->next();
457 if (active) {
458 SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
459 angle->set(span, next);
460 span->setToAngle(angle);
reed0dc4dd62015-03-24 13:55:33 -0700461 }
reed0dc4dd62015-03-24 13:55:33 -0700462 activePrior = active;
caryclark54359292015-03-26 07:52:43 -0700463 prior = span;
464 spanBase = next;
reed0dc4dd62015-03-24 13:55:33 -0700465 }
caryclark54359292015-03-26 07:52:43 -0700466 if (activePrior && !fTail.simple()) {
467 addEndSpan(allocator);
reed0dc4dd62015-03-24 13:55:33 -0700468 }
reed0dc4dd62015-03-24 13:55:33 -0700469}
470
caryclark54359292015-03-26 07:52:43 -0700471void SkOpSegment::checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
472 SkOpSpanBase* base = &fHead;
473 SkOpSpan* span;
reed0dc4dd62015-03-24 13:55:33 -0700474 do {
caryclark54359292015-03-26 07:52:43 -0700475 SkOpAngle* angle = base->fromAngle();
476 if (angle && angle->fCheckCoincidence) {
477 angle->checkNearCoincidence();
reed0dc4dd62015-03-24 13:55:33 -0700478 }
caryclark54359292015-03-26 07:52:43 -0700479 if (base->final()) {
480 break;
reed0dc4dd62015-03-24 13:55:33 -0700481 }
caryclark54359292015-03-26 07:52:43 -0700482 span = base->upCast();
483 angle = span->toAngle();
484 if (angle && angle->fCheckCoincidence) {
485 angle->checkNearCoincidence();
reed0dc4dd62015-03-24 13:55:33 -0700486 }
caryclark54359292015-03-26 07:52:43 -0700487 } while ((base = span->next()));
488}
489
490// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
491bool SkOpSegment::clockwise(const SkOpSpanBase* start, const SkOpSpanBase* end, bool* swap) const {
492 SkASSERT(fVerb != SkPath::kLine_Verb);
caryclark03b03ca2015-04-23 09:13:37 -0700493 if (fVerb != SkPath::kCubic_Verb) {
494 SkOpCurve edge;
495 this->subDivide(start, end, &edge);
496 return SkDQuad::Clockwise(edge, swap);
reed0dc4dd62015-03-24 13:55:33 -0700497 }
caryclark03b03ca2015-04-23 09:13:37 -0700498 return SkDCubic::Clockwise(fPts, start->t(), end->t(), swap);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000499}
500
caryclark@google.com570863f2013-09-16 15:55:01 +0000501void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
502 SkOpAngle::IncludeType includeType) {
503 const SkOpSegment* baseSegment = baseAngle->segment();
504 int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
505 int sumSuWinding;
506 bool binary = includeType >= SkOpAngle::kBinarySingle;
507 if (binary) {
508 sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
509 if (baseSegment->operand()) {
510 SkTSwap<int>(sumMiWinding, sumSuWinding);
511 }
512 }
513 SkOpSegment* nextSegment = nextAngle->segment();
514 int maxWinding, sumWinding;
caryclark54359292015-03-26 07:52:43 -0700515 SkOpSpanBase* last;
caryclark@google.com570863f2013-09-16 15:55:01 +0000516 if (binary) {
517 int oppMaxWinding, oppSumWinding;
518 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
519 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
520 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000521 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +0000522 } else {
523 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
524 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000525 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +0000526 }
527 nextAngle->setLastMarked(last);
528}
529
530void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
531 SkOpAngle::IncludeType includeType) {
532 const SkOpSegment* baseSegment = baseAngle->segment();
533 int sumMiWinding = baseSegment->updateWinding(baseAngle);
534 int sumSuWinding;
535 bool binary = includeType >= SkOpAngle::kBinarySingle;
536 if (binary) {
537 sumSuWinding = baseSegment->updateOppWinding(baseAngle);
538 if (baseSegment->operand()) {
539 SkTSwap<int>(sumMiWinding, sumSuWinding);
540 }
541 }
542 SkOpSegment* nextSegment = nextAngle->segment();
543 int maxWinding, sumWinding;
caryclark54359292015-03-26 07:52:43 -0700544 SkOpSpanBase* last;
caryclark@google.com570863f2013-09-16 15:55:01 +0000545 if (binary) {
546 int oppMaxWinding, oppSumWinding;
547 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
548 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
549 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000550 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +0000551 } else {
552 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
553 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000554 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +0000555 }
556 nextAngle->setLastMarked(last);
557}
558
caryclark54359292015-03-26 07:52:43 -0700559// at this point, the span is already ordered, or unorderable
560int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
561 SkOpAngle::IncludeType includeType) {
562 SkASSERT(includeType != SkOpAngle::kUnaryXor);
563 SkOpAngle* firstAngle = this->spanToAngle(end, start);
564 if (NULL == firstAngle || NULL == firstAngle->next()) {
565 return SK_NaN32;
566 }
567 // if all angles have a computed winding,
568 // or if no adjacent angles are orderable,
569 // or if adjacent orderable angles have no computed winding,
570 // there's nothing to do
571 // if two orderable angles are adjacent, and both are next to orderable angles,
572 // and one has winding computed, transfer to the other
573 SkOpAngle* baseAngle = NULL;
574 bool tryReverse = false;
575 // look for counterclockwise transfers
576 SkOpAngle* angle = firstAngle->previous();
577 SkOpAngle* next = angle->next();
578 firstAngle = next;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000579 do {
caryclark54359292015-03-26 07:52:43 -0700580 SkOpAngle* prior = angle;
581 angle = next;
582 next = angle->next();
583 SkASSERT(prior->next() == angle);
584 SkASSERT(angle->next() == next);
585 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
586 baseAngle = NULL;
587 continue;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000588 }
caryclark54359292015-03-26 07:52:43 -0700589 int testWinding = angle->starter()->windSum();
590 if (SK_MinS32 != testWinding) {
591 baseAngle = angle;
592 tryReverse = true;
593 continue;
reed0dc4dd62015-03-24 13:55:33 -0700594 }
caryclark54359292015-03-26 07:52:43 -0700595 if (baseAngle) {
596 ComputeOneSum(baseAngle, angle, includeType);
597 baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
598 }
599 } while (next != firstAngle);
600 if (baseAngle && SK_MinS32 == firstAngle->starter()->windSum()) {
601 firstAngle = baseAngle;
602 tryReverse = true;
603 }
604 if (tryReverse) {
605 baseAngle = NULL;
606 SkOpAngle* prior = firstAngle;
607 do {
608 angle = prior;
609 prior = angle->previous();
610 SkASSERT(prior->next() == angle);
611 next = angle->next();
612 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
613 baseAngle = NULL;
reed0dc4dd62015-03-24 13:55:33 -0700614 continue;
615 }
caryclark54359292015-03-26 07:52:43 -0700616 int testWinding = angle->starter()->windSum();
617 if (SK_MinS32 != testWinding) {
618 baseAngle = angle;
619 continue;
reed0dc4dd62015-03-24 13:55:33 -0700620 }
caryclark54359292015-03-26 07:52:43 -0700621 if (baseAngle) {
622 ComputeOneSumReverse(baseAngle, angle, includeType);
623 baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
reed0dc4dd62015-03-24 13:55:33 -0700624 }
caryclark54359292015-03-26 07:52:43 -0700625 } while (prior != firstAngle);
reed0dc4dd62015-03-24 13:55:33 -0700626 }
caryclark54359292015-03-26 07:52:43 -0700627 return start->starter(end)->windSum();
reed0dc4dd62015-03-24 13:55:33 -0700628}
629
caryclark54359292015-03-26 07:52:43 -0700630SkOpSpan* SkOpSegment::crossedSpanY(const SkPoint& basePt, double mid, bool opp, bool current,
631 SkScalar* bestY, double* hitT, bool* hitSomething, bool* vertical) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000632 SkScalar bottom = fBounds.fBottom;
caryclark54359292015-03-26 07:52:43 -0700633 *vertical = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000634 if (bottom <= *bestY) {
caryclark54359292015-03-26 07:52:43 -0700635 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000636 }
637 SkScalar top = fBounds.fTop;
638 if (top >= basePt.fY) {
caryclark54359292015-03-26 07:52:43 -0700639 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000640 }
641 if (fBounds.fLeft > basePt.fX) {
caryclark54359292015-03-26 07:52:43 -0700642 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000643 }
644 if (fBounds.fRight < basePt.fX) {
caryclark54359292015-03-26 07:52:43 -0700645 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000646 }
647 if (fBounds.fLeft == fBounds.fRight) {
648 // if vertical, and directly above test point, wait for another one
caryclark54359292015-03-26 07:52:43 -0700649 *vertical = AlmostEqualUlps(basePt.fX, fBounds.fLeft);
650 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000651 }
652 // intersect ray starting at basePt with edge
653 SkIntersections intersections;
654 // OPTIMIZE: use specialty function that intersects ray with curve,
655 // returning t values only for curve (we don't care about t on ray)
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000656 intersections.allowNear(false);
caryclark1049f122015-04-20 08:31:59 -0700657 int pts = (intersections.*CurveVertical[fVerb])(fPts, fWeight, top, bottom, basePt.fX, false);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000658 if (pts == 0 || (current && pts == 1)) {
caryclark54359292015-03-26 07:52:43 -0700659 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000660 }
661 if (current) {
662 SkASSERT(pts > 1);
663 int closestIdx = 0;
664 double closest = fabs(intersections[0][0] - mid);
665 for (int idx = 1; idx < pts; ++idx) {
666 double test = fabs(intersections[0][idx] - mid);
667 if (closest > test) {
668 closestIdx = idx;
669 closest = test;
670 }
671 }
672 intersections.quickRemoveOne(closestIdx, --pts);
673 }
674 double bestT = -1;
675 for (int index = 0; index < pts; ++index) {
676 double foundT = intersections[0][index];
677 if (approximately_less_than_zero(foundT)
678 || approximately_greater_than_one(foundT)) {
679 continue;
680 }
caryclark1049f122015-04-20 08:31:59 -0700681 SkScalar testY = (*CurvePointAtT[fVerb])(fPts, fWeight, foundT).fY;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000682 if (approximately_negative(testY - *bestY)
683 || approximately_negative(basePt.fY - testY)) {
684 continue;
685 }
686 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
caryclark54359292015-03-26 07:52:43 -0700687 *vertical = true;
688 return NULL; // if the intersection is edge on, wait for another one
caryclark@google.com07393ca2013-04-08 11:47:37 +0000689 }
690 if (fVerb > SkPath::kLine_Verb) {
caryclark1049f122015-04-20 08:31:59 -0700691 SkScalar dx = (*CurveSlopeAtT[fVerb])(fPts, fWeight, foundT).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000692 if (approximately_zero(dx)) {
caryclark54359292015-03-26 07:52:43 -0700693 *vertical = true;
694 return NULL; // hit vertical, wait for another one
caryclark@google.com07393ca2013-04-08 11:47:37 +0000695 }
696 }
697 *bestY = testY;
698 bestT = foundT;
699 }
700 if (bestT < 0) {
caryclark54359292015-03-26 07:52:43 -0700701 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000702 }
703 SkASSERT(bestT >= 0);
caryclark54359292015-03-26 07:52:43 -0700704 SkASSERT(bestT < 1);
705 SkOpSpanBase* testTSpanBase = &this->fHead;
706 SkOpSpanBase* nextTSpan;
707 double endT = 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000708 do {
caryclark54359292015-03-26 07:52:43 -0700709 nextTSpan = testTSpanBase->upCast()->next();
710 endT = nextTSpan->t();
711 if (endT >= bestT) {
712 break;
713 }
714 testTSpanBase = nextTSpan;
715 } while (testTSpanBase);
716 SkOpSpan* bestTSpan = NULL;
717 SkOpSpan* testTSpan = testTSpanBase->upCast();
718 if (!testTSpan->isCanceled()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000719 *hitT = bestT;
caryclark54359292015-03-26 07:52:43 -0700720 bestTSpan = testTSpan;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000721 *hitSomething = true;
722 }
caryclark54359292015-03-26 07:52:43 -0700723 return bestTSpan;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000724}
725
caryclark54359292015-03-26 07:52:43 -0700726void SkOpSegment::detach(const SkOpSpan* span) {
727 if (span->done()) {
728 --this->fDoneCount;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000729 }
caryclark54359292015-03-26 07:52:43 -0700730 --this->fCount;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000731}
732
caryclark54359292015-03-26 07:52:43 -0700733double SkOpSegment::distSq(double t, SkOpAngle* oppAngle) {
734 SkDPoint testPt = this->dPtAtT(t);
735 SkDLine testPerp = {{ testPt, testPt }};
736 SkDVector slope = this->dSlopeAtT(t);
737 testPerp[1].fX += slope.fY;
738 testPerp[1].fY -= slope.fX;
739 SkIntersections i;
740 SkOpSegment* oppSegment = oppAngle->segment();
caryclark1049f122015-04-20 08:31:59 -0700741 (*CurveIntersectRay[oppSegment->verb()])(oppSegment->pts(), oppSegment->weight(), testPerp, &i);
caryclark54359292015-03-26 07:52:43 -0700742 double closestDistSq = SK_ScalarInfinity;
743 for (int index = 0; index < i.used(); ++index) {
744 if (!between(oppAngle->start()->t(), i[0][index], oppAngle->end()->t())) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000745 continue;
746 }
caryclark54359292015-03-26 07:52:43 -0700747 double testDistSq = testPt.distanceSquared(i.pt(index));
748 if (closestDistSq > testDistSq) {
749 closestDistSq = testDistSq;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000750 }
751 }
caryclark54359292015-03-26 07:52:43 -0700752 return closestDistSq;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000753}
754
caryclark@google.com07393ca2013-04-08 11:47:37 +0000755/*
756 The M and S variable name parts stand for the operators.
757 Mi stands for Minuend (see wiki subtraction, analogous to difference)
758 Su stands for Subtrahend
759 The Opp variable name part designates that the value is for the Opposite operator.
760 Opposite values result from combining coincident spans.
761 */
caryclark54359292015-03-26 07:52:43 -0700762SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
763 SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op, int xorMiMask, int xorSuMask) {
764 SkOpSpanBase* start = *nextStart;
765 SkOpSpanBase* end = *nextEnd;
766 SkASSERT(start != end);
767 int step = start->step(end);
768 SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
769 if (other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000770 // mark the smaller of startIndex, endIndex done, and all adjacent
771 // spans with the same T value (but not 'other' spans)
772#if DEBUG_WINDING
773 SkDebugf("%s simple\n", __FUNCTION__);
774#endif
caryclark54359292015-03-26 07:52:43 -0700775 SkOpSpan* startSpan = start->starter(end);
776 if (startSpan->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000777 return NULL;
778 }
caryclark54359292015-03-26 07:52:43 -0700779 markDone(startSpan);
780 *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000781 return other;
782 }
caryclark54359292015-03-26 07:52:43 -0700783 SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
784 SkASSERT(endNear == end); // is this ever not end?
785 SkASSERT(endNear);
786 SkASSERT(start != endNear);
787 SkASSERT((start->t() < endNear->t()) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000788 // more than one viable candidate -- measure angles to find best
caryclark54359292015-03-26 07:52:43 -0700789 int calcWinding = computeSum(start, endNear, SkOpAngle::kBinaryOpp);
caryclark@google.com570863f2013-09-16 15:55:01 +0000790 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000791 if (!sortable) {
792 *unsortable = true;
caryclark54359292015-03-26 07:52:43 -0700793 markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000794 return NULL;
795 }
caryclark54359292015-03-26 07:52:43 -0700796 SkOpAngle* angle = this->spanToAngle(end, start);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000797 if (angle->unorderable()) {
798 *unsortable = true;
caryclark54359292015-03-26 07:52:43 -0700799 markDone(start->starter(end));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000800 return NULL;
801 }
802#if DEBUG_SORT
803 SkDebugf("%s\n", __FUNCTION__);
804 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000805#endif
caryclark54359292015-03-26 07:52:43 -0700806 int sumMiWinding = updateWinding(end, start);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000807 if (sumMiWinding == SK_MinS32) {
808 *unsortable = true;
caryclark54359292015-03-26 07:52:43 -0700809 markDone(start->starter(end));
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000810 return NULL;
811 }
caryclark54359292015-03-26 07:52:43 -0700812 int sumSuWinding = updateOppWinding(end, start);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000813 if (operand()) {
814 SkTSwap<int>(sumMiWinding, sumSuWinding);
815 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000816 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000817 const SkOpAngle* foundAngle = NULL;
818 bool foundDone = false;
819 // iterate through the angle, and compute everyone's winding
820 SkOpSegment* nextSegment;
821 int activeCount = 0;
822 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000823 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000824 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000825 nextAngle->end(), op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000826 if (activeAngle) {
827 ++activeCount;
828 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000829 foundAngle = nextAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +0000830 foundDone = nextSegment->done(nextAngle);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000831 }
832 }
833 if (nextSegment->done()) {
834 continue;
835 }
reed0dc4dd62015-03-24 13:55:33 -0700836 if (!activeAngle) {
caryclark54359292015-03-26 07:52:43 -0700837 (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
reed0dc4dd62015-03-24 13:55:33 -0700838 }
caryclark54359292015-03-26 07:52:43 -0700839 SkOpSpanBase* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000840 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000841 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000842 *chase->append() = last;
843#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -0700844 SkDebugf("%s chase.append segment=%d span=%d", __FUNCTION__,
845 last->segment()->debugID(), last->debugID());
846 if (!last->final()) {
847 SkDebugf(" windSum=%d", last->upCast()->windSum());
848 }
849 SkDebugf("\n");
caryclark@google.com07393ca2013-04-08 11:47:37 +0000850#endif
851 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000852 } while ((nextAngle = nextAngle->next()) != angle);
caryclark54359292015-03-26 07:52:43 -0700853 start->segment()->markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000854 if (!foundAngle) {
855 return NULL;
856 }
857 *nextStart = foundAngle->start();
858 *nextEnd = foundAngle->end();
859 nextSegment = foundAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000860#if DEBUG_WINDING
861 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
862 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
863 #endif
864 return nextSegment;
865}
866
caryclark54359292015-03-26 07:52:43 -0700867SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
868 SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable) {
869 SkOpSpanBase* start = *nextStart;
870 SkOpSpanBase* end = *nextEnd;
871 SkASSERT(start != end);
872 int step = start->step(end);
873 SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
874 if (other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000875 // mark the smaller of startIndex, endIndex done, and all adjacent
876 // spans with the same T value (but not 'other' spans)
877#if DEBUG_WINDING
878 SkDebugf("%s simple\n", __FUNCTION__);
879#endif
caryclark54359292015-03-26 07:52:43 -0700880 SkOpSpan* startSpan = start->starter(end);
881 if (startSpan->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000882 return NULL;
883 }
caryclark54359292015-03-26 07:52:43 -0700884 markDone(startSpan);
885 *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000886 return other;
887 }
caryclark54359292015-03-26 07:52:43 -0700888 SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
889 SkASSERT(endNear == end); // is this ever not end?
890 SkASSERT(endNear);
891 SkASSERT(start != endNear);
892 SkASSERT((start->t() < endNear->t()) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000893 // more than one viable candidate -- measure angles to find best
caryclark54359292015-03-26 07:52:43 -0700894 int calcWinding = computeSum(start, endNear, SkOpAngle::kUnaryWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +0000895 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000896 if (!sortable) {
897 *unsortable = true;
caryclark54359292015-03-26 07:52:43 -0700898 markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000899 return NULL;
900 }
caryclark54359292015-03-26 07:52:43 -0700901 SkOpAngle* angle = this->spanToAngle(end, start);
902 if (angle->unorderable()) {
903 *unsortable = true;
904 markDone(start->starter(end));
905 return NULL;
906 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000907#if DEBUG_SORT
908 SkDebugf("%s\n", __FUNCTION__);
909 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000910#endif
caryclark54359292015-03-26 07:52:43 -0700911 int sumWinding = updateWinding(end, start);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000912 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000913 const SkOpAngle* foundAngle = NULL;
914 bool foundDone = false;
caryclark54359292015-03-26 07:52:43 -0700915 // iterate through the angle, and compute everyone's winding
caryclark@google.com07393ca2013-04-08 11:47:37 +0000916 SkOpSegment* nextSegment;
917 int activeCount = 0;
918 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000919 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000920 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000921 &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000922 if (activeAngle) {
923 ++activeCount;
924 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000925 foundAngle = nextAngle;
926 foundDone = nextSegment->done(nextAngle);
927 }
928 }
929 if (nextSegment->done()) {
930 continue;
931 }
reed0dc4dd62015-03-24 13:55:33 -0700932 if (!activeAngle) {
caryclark54359292015-03-26 07:52:43 -0700933 (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
reed0dc4dd62015-03-24 13:55:33 -0700934 }
caryclark54359292015-03-26 07:52:43 -0700935 SkOpSpanBase* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000936 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000937 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000938 *chase->append() = last;
939#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -0700940 SkDebugf("%s chase.append segment=%d span=%d", __FUNCTION__,
941 last->segment()->debugID(), last->debugID());
942 if (!last->final()) {
943 SkDebugf(" windSum=%d", last->upCast()->windSum());
944 }
945 SkDebugf("\n");
caryclark@google.com07393ca2013-04-08 11:47:37 +0000946#endif
947 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000948 } while ((nextAngle = nextAngle->next()) != angle);
caryclark54359292015-03-26 07:52:43 -0700949 start->segment()->markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000950 if (!foundAngle) {
951 return NULL;
952 }
953 *nextStart = foundAngle->start();
954 *nextEnd = foundAngle->end();
955 nextSegment = foundAngle->segment();
956#if DEBUG_WINDING
957 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
958 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
959 #endif
960 return nextSegment;
961}
962
caryclark54359292015-03-26 07:52:43 -0700963SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd,
964 bool* unsortable) {
965 SkOpSpanBase* start = *nextStart;
966 SkOpSpanBase* end = *nextEnd;
967 SkASSERT(start != end);
968 int step = start->step(end);
969 SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
970 if (other) {
971 // mark the smaller of startIndex, endIndex done, and all adjacent
972 // spans with the same T value (but not 'other' spans)
caryclark@google.com07393ca2013-04-08 11:47:37 +0000973#if DEBUG_WINDING
974 SkDebugf("%s simple\n", __FUNCTION__);
975#endif
caryclark54359292015-03-26 07:52:43 -0700976 SkOpSpan* startSpan = start->starter(end);
977 if (startSpan->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000978 return NULL;
979 }
caryclark54359292015-03-26 07:52:43 -0700980 markDone(startSpan);
981 *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000982 return other;
983 }
caryclark54359292015-03-26 07:52:43 -0700984 SkDEBUGCODE(SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() \
985 : (*nextStart)->prev());
986 SkASSERT(endNear == end); // is this ever not end?
987 SkASSERT(endNear);
988 SkASSERT(start != endNear);
989 SkASSERT((start->t() < endNear->t()) ^ (step < 0));
990 SkOpAngle* angle = this->spanToAngle(end, start);
991 if (angle->unorderable()) {
992 *unsortable = true;
993 markDone(start->starter(end));
994 return NULL;
995 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000996#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000997 SkDebugf("%s\n", __FUNCTION__);
998 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000999#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001000 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001001 const SkOpAngle* foundAngle = NULL;
1002 bool foundDone = false;
caryclark54359292015-03-26 07:52:43 -07001003 // iterate through the angle, and compute everyone's winding
caryclark@google.com07393ca2013-04-08 11:47:37 +00001004 SkOpSegment* nextSegment;
1005 int activeCount = 0;
1006 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001007 nextSegment = nextAngle->segment();
1008 ++activeCount;
1009 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001010 foundAngle = nextAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001011 if (!(foundDone = nextSegment->done(nextAngle))) {
1012 break;
1013 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001014 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001015 nextAngle = nextAngle->next();
1016 } while (nextAngle != angle);
caryclark54359292015-03-26 07:52:43 -07001017 start->segment()->markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001018 if (!foundAngle) {
1019 return NULL;
1020 }
1021 *nextStart = foundAngle->start();
1022 *nextEnd = foundAngle->end();
1023 nextSegment = foundAngle->segment();
1024#if DEBUG_WINDING
1025 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
1026 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
1027 #endif
1028 return nextSegment;
1029}
1030
caryclark54359292015-03-26 07:52:43 -07001031SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
1032 bool* unsortable, SkChunkAlloc* allocator) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001033 // iterate through T intersections and return topmost
1034 // topmost tangent from y-min to first pt is closer to horizontal
1035 SkASSERT(!done());
caryclark54359292015-03-26 07:52:43 -07001036 SkOpSpanBase* firstT = NULL;
1037 (void) this->activeLeftTop(&firstT);
1038 if (!firstT) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001039 *unsortable = !firstPass;
caryclark54359292015-03-26 07:52:43 -07001040 firstT = &fHead;
1041 while (firstT->upCast()->done()) {
1042 firstT = firstT->upCast()->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001043 }
caryclark54359292015-03-26 07:52:43 -07001044 *startPtr = firstT;
1045 *endPtr = firstT->upCast()->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001046 return this;
1047 }
1048 // sort the edges to find the leftmost
1049 int step = 1;
caryclark54359292015-03-26 07:52:43 -07001050 SkOpSpanBase* end;
1051 if (firstT->final() || firstT->upCast()->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001052 step = -1;
caryclark54359292015-03-26 07:52:43 -07001053 end = firstT->prev();
1054 SkASSERT(end);
1055 } else {
1056 end = firstT->upCast()->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001057 }
1058 // if the topmost T is not on end, or is three-way or more, find left
1059 // look for left-ness from tLeft to firstT (matching y of other)
caryclark54359292015-03-26 07:52:43 -07001060 SkASSERT(firstT != end);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001061 SkOpAngle* markAngle = spanToAngle(firstT, end);
1062 if (!markAngle) {
caryclark54359292015-03-26 07:52:43 -07001063 markAngle = addSingletonAngles(step, allocator);
caryclark@google.com570863f2013-09-16 15:55:01 +00001064 }
caryclark1049f122015-04-20 08:31:59 -07001065 if (!markAngle) {
1066 return NULL;
1067 }
1068 if (!markAngle->markStops()) {
1069 return NULL;
1070 }
caryclarke4097e32014-06-18 07:24:19 -07001071 const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
1072 : markAngle->findFirst();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001073 if (!baseAngle) {
1074 return NULL; // nothing to do
caryclark@google.com570863f2013-09-16 15:55:01 +00001075 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001076 SkScalar top = SK_ScalarMax;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001077 const SkOpAngle* firstAngle = NULL;
1078 const SkOpAngle* angle = baseAngle;
caryclark03b03ca2015-04-23 09:13:37 -07001079#if DEBUG_SWAP_TOP
1080 SkDebugf("-%s- baseAngle\n", __FUNCTION__);
1081 baseAngle->debugLoop();
1082#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001083 do {
1084 if (!angle->unorderable()) {
caryclark54359292015-03-26 07:52:43 -07001085 const SkOpSegment* next = angle->segment();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001086 SkPathOpsBounds bounds;
1087 next->subDivideBounds(angle->end(), angle->start(), &bounds);
caryclark65f55312014-11-13 06:58:52 -08001088 bool nearSame = AlmostEqualUlps(top, bounds.top());
1089 bool lowerSector = !firstAngle || angle->sectorEnd() < firstAngle->sectorStart();
1090 bool lesserSector = top > bounds.fTop;
1091 if (lesserSector && (!nearSame || lowerSector)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001092 top = bounds.fTop;
1093 firstAngle = angle;
1094 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001095 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001096 angle = angle->next();
1097 } while (angle != baseAngle);
caryclark65f55312014-11-13 06:58:52 -08001098 if (!firstAngle) {
1099 return NULL; // if all are unorderable, give up
1100 }
caryclark03b03ca2015-04-23 09:13:37 -07001101#if DEBUG_SWAP_TOP
1102 SkDebugf("-%s- firstAngle\n", __FUNCTION__);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001103 firstAngle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001104#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00001105 // skip edges that have already been processed
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001106 angle = firstAngle;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001107 SkOpSegment* leftSegment = NULL;
1108 bool looped = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001109 do {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001110 *unsortable = angle->unorderable();
1111 if (firstPass || !*unsortable) {
1112 leftSegment = angle->segment();
caryclark54359292015-03-26 07:52:43 -07001113 *startPtr = angle->end();
1114 *endPtr = angle->start();
1115 const SkOpSpan* firstSpan = (*startPtr)->starter(*endPtr);
1116 if (!firstSpan->done()) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001117 break;
1118 }
1119 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001120 angle = angle->next();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001121 looped = true;
1122 } while (angle != firstAngle);
1123 if (angle == firstAngle && looped) {
1124 return NULL;
1125 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001126 if (leftSegment->verb() >= SkPath::kQuad_Verb) {
caryclark54359292015-03-26 07:52:43 -07001127 SkOpSpanBase* start = *startPtr;
1128 SkOpSpanBase* end = *endPtr;
caryclarke4097e32014-06-18 07:24:19 -07001129 bool swap;
caryclark03b03ca2015-04-23 09:13:37 -07001130 bool cw = leftSegment->clockwise(start, end, &swap);
1131#if DEBUG_SWAP_TOP
1132 SkDebugf("%s id=%d s=%1.9g e=%1.9g (%c) cw=%d swap=%d inflections=%d monotonic=%d\n",
1133 __FUNCTION__, leftSegment->debugID(), start->t(), end->t(),
1134 start->t() < end->t() ? '-' : '+', cw,
1135 swap, leftSegment->debugInflections(start, end),
1136 leftSegment->monotonicInY(start, end));
1137#endif
1138 if (!cw && swap) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001139 // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
1140 // sorted but merely the first not already processed (i.e., not done)
caryclark03b03ca2015-04-23 09:13:37 -07001141 SkTSwap(*startPtr, *endPtr);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001142 }
caryclark1049f122015-04-20 08:31:59 -07001143 // FIXME: clockwise isn't reliable -- try computing swap from tangent ?
caryclark03b03ca2015-04-23 09:13:37 -07001144 } else {
1145#if DEBUG_SWAP_TOP
1146 SkDebugf("%s id=%d s=%1.9g e=%1.9g (%c) cw=%d swap=%d inflections=%d monotonic=%d\n",
1147 __FUNCTION__, leftSegment->debugID(), (*startPtr)->t(), (*endPtr)->t(),
1148 (*startPtr)->t() < (*endPtr)->t() ? '-' : '+', -1, -1, -1, 1);
1149#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00001150 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001151 return leftSegment;
1152}
1153
caryclark54359292015-03-26 07:52:43 -07001154SkOpGlobalState* SkOpSegment::globalState() const {
1155 return contour()->globalState();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001156}
1157
caryclark1049f122015-04-20 08:31:59 -07001158void SkOpSegment::init(SkPoint pts[], SkScalar weight, SkOpContour* contour, SkPath::Verb verb) {
caryclark54359292015-03-26 07:52:43 -07001159 fContour = contour;
1160 fNext = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001161 fPts = pts;
caryclark1049f122015-04-20 08:31:59 -07001162 fWeight = weight;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001163 fVerb = verb;
caryclark03b03ca2015-04-23 09:13:37 -07001164 fCubicType = SkDCubic::kUnsplit_SkDCubicType;
caryclark54359292015-03-26 07:52:43 -07001165 fCount = 0;
1166 fDoneCount = 0;
1167 fVisited = false;
1168 SkOpSpan* zeroSpan = &fHead;
1169 zeroSpan->init(this, NULL, 0, fPts[0]);
1170 SkOpSpanBase* oneSpan = &fTail;
1171 zeroSpan->setNext(oneSpan);
1172 oneSpan->initBase(this, zeroSpan, 1, fPts[SkPathOpsVerbToPoints(fVerb)]);
caryclark1049f122015-04-20 08:31:59 -07001173 SkDEBUGCODE(fID = globalState()->nextSegmentID());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001174}
1175
caryclark54359292015-03-26 07:52:43 -07001176void SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end,
1177 SkOpAngle::IncludeType angleIncludeType) {
1178 int local = SkOpSegment::SpanSign(start, end);
caryclark65f55312014-11-13 06:58:52 -08001179 SkDEBUGCODE(bool success);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001180 if (angleIncludeType == SkOpAngle::kBinarySingle) {
caryclark54359292015-03-26 07:52:43 -07001181 int oppLocal = SkOpSegment::OppSign(start, end);
caryclark65f55312014-11-13 06:58:52 -08001182 SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, oppLocal, NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001183 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08001184 SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, oppLocal, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001185 } else {
caryclark65f55312014-11-13 06:58:52 -08001186 SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001187 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08001188 SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001189 }
caryclark65f55312014-11-13 06:58:52 -08001190 SkASSERT(success);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001191}
1192
1193/*
1194when we start with a vertical intersect, we try to use the dx to determine if the edge is to
1195the left or the right of vertical. This determines if we need to add the span's
1196sign or not. However, this isn't enough.
1197If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
1198If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
1199from has the same x direction as this span, the winding should change. If the dx is opposite, then
1200the same winding is shared by both.
1201*/
caryclark54359292015-03-26 07:52:43 -07001202bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHit,
1203 int winding, SkScalar hitDx, int oppWind, SkScalar hitOppDx) {
1204 SkASSERT(this == start->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001205 SkASSERT(hitDx || !winding);
caryclark1049f122015-04-20 08:31:59 -07001206 SkScalar dx = (*CurveSlopeAtT[fVerb])(fPts, fWeight, tHit).fX;
caryclark54359292015-03-26 07:52:43 -07001207// SkASSERT(dx);
1208 int windVal = start->starter(end)->windValue();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001209#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07001210 SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), winding,
caryclark@google.com07393ca2013-04-08 11:47:37 +00001211 hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
1212#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001213 int sideWind = winding + (dx < 0 ? windVal : -windVal);
1214 if (abs(winding) < abs(sideWind)) {
1215 winding = sideWind;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001216 }
caryclark54359292015-03-26 07:52:43 -07001217 SkDEBUGCODE(int oppLocal = SkOpSegment::OppSign(start, end));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001218 SkASSERT(hitOppDx || !oppWind || !oppLocal);
caryclark54359292015-03-26 07:52:43 -07001219 int oppWindVal = start->starter(end)->oppValue();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001220 if (!oppWind) {
1221 oppWind = dx < 0 ? oppWindVal : -oppWindVal;
1222 } else if (hitOppDx * dx >= 0) {
1223 int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
1224 if (abs(oppWind) < abs(oppSideWind)) {
1225 oppWind = oppSideWind;
1226 }
1227 }
caryclarkdac1d172014-06-17 05:15:38 -07001228#if DEBUG_WINDING_AT_T
1229 SkDebugf(" winding=%d oppWind=%d\n", winding, oppWind);
1230#endif
caryclark65f55312014-11-13 06:58:52 -08001231 // if this fails to mark (because the edges are too small) inform caller to try again
1232 bool success = markAndChaseWinding(start, end, winding, oppWind, NULL);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001233 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08001234 success |= markAndChaseWinding(end, start, winding, oppWind, NULL);
1235 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001236}
1237
caryclark54359292015-03-26 07:52:43 -07001238bool SkOpSegment::isClose(double t, const SkOpSegment* opp) const {
1239 SkDPoint cPt = this->dPtAtT(t);
caryclark1049f122015-04-20 08:31:59 -07001240 SkDVector dxdy = (*CurveDSlopeAtT[this->verb()])(this->pts(), this->weight(), t);
caryclark54359292015-03-26 07:52:43 -07001241 SkDLine perp = {{ cPt, {cPt.fX + dxdy.fY, cPt.fY - dxdy.fX} }};
1242 SkIntersections i;
caryclark1049f122015-04-20 08:31:59 -07001243 (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), perp, &i);
caryclark54359292015-03-26 07:52:43 -07001244 int used = i.used();
1245 for (int index = 0; index < used; ++index) {
1246 if (cPt.roughlyEqual(i.pt(index))) {
reed0dc4dd62015-03-24 13:55:33 -07001247 return true;
1248 }
caryclark54359292015-03-26 07:52:43 -07001249 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00001250 return false;
1251}
1252
caryclark54359292015-03-26 07:52:43 -07001253bool SkOpSegment::isXor() const {
1254 return fContour->isXor();
1255}
caryclark@google.com07393ca2013-04-08 11:47:37 +00001256
caryclark54359292015-03-26 07:52:43 -07001257SkOpSpanBase* SkOpSegment::markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end) {
1258 int step = start->step(end);
1259 SkOpSpan* minSpan = start->starter(end);
1260 markDone(minSpan);
1261 SkOpSpanBase* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001262 SkOpSegment* other = this;
caryclark54359292015-03-26 07:52:43 -07001263 while ((other = other->nextChase(&start, &step, &minSpan, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001264 if (other->done()) {
caryclarkdac1d172014-06-17 05:15:38 -07001265 SkASSERT(!last);
1266 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001267 }
caryclark54359292015-03-26 07:52:43 -07001268 other->markDone(minSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001269 }
1270 return last;
1271}
1272
caryclark54359292015-03-26 07:52:43 -07001273bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
1274 SkOpSpanBase** lastPtr) {
1275 SkOpSpan* spanStart = start->starter(end);
1276 int step = start->step(end);
1277 bool success = markWinding(spanStart, winding);
1278 SkOpSpanBase* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001279 SkOpSegment* other = this;
caryclark54359292015-03-26 07:52:43 -07001280 while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
1281 if (spanStart->windSum() != SK_MinS32) {
1282 SkASSERT(spanStart->windSum() == winding);
caryclarkdac1d172014-06-17 05:15:38 -07001283 SkASSERT(!last);
1284 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001285 }
caryclark54359292015-03-26 07:52:43 -07001286 (void) other->markWinding(spanStart, winding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001287 }
caryclark65f55312014-11-13 06:58:52 -08001288 if (lastPtr) {
1289 *lastPtr = last;
1290 }
1291 return success;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001292}
1293
caryclark54359292015-03-26 07:52:43 -07001294bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end,
1295 int winding, int oppWinding, SkOpSpanBase** lastPtr) {
1296 SkOpSpan* spanStart = start->starter(end);
1297 int step = start->step(end);
1298 bool success = markWinding(spanStart, winding, oppWinding);
1299 SkOpSpanBase* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001300 SkOpSegment* other = this;
caryclark54359292015-03-26 07:52:43 -07001301 while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
1302 if (spanStart->windSum() != SK_MinS32) {
1303 if (this->operand() == other->operand()) {
1304 SkASSERT(spanStart->windSum() == winding);
1305 if (spanStart->oppSum() != oppWinding) {
1306 this->globalState()->setWindingFailed();
1307 return false;
caryclarkdac1d172014-06-17 05:15:38 -07001308 }
caryclark54359292015-03-26 07:52:43 -07001309 } else {
1310 SkASSERT(spanStart->windSum() == oppWinding);
1311 SkASSERT(spanStart->oppSum() == winding);
caryclarkdac1d172014-06-17 05:15:38 -07001312 }
1313 SkASSERT(!last);
caryclarkdac1d172014-06-17 05:15:38 -07001314 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001315 }
caryclark54359292015-03-26 07:52:43 -07001316 if (this->operand() == other->operand()) {
1317 (void) other->markWinding(spanStart, winding, oppWinding);
caryclarkdac1d172014-06-17 05:15:38 -07001318 } else {
caryclark54359292015-03-26 07:52:43 -07001319 (void) other->markWinding(spanStart, oppWinding, winding);
caryclarkdac1d172014-06-17 05:15:38 -07001320 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001321 }
caryclark65f55312014-11-13 06:58:52 -08001322 if (lastPtr) {
1323 *lastPtr = last;
1324 }
1325 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001326}
1327
caryclark54359292015-03-26 07:52:43 -07001328SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001329 SkASSERT(angle->segment() == this);
1330 if (UseInnerWinding(maxWinding, sumWinding)) {
1331 maxWinding = sumWinding;
1332 }
caryclark54359292015-03-26 07:52:43 -07001333 SkOpSpanBase* last;
1334 (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, &last);
caryclark@google.com570863f2013-09-16 15:55:01 +00001335#if DEBUG_WINDING
1336 if (last) {
caryclark54359292015-03-26 07:52:43 -07001337 SkDebugf("%s last seg=%d span=%d", __FUNCTION__,
1338 last->segment()->debugID(), last->debugID());
1339 if (!last->final()) {
1340 SkDebugf(" windSum=");
1341 SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
1342 }
1343 SkDebugf("\n");
caryclark@google.com570863f2013-09-16 15:55:01 +00001344 }
1345#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00001346 return last;
1347}
1348
caryclark54359292015-03-26 07:52:43 -07001349SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
1350 int oppSumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001351 SkASSERT(angle->segment() == this);
1352 if (UseInnerWinding(maxWinding, sumWinding)) {
1353 maxWinding = sumWinding;
1354 }
1355 if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
1356 oppMaxWinding = oppSumWinding;
1357 }
caryclark54359292015-03-26 07:52:43 -07001358 SkOpSpanBase* last = NULL;
caryclark65f55312014-11-13 06:58:52 -08001359 // caller doesn't require that this marks anything
caryclark54359292015-03-26 07:52:43 -07001360 (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, oppMaxWinding, &last);
caryclark@google.com570863f2013-09-16 15:55:01 +00001361#if DEBUG_WINDING
1362 if (last) {
caryclark54359292015-03-26 07:52:43 -07001363 SkDebugf("%s last segment=%d span=%d", __FUNCTION__,
1364 last->segment()->debugID(), last->debugID());
1365 if (!last->final()) {
1366 SkDebugf(" windSum=");
1367 SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
1368 }
1369 SkDebugf(" \n");
caryclark@google.com570863f2013-09-16 15:55:01 +00001370 }
1371#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00001372 return last;
1373}
1374
caryclark54359292015-03-26 07:52:43 -07001375void SkOpSegment::markDone(SkOpSpan* span) {
1376 SkASSERT(this == span->segment());
1377 if (span->done()) {
1378 return;
1379 }
1380#if DEBUG_MARK_DONE
1381 debugShowNewWinding(__FUNCTION__, span, span->windSum(), span->oppSum());
1382#endif
1383 span->setDone(true);
1384 ++fDoneCount;
1385 debugValidate();
1386}
1387
1388bool SkOpSegment::markWinding(SkOpSpan* span, int winding) {
1389 SkASSERT(this == span->segment());
reed0dc4dd62015-03-24 13:55:33 -07001390 SkASSERT(winding);
caryclark54359292015-03-26 07:52:43 -07001391 if (span->done()) {
reed0dc4dd62015-03-24 13:55:33 -07001392 return false;
1393 }
1394#if DEBUG_MARK_DONE
caryclark54359292015-03-26 07:52:43 -07001395 debugShowNewWinding(__FUNCTION__, span, winding);
reed0dc4dd62015-03-24 13:55:33 -07001396#endif
caryclark54359292015-03-26 07:52:43 -07001397 span->setWindSum(winding);
1398 debugValidate();
reed0dc4dd62015-03-24 13:55:33 -07001399 return true;
1400}
1401
caryclark54359292015-03-26 07:52:43 -07001402bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
1403 SkASSERT(this == span->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001404 SkASSERT(winding || oppWinding);
caryclark54359292015-03-26 07:52:43 -07001405 if (span->done()) {
1406 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001407 }
caryclark54359292015-03-26 07:52:43 -07001408#if DEBUG_MARK_DONE
1409 debugShowNewWinding(__FUNCTION__, span, winding, oppWinding);
1410#endif
1411 span->setWindSum(winding);
1412 span->setOppSum(oppWinding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001413 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001414 return true;
1415}
1416
caryclark54359292015-03-26 07:52:43 -07001417bool SkOpSegment::match(const SkOpPtT* base, const SkOpSegment* testParent, double testT,
1418 const SkPoint& testPt) const {
1419 const SkOpSegment* baseParent = base->segment();
1420 if (this == baseParent && this == testParent && precisely_equal(base->fT, testT)) {
1421 return true;
1422 }
1423 if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
1424 return false;
1425 }
1426 return !ptsDisjoint(base->fT, base->fPt, testT, testPt);
1427}
1428
1429static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
1430 if (last) {
1431 *last = endSpan;
caryclarkdac1d172014-06-17 05:15:38 -07001432 }
1433 return NULL;
1434}
1435
caryclark54359292015-03-26 07:52:43 -07001436bool SkOpSegment::monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
1437 SkASSERT(fVerb != SkPath::kLine_Verb);
1438 if (fVerb == SkPath::kQuad_Verb) {
1439 SkDQuad dst = SkDQuad::SubDivide(fPts, start->t(), end->t());
1440 return dst.monotonicInY();
1441 }
caryclark1049f122015-04-20 08:31:59 -07001442 if (fVerb == SkPath::kConic_Verb) {
1443 SkDConic dst = SkDConic::SubDivide(fPts, fWeight, start->t(), end->t());
1444 return dst.monotonicInY();
1445 }
caryclark54359292015-03-26 07:52:43 -07001446 SkASSERT(fVerb == SkPath::kCubic_Verb);
1447 SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
1448 return dst.monotonicInY();
1449}
1450
1451bool SkOpSegment::NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start,
1452 SkOpSpanBase** end) {
1453 while (span->final() || span->upCast()->done()) {
1454 if (span->final()) {
1455 return false;
1456 }
1457 span = span->upCast()->next();
1458 }
1459 *start = span;
1460 *end = span->upCast()->next();
1461 return true;
1462}
1463
1464SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpSpan** minPtr,
1465 SkOpSpanBase** last) const {
1466 SkOpSpanBase* origStart = *startPtr;
caryclarkdac1d172014-06-17 05:15:38 -07001467 int step = *stepPtr;
caryclark54359292015-03-26 07:52:43 -07001468 SkOpSpanBase* endSpan = step > 0 ? origStart->upCast()->next() : origStart->prev();
1469 SkASSERT(endSpan);
1470 SkOpAngle* angle = step > 0 ? endSpan->fromAngle() : endSpan->upCast()->toAngle();
1471 SkOpSpanBase* foundSpan;
1472 SkOpSpanBase* otherEnd;
caryclarkdac1d172014-06-17 05:15:38 -07001473 SkOpSegment* other;
1474 if (angle == NULL) {
caryclark54359292015-03-26 07:52:43 -07001475 if (endSpan->t() != 0 && endSpan->t() != 1) {
caryclarkdac1d172014-06-17 05:15:38 -07001476 return NULL;
1477 }
caryclark54359292015-03-26 07:52:43 -07001478 SkOpPtT* otherPtT = endSpan->ptT()->next();
1479 other = otherPtT->segment();
1480 foundSpan = otherPtT->span();
1481 otherEnd = step > 0 ? foundSpan->upCast()->next() : foundSpan->prev();
caryclarkdac1d172014-06-17 05:15:38 -07001482 } else {
1483 int loopCount = angle->loopCount();
1484 if (loopCount > 2) {
caryclark54359292015-03-26 07:52:43 -07001485 return set_last(last, endSpan);
caryclarkdac1d172014-06-17 05:15:38 -07001486 }
1487 const SkOpAngle* next = angle->next();
caryclark65b427c2014-09-18 10:32:57 -07001488 if (NULL == next) {
1489 return NULL;
1490 }
caryclarkdac1d172014-06-17 05:15:38 -07001491#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -07001492 if (angle->sign() != next->sign() && !angle->segment()->contour()->isXor()
1493 && !next->segment()->contour()->isXor()) {
caryclarkdac1d172014-06-17 05:15:38 -07001494 SkDebugf("%s mismatched signs\n", __FUNCTION__);
reed0dc4dd62015-03-24 13:55:33 -07001495 }
caryclark54359292015-03-26 07:52:43 -07001496#endif
caryclarkdac1d172014-06-17 05:15:38 -07001497 other = next->segment();
caryclark54359292015-03-26 07:52:43 -07001498 foundSpan = endSpan = next->start();
caryclarkdac1d172014-06-17 05:15:38 -07001499 otherEnd = next->end();
caryclark@google.com570863f2013-09-16 15:55:01 +00001500 }
caryclark54359292015-03-26 07:52:43 -07001501 int foundStep = foundSpan->step(otherEnd);
caryclarkdac1d172014-06-17 05:15:38 -07001502 if (*stepPtr != foundStep) {
caryclark54359292015-03-26 07:52:43 -07001503 return set_last(last, endSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001504 }
caryclark54359292015-03-26 07:52:43 -07001505 SkASSERT(*startPtr);
1506 if (!otherEnd) {
caryclark65b427c2014-09-18 10:32:57 -07001507 return NULL;
1508 }
1509// SkASSERT(otherEnd >= 0);
caryclark54359292015-03-26 07:52:43 -07001510 SkOpSpan* origMin = step < 0 ? origStart->prev() : origStart->upCast();
1511 SkOpSpan* foundMin = foundSpan->starter(otherEnd);
1512 if (foundMin->windValue() != origMin->windValue()
1513 || foundMin->oppValue() != origMin->oppValue()) {
1514 return set_last(last, endSpan);
caryclarkdac1d172014-06-17 05:15:38 -07001515 }
caryclark54359292015-03-26 07:52:43 -07001516 *startPtr = foundSpan;
caryclarkdac1d172014-06-17 05:15:38 -07001517 *stepPtr = foundStep;
1518 if (minPtr) {
1519 *minPtr = foundMin;
caryclark@google.coma5e55922013-05-07 18:51:31 +00001520 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001521 return other;
1522}
1523
caryclark54359292015-03-26 07:52:43 -07001524static void clear_visited(SkOpSpan* span) {
1525 // reset visited flag back to false
1526 do {
1527 SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
1528 while ((ptT = ptT->next()) != stopPtT) {
1529 SkOpSegment* opp = ptT->segment();
1530 opp->resetVisited();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001531 }
caryclark54359292015-03-26 07:52:43 -07001532 } while ((span = span->next()->upCastable()));
reed0dc4dd62015-03-24 13:55:33 -07001533}
1534
caryclark54359292015-03-26 07:52:43 -07001535// look for pairs of undetected coincident curves
1536// assumes that segments going in have visited flag clear
1537// curve/curve intersection should now do a pretty good job of finding coincident runs so
1538// this may be only be necessary for line/curve pairs -- so skip unless this is a line and the
1539// the opp is not a line
1540void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
1541 if (this->verb() != SkPath::kLine_Verb) {
1542 return;
1543 }
1544 SkOpSpan* prior = NULL;
1545 SkOpSpan* span = &fHead;
1546 do {
1547 SkOpPtT* ptT = span->ptT(), * spanStopPtT = ptT;
1548 SkASSERT(ptT->span() == span);
1549 while ((ptT = ptT->next()) != spanStopPtT) {
1550 SkOpSegment* opp = ptT->span()->segment();
1551 if (opp->setVisited()) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001552 continue;
1553 }
caryclark54359292015-03-26 07:52:43 -07001554 if (opp->verb() == SkPath::kLine_Verb) {
caryclarkccec0f92015-03-24 07:28:17 -07001555 continue;
1556 }
caryclark54359292015-03-26 07:52:43 -07001557 if (span->containsCoincidence(opp)) { // FIXME: this assumes that if the opposite
1558 // segment is coincident then no more coincidence
1559 // needs to be detected. This may not be true.
1560 continue;
reed0dc4dd62015-03-24 13:55:33 -07001561 }
caryclark54359292015-03-26 07:52:43 -07001562 if (span->containsCoinEnd(opp)) {
1563 continue;
1564 }
1565 // if already visited and visited again, check for coin
1566 if (span == &fHead) {
1567 continue;
1568 }
1569 SkOpPtT* priorPtT = NULL, * priorStopPtT;
1570 // find prior span containing opp segment
1571 SkOpSegment* priorOpp = NULL;
1572 prior = span;
1573 while (!priorOpp && (prior = prior->prev())) {
1574 priorStopPtT = priorPtT = prior->ptT();
1575 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
1576 SkOpSegment* segment = priorPtT->span()->segment();
1577 if (segment == opp) {
1578 priorOpp = opp;
1579 break;
1580 }
1581 }
1582 }
1583 if (!priorOpp) {
1584 continue;
1585 }
1586 SkOpPtT* oppStart = prior->ptT();
1587 SkOpPtT* oppEnd = span->ptT();
1588 bool swapped = priorPtT->fT > ptT->fT;
1589 if (swapped) {
1590 SkTSwap(priorPtT, ptT);
1591 SkTSwap(oppStart, oppEnd);
1592 }
1593 bool flipped = oppStart->fT > oppEnd->fT;
1594 bool coincident;
1595 if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
1596 goto swapBack;
1597 }
1598 {
1599 // average t, find mid pt
1600 double midT = (prior->t() + span->t()) / 2;
1601 SkPoint midPt = this->ptAtT(midT);
1602 coincident = true;
1603 // if the mid pt is not near either end pt, project perpendicular through opp seg
1604 if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt)
1605 && !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) {
1606 coincident = false;
1607 SkIntersections i;
caryclark1049f122015-04-20 08:31:59 -07001608 SkVector dxdy = (*CurveSlopeAtT[fVerb])(this->pts(), this->weight(), midT);
caryclark54359292015-03-26 07:52:43 -07001609 SkDLine ray = {{{midPt.fX, midPt.fY},
1610 {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}};
caryclark1049f122015-04-20 08:31:59 -07001611 (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), ray, &i);
caryclark54359292015-03-26 07:52:43 -07001612 // measure distance and see if it's small enough to denote coincidence
1613 for (int index = 0; index < i.used(); ++index) {
1614 SkDPoint oppPt = i.pt(index);
1615 if (oppPt.approximatelyEqual(midPt)) {
caryclark1049f122015-04-20 08:31:59 -07001616 SkVector oppDxdy = (*CurveSlopeAtT[opp->verb()])(opp->pts(),
1617 opp->weight(), i[index][0]);
caryclark54359292015-03-26 07:52:43 -07001618 oppDxdy.normalize();
1619 dxdy.normalize();
1620 SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON);
1621 coincident |= flatness < 5000; // FIXME: replace with tuned value
1622 }
1623 }
1624 }
1625 }
1626 if (coincident) {
1627 // mark coincidence
1628 coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator);
1629 clear_visited(&fHead);
1630 missingCoincidence(coincidences, allocator);
1631 return;
1632 }
1633 swapBack:
1634 if (swapped) {
1635 SkTSwap(priorPtT, ptT);
1636 }
reed0dc4dd62015-03-24 13:55:33 -07001637 }
caryclark54359292015-03-26 07:52:43 -07001638 } while ((span = span->next()->upCastable()));
1639 clear_visited(&fHead);
reed0dc4dd62015-03-24 13:55:33 -07001640}
1641
caryclark54359292015-03-26 07:52:43 -07001642// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
1643bool SkOpSegment::moveNearby() {
1644 debugValidate();
1645 SkOpSpanBase* spanS = &fHead;
1646 do {
1647 SkOpSpanBase* test = spanS->upCast()->next();
1648 SkOpSpanBase* next;
1649 if (spanS->contains(test)) {
1650 if (!test->final()) {
1651 test->upCast()->detach(spanS->ptT());
1652 continue;
1653 } else if (spanS != &fHead) {
1654 spanS->upCast()->detach(test->ptT());
1655 spanS = test;
1656 continue;
1657 }
reed0dc4dd62015-03-24 13:55:33 -07001658 }
caryclark54359292015-03-26 07:52:43 -07001659 do { // iterate through all spans associated with start
1660 SkOpPtT* startBase = spanS->ptT();
1661 next = test->final() ? NULL : test->upCast()->next();
1662 do {
1663 SkOpPtT* testBase = test->ptT();
1664 do {
1665 if (startBase == testBase) {
1666 goto checkNextSpan;
1667 }
1668 if (testBase->duplicate()) {
1669 continue;
1670 }
1671 if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) {
1672 if (test == &this->fTail) {
1673 if (spanS == &fHead) {
1674 debugValidate();
1675 return true; // if this span has collapsed, remove it from parent
1676 }
1677 this->fTail.merge(spanS->upCast());
1678 debugValidate();
1679 return true;
1680 }
1681 spanS->merge(test->upCast());
1682 spanS->upCast()->setNext(next);
1683 goto checkNextSpan;
1684 }
1685 } while ((testBase = testBase->next()) != test->ptT());
1686 } while ((startBase = startBase->next()) != spanS->ptT());
1687 checkNextSpan:
1688 ;
1689 } while ((test = next));
1690 spanS = spanS->upCast()->next();
1691 } while (!spanS->final());
1692 debugValidate();
1693 return true;
reed0dc4dd62015-03-24 13:55:33 -07001694}
1695
caryclark54359292015-03-26 07:52:43 -07001696bool SkOpSegment::operand() const {
1697 return fContour->operand();
1698}
1699
1700bool SkOpSegment::oppXor() const {
1701 return fContour->oppXor();
1702}
1703
1704bool SkOpSegment::ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const {
1705 if (fVerb == SkPath::kLine_Verb) {
1706 return false;
1707 }
1708 // quads (and cubics) can loop back to nearly a line so that an opposite curve
1709 // hits in two places with very different t values.
1710 // OPTIMIZATION: curves could be preflighted so that, for example, something like
1711 // 'controls contained by ends' could avoid this check for common curves
1712 // 'ends are extremes in x or y' is cheaper to compute and real-world common
1713 // on the other hand, the below check is relatively inexpensive
1714 double midT = (t1 + t2) / 2;
1715 SkPoint midPt = this->ptAtT(midT);
1716 double seDistSq = SkTMax(pt1.distanceToSqd(pt2) * 2, FLT_EPSILON * 2);
1717 return midPt.distanceToSqd(pt1) > seDistSq || midPt.distanceToSqd(pt2) > seDistSq;
1718}
1719
1720void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
1721 int* maxWinding, int* sumWinding) {
1722 int deltaSum = SpanSign(start, end);
1723 *maxWinding = *sumMiWinding;
1724 *sumWinding = *sumMiWinding -= deltaSum;
1725 SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
1726}
1727
1728void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
1729 int* sumSuWinding, int* maxWinding, int* sumWinding, int* oppMaxWinding,
1730 int* oppSumWinding) {
1731 int deltaSum = SpanSign(start, end);
1732 int oppDeltaSum = OppSign(start, end);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001733 if (operand()) {
1734 *maxWinding = *sumSuWinding;
1735 *sumWinding = *sumSuWinding -= deltaSum;
1736 *oppMaxWinding = *sumMiWinding;
1737 *oppSumWinding = *sumMiWinding -= oppDeltaSum;
1738 } else {
1739 *maxWinding = *sumMiWinding;
1740 *sumWinding = *sumMiWinding -= deltaSum;
1741 *oppMaxWinding = *sumSuWinding;
1742 *oppSumWinding = *sumSuWinding -= oppDeltaSum;
1743 }
caryclark54359292015-03-26 07:52:43 -07001744 SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
1745 SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001746}
1747
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001748void SkOpSegment::sortAngles() {
caryclark54359292015-03-26 07:52:43 -07001749 SkOpSpanBase* span = &this->fHead;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001750 do {
caryclark54359292015-03-26 07:52:43 -07001751 SkOpAngle* fromAngle = span->fromAngle();
1752 SkOpAngle* toAngle = span->final() ? NULL : span->upCast()->toAngle();
caryclarkdac1d172014-06-17 05:15:38 -07001753 if (!fromAngle && !toAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001754 continue;
1755 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001756#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001757 bool wroteAfterHeader = false;
caryclark@google.com570863f2013-09-16 15:55:01 +00001758#endif
caryclark54359292015-03-26 07:52:43 -07001759 SkOpAngle* baseAngle = fromAngle;
1760 if (fromAngle && toAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001761#if DEBUG_ANGLE
caryclark54359292015-03-26 07:52:43 -07001762 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), span->t(),
1763 span->debugID());
1764 wroteAfterHeader = true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001765#endif
caryclark54359292015-03-26 07:52:43 -07001766 fromAngle->insert(toAngle);
1767 } else if (!fromAngle) {
1768 baseAngle = toAngle;
reed0dc4dd62015-03-24 13:55:33 -07001769 }
caryclark54359292015-03-26 07:52:43 -07001770 SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
reed0dc4dd62015-03-24 13:55:33 -07001771 do {
caryclark54359292015-03-26 07:52:43 -07001772 SkOpSpanBase* oSpan = ptT->span();
1773 if (oSpan == span) {
1774 continue;
1775 }
1776 SkOpAngle* oAngle = oSpan->fromAngle();
caryclarkdac1d172014-06-17 05:15:38 -07001777 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001778#if DEBUG_ANGLE
1779 if (!wroteAfterHeader) {
caryclark54359292015-03-26 07:52:43 -07001780 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
1781 span->t(), span->debugID());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001782 wroteAfterHeader = true;
1783 }
1784#endif
caryclark54359292015-03-26 07:52:43 -07001785 if (!oAngle->loopContains(baseAngle)) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001786 baseAngle->insert(oAngle);
1787 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001788 }
caryclark54359292015-03-26 07:52:43 -07001789 if (!oSpan->final()) {
1790 oAngle = oSpan->upCast()->toAngle();
1791 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001792#if DEBUG_ANGLE
caryclark54359292015-03-26 07:52:43 -07001793 if (!wroteAfterHeader) {
1794 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
1795 span->t(), span->debugID());
1796 wroteAfterHeader = true;
1797 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001798#endif
caryclark54359292015-03-26 07:52:43 -07001799 if (!oAngle->loopContains(baseAngle)) {
1800 baseAngle->insert(oAngle);
1801 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001802 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001803 }
caryclark54359292015-03-26 07:52:43 -07001804 } while ((ptT = ptT->next()) != stopPtT);
1805 if (baseAngle->loopCount() == 1) {
1806 span->setFromAngle(NULL);
1807 if (toAngle) {
1808 span->upCast()->setToAngle(NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001809 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001810 baseAngle = NULL;
1811 }
1812#if DEBUG_SORT
1813 SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
1814#endif
caryclark54359292015-03-26 07:52:43 -07001815 } while (!span->final() && (span = span->upCast()->next()));
caryclark@google.com570863f2013-09-16 15:55:01 +00001816}
1817
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001818// return true if midpoints were computed
caryclark54359292015-03-26 07:52:43 -07001819bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
caryclark1049f122015-04-20 08:31:59 -07001820 SkOpCurve* edge) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001821 SkASSERT(start != end);
caryclark54359292015-03-26 07:52:43 -07001822 const SkOpPtT& startPtT = *start->ptT();
1823 const SkOpPtT& endPtT = *end->ptT();
caryclark1049f122015-04-20 08:31:59 -07001824 SkDEBUGCODE(edge->fVerb = fVerb);
1825 edge->fPts[0] = startPtT.fPt;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001826 int points = SkPathOpsVerbToPoints(fVerb);
caryclark1049f122015-04-20 08:31:59 -07001827 edge->fPts[points] = endPtT.fPt;
1828 edge->fWeight = 1;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001829 if (fVerb == SkPath::kLine_Verb) {
1830 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001831 }
caryclark54359292015-03-26 07:52:43 -07001832 double startT = startPtT.fT;
1833 double endT = endPtT.fT;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001834 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
1835 // don't compute midpoints if we already have them
1836 if (fVerb == SkPath::kQuad_Verb) {
caryclark1049f122015-04-20 08:31:59 -07001837 edge->fPts[1] = fPts[1];
1838 return false;
1839 }
1840 if (fVerb == SkPath::kConic_Verb) {
1841 edge->fPts[1] = fPts[1];
1842 edge->fWeight = fWeight;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001843 return false;
1844 }
1845 SkASSERT(fVerb == SkPath::kCubic_Verb);
1846 if (start < end) {
caryclark1049f122015-04-20 08:31:59 -07001847 edge->fPts[1] = fPts[1];
1848 edge->fPts[2] = fPts[2];
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001849 return false;
1850 }
caryclark1049f122015-04-20 08:31:59 -07001851 edge->fPts[1] = fPts[2];
1852 edge->fPts[2] = fPts[1];
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001853 return false;
1854 }
caryclark1049f122015-04-20 08:31:59 -07001855 const SkDPoint sub[2] = {{ edge->fPts[0].fX, edge->fPts[0].fY},
1856 {edge->fPts[points].fX, edge->fPts[points].fY }};
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001857 if (fVerb == SkPath::kQuad_Verb) {
caryclark1049f122015-04-20 08:31:59 -07001858 edge->fPts[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint();
1859 } else if (fVerb == SkPath::kConic_Verb) {
1860 edge->fPts[1] = SkDConic::SubDivide(fPts, fWeight, sub[0], sub[1],
1861 startT, endT, &edge->fWeight).asSkPoint();
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001862 } else {
1863 SkASSERT(fVerb == SkPath::kCubic_Verb);
1864 SkDPoint ctrl[2];
1865 SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl);
caryclark1049f122015-04-20 08:31:59 -07001866 edge->fPts[1] = ctrl[0].asSkPoint();
1867 edge->fPts[2] = ctrl[1].asSkPoint();
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001868 }
1869 return true;
1870}
1871
caryclark54359292015-03-26 07:52:43 -07001872bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
caryclark1049f122015-04-20 08:31:59 -07001873 SkDCurve* edge) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001874 SkASSERT(start != end);
caryclark54359292015-03-26 07:52:43 -07001875 const SkOpPtT& startPtT = *start->ptT();
1876 const SkOpPtT& endPtT = *end->ptT();
caryclark1049f122015-04-20 08:31:59 -07001877 SkDEBUGCODE(edge->fVerb = fVerb);
1878 edge->fCubic[0].set(startPtT.fPt);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001879 int points = SkPathOpsVerbToPoints(fVerb);
caryclark1049f122015-04-20 08:31:59 -07001880 edge->fCubic[points].set(endPtT.fPt);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001881 if (fVerb == SkPath::kLine_Verb) {
1882 return false;
1883 }
caryclark54359292015-03-26 07:52:43 -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) {
caryclark1049f122015-04-20 08:31:59 -07001889 edge->fLine[1].set(fPts[1]);
1890 return false;
1891 }
1892 if (fVerb == SkPath::kConic_Verb) {
1893 edge->fConic[1].set(fPts[1]);
1894 edge->fConic.fWeight = fWeight;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001895 return false;
1896 }
1897 SkASSERT(fVerb == SkPath::kCubic_Verb);
caryclark54359292015-03-26 07:52:43 -07001898 if (startT == 0) {
caryclark1049f122015-04-20 08:31:59 -07001899 edge->fCubic[1].set(fPts[1]);
1900 edge->fCubic[2].set(fPts[2]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001901 return false;
1902 }
caryclark1049f122015-04-20 08:31:59 -07001903 edge->fCubic[1].set(fPts[2]);
1904 edge->fCubic[2].set(fPts[1]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001905 return false;
1906 }
1907 if (fVerb == SkPath::kQuad_Verb) {
caryclark1049f122015-04-20 08:31:59 -07001908 edge->fQuad[1] = SkDQuad::SubDivide(fPts, edge->fQuad[0], edge->fQuad[2], startT, endT);
1909 } else if (fVerb == SkPath::kConic_Verb) {
1910 edge->fConic[1] = SkDConic::SubDivide(fPts, fWeight, edge->fQuad[0], edge->fQuad[2],
1911 startT, endT, &edge->fConic.fWeight);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001912 } else {
1913 SkASSERT(fVerb == SkPath::kCubic_Verb);
caryclark1049f122015-04-20 08:31:59 -07001914 SkDCubic::SubDivide(fPts, edge->fCubic[0], edge->fCubic[3], startT, endT, &edge->fCubic[1]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001915 }
1916 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001917}
1918
caryclark54359292015-03-26 07:52:43 -07001919void SkOpSegment::subDivideBounds(const SkOpSpanBase* start, const SkOpSpanBase* end,
1920 SkPathOpsBounds* bounds) const {
caryclark1049f122015-04-20 08:31:59 -07001921 SkOpCurve edge;
1922 subDivide(start, end, &edge);
1923 (bounds->*SetCurveBounds[fVerb])(edge.fPts, edge.fWeight);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001924}
1925
caryclark54359292015-03-26 07:52:43 -07001926void SkOpSegment::undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end) {
1927 SkOpSpan* span = this->head();
1928 do {
1929 if (!span->done()) {
reed0dc4dd62015-03-24 13:55:33 -07001930 break;
1931 }
caryclark54359292015-03-26 07:52:43 -07001932 } while ((span = span->next()->upCastable()));
1933 SkASSERT(span);
1934 *start = span;
1935 *end = span->next();
reed0dc4dd62015-03-24 13:55:33 -07001936}
1937
caryclark54359292015-03-26 07:52:43 -07001938int SkOpSegment::updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
1939 const SkOpSpan* lesser = start->starter(end);
1940 int oppWinding = lesser->oppSum();
1941 int oppSpanWinding = SkOpSegment::OppSign(start, end);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001942 if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
1943 && oppWinding != SK_MaxS32) {
1944 oppWinding -= oppSpanWinding;
1945 }
1946 return oppWinding;
1947}
1948
1949int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
caryclark54359292015-03-26 07:52:43 -07001950 const SkOpSpanBase* startSpan = angle->start();
1951 const SkOpSpanBase* endSpan = angle->end();
1952 return updateOppWinding(endSpan, startSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001953}
1954
1955int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
caryclark54359292015-03-26 07:52:43 -07001956 const SkOpSpanBase* startSpan = angle->start();
1957 const SkOpSpanBase* endSpan = angle->end();
1958 return updateOppWinding(startSpan, endSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001959}
1960
caryclark54359292015-03-26 07:52:43 -07001961int SkOpSegment::updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
1962 const SkOpSpan* lesser = start->starter(end);
1963 int winding = lesser->windSum();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001964 if (winding == SK_MinS32) {
1965 return winding;
1966 }
caryclark54359292015-03-26 07:52:43 -07001967 int spanWinding = SkOpSegment::SpanSign(start, end);
caryclark@google.com570863f2013-09-16 15:55:01 +00001968 if (winding && UseInnerWinding(winding - spanWinding, winding)
1969 && winding != SK_MaxS32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001970 winding -= spanWinding;
1971 }
1972 return winding;
1973}
1974
1975int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
caryclark54359292015-03-26 07:52:43 -07001976 const SkOpSpanBase* startSpan = angle->start();
1977 const SkOpSpanBase* endSpan = angle->end();
1978 return updateWinding(endSpan, startSpan);
caryclark@google.com570863f2013-09-16 15:55:01 +00001979}
1980
caryclark@google.com07393ca2013-04-08 11:47:37 +00001981int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
caryclark54359292015-03-26 07:52:43 -07001982 const SkOpSpanBase* startSpan = angle->start();
1983 const SkOpSpanBase* endSpan = angle->end();
1984 return updateWinding(startSpan, endSpan);
caryclark@google.com570863f2013-09-16 15:55:01 +00001985}
1986
1987// OPTIMIZATION: does the following also work, and is it any faster?
1988// return outerWinding * innerWinding > 0
1989// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
1990bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
1991 SkASSERT(outerWinding != SK_MaxS32);
1992 SkASSERT(innerWinding != SK_MaxS32);
1993 int absOut = abs(outerWinding);
1994 int absIn = abs(innerWinding);
1995 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
1996 return result;
1997}
1998
caryclark54359292015-03-26 07:52:43 -07001999int SkOpSegment::windingAtT(double tHit, const SkOpSpan* span, bool crossOpp,
2000 SkScalar* dx) const {
2001 if (approximately_zero(tHit - span->t())) { // if we hit the end of a span, disregard
caryclark@google.com07393ca2013-04-08 11:47:37 +00002002 return SK_MinS32;
2003 }
caryclark54359292015-03-26 07:52:43 -07002004 int winding = crossOpp ? span->oppSum() : span->windSum();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002005 SkASSERT(winding != SK_MinS32);
caryclark54359292015-03-26 07:52:43 -07002006 int windVal = crossOpp ? span->oppValue() : span->windValue();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002007#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07002008 SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -07002009 debugID(), crossOpp, tHit, span->t(), winding, windVal);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002010#endif
2011 // see if a + change in T results in a +/- change in X (compute x'(T))
caryclark1049f122015-04-20 08:31:59 -07002012 *dx = (*CurveSlopeAtT[fVerb])(fPts, fWeight, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002013 if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
2014 *dx = fPts[2].fX - fPts[1].fX - *dx;
2015 }
2016 if (*dx == 0) {
2017#if DEBUG_WINDING_AT_T
2018 SkDebugf(" dx=0 winding=SK_MinS32\n");
2019#endif
2020 return SK_MinS32;
2021 }
skia.committer@gmail.coma4aced42013-07-09 07:00:56 +00002022 if (windVal < 0) { // reverse sign if opp contour traveled in reverse
caryclark@google.com07e97fc2013-07-08 17:17:02 +00002023 *dx = -*dx;
2024 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002025 if (winding * *dx > 0) { // if same signs, result is negative
2026 winding += *dx > 0 ? -windVal : windVal;
2027 }
2028#if DEBUG_WINDING_AT_T
2029 SkDebugf(" dx=%c winding=%d\n", *dx > 0 ? '+' : '-', winding);
2030#endif
2031 return winding;
2032}
2033
2034int SkOpSegment::windSum(const SkOpAngle* angle) const {
caryclark54359292015-03-26 07:52:43 -07002035 const SkOpSpan* minSpan = angle->start()->starter(angle->end());
2036 return minSpan->windSum();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002037}