blob: a571609f53b310425c91a5d65b2f9570e295e90e [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();
caryclark08bc8482015-04-24 09:08:57 -0700368 SkOpPtT* loop;
369 bool duplicatePt;
caryclark54359292015-03-26 07:52:43 -0700370 if (t == result->fT) {
caryclark08bc8482015-04-24 09:08:57 -0700371 goto bumpSpan;
reed0dc4dd62015-03-24 13:55:33 -0700372 }
caryclark54359292015-03-26 07:52:43 -0700373 if (this->match(result, this, t, pt)) {
374 // see if any existing alias matches segment, pt, and t
caryclark08bc8482015-04-24 09:08:57 -0700375 loop = result->next();
376 duplicatePt = false;
caryclark54359292015-03-26 07:52:43 -0700377 while (loop != result) {
378 bool ptMatch = loop->fPt == pt;
379 if (loop->segment() == this && loop->fT == t && ptMatch) {
caryclark08bc8482015-04-24 09:08:57 -0700380 goto bumpSpan;
reed0dc4dd62015-03-24 13:55:33 -0700381 }
caryclark54359292015-03-26 07:52:43 -0700382 duplicatePt |= ptMatch;
383 loop = loop->next();
reed0dc4dd62015-03-24 13:55:33 -0700384 }
caryclark54359292015-03-26 07:52:43 -0700385 if (kNoAlias == allowAlias) {
caryclark08bc8482015-04-24 09:08:57 -0700386 bumpSpan:
387 span->bumpSpanAdds();
caryclark54359292015-03-26 07:52:43 -0700388 return result;
389 }
390 SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator);
391 alias->init(result->span(), t, pt, duplicatePt);
392 result->insert(alias);
393 result->span()->unaligned();
394 this->debugValidate();
395#if DEBUG_ADD_T
396 SkDebugf("%s alias t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
397 alias->segment()->debugID(), alias->span()->debugID());
398#endif
caryclark08bc8482015-04-24 09:08:57 -0700399 span->bumpSpanAdds();
caryclark54359292015-03-26 07:52:43 -0700400 return alias;
reed0dc4dd62015-03-24 13:55:33 -0700401 }
caryclark54359292015-03-26 07:52:43 -0700402 if (t < result->fT) {
403 SkOpSpan* prev = result->span()->prev();
404 SkOpSpan* span = insert(prev, allocator);
405 span->init(this, prev, t, pt);
406 this->debugValidate();
407#if DEBUG_ADD_T
408 SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
409 span->segment()->debugID(), span->debugID());
410#endif
caryclark08bc8482015-04-24 09:08:57 -0700411 span->bumpSpanAdds();
caryclark54359292015-03-26 07:52:43 -0700412 return span->ptT();
413 }
414 SkASSERT(span != &fTail);
415 } while ((span = span->upCast()->next()));
416 SkASSERT(0);
417 return NULL;
418}
419
420// choose a solitary t and pt value; remove aliases; align the opposite ends
421void SkOpSegment::align() {
422 debugValidate();
423 SkOpSpanBase* span = &fHead;
424 if (!span->aligned()) {
425 span->alignEnd(0, fPts[0]);
426 }
427 while ((span = span->upCast()->next())) {
428 if (span == &fTail) {
429 break;
430 }
431 span->align();
432 }
433 if (!span->aligned()) {
434 span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
reed0dc4dd62015-03-24 13:55:33 -0700435 }
436 debugValidate();
437}
438
caryclark54359292015-03-26 07:52:43 -0700439bool SkOpSegment::BetweenTs(const SkOpSpanBase* lesser, double testT,
440 const SkOpSpanBase* greater) {
441 if (lesser->t() > greater->t()) {
442 SkTSwap<const SkOpSpanBase*>(lesser, greater);
reed0dc4dd62015-03-24 13:55:33 -0700443 }
caryclark54359292015-03-26 07:52:43 -0700444 return approximately_between(lesser->t(), testT, greater->t());
reed0dc4dd62015-03-24 13:55:33 -0700445}
446
caryclark54359292015-03-26 07:52:43 -0700447void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
448 bool activePrior = !fHead.isCanceled();
449 if (activePrior && !fHead.simple()) {
450 addStartSpan(allocator);
reed0dc4dd62015-03-24 13:55:33 -0700451 }
caryclark54359292015-03-26 07:52:43 -0700452 SkOpSpan* prior = &fHead;
453 SkOpSpanBase* spanBase = fHead.next();
454 while (spanBase != &fTail) {
455 if (activePrior) {
456 SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
457 priorAngle->set(spanBase, prior);
458 spanBase->setFromAngle(priorAngle);
reed0dc4dd62015-03-24 13:55:33 -0700459 }
caryclark54359292015-03-26 07:52:43 -0700460 SkOpSpan* span = spanBase->upCast();
461 bool active = !span->isCanceled();
462 SkOpSpanBase* next = span->next();
463 if (active) {
464 SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
465 angle->set(span, next);
466 span->setToAngle(angle);
reed0dc4dd62015-03-24 13:55:33 -0700467 }
reed0dc4dd62015-03-24 13:55:33 -0700468 activePrior = active;
caryclark54359292015-03-26 07:52:43 -0700469 prior = span;
470 spanBase = next;
reed0dc4dd62015-03-24 13:55:33 -0700471 }
caryclark54359292015-03-26 07:52:43 -0700472 if (activePrior && !fTail.simple()) {
473 addEndSpan(allocator);
reed0dc4dd62015-03-24 13:55:33 -0700474 }
reed0dc4dd62015-03-24 13:55:33 -0700475}
476
caryclark54359292015-03-26 07:52:43 -0700477void SkOpSegment::checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
478 SkOpSpanBase* base = &fHead;
479 SkOpSpan* span;
reed0dc4dd62015-03-24 13:55:33 -0700480 do {
caryclark54359292015-03-26 07:52:43 -0700481 SkOpAngle* angle = base->fromAngle();
482 if (angle && angle->fCheckCoincidence) {
483 angle->checkNearCoincidence();
reed0dc4dd62015-03-24 13:55:33 -0700484 }
caryclark54359292015-03-26 07:52:43 -0700485 if (base->final()) {
486 break;
reed0dc4dd62015-03-24 13:55:33 -0700487 }
caryclark54359292015-03-26 07:52:43 -0700488 span = base->upCast();
489 angle = span->toAngle();
490 if (angle && angle->fCheckCoincidence) {
491 angle->checkNearCoincidence();
reed0dc4dd62015-03-24 13:55:33 -0700492 }
caryclark54359292015-03-26 07:52:43 -0700493 } while ((base = span->next()));
494}
495
496// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
497bool SkOpSegment::clockwise(const SkOpSpanBase* start, const SkOpSpanBase* end, bool* swap) const {
498 SkASSERT(fVerb != SkPath::kLine_Verb);
caryclark03b03ca2015-04-23 09:13:37 -0700499 if (fVerb != SkPath::kCubic_Verb) {
500 SkOpCurve edge;
501 this->subDivide(start, end, &edge);
502 return SkDQuad::Clockwise(edge, swap);
reed0dc4dd62015-03-24 13:55:33 -0700503 }
caryclark03b03ca2015-04-23 09:13:37 -0700504 return SkDCubic::Clockwise(fPts, start->t(), end->t(), swap);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000505}
506
caryclark@google.com570863f2013-09-16 15:55:01 +0000507void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
508 SkOpAngle::IncludeType includeType) {
509 const SkOpSegment* baseSegment = baseAngle->segment();
510 int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
511 int sumSuWinding;
512 bool binary = includeType >= SkOpAngle::kBinarySingle;
513 if (binary) {
514 sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
515 if (baseSegment->operand()) {
516 SkTSwap<int>(sumMiWinding, sumSuWinding);
517 }
518 }
519 SkOpSegment* nextSegment = nextAngle->segment();
520 int maxWinding, sumWinding;
caryclark54359292015-03-26 07:52:43 -0700521 SkOpSpanBase* last;
caryclark@google.com570863f2013-09-16 15:55:01 +0000522 if (binary) {
523 int oppMaxWinding, oppSumWinding;
524 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
525 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
526 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000527 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +0000528 } else {
529 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
530 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000531 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +0000532 }
533 nextAngle->setLastMarked(last);
534}
535
536void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
537 SkOpAngle::IncludeType includeType) {
538 const SkOpSegment* baseSegment = baseAngle->segment();
539 int sumMiWinding = baseSegment->updateWinding(baseAngle);
540 int sumSuWinding;
541 bool binary = includeType >= SkOpAngle::kBinarySingle;
542 if (binary) {
543 sumSuWinding = baseSegment->updateOppWinding(baseAngle);
544 if (baseSegment->operand()) {
545 SkTSwap<int>(sumMiWinding, sumSuWinding);
546 }
547 }
548 SkOpSegment* nextSegment = nextAngle->segment();
549 int maxWinding, sumWinding;
caryclark54359292015-03-26 07:52:43 -0700550 SkOpSpanBase* last;
caryclark@google.com570863f2013-09-16 15:55:01 +0000551 if (binary) {
552 int oppMaxWinding, oppSumWinding;
553 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
554 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
555 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000556 nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +0000557 } else {
558 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
559 &maxWinding, &sumWinding);
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000560 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
caryclark@google.com570863f2013-09-16 15:55:01 +0000561 }
562 nextAngle->setLastMarked(last);
563}
564
caryclark54359292015-03-26 07:52:43 -0700565// at this point, the span is already ordered, or unorderable
566int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
567 SkOpAngle::IncludeType includeType) {
568 SkASSERT(includeType != SkOpAngle::kUnaryXor);
569 SkOpAngle* firstAngle = this->spanToAngle(end, start);
570 if (NULL == firstAngle || NULL == firstAngle->next()) {
571 return SK_NaN32;
572 }
573 // if all angles have a computed winding,
574 // or if no adjacent angles are orderable,
575 // or if adjacent orderable angles have no computed winding,
576 // there's nothing to do
577 // if two orderable angles are adjacent, and both are next to orderable angles,
578 // and one has winding computed, transfer to the other
579 SkOpAngle* baseAngle = NULL;
580 bool tryReverse = false;
581 // look for counterclockwise transfers
582 SkOpAngle* angle = firstAngle->previous();
583 SkOpAngle* next = angle->next();
584 firstAngle = next;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000585 do {
caryclark54359292015-03-26 07:52:43 -0700586 SkOpAngle* prior = angle;
587 angle = next;
588 next = angle->next();
589 SkASSERT(prior->next() == angle);
590 SkASSERT(angle->next() == next);
591 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
592 baseAngle = NULL;
593 continue;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000594 }
caryclark54359292015-03-26 07:52:43 -0700595 int testWinding = angle->starter()->windSum();
596 if (SK_MinS32 != testWinding) {
597 baseAngle = angle;
598 tryReverse = true;
599 continue;
reed0dc4dd62015-03-24 13:55:33 -0700600 }
caryclark54359292015-03-26 07:52:43 -0700601 if (baseAngle) {
602 ComputeOneSum(baseAngle, angle, includeType);
603 baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
604 }
605 } while (next != firstAngle);
606 if (baseAngle && SK_MinS32 == firstAngle->starter()->windSum()) {
607 firstAngle = baseAngle;
608 tryReverse = true;
609 }
610 if (tryReverse) {
611 baseAngle = NULL;
612 SkOpAngle* prior = firstAngle;
613 do {
614 angle = prior;
615 prior = angle->previous();
616 SkASSERT(prior->next() == angle);
617 next = angle->next();
618 if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
619 baseAngle = NULL;
reed0dc4dd62015-03-24 13:55:33 -0700620 continue;
621 }
caryclark54359292015-03-26 07:52:43 -0700622 int testWinding = angle->starter()->windSum();
623 if (SK_MinS32 != testWinding) {
624 baseAngle = angle;
625 continue;
reed0dc4dd62015-03-24 13:55:33 -0700626 }
caryclark54359292015-03-26 07:52:43 -0700627 if (baseAngle) {
628 ComputeOneSumReverse(baseAngle, angle, includeType);
629 baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
reed0dc4dd62015-03-24 13:55:33 -0700630 }
caryclark54359292015-03-26 07:52:43 -0700631 } while (prior != firstAngle);
reed0dc4dd62015-03-24 13:55:33 -0700632 }
caryclark54359292015-03-26 07:52:43 -0700633 return start->starter(end)->windSum();
reed0dc4dd62015-03-24 13:55:33 -0700634}
635
caryclark54359292015-03-26 07:52:43 -0700636SkOpSpan* SkOpSegment::crossedSpanY(const SkPoint& basePt, double mid, bool opp, bool current,
637 SkScalar* bestY, double* hitT, bool* hitSomething, bool* vertical) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000638 SkScalar bottom = fBounds.fBottom;
caryclark54359292015-03-26 07:52:43 -0700639 *vertical = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000640 if (bottom <= *bestY) {
caryclark54359292015-03-26 07:52:43 -0700641 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000642 }
643 SkScalar top = fBounds.fTop;
644 if (top >= basePt.fY) {
caryclark54359292015-03-26 07:52:43 -0700645 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000646 }
647 if (fBounds.fLeft > basePt.fX) {
caryclark54359292015-03-26 07:52:43 -0700648 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000649 }
650 if (fBounds.fRight < basePt.fX) {
caryclark54359292015-03-26 07:52:43 -0700651 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000652 }
653 if (fBounds.fLeft == fBounds.fRight) {
654 // if vertical, and directly above test point, wait for another one
caryclark54359292015-03-26 07:52:43 -0700655 *vertical = AlmostEqualUlps(basePt.fX, fBounds.fLeft);
656 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000657 }
658 // intersect ray starting at basePt with edge
659 SkIntersections intersections;
660 // OPTIMIZE: use specialty function that intersects ray with curve,
661 // returning t values only for curve (we don't care about t on ray)
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000662 intersections.allowNear(false);
caryclark1049f122015-04-20 08:31:59 -0700663 int pts = (intersections.*CurveVertical[fVerb])(fPts, fWeight, top, bottom, basePt.fX, false);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000664 if (pts == 0 || (current && pts == 1)) {
caryclark54359292015-03-26 07:52:43 -0700665 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000666 }
667 if (current) {
668 SkASSERT(pts > 1);
669 int closestIdx = 0;
670 double closest = fabs(intersections[0][0] - mid);
671 for (int idx = 1; idx < pts; ++idx) {
672 double test = fabs(intersections[0][idx] - mid);
673 if (closest > test) {
674 closestIdx = idx;
675 closest = test;
676 }
677 }
678 intersections.quickRemoveOne(closestIdx, --pts);
679 }
680 double bestT = -1;
681 for (int index = 0; index < pts; ++index) {
682 double foundT = intersections[0][index];
683 if (approximately_less_than_zero(foundT)
684 || approximately_greater_than_one(foundT)) {
685 continue;
686 }
caryclark1049f122015-04-20 08:31:59 -0700687 SkScalar testY = (*CurvePointAtT[fVerb])(fPts, fWeight, foundT).fY;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000688 if (approximately_negative(testY - *bestY)
689 || approximately_negative(basePt.fY - testY)) {
690 continue;
691 }
692 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
caryclark54359292015-03-26 07:52:43 -0700693 *vertical = true;
694 return NULL; // if the intersection is edge on, wait for another one
caryclark@google.com07393ca2013-04-08 11:47:37 +0000695 }
696 if (fVerb > SkPath::kLine_Verb) {
caryclark1049f122015-04-20 08:31:59 -0700697 SkScalar dx = (*CurveSlopeAtT[fVerb])(fPts, fWeight, foundT).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000698 if (approximately_zero(dx)) {
caryclark54359292015-03-26 07:52:43 -0700699 *vertical = true;
700 return NULL; // hit vertical, wait for another one
caryclark@google.com07393ca2013-04-08 11:47:37 +0000701 }
702 }
703 *bestY = testY;
704 bestT = foundT;
705 }
706 if (bestT < 0) {
caryclark54359292015-03-26 07:52:43 -0700707 return NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000708 }
709 SkASSERT(bestT >= 0);
caryclark54359292015-03-26 07:52:43 -0700710 SkASSERT(bestT < 1);
711 SkOpSpanBase* testTSpanBase = &this->fHead;
712 SkOpSpanBase* nextTSpan;
713 double endT = 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000714 do {
caryclark54359292015-03-26 07:52:43 -0700715 nextTSpan = testTSpanBase->upCast()->next();
716 endT = nextTSpan->t();
717 if (endT >= bestT) {
718 break;
719 }
720 testTSpanBase = nextTSpan;
721 } while (testTSpanBase);
722 SkOpSpan* bestTSpan = NULL;
723 SkOpSpan* testTSpan = testTSpanBase->upCast();
724 if (!testTSpan->isCanceled()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000725 *hitT = bestT;
caryclark54359292015-03-26 07:52:43 -0700726 bestTSpan = testTSpan;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000727 *hitSomething = true;
728 }
caryclark54359292015-03-26 07:52:43 -0700729 return bestTSpan;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000730}
731
caryclark54359292015-03-26 07:52:43 -0700732void SkOpSegment::detach(const SkOpSpan* span) {
733 if (span->done()) {
caryclark08bc8482015-04-24 09:08:57 -0700734 --fDoneCount;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000735 }
caryclark08bc8482015-04-24 09:08:57 -0700736 --fCount;
737 SkASSERT(fCount >= fDoneCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000738}
739
caryclark54359292015-03-26 07:52:43 -0700740double SkOpSegment::distSq(double t, SkOpAngle* oppAngle) {
741 SkDPoint testPt = this->dPtAtT(t);
742 SkDLine testPerp = {{ testPt, testPt }};
743 SkDVector slope = this->dSlopeAtT(t);
744 testPerp[1].fX += slope.fY;
745 testPerp[1].fY -= slope.fX;
746 SkIntersections i;
747 SkOpSegment* oppSegment = oppAngle->segment();
caryclark1049f122015-04-20 08:31:59 -0700748 (*CurveIntersectRay[oppSegment->verb()])(oppSegment->pts(), oppSegment->weight(), testPerp, &i);
caryclark54359292015-03-26 07:52:43 -0700749 double closestDistSq = SK_ScalarInfinity;
750 for (int index = 0; index < i.used(); ++index) {
751 if (!between(oppAngle->start()->t(), i[0][index], oppAngle->end()->t())) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000752 continue;
753 }
caryclark54359292015-03-26 07:52:43 -0700754 double testDistSq = testPt.distanceSquared(i.pt(index));
755 if (closestDistSq > testDistSq) {
756 closestDistSq = testDistSq;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000757 }
758 }
caryclark54359292015-03-26 07:52:43 -0700759 return closestDistSq;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000760}
761
caryclark@google.com07393ca2013-04-08 11:47:37 +0000762/*
763 The M and S variable name parts stand for the operators.
764 Mi stands for Minuend (see wiki subtraction, analogous to difference)
765 Su stands for Subtrahend
766 The Opp variable name part designates that the value is for the Opposite operator.
767 Opposite values result from combining coincident spans.
768 */
caryclark54359292015-03-26 07:52:43 -0700769SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
770 SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op, int xorMiMask, int xorSuMask) {
771 SkOpSpanBase* start = *nextStart;
772 SkOpSpanBase* end = *nextEnd;
773 SkASSERT(start != end);
774 int step = start->step(end);
775 SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
776 if (other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000777 // mark the smaller of startIndex, endIndex done, and all adjacent
778 // spans with the same T value (but not 'other' spans)
779#if DEBUG_WINDING
780 SkDebugf("%s simple\n", __FUNCTION__);
781#endif
caryclark54359292015-03-26 07:52:43 -0700782 SkOpSpan* startSpan = start->starter(end);
783 if (startSpan->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000784 return NULL;
785 }
caryclark54359292015-03-26 07:52:43 -0700786 markDone(startSpan);
787 *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000788 return other;
789 }
caryclark54359292015-03-26 07:52:43 -0700790 SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
791 SkASSERT(endNear == end); // is this ever not end?
792 SkASSERT(endNear);
793 SkASSERT(start != endNear);
794 SkASSERT((start->t() < endNear->t()) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000795 // more than one viable candidate -- measure angles to find best
caryclark54359292015-03-26 07:52:43 -0700796 int calcWinding = computeSum(start, endNear, SkOpAngle::kBinaryOpp);
caryclark@google.com570863f2013-09-16 15:55:01 +0000797 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000798 if (!sortable) {
799 *unsortable = true;
caryclark54359292015-03-26 07:52:43 -0700800 markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000801 return NULL;
802 }
caryclark54359292015-03-26 07:52:43 -0700803 SkOpAngle* angle = this->spanToAngle(end, start);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000804 if (angle->unorderable()) {
805 *unsortable = true;
caryclark54359292015-03-26 07:52:43 -0700806 markDone(start->starter(end));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000807 return NULL;
808 }
809#if DEBUG_SORT
810 SkDebugf("%s\n", __FUNCTION__);
811 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000812#endif
caryclark54359292015-03-26 07:52:43 -0700813 int sumMiWinding = updateWinding(end, start);
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000814 if (sumMiWinding == SK_MinS32) {
815 *unsortable = true;
caryclark54359292015-03-26 07:52:43 -0700816 markDone(start->starter(end));
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000817 return NULL;
818 }
caryclark54359292015-03-26 07:52:43 -0700819 int sumSuWinding = updateOppWinding(end, start);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000820 if (operand()) {
821 SkTSwap<int>(sumMiWinding, sumSuWinding);
822 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000823 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000824 const SkOpAngle* foundAngle = NULL;
825 bool foundDone = false;
826 // iterate through the angle, and compute everyone's winding
827 SkOpSegment* nextSegment;
828 int activeCount = 0;
829 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000830 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000831 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000832 nextAngle->end(), op, &sumMiWinding, &sumSuWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000833 if (activeAngle) {
834 ++activeCount;
835 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000836 foundAngle = nextAngle;
caryclark@google.com570863f2013-09-16 15:55:01 +0000837 foundDone = nextSegment->done(nextAngle);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000838 }
839 }
840 if (nextSegment->done()) {
841 continue;
842 }
reed0dc4dd62015-03-24 13:55:33 -0700843 if (!activeAngle) {
caryclark54359292015-03-26 07:52:43 -0700844 (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
reed0dc4dd62015-03-24 13:55:33 -0700845 }
caryclark54359292015-03-26 07:52:43 -0700846 SkOpSpanBase* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000847 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000848 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000849 *chase->append() = last;
850#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -0700851 SkDebugf("%s chase.append segment=%d span=%d", __FUNCTION__,
852 last->segment()->debugID(), last->debugID());
853 if (!last->final()) {
854 SkDebugf(" windSum=%d", last->upCast()->windSum());
855 }
856 SkDebugf("\n");
caryclark@google.com07393ca2013-04-08 11:47:37 +0000857#endif
858 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000859 } while ((nextAngle = nextAngle->next()) != angle);
caryclark54359292015-03-26 07:52:43 -0700860 start->segment()->markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000861 if (!foundAngle) {
862 return NULL;
863 }
864 *nextStart = foundAngle->start();
865 *nextEnd = foundAngle->end();
866 nextSegment = foundAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000867#if DEBUG_WINDING
868 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
869 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
870 #endif
871 return nextSegment;
872}
873
caryclark54359292015-03-26 07:52:43 -0700874SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
875 SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable) {
876 SkOpSpanBase* start = *nextStart;
877 SkOpSpanBase* end = *nextEnd;
878 SkASSERT(start != end);
879 int step = start->step(end);
880 SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
881 if (other) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000882 // mark the smaller of startIndex, endIndex done, and all adjacent
883 // spans with the same T value (but not 'other' spans)
884#if DEBUG_WINDING
885 SkDebugf("%s simple\n", __FUNCTION__);
886#endif
caryclark54359292015-03-26 07:52:43 -0700887 SkOpSpan* startSpan = start->starter(end);
888 if (startSpan->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000889 return NULL;
890 }
caryclark54359292015-03-26 07:52:43 -0700891 markDone(startSpan);
892 *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000893 return other;
894 }
caryclark54359292015-03-26 07:52:43 -0700895 SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
896 SkASSERT(endNear == end); // is this ever not end?
897 SkASSERT(endNear);
898 SkASSERT(start != endNear);
899 SkASSERT((start->t() < endNear->t()) ^ (step < 0));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000900 // more than one viable candidate -- measure angles to find best
caryclark54359292015-03-26 07:52:43 -0700901 int calcWinding = computeSum(start, endNear, SkOpAngle::kUnaryWinding);
caryclark@google.com570863f2013-09-16 15:55:01 +0000902 bool sortable = calcWinding != SK_NaN32;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000903 if (!sortable) {
904 *unsortable = true;
caryclark54359292015-03-26 07:52:43 -0700905 markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000906 return NULL;
907 }
caryclark54359292015-03-26 07:52:43 -0700908 SkOpAngle* angle = this->spanToAngle(end, start);
909 if (angle->unorderable()) {
910 *unsortable = true;
911 markDone(start->starter(end));
912 return NULL;
913 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000914#if DEBUG_SORT
915 SkDebugf("%s\n", __FUNCTION__);
916 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000917#endif
caryclark54359292015-03-26 07:52:43 -0700918 int sumWinding = updateWinding(end, start);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000919 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000920 const SkOpAngle* foundAngle = NULL;
921 bool foundDone = false;
caryclark54359292015-03-26 07:52:43 -0700922 // iterate through the angle, and compute everyone's winding
caryclark@google.com07393ca2013-04-08 11:47:37 +0000923 SkOpSegment* nextSegment;
924 int activeCount = 0;
925 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000926 nextSegment = nextAngle->segment();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000927 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000928 &sumWinding);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000929 if (activeAngle) {
930 ++activeCount;
931 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000932 foundAngle = nextAngle;
933 foundDone = nextSegment->done(nextAngle);
934 }
935 }
936 if (nextSegment->done()) {
937 continue;
938 }
reed0dc4dd62015-03-24 13:55:33 -0700939 if (!activeAngle) {
caryclark54359292015-03-26 07:52:43 -0700940 (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
reed0dc4dd62015-03-24 13:55:33 -0700941 }
caryclark54359292015-03-26 07:52:43 -0700942 SkOpSpanBase* last = nextAngle->lastMarked();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000943 if (last) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000944 SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000945 *chase->append() = last;
946#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -0700947 SkDebugf("%s chase.append segment=%d span=%d", __FUNCTION__,
948 last->segment()->debugID(), last->debugID());
949 if (!last->final()) {
950 SkDebugf(" windSum=%d", last->upCast()->windSum());
951 }
952 SkDebugf("\n");
caryclark@google.com07393ca2013-04-08 11:47:37 +0000953#endif
954 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000955 } while ((nextAngle = nextAngle->next()) != angle);
caryclark54359292015-03-26 07:52:43 -0700956 start->segment()->markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000957 if (!foundAngle) {
958 return NULL;
959 }
960 *nextStart = foundAngle->start();
961 *nextEnd = foundAngle->end();
962 nextSegment = foundAngle->segment();
963#if DEBUG_WINDING
964 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
965 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
966 #endif
967 return nextSegment;
968}
969
caryclark54359292015-03-26 07:52:43 -0700970SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd,
971 bool* unsortable) {
972 SkOpSpanBase* start = *nextStart;
973 SkOpSpanBase* end = *nextEnd;
974 SkASSERT(start != end);
975 int step = start->step(end);
976 SkOpSegment* other = this->isSimple(nextStart, &step); // advances nextStart
977 if (other) {
978 // mark the smaller of startIndex, endIndex done, and all adjacent
979 // spans with the same T value (but not 'other' spans)
caryclark@google.com07393ca2013-04-08 11:47:37 +0000980#if DEBUG_WINDING
981 SkDebugf("%s simple\n", __FUNCTION__);
982#endif
caryclark54359292015-03-26 07:52:43 -0700983 SkOpSpan* startSpan = start->starter(end);
984 if (startSpan->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000985 return NULL;
986 }
caryclark54359292015-03-26 07:52:43 -0700987 markDone(startSpan);
988 *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000989 return other;
990 }
caryclark54359292015-03-26 07:52:43 -0700991 SkDEBUGCODE(SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() \
992 : (*nextStart)->prev());
993 SkASSERT(endNear == end); // is this ever not end?
994 SkASSERT(endNear);
995 SkASSERT(start != endNear);
996 SkASSERT((start->t() < endNear->t()) ^ (step < 0));
997 SkOpAngle* angle = this->spanToAngle(end, start);
998 if (angle->unorderable()) {
999 *unsortable = true;
1000 markDone(start->starter(end));
1001 return NULL;
1002 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001003#if DEBUG_SORT
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001004 SkDebugf("%s\n", __FUNCTION__);
1005 angle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001006#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001007 SkOpAngle* nextAngle = angle->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001008 const SkOpAngle* foundAngle = NULL;
1009 bool foundDone = false;
caryclark54359292015-03-26 07:52:43 -07001010 // iterate through the angle, and compute everyone's winding
caryclark@google.com07393ca2013-04-08 11:47:37 +00001011 SkOpSegment* nextSegment;
1012 int activeCount = 0;
1013 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001014 nextSegment = nextAngle->segment();
1015 ++activeCount;
1016 if (!foundAngle || (foundDone && activeCount & 1)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001017 foundAngle = nextAngle;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001018 if (!(foundDone = nextSegment->done(nextAngle))) {
1019 break;
1020 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001021 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001022 nextAngle = nextAngle->next();
1023 } while (nextAngle != angle);
caryclark54359292015-03-26 07:52:43 -07001024 start->segment()->markDone(start->starter(end));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001025 if (!foundAngle) {
1026 return NULL;
1027 }
1028 *nextStart = foundAngle->start();
1029 *nextEnd = foundAngle->end();
1030 nextSegment = foundAngle->segment();
1031#if DEBUG_WINDING
1032 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
1033 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
1034 #endif
1035 return nextSegment;
1036}
1037
caryclark54359292015-03-26 07:52:43 -07001038SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
1039 bool* unsortable, SkChunkAlloc* allocator) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001040 // iterate through T intersections and return topmost
1041 // topmost tangent from y-min to first pt is closer to horizontal
1042 SkASSERT(!done());
caryclark54359292015-03-26 07:52:43 -07001043 SkOpSpanBase* firstT = NULL;
1044 (void) this->activeLeftTop(&firstT);
1045 if (!firstT) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001046 *unsortable = !firstPass;
caryclark54359292015-03-26 07:52:43 -07001047 firstT = &fHead;
1048 while (firstT->upCast()->done()) {
1049 firstT = firstT->upCast()->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001050 }
caryclark54359292015-03-26 07:52:43 -07001051 *startPtr = firstT;
1052 *endPtr = firstT->upCast()->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001053 return this;
1054 }
1055 // sort the edges to find the leftmost
1056 int step = 1;
caryclark54359292015-03-26 07:52:43 -07001057 SkOpSpanBase* end;
1058 if (firstT->final() || firstT->upCast()->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001059 step = -1;
caryclark54359292015-03-26 07:52:43 -07001060 end = firstT->prev();
1061 SkASSERT(end);
1062 } else {
1063 end = firstT->upCast()->next();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001064 }
1065 // if the topmost T is not on end, or is three-way or more, find left
1066 // look for left-ness from tLeft to firstT (matching y of other)
caryclark54359292015-03-26 07:52:43 -07001067 SkASSERT(firstT != end);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001068 SkOpAngle* markAngle = spanToAngle(firstT, end);
1069 if (!markAngle) {
caryclark54359292015-03-26 07:52:43 -07001070 markAngle = addSingletonAngles(step, allocator);
caryclark@google.com570863f2013-09-16 15:55:01 +00001071 }
caryclark1049f122015-04-20 08:31:59 -07001072 if (!markAngle) {
1073 return NULL;
1074 }
1075 if (!markAngle->markStops()) {
1076 return NULL;
1077 }
caryclarke4097e32014-06-18 07:24:19 -07001078 const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
1079 : markAngle->findFirst();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001080 if (!baseAngle) {
1081 return NULL; // nothing to do
caryclark@google.com570863f2013-09-16 15:55:01 +00001082 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001083 SkScalar top = SK_ScalarMax;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001084 const SkOpAngle* firstAngle = NULL;
1085 const SkOpAngle* angle = baseAngle;
caryclark03b03ca2015-04-23 09:13:37 -07001086#if DEBUG_SWAP_TOP
1087 SkDebugf("-%s- baseAngle\n", __FUNCTION__);
1088 baseAngle->debugLoop();
1089#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001090 do {
1091 if (!angle->unorderable()) {
caryclark54359292015-03-26 07:52:43 -07001092 const SkOpSegment* next = angle->segment();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001093 SkPathOpsBounds bounds;
1094 next->subDivideBounds(angle->end(), angle->start(), &bounds);
caryclark65f55312014-11-13 06:58:52 -08001095 bool nearSame = AlmostEqualUlps(top, bounds.top());
1096 bool lowerSector = !firstAngle || angle->sectorEnd() < firstAngle->sectorStart();
1097 bool lesserSector = top > bounds.fTop;
1098 if (lesserSector && (!nearSame || lowerSector)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001099 top = bounds.fTop;
1100 firstAngle = angle;
1101 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001102 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001103 angle = angle->next();
1104 } while (angle != baseAngle);
caryclark65f55312014-11-13 06:58:52 -08001105 if (!firstAngle) {
1106 return NULL; // if all are unorderable, give up
1107 }
caryclark03b03ca2015-04-23 09:13:37 -07001108#if DEBUG_SWAP_TOP
1109 SkDebugf("-%s- firstAngle\n", __FUNCTION__);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001110 firstAngle->debugLoop();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001111#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00001112 // skip edges that have already been processed
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001113 angle = firstAngle;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001114 SkOpSegment* leftSegment = NULL;
1115 bool looped = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001116 do {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001117 *unsortable = angle->unorderable();
1118 if (firstPass || !*unsortable) {
1119 leftSegment = angle->segment();
caryclark54359292015-03-26 07:52:43 -07001120 *startPtr = angle->end();
1121 *endPtr = angle->start();
1122 const SkOpSpan* firstSpan = (*startPtr)->starter(*endPtr);
1123 if (!firstSpan->done()) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001124 break;
1125 }
1126 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001127 angle = angle->next();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001128 looped = true;
1129 } while (angle != firstAngle);
1130 if (angle == firstAngle && looped) {
1131 return NULL;
1132 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001133 if (leftSegment->verb() >= SkPath::kQuad_Verb) {
caryclark54359292015-03-26 07:52:43 -07001134 SkOpSpanBase* start = *startPtr;
1135 SkOpSpanBase* end = *endPtr;
caryclarke4097e32014-06-18 07:24:19 -07001136 bool swap;
caryclark03b03ca2015-04-23 09:13:37 -07001137 bool cw = leftSegment->clockwise(start, end, &swap);
1138#if DEBUG_SWAP_TOP
1139 SkDebugf("%s id=%d s=%1.9g e=%1.9g (%c) cw=%d swap=%d inflections=%d monotonic=%d\n",
1140 __FUNCTION__, leftSegment->debugID(), start->t(), end->t(),
1141 start->t() < end->t() ? '-' : '+', cw,
1142 swap, leftSegment->debugInflections(start, end),
1143 leftSegment->monotonicInY(start, end));
1144#endif
1145 if (!cw && swap) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001146 // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
1147 // sorted but merely the first not already processed (i.e., not done)
caryclark03b03ca2015-04-23 09:13:37 -07001148 SkTSwap(*startPtr, *endPtr);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001149 }
caryclark1049f122015-04-20 08:31:59 -07001150 // FIXME: clockwise isn't reliable -- try computing swap from tangent ?
caryclark03b03ca2015-04-23 09:13:37 -07001151 } else {
1152#if DEBUG_SWAP_TOP
1153 SkDebugf("%s id=%d s=%1.9g e=%1.9g (%c) cw=%d swap=%d inflections=%d monotonic=%d\n",
1154 __FUNCTION__, leftSegment->debugID(), (*startPtr)->t(), (*endPtr)->t(),
1155 (*startPtr)->t() < (*endPtr)->t() ? '-' : '+', -1, -1, -1, 1);
1156#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00001157 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001158 return leftSegment;
1159}
1160
caryclark54359292015-03-26 07:52:43 -07001161SkOpGlobalState* SkOpSegment::globalState() const {
1162 return contour()->globalState();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001163}
1164
caryclark1049f122015-04-20 08:31:59 -07001165void SkOpSegment::init(SkPoint pts[], SkScalar weight, SkOpContour* contour, SkPath::Verb verb) {
caryclark54359292015-03-26 07:52:43 -07001166 fContour = contour;
1167 fNext = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001168 fPts = pts;
caryclark1049f122015-04-20 08:31:59 -07001169 fWeight = weight;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001170 fVerb = verb;
caryclark03b03ca2015-04-23 09:13:37 -07001171 fCubicType = SkDCubic::kUnsplit_SkDCubicType;
caryclark54359292015-03-26 07:52:43 -07001172 fCount = 0;
1173 fDoneCount = 0;
1174 fVisited = false;
1175 SkOpSpan* zeroSpan = &fHead;
1176 zeroSpan->init(this, NULL, 0, fPts[0]);
1177 SkOpSpanBase* oneSpan = &fTail;
1178 zeroSpan->setNext(oneSpan);
1179 oneSpan->initBase(this, zeroSpan, 1, fPts[SkPathOpsVerbToPoints(fVerb)]);
caryclark1049f122015-04-20 08:31:59 -07001180 SkDEBUGCODE(fID = globalState()->nextSegmentID());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001181}
1182
caryclark54359292015-03-26 07:52:43 -07001183void SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end,
1184 SkOpAngle::IncludeType angleIncludeType) {
1185 int local = SkOpSegment::SpanSign(start, end);
caryclark65f55312014-11-13 06:58:52 -08001186 SkDEBUGCODE(bool success);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001187 if (angleIncludeType == SkOpAngle::kBinarySingle) {
caryclark54359292015-03-26 07:52:43 -07001188 int oppLocal = SkOpSegment::OppSign(start, end);
caryclark65f55312014-11-13 06:58:52 -08001189 SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, oppLocal, NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001190 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08001191 SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, oppLocal, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001192 } else {
caryclark65f55312014-11-13 06:58:52 -08001193 SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001194 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08001195 SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001196 }
caryclark65f55312014-11-13 06:58:52 -08001197 SkASSERT(success);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001198}
1199
1200/*
1201when we start with a vertical intersect, we try to use the dx to determine if the edge is to
1202the left or the right of vertical. This determines if we need to add the span's
1203sign or not. However, this isn't enough.
1204If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
1205If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
1206from has the same x direction as this span, the winding should change. If the dx is opposite, then
1207the same winding is shared by both.
1208*/
caryclark54359292015-03-26 07:52:43 -07001209bool SkOpSegment::initWinding(SkOpSpanBase* start, SkOpSpanBase* end, double tHit,
1210 int winding, SkScalar hitDx, int oppWind, SkScalar hitOppDx) {
1211 SkASSERT(this == start->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001212 SkASSERT(hitDx || !winding);
caryclark1049f122015-04-20 08:31:59 -07001213 SkScalar dx = (*CurveSlopeAtT[fVerb])(fPts, fWeight, tHit).fX;
caryclark54359292015-03-26 07:52:43 -07001214// SkASSERT(dx);
1215 int windVal = start->starter(end)->windValue();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001216#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07001217 SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), winding,
caryclark@google.com07393ca2013-04-08 11:47:37 +00001218 hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
1219#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001220 int sideWind = winding + (dx < 0 ? windVal : -windVal);
1221 if (abs(winding) < abs(sideWind)) {
1222 winding = sideWind;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001223 }
caryclark54359292015-03-26 07:52:43 -07001224 SkDEBUGCODE(int oppLocal = SkOpSegment::OppSign(start, end));
caryclark@google.com07393ca2013-04-08 11:47:37 +00001225 SkASSERT(hitOppDx || !oppWind || !oppLocal);
caryclark54359292015-03-26 07:52:43 -07001226 int oppWindVal = start->starter(end)->oppValue();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001227 if (!oppWind) {
1228 oppWind = dx < 0 ? oppWindVal : -oppWindVal;
1229 } else if (hitOppDx * dx >= 0) {
1230 int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
1231 if (abs(oppWind) < abs(oppSideWind)) {
1232 oppWind = oppSideWind;
1233 }
1234 }
caryclarkdac1d172014-06-17 05:15:38 -07001235#if DEBUG_WINDING_AT_T
1236 SkDebugf(" winding=%d oppWind=%d\n", winding, oppWind);
1237#endif
caryclark65f55312014-11-13 06:58:52 -08001238 // if this fails to mark (because the edges are too small) inform caller to try again
1239 bool success = markAndChaseWinding(start, end, winding, oppWind, NULL);
caryclark@google.com7eaa53d2013-10-02 14:49:34 +00001240 // OPTIMIZATION: the reverse mark and chase could skip the first marking
caryclark65f55312014-11-13 06:58:52 -08001241 success |= markAndChaseWinding(end, start, winding, oppWind, NULL);
1242 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001243}
1244
caryclark54359292015-03-26 07:52:43 -07001245bool SkOpSegment::isClose(double t, const SkOpSegment* opp) const {
1246 SkDPoint cPt = this->dPtAtT(t);
caryclark1049f122015-04-20 08:31:59 -07001247 SkDVector dxdy = (*CurveDSlopeAtT[this->verb()])(this->pts(), this->weight(), t);
caryclark54359292015-03-26 07:52:43 -07001248 SkDLine perp = {{ cPt, {cPt.fX + dxdy.fY, cPt.fY - dxdy.fX} }};
1249 SkIntersections i;
caryclark1049f122015-04-20 08:31:59 -07001250 (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), perp, &i);
caryclark54359292015-03-26 07:52:43 -07001251 int used = i.used();
1252 for (int index = 0; index < used; ++index) {
1253 if (cPt.roughlyEqual(i.pt(index))) {
reed0dc4dd62015-03-24 13:55:33 -07001254 return true;
1255 }
caryclark54359292015-03-26 07:52:43 -07001256 }
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +00001257 return false;
1258}
1259
caryclark54359292015-03-26 07:52:43 -07001260bool SkOpSegment::isXor() const {
1261 return fContour->isXor();
1262}
caryclark@google.com07393ca2013-04-08 11:47:37 +00001263
caryclark54359292015-03-26 07:52:43 -07001264SkOpSpanBase* SkOpSegment::markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end) {
1265 int step = start->step(end);
1266 SkOpSpan* minSpan = start->starter(end);
1267 markDone(minSpan);
1268 SkOpSpanBase* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001269 SkOpSegment* other = this;
caryclark54359292015-03-26 07:52:43 -07001270 while ((other = other->nextChase(&start, &step, &minSpan, &last))) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001271 if (other->done()) {
caryclarkdac1d172014-06-17 05:15:38 -07001272 SkASSERT(!last);
1273 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001274 }
caryclark54359292015-03-26 07:52:43 -07001275 other->markDone(minSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001276 }
1277 return last;
1278}
1279
caryclark54359292015-03-26 07:52:43 -07001280bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
1281 SkOpSpanBase** lastPtr) {
1282 SkOpSpan* spanStart = start->starter(end);
1283 int step = start->step(end);
1284 bool success = markWinding(spanStart, winding);
1285 SkOpSpanBase* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001286 SkOpSegment* other = this;
caryclark54359292015-03-26 07:52:43 -07001287 while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
1288 if (spanStart->windSum() != SK_MinS32) {
1289 SkASSERT(spanStart->windSum() == winding);
caryclarkdac1d172014-06-17 05:15:38 -07001290 SkASSERT(!last);
1291 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001292 }
caryclark54359292015-03-26 07:52:43 -07001293 (void) other->markWinding(spanStart, winding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001294 }
caryclark65f55312014-11-13 06:58:52 -08001295 if (lastPtr) {
1296 *lastPtr = last;
1297 }
1298 return success;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001299}
1300
caryclark54359292015-03-26 07:52:43 -07001301bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end,
1302 int winding, int oppWinding, SkOpSpanBase** lastPtr) {
1303 SkOpSpan* spanStart = start->starter(end);
1304 int step = start->step(end);
1305 bool success = markWinding(spanStart, winding, oppWinding);
1306 SkOpSpanBase* last = NULL;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001307 SkOpSegment* other = this;
caryclark54359292015-03-26 07:52:43 -07001308 while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
1309 if (spanStart->windSum() != SK_MinS32) {
1310 if (this->operand() == other->operand()) {
1311 SkASSERT(spanStart->windSum() == winding);
1312 if (spanStart->oppSum() != oppWinding) {
1313 this->globalState()->setWindingFailed();
1314 return false;
caryclarkdac1d172014-06-17 05:15:38 -07001315 }
caryclark54359292015-03-26 07:52:43 -07001316 } else {
1317 SkASSERT(spanStart->windSum() == oppWinding);
1318 SkASSERT(spanStart->oppSum() == winding);
caryclarkdac1d172014-06-17 05:15:38 -07001319 }
1320 SkASSERT(!last);
caryclarkdac1d172014-06-17 05:15:38 -07001321 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001322 }
caryclark54359292015-03-26 07:52:43 -07001323 if (this->operand() == other->operand()) {
1324 (void) other->markWinding(spanStart, winding, oppWinding);
caryclarkdac1d172014-06-17 05:15:38 -07001325 } else {
caryclark54359292015-03-26 07:52:43 -07001326 (void) other->markWinding(spanStart, oppWinding, winding);
caryclarkdac1d172014-06-17 05:15:38 -07001327 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001328 }
caryclark65f55312014-11-13 06:58:52 -08001329 if (lastPtr) {
1330 *lastPtr = last;
1331 }
1332 return success;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001333}
1334
caryclark54359292015-03-26 07:52:43 -07001335SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001336 SkASSERT(angle->segment() == this);
1337 if (UseInnerWinding(maxWinding, sumWinding)) {
1338 maxWinding = sumWinding;
1339 }
caryclark54359292015-03-26 07:52:43 -07001340 SkOpSpanBase* last;
1341 (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, &last);
caryclark@google.com570863f2013-09-16 15:55:01 +00001342#if DEBUG_WINDING
1343 if (last) {
caryclark54359292015-03-26 07:52:43 -07001344 SkDebugf("%s last seg=%d span=%d", __FUNCTION__,
1345 last->segment()->debugID(), last->debugID());
1346 if (!last->final()) {
1347 SkDebugf(" windSum=");
1348 SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
1349 }
1350 SkDebugf("\n");
caryclark@google.com570863f2013-09-16 15:55:01 +00001351 }
1352#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00001353 return last;
1354}
1355
caryclark54359292015-03-26 07:52:43 -07001356SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
1357 int oppSumWinding, const SkOpAngle* angle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00001358 SkASSERT(angle->segment() == this);
1359 if (UseInnerWinding(maxWinding, sumWinding)) {
1360 maxWinding = sumWinding;
1361 }
1362 if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
1363 oppMaxWinding = oppSumWinding;
1364 }
caryclark54359292015-03-26 07:52:43 -07001365 SkOpSpanBase* last = NULL;
caryclark65f55312014-11-13 06:58:52 -08001366 // caller doesn't require that this marks anything
caryclark54359292015-03-26 07:52:43 -07001367 (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, oppMaxWinding, &last);
caryclark@google.com570863f2013-09-16 15:55:01 +00001368#if DEBUG_WINDING
1369 if (last) {
caryclark54359292015-03-26 07:52:43 -07001370 SkDebugf("%s last segment=%d span=%d", __FUNCTION__,
1371 last->segment()->debugID(), last->debugID());
1372 if (!last->final()) {
1373 SkDebugf(" windSum=");
1374 SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
1375 }
1376 SkDebugf(" \n");
caryclark@google.com570863f2013-09-16 15:55:01 +00001377 }
1378#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +00001379 return last;
1380}
1381
caryclark54359292015-03-26 07:52:43 -07001382void SkOpSegment::markDone(SkOpSpan* span) {
1383 SkASSERT(this == span->segment());
1384 if (span->done()) {
1385 return;
1386 }
1387#if DEBUG_MARK_DONE
1388 debugShowNewWinding(__FUNCTION__, span, span->windSum(), span->oppSum());
1389#endif
1390 span->setDone(true);
1391 ++fDoneCount;
1392 debugValidate();
1393}
1394
1395bool SkOpSegment::markWinding(SkOpSpan* span, int winding) {
1396 SkASSERT(this == span->segment());
reed0dc4dd62015-03-24 13:55:33 -07001397 SkASSERT(winding);
caryclark54359292015-03-26 07:52:43 -07001398 if (span->done()) {
reed0dc4dd62015-03-24 13:55:33 -07001399 return false;
1400 }
1401#if DEBUG_MARK_DONE
caryclark54359292015-03-26 07:52:43 -07001402 debugShowNewWinding(__FUNCTION__, span, winding);
reed0dc4dd62015-03-24 13:55:33 -07001403#endif
caryclark54359292015-03-26 07:52:43 -07001404 span->setWindSum(winding);
1405 debugValidate();
reed0dc4dd62015-03-24 13:55:33 -07001406 return true;
1407}
1408
caryclark54359292015-03-26 07:52:43 -07001409bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
1410 SkASSERT(this == span->segment());
caryclark@google.com07393ca2013-04-08 11:47:37 +00001411 SkASSERT(winding || oppWinding);
caryclark54359292015-03-26 07:52:43 -07001412 if (span->done()) {
1413 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001414 }
caryclark54359292015-03-26 07:52:43 -07001415#if DEBUG_MARK_DONE
1416 debugShowNewWinding(__FUNCTION__, span, winding, oppWinding);
1417#endif
1418 span->setWindSum(winding);
1419 span->setOppSum(oppWinding);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001420 debugValidate();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001421 return true;
1422}
1423
caryclark54359292015-03-26 07:52:43 -07001424bool SkOpSegment::match(const SkOpPtT* base, const SkOpSegment* testParent, double testT,
1425 const SkPoint& testPt) const {
1426 const SkOpSegment* baseParent = base->segment();
1427 if (this == baseParent && this == testParent && precisely_equal(base->fT, testT)) {
1428 return true;
1429 }
1430 if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
1431 return false;
1432 }
caryclark08bc8482015-04-24 09:08:57 -07001433 return !this->ptsDisjoint(base->fT, base->fPt, testT, testPt);
caryclark54359292015-03-26 07:52:43 -07001434}
1435
1436static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
1437 if (last) {
1438 *last = endSpan;
caryclarkdac1d172014-06-17 05:15:38 -07001439 }
1440 return NULL;
1441}
1442
caryclark54359292015-03-26 07:52:43 -07001443bool SkOpSegment::monotonicInY(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
1444 SkASSERT(fVerb != SkPath::kLine_Verb);
1445 if (fVerb == SkPath::kQuad_Verb) {
1446 SkDQuad dst = SkDQuad::SubDivide(fPts, start->t(), end->t());
1447 return dst.monotonicInY();
1448 }
caryclark1049f122015-04-20 08:31:59 -07001449 if (fVerb == SkPath::kConic_Verb) {
1450 SkDConic dst = SkDConic::SubDivide(fPts, fWeight, start->t(), end->t());
1451 return dst.monotonicInY();
1452 }
caryclark54359292015-03-26 07:52:43 -07001453 SkASSERT(fVerb == SkPath::kCubic_Verb);
1454 SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t());
1455 return dst.monotonicInY();
1456}
1457
1458bool SkOpSegment::NextCandidate(SkOpSpanBase* span, SkOpSpanBase** start,
1459 SkOpSpanBase** end) {
1460 while (span->final() || span->upCast()->done()) {
1461 if (span->final()) {
1462 return false;
1463 }
1464 span = span->upCast()->next();
1465 }
1466 *start = span;
1467 *end = span->upCast()->next();
1468 return true;
1469}
1470
1471SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpSpan** minPtr,
1472 SkOpSpanBase** last) const {
1473 SkOpSpanBase* origStart = *startPtr;
caryclarkdac1d172014-06-17 05:15:38 -07001474 int step = *stepPtr;
caryclark54359292015-03-26 07:52:43 -07001475 SkOpSpanBase* endSpan = step > 0 ? origStart->upCast()->next() : origStart->prev();
1476 SkASSERT(endSpan);
1477 SkOpAngle* angle = step > 0 ? endSpan->fromAngle() : endSpan->upCast()->toAngle();
1478 SkOpSpanBase* foundSpan;
1479 SkOpSpanBase* otherEnd;
caryclarkdac1d172014-06-17 05:15:38 -07001480 SkOpSegment* other;
1481 if (angle == NULL) {
caryclark54359292015-03-26 07:52:43 -07001482 if (endSpan->t() != 0 && endSpan->t() != 1) {
caryclarkdac1d172014-06-17 05:15:38 -07001483 return NULL;
1484 }
caryclark54359292015-03-26 07:52:43 -07001485 SkOpPtT* otherPtT = endSpan->ptT()->next();
1486 other = otherPtT->segment();
1487 foundSpan = otherPtT->span();
1488 otherEnd = step > 0 ? foundSpan->upCast()->next() : foundSpan->prev();
caryclarkdac1d172014-06-17 05:15:38 -07001489 } else {
1490 int loopCount = angle->loopCount();
1491 if (loopCount > 2) {
caryclark54359292015-03-26 07:52:43 -07001492 return set_last(last, endSpan);
caryclarkdac1d172014-06-17 05:15:38 -07001493 }
1494 const SkOpAngle* next = angle->next();
caryclark65b427c2014-09-18 10:32:57 -07001495 if (NULL == next) {
1496 return NULL;
1497 }
caryclarkdac1d172014-06-17 05:15:38 -07001498#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -07001499 if (angle->sign() != next->sign() && !angle->segment()->contour()->isXor()
1500 && !next->segment()->contour()->isXor()) {
caryclarkdac1d172014-06-17 05:15:38 -07001501 SkDebugf("%s mismatched signs\n", __FUNCTION__);
reed0dc4dd62015-03-24 13:55:33 -07001502 }
caryclark54359292015-03-26 07:52:43 -07001503#endif
caryclarkdac1d172014-06-17 05:15:38 -07001504 other = next->segment();
caryclark54359292015-03-26 07:52:43 -07001505 foundSpan = endSpan = next->start();
caryclarkdac1d172014-06-17 05:15:38 -07001506 otherEnd = next->end();
caryclark@google.com570863f2013-09-16 15:55:01 +00001507 }
caryclark54359292015-03-26 07:52:43 -07001508 int foundStep = foundSpan->step(otherEnd);
caryclarkdac1d172014-06-17 05:15:38 -07001509 if (*stepPtr != foundStep) {
caryclark54359292015-03-26 07:52:43 -07001510 return set_last(last, endSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001511 }
caryclark54359292015-03-26 07:52:43 -07001512 SkASSERT(*startPtr);
1513 if (!otherEnd) {
caryclark65b427c2014-09-18 10:32:57 -07001514 return NULL;
1515 }
1516// SkASSERT(otherEnd >= 0);
caryclark54359292015-03-26 07:52:43 -07001517 SkOpSpan* origMin = step < 0 ? origStart->prev() : origStart->upCast();
1518 SkOpSpan* foundMin = foundSpan->starter(otherEnd);
1519 if (foundMin->windValue() != origMin->windValue()
1520 || foundMin->oppValue() != origMin->oppValue()) {
1521 return set_last(last, endSpan);
caryclarkdac1d172014-06-17 05:15:38 -07001522 }
caryclark54359292015-03-26 07:52:43 -07001523 *startPtr = foundSpan;
caryclarkdac1d172014-06-17 05:15:38 -07001524 *stepPtr = foundStep;
1525 if (minPtr) {
1526 *minPtr = foundMin;
caryclark@google.coma5e55922013-05-07 18:51:31 +00001527 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00001528 return other;
1529}
1530
caryclark54359292015-03-26 07:52:43 -07001531static void clear_visited(SkOpSpan* span) {
1532 // reset visited flag back to false
1533 do {
1534 SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
1535 while ((ptT = ptT->next()) != stopPtT) {
1536 SkOpSegment* opp = ptT->segment();
1537 opp->resetVisited();
caryclark@google.com07393ca2013-04-08 11:47:37 +00001538 }
caryclark54359292015-03-26 07:52:43 -07001539 } while ((span = span->next()->upCastable()));
reed0dc4dd62015-03-24 13:55:33 -07001540}
1541
caryclark54359292015-03-26 07:52:43 -07001542// look for pairs of undetected coincident curves
1543// assumes that segments going in have visited flag clear
1544// curve/curve intersection should now do a pretty good job of finding coincident runs so
1545// this may be only be necessary for line/curve pairs -- so skip unless this is a line and the
1546// the opp is not a line
1547void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
1548 if (this->verb() != SkPath::kLine_Verb) {
1549 return;
1550 }
1551 SkOpSpan* prior = NULL;
1552 SkOpSpan* span = &fHead;
1553 do {
1554 SkOpPtT* ptT = span->ptT(), * spanStopPtT = ptT;
1555 SkASSERT(ptT->span() == span);
1556 while ((ptT = ptT->next()) != spanStopPtT) {
1557 SkOpSegment* opp = ptT->span()->segment();
1558 if (opp->setVisited()) {
caryclark@google.com570863f2013-09-16 15:55:01 +00001559 continue;
1560 }
caryclark54359292015-03-26 07:52:43 -07001561 if (opp->verb() == SkPath::kLine_Verb) {
caryclarkccec0f92015-03-24 07:28:17 -07001562 continue;
1563 }
caryclark54359292015-03-26 07:52:43 -07001564 if (span->containsCoincidence(opp)) { // FIXME: this assumes that if the opposite
1565 // segment is coincident then no more coincidence
1566 // needs to be detected. This may not be true.
1567 continue;
reed0dc4dd62015-03-24 13:55:33 -07001568 }
caryclark54359292015-03-26 07:52:43 -07001569 if (span->containsCoinEnd(opp)) {
1570 continue;
1571 }
1572 // if already visited and visited again, check for coin
1573 if (span == &fHead) {
1574 continue;
1575 }
1576 SkOpPtT* priorPtT = NULL, * priorStopPtT;
1577 // find prior span containing opp segment
1578 SkOpSegment* priorOpp = NULL;
1579 prior = span;
1580 while (!priorOpp && (prior = prior->prev())) {
1581 priorStopPtT = priorPtT = prior->ptT();
1582 while ((priorPtT = priorPtT->next()) != priorStopPtT) {
1583 SkOpSegment* segment = priorPtT->span()->segment();
1584 if (segment == opp) {
1585 priorOpp = opp;
1586 break;
1587 }
1588 }
1589 }
1590 if (!priorOpp) {
1591 continue;
1592 }
1593 SkOpPtT* oppStart = prior->ptT();
1594 SkOpPtT* oppEnd = span->ptT();
1595 bool swapped = priorPtT->fT > ptT->fT;
1596 if (swapped) {
1597 SkTSwap(priorPtT, ptT);
1598 SkTSwap(oppStart, oppEnd);
1599 }
1600 bool flipped = oppStart->fT > oppEnd->fT;
1601 bool coincident;
1602 if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
1603 goto swapBack;
1604 }
1605 {
1606 // average t, find mid pt
1607 double midT = (prior->t() + span->t()) / 2;
1608 SkPoint midPt = this->ptAtT(midT);
1609 coincident = true;
1610 // if the mid pt is not near either end pt, project perpendicular through opp seg
1611 if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt)
1612 && !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) {
1613 coincident = false;
1614 SkIntersections i;
caryclark1049f122015-04-20 08:31:59 -07001615 SkVector dxdy = (*CurveSlopeAtT[fVerb])(this->pts(), this->weight(), midT);
caryclark54359292015-03-26 07:52:43 -07001616 SkDLine ray = {{{midPt.fX, midPt.fY},
1617 {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}};
caryclark1049f122015-04-20 08:31:59 -07001618 (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), ray, &i);
caryclark54359292015-03-26 07:52:43 -07001619 // measure distance and see if it's small enough to denote coincidence
1620 for (int index = 0; index < i.used(); ++index) {
1621 SkDPoint oppPt = i.pt(index);
1622 if (oppPt.approximatelyEqual(midPt)) {
caryclark1049f122015-04-20 08:31:59 -07001623 SkVector oppDxdy = (*CurveSlopeAtT[opp->verb()])(opp->pts(),
1624 opp->weight(), i[index][0]);
caryclark54359292015-03-26 07:52:43 -07001625 oppDxdy.normalize();
1626 dxdy.normalize();
1627 SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON);
1628 coincident |= flatness < 5000; // FIXME: replace with tuned value
1629 }
1630 }
1631 }
1632 }
1633 if (coincident) {
1634 // mark coincidence
1635 coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator);
1636 clear_visited(&fHead);
1637 missingCoincidence(coincidences, allocator);
1638 return;
1639 }
1640 swapBack:
1641 if (swapped) {
1642 SkTSwap(priorPtT, ptT);
1643 }
reed0dc4dd62015-03-24 13:55:33 -07001644 }
caryclark54359292015-03-26 07:52:43 -07001645 } while ((span = span->next()->upCastable()));
1646 clear_visited(&fHead);
reed0dc4dd62015-03-24 13:55:33 -07001647}
1648
caryclark08bc8482015-04-24 09:08:57 -07001649// if a span has more than one intersection, merge the other segments' span as needed
1650void SkOpSegment::moveMultiples() {
1651 debugValidate();
1652 SkOpSpanBase* test = &fHead;
1653 do {
1654 int addCount = test->spanAddsCount();
1655 SkASSERT(addCount >= 1);
1656 if (addCount == 1) {
1657 continue;
1658 }
1659 SkOpPtT* startPtT = test->ptT();
1660 SkOpPtT* testPtT = startPtT;
1661 do { // iterate through all spans associated with start
1662 SkOpSpanBase* oppSpan = testPtT->span();
1663 if (oppSpan->spanAddsCount() == addCount) {
1664 continue;
1665 }
1666 if (oppSpan->deleted()) {
1667 continue;
1668 }
1669 SkOpSegment* oppSegment = oppSpan->segment();
1670 if (oppSegment == this) {
1671 continue;
1672 }
1673 // find range of spans to consider merging
1674 SkOpSpanBase* oppPrev = oppSpan;
1675 SkOpSpanBase* oppFirst = oppSpan;
1676 while ((oppPrev = oppPrev->prev())) {
1677 if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
1678 break;
1679 }
1680 if (oppPrev->spanAddsCount() == addCount) {
1681 continue;
1682 }
1683 if (oppPrev->deleted()) {
1684 continue;
1685 }
1686 oppFirst = oppPrev;
1687 }
1688 SkOpSpanBase* oppNext = oppSpan;
1689 SkOpSpanBase* oppLast = oppSpan;
1690 while ((oppNext = oppNext->final() ? NULL : oppNext->upCast()->next())) {
1691 if (!roughly_equal(oppNext->t(), oppSpan->t())) {
1692 break;
1693 }
1694 if (oppNext->spanAddsCount() == addCount) {
1695 continue;
1696 }
1697 if (oppNext->deleted()) {
1698 continue;
1699 }
1700 oppLast = oppNext;
1701 }
1702 if (oppFirst == oppLast) {
1703 continue;
1704 }
1705 SkOpSpanBase* oppTest = oppFirst;
1706 do {
1707 if (oppTest == oppSpan) {
1708 continue;
1709 }
1710 // check to see if the candidate meets specific criteria:
1711 // it contains spans of segments in test's loop but not including 'this'
1712 SkOpPtT* oppStartPtT = oppTest->ptT();
1713 SkOpPtT* oppPtT = oppStartPtT;
1714 while ((oppPtT = oppPtT->next()) != oppStartPtT) {
1715 SkOpSegment* oppPtTSegment = oppPtT->segment();
1716 if (oppPtTSegment == this) {
1717 goto tryNextSpan;
1718 }
1719 SkOpPtT* matchPtT = startPtT;
1720 do {
1721 if (matchPtT->segment() == oppPtTSegment) {
1722 goto foundMatch;
1723 }
1724 } while ((matchPtT = matchPtT->next()) != startPtT);
1725 goto tryNextSpan;
1726 foundMatch: // merge oppTest and oppSpan
1727 oppSegment->debugValidate();
1728 if (oppTest == &oppSegment->fTail || oppTest == &oppSegment->fHead) {
1729 SkASSERT(oppSpan != &oppSegment->fHead); // don't expect collapse
1730 SkASSERT(oppSpan != &oppSegment->fTail);
1731 oppTest->merge(oppSpan->upCast());
1732 } else {
1733 oppSpan->merge(oppTest->upCast());
1734 }
1735 oppSegment->debugValidate();
1736 goto checkNextSpan;
1737 }
1738 tryNextSpan:
1739 ;
1740 } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
1741 } while ((testPtT = testPtT->next()) != startPtT);
1742checkNextSpan:
1743 ;
1744 } while ((test = test->final() ? NULL : test->upCast()->next()));
1745 debugValidate();
1746}
1747
caryclark54359292015-03-26 07:52:43 -07001748// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
caryclark08bc8482015-04-24 09:08:57 -07001749void SkOpSegment::moveNearby() {
caryclark54359292015-03-26 07:52:43 -07001750 debugValidate();
1751 SkOpSpanBase* spanS = &fHead;
1752 do {
1753 SkOpSpanBase* test = spanS->upCast()->next();
1754 SkOpSpanBase* next;
1755 if (spanS->contains(test)) {
1756 if (!test->final()) {
1757 test->upCast()->detach(spanS->ptT());
1758 continue;
1759 } else if (spanS != &fHead) {
1760 spanS->upCast()->detach(test->ptT());
1761 spanS = test;
1762 continue;
1763 }
reed0dc4dd62015-03-24 13:55:33 -07001764 }
caryclark54359292015-03-26 07:52:43 -07001765 do { // iterate through all spans associated with start
1766 SkOpPtT* startBase = spanS->ptT();
1767 next = test->final() ? NULL : test->upCast()->next();
1768 do {
1769 SkOpPtT* testBase = test->ptT();
1770 do {
1771 if (startBase == testBase) {
1772 goto checkNextSpan;
1773 }
1774 if (testBase->duplicate()) {
1775 continue;
1776 }
1777 if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) {
1778 if (test == &this->fTail) {
1779 if (spanS == &fHead) {
1780 debugValidate();
caryclark08bc8482015-04-24 09:08:57 -07001781 return; // if this span has collapsed, remove it from parent
caryclark54359292015-03-26 07:52:43 -07001782 }
1783 this->fTail.merge(spanS->upCast());
1784 debugValidate();
caryclark08bc8482015-04-24 09:08:57 -07001785 return;
caryclark54359292015-03-26 07:52:43 -07001786 }
1787 spanS->merge(test->upCast());
1788 spanS->upCast()->setNext(next);
1789 goto checkNextSpan;
1790 }
1791 } while ((testBase = testBase->next()) != test->ptT());
1792 } while ((startBase = startBase->next()) != spanS->ptT());
1793 checkNextSpan:
1794 ;
1795 } while ((test = next));
1796 spanS = spanS->upCast()->next();
1797 } while (!spanS->final());
1798 debugValidate();
reed0dc4dd62015-03-24 13:55:33 -07001799}
1800
caryclark54359292015-03-26 07:52:43 -07001801bool SkOpSegment::operand() const {
1802 return fContour->operand();
1803}
1804
1805bool SkOpSegment::oppXor() const {
1806 return fContour->oppXor();
1807}
1808
1809bool SkOpSegment::ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const {
1810 if (fVerb == SkPath::kLine_Verb) {
1811 return false;
1812 }
1813 // quads (and cubics) can loop back to nearly a line so that an opposite curve
1814 // hits in two places with very different t values.
1815 // OPTIMIZATION: curves could be preflighted so that, for example, something like
1816 // 'controls contained by ends' could avoid this check for common curves
1817 // 'ends are extremes in x or y' is cheaper to compute and real-world common
1818 // on the other hand, the below check is relatively inexpensive
1819 double midT = (t1 + t2) / 2;
1820 SkPoint midPt = this->ptAtT(midT);
1821 double seDistSq = SkTMax(pt1.distanceToSqd(pt2) * 2, FLT_EPSILON * 2);
1822 return midPt.distanceToSqd(pt1) > seDistSq || midPt.distanceToSqd(pt2) > seDistSq;
1823}
1824
1825void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
1826 int* maxWinding, int* sumWinding) {
1827 int deltaSum = SpanSign(start, end);
1828 *maxWinding = *sumMiWinding;
1829 *sumWinding = *sumMiWinding -= deltaSum;
1830 SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
1831}
1832
1833void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
1834 int* sumSuWinding, int* maxWinding, int* sumWinding, int* oppMaxWinding,
1835 int* oppSumWinding) {
1836 int deltaSum = SpanSign(start, end);
1837 int oppDeltaSum = OppSign(start, end);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001838 if (operand()) {
1839 *maxWinding = *sumSuWinding;
1840 *sumWinding = *sumSuWinding -= deltaSum;
1841 *oppMaxWinding = *sumMiWinding;
1842 *oppSumWinding = *sumMiWinding -= oppDeltaSum;
1843 } else {
1844 *maxWinding = *sumMiWinding;
1845 *sumWinding = *sumMiWinding -= deltaSum;
1846 *oppMaxWinding = *sumSuWinding;
1847 *oppSumWinding = *sumSuWinding -= oppDeltaSum;
1848 }
caryclark54359292015-03-26 07:52:43 -07001849 SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
1850 SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
caryclark@google.com07393ca2013-04-08 11:47:37 +00001851}
1852
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001853void SkOpSegment::sortAngles() {
caryclark54359292015-03-26 07:52:43 -07001854 SkOpSpanBase* span = &this->fHead;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001855 do {
caryclark54359292015-03-26 07:52:43 -07001856 SkOpAngle* fromAngle = span->fromAngle();
1857 SkOpAngle* toAngle = span->final() ? NULL : span->upCast()->toAngle();
caryclarkdac1d172014-06-17 05:15:38 -07001858 if (!fromAngle && !toAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001859 continue;
1860 }
caryclark@google.com570863f2013-09-16 15:55:01 +00001861#if DEBUG_ANGLE
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001862 bool wroteAfterHeader = false;
caryclark@google.com570863f2013-09-16 15:55:01 +00001863#endif
caryclark54359292015-03-26 07:52:43 -07001864 SkOpAngle* baseAngle = fromAngle;
1865 if (fromAngle && toAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001866#if DEBUG_ANGLE
caryclark54359292015-03-26 07:52:43 -07001867 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), span->t(),
1868 span->debugID());
1869 wroteAfterHeader = true;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001870#endif
caryclark54359292015-03-26 07:52:43 -07001871 fromAngle->insert(toAngle);
1872 } else if (!fromAngle) {
1873 baseAngle = toAngle;
reed0dc4dd62015-03-24 13:55:33 -07001874 }
caryclark54359292015-03-26 07:52:43 -07001875 SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
reed0dc4dd62015-03-24 13:55:33 -07001876 do {
caryclark54359292015-03-26 07:52:43 -07001877 SkOpSpanBase* oSpan = ptT->span();
1878 if (oSpan == span) {
1879 continue;
1880 }
1881 SkOpAngle* oAngle = oSpan->fromAngle();
caryclarkdac1d172014-06-17 05:15:38 -07001882 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001883#if DEBUG_ANGLE
1884 if (!wroteAfterHeader) {
caryclark54359292015-03-26 07:52:43 -07001885 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
1886 span->t(), span->debugID());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001887 wroteAfterHeader = true;
1888 }
1889#endif
caryclark54359292015-03-26 07:52:43 -07001890 if (!oAngle->loopContains(baseAngle)) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001891 baseAngle->insert(oAngle);
1892 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001893 }
caryclark54359292015-03-26 07:52:43 -07001894 if (!oSpan->final()) {
1895 oAngle = oSpan->upCast()->toAngle();
1896 if (oAngle) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001897#if DEBUG_ANGLE
caryclark54359292015-03-26 07:52:43 -07001898 if (!wroteAfterHeader) {
1899 SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
1900 span->t(), span->debugID());
1901 wroteAfterHeader = true;
1902 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001903#endif
caryclark54359292015-03-26 07:52:43 -07001904 if (!oAngle->loopContains(baseAngle)) {
1905 baseAngle->insert(oAngle);
1906 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00001907 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001908 }
caryclark54359292015-03-26 07:52:43 -07001909 } while ((ptT = ptT->next()) != stopPtT);
1910 if (baseAngle->loopCount() == 1) {
1911 span->setFromAngle(NULL);
1912 if (toAngle) {
1913 span->upCast()->setToAngle(NULL);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001914 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +00001915 baseAngle = NULL;
1916 }
1917#if DEBUG_SORT
1918 SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
1919#endif
caryclark54359292015-03-26 07:52:43 -07001920 } while (!span->final() && (span = span->upCast()->next()));
caryclark@google.com570863f2013-09-16 15:55:01 +00001921}
1922
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001923// return true if midpoints were computed
caryclark54359292015-03-26 07:52:43 -07001924bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
caryclark1049f122015-04-20 08:31:59 -07001925 SkOpCurve* edge) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001926 SkASSERT(start != end);
caryclark54359292015-03-26 07:52:43 -07001927 const SkOpPtT& startPtT = *start->ptT();
1928 const SkOpPtT& endPtT = *end->ptT();
caryclark1049f122015-04-20 08:31:59 -07001929 SkDEBUGCODE(edge->fVerb = fVerb);
1930 edge->fPts[0] = startPtT.fPt;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001931 int points = SkPathOpsVerbToPoints(fVerb);
caryclark1049f122015-04-20 08:31:59 -07001932 edge->fPts[points] = endPtT.fPt;
1933 edge->fWeight = 1;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001934 if (fVerb == SkPath::kLine_Verb) {
1935 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +00001936 }
caryclark54359292015-03-26 07:52:43 -07001937 double startT = startPtT.fT;
1938 double endT = endPtT.fT;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001939 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
1940 // don't compute midpoints if we already have them
1941 if (fVerb == SkPath::kQuad_Verb) {
caryclark1049f122015-04-20 08:31:59 -07001942 edge->fPts[1] = fPts[1];
1943 return false;
1944 }
1945 if (fVerb == SkPath::kConic_Verb) {
1946 edge->fPts[1] = fPts[1];
1947 edge->fWeight = fWeight;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001948 return false;
1949 }
1950 SkASSERT(fVerb == SkPath::kCubic_Verb);
1951 if (start < end) {
caryclark1049f122015-04-20 08:31:59 -07001952 edge->fPts[1] = fPts[1];
1953 edge->fPts[2] = fPts[2];
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001954 return false;
1955 }
caryclark1049f122015-04-20 08:31:59 -07001956 edge->fPts[1] = fPts[2];
1957 edge->fPts[2] = fPts[1];
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001958 return false;
1959 }
caryclark1049f122015-04-20 08:31:59 -07001960 const SkDPoint sub[2] = {{ edge->fPts[0].fX, edge->fPts[0].fY},
1961 {edge->fPts[points].fX, edge->fPts[points].fY }};
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001962 if (fVerb == SkPath::kQuad_Verb) {
caryclark1049f122015-04-20 08:31:59 -07001963 edge->fPts[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint();
1964 } else if (fVerb == SkPath::kConic_Verb) {
1965 edge->fPts[1] = SkDConic::SubDivide(fPts, fWeight, sub[0], sub[1],
1966 startT, endT, &edge->fWeight).asSkPoint();
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001967 } else {
1968 SkASSERT(fVerb == SkPath::kCubic_Verb);
1969 SkDPoint ctrl[2];
1970 SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl);
caryclark1049f122015-04-20 08:31:59 -07001971 edge->fPts[1] = ctrl[0].asSkPoint();
1972 edge->fPts[2] = ctrl[1].asSkPoint();
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001973 }
1974 return true;
1975}
1976
caryclark54359292015-03-26 07:52:43 -07001977bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
caryclark1049f122015-04-20 08:31:59 -07001978 SkDCurve* edge) const {
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001979 SkASSERT(start != end);
caryclark54359292015-03-26 07:52:43 -07001980 const SkOpPtT& startPtT = *start->ptT();
1981 const SkOpPtT& endPtT = *end->ptT();
caryclark1049f122015-04-20 08:31:59 -07001982 SkDEBUGCODE(edge->fVerb = fVerb);
1983 edge->fCubic[0].set(startPtT.fPt);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001984 int points = SkPathOpsVerbToPoints(fVerb);
caryclark1049f122015-04-20 08:31:59 -07001985 edge->fCubic[points].set(endPtT.fPt);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001986 if (fVerb == SkPath::kLine_Verb) {
1987 return false;
1988 }
caryclark54359292015-03-26 07:52:43 -07001989 double startT = startPtT.fT;
1990 double endT = endPtT.fT;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00001991 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
1992 // don't compute midpoints if we already have them
1993 if (fVerb == SkPath::kQuad_Verb) {
caryclark1049f122015-04-20 08:31:59 -07001994 edge->fLine[1].set(fPts[1]);
1995 return false;
1996 }
1997 if (fVerb == SkPath::kConic_Verb) {
1998 edge->fConic[1].set(fPts[1]);
1999 edge->fConic.fWeight = fWeight;
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002000 return false;
2001 }
2002 SkASSERT(fVerb == SkPath::kCubic_Verb);
caryclark54359292015-03-26 07:52:43 -07002003 if (startT == 0) {
caryclark1049f122015-04-20 08:31:59 -07002004 edge->fCubic[1].set(fPts[1]);
2005 edge->fCubic[2].set(fPts[2]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002006 return false;
2007 }
caryclark1049f122015-04-20 08:31:59 -07002008 edge->fCubic[1].set(fPts[2]);
2009 edge->fCubic[2].set(fPts[1]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002010 return false;
2011 }
2012 if (fVerb == SkPath::kQuad_Verb) {
caryclark1049f122015-04-20 08:31:59 -07002013 edge->fQuad[1] = SkDQuad::SubDivide(fPts, edge->fQuad[0], edge->fQuad[2], startT, endT);
2014 } else if (fVerb == SkPath::kConic_Verb) {
2015 edge->fConic[1] = SkDConic::SubDivide(fPts, fWeight, edge->fQuad[0], edge->fQuad[2],
2016 startT, endT, &edge->fConic.fWeight);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002017 } else {
2018 SkASSERT(fVerb == SkPath::kCubic_Verb);
caryclark1049f122015-04-20 08:31:59 -07002019 SkDCubic::SubDivide(fPts, edge->fCubic[0], edge->fCubic[3], startT, endT, &edge->fCubic[1]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +00002020 }
2021 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002022}
2023
caryclark54359292015-03-26 07:52:43 -07002024void SkOpSegment::subDivideBounds(const SkOpSpanBase* start, const SkOpSpanBase* end,
2025 SkPathOpsBounds* bounds) const {
caryclark1049f122015-04-20 08:31:59 -07002026 SkOpCurve edge;
2027 subDivide(start, end, &edge);
2028 (bounds->*SetCurveBounds[fVerb])(edge.fPts, edge.fWeight);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002029}
2030
caryclark54359292015-03-26 07:52:43 -07002031void SkOpSegment::undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end) {
2032 SkOpSpan* span = this->head();
2033 do {
2034 if (!span->done()) {
reed0dc4dd62015-03-24 13:55:33 -07002035 break;
2036 }
caryclark54359292015-03-26 07:52:43 -07002037 } while ((span = span->next()->upCastable()));
2038 SkASSERT(span);
2039 *start = span;
2040 *end = span->next();
reed0dc4dd62015-03-24 13:55:33 -07002041}
2042
caryclark54359292015-03-26 07:52:43 -07002043int SkOpSegment::updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
2044 const SkOpSpan* lesser = start->starter(end);
2045 int oppWinding = lesser->oppSum();
2046 int oppSpanWinding = SkOpSegment::OppSign(start, end);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002047 if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
2048 && oppWinding != SK_MaxS32) {
2049 oppWinding -= oppSpanWinding;
2050 }
2051 return oppWinding;
2052}
2053
2054int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
caryclark54359292015-03-26 07:52:43 -07002055 const SkOpSpanBase* startSpan = angle->start();
2056 const SkOpSpanBase* endSpan = angle->end();
2057 return updateOppWinding(endSpan, startSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002058}
2059
2060int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
caryclark54359292015-03-26 07:52:43 -07002061 const SkOpSpanBase* startSpan = angle->start();
2062 const SkOpSpanBase* endSpan = angle->end();
2063 return updateOppWinding(startSpan, endSpan);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002064}
2065
caryclark54359292015-03-26 07:52:43 -07002066int SkOpSegment::updateWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
2067 const SkOpSpan* lesser = start->starter(end);
2068 int winding = lesser->windSum();
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +00002069 if (winding == SK_MinS32) {
2070 return winding;
2071 }
caryclark54359292015-03-26 07:52:43 -07002072 int spanWinding = SkOpSegment::SpanSign(start, end);
caryclark@google.com570863f2013-09-16 15:55:01 +00002073 if (winding && UseInnerWinding(winding - spanWinding, winding)
2074 && winding != SK_MaxS32) {
caryclark@google.com07393ca2013-04-08 11:47:37 +00002075 winding -= spanWinding;
2076 }
2077 return winding;
2078}
2079
2080int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
caryclark54359292015-03-26 07:52:43 -07002081 const SkOpSpanBase* startSpan = angle->start();
2082 const SkOpSpanBase* endSpan = angle->end();
2083 return updateWinding(endSpan, startSpan);
caryclark@google.com570863f2013-09-16 15:55:01 +00002084}
2085
caryclark@google.com07393ca2013-04-08 11:47:37 +00002086int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
caryclark54359292015-03-26 07:52:43 -07002087 const SkOpSpanBase* startSpan = angle->start();
2088 const SkOpSpanBase* endSpan = angle->end();
2089 return updateWinding(startSpan, endSpan);
caryclark@google.com570863f2013-09-16 15:55:01 +00002090}
2091
2092// OPTIMIZATION: does the following also work, and is it any faster?
2093// return outerWinding * innerWinding > 0
2094// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
2095bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
2096 SkASSERT(outerWinding != SK_MaxS32);
2097 SkASSERT(innerWinding != SK_MaxS32);
2098 int absOut = abs(outerWinding);
2099 int absIn = abs(innerWinding);
2100 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
2101 return result;
2102}
2103
caryclark54359292015-03-26 07:52:43 -07002104int SkOpSegment::windingAtT(double tHit, const SkOpSpan* span, bool crossOpp,
2105 SkScalar* dx) const {
2106 if (approximately_zero(tHit - span->t())) { // if we hit the end of a span, disregard
caryclark@google.com07393ca2013-04-08 11:47:37 +00002107 return SK_MinS32;
2108 }
caryclark54359292015-03-26 07:52:43 -07002109 int winding = crossOpp ? span->oppSum() : span->windSum();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002110 SkASSERT(winding != SK_MinS32);
caryclark54359292015-03-26 07:52:43 -07002111 int windVal = crossOpp ? span->oppValue() : span->windValue();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002112#if DEBUG_WINDING_AT_T
caryclarkdac1d172014-06-17 05:15:38 -07002113 SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -07002114 debugID(), crossOpp, tHit, span->t(), winding, windVal);
caryclark@google.com07393ca2013-04-08 11:47:37 +00002115#endif
2116 // see if a + change in T results in a +/- change in X (compute x'(T))
caryclark1049f122015-04-20 08:31:59 -07002117 *dx = (*CurveSlopeAtT[fVerb])(fPts, fWeight, tHit).fX;
caryclark@google.com07393ca2013-04-08 11:47:37 +00002118 if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
2119 *dx = fPts[2].fX - fPts[1].fX - *dx;
2120 }
2121 if (*dx == 0) {
2122#if DEBUG_WINDING_AT_T
2123 SkDebugf(" dx=0 winding=SK_MinS32\n");
2124#endif
2125 return SK_MinS32;
2126 }
skia.committer@gmail.coma4aced42013-07-09 07:00:56 +00002127 if (windVal < 0) { // reverse sign if opp contour traveled in reverse
caryclark@google.com07e97fc2013-07-08 17:17:02 +00002128 *dx = -*dx;
2129 }
caryclark@google.com07393ca2013-04-08 11:47:37 +00002130 if (winding * *dx > 0) { // if same signs, result is negative
2131 winding += *dx > 0 ? -windVal : windVal;
2132 }
2133#if DEBUG_WINDING_AT_T
2134 SkDebugf(" dx=%c winding=%d\n", *dx > 0 ? '+' : '-', winding);
2135#endif
2136 return winding;
2137}
2138
2139int SkOpSegment::windSum(const SkOpAngle* angle) const {
caryclark54359292015-03-26 07:52:43 -07002140 const SkOpSpan* minSpan = angle->start()->starter(angle->end());
2141 return minSpan->windSum();
caryclark@google.com07393ca2013-04-08 11:47:37 +00002142}