blob: e3137b756c899cb1663801c59d00259504fc8eb4 [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2* Copyright 2013 Google Inc.
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7#include "SkIntersections.h"
8#include "SkOpContour.h"
9#include "SkPathWriter.h"
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +000010#include "SkTSort.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000011
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000012bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
caryclark@google.com07393ca2013-04-08 11:47:37 +000013 const SkIntersections& ts, bool swap) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000014 SkPoint pt0 = ts.pt(0).asSkPoint();
15 SkPoint pt1 = ts.pt(1).asSkPoint();
16 if (pt0 == pt1) {
17 // FIXME: one could imagine a case where it would be incorrect to ignore this
18 // suppose two self-intersecting cubics overlap to be coincident --
19 // this needs to check that by some measure the t values are far enough apart
20 // or needs to check to see if the self-intersection bit was set on the cubic segment
21 return false;
22 }
caryclark@google.comd892bd82013-06-17 14:10:36 +000023 SkCoincidence& coincidence = fCoincidences.push_back();
caryclark@google.com570863f2013-09-16 15:55:01 +000024 coincidence.fOther = other;
caryclark@google.com07393ca2013-04-08 11:47:37 +000025 coincidence.fSegments[0] = index;
26 coincidence.fSegments[1] = otherIndex;
27 coincidence.fTs[swap][0] = ts[0][0];
28 coincidence.fTs[swap][1] = ts[0][1];
29 coincidence.fTs[!swap][0] = ts[1][0];
30 coincidence.fTs[!swap][1] = ts[1][1];
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000031 coincidence.fPts[0] = pt0;
32 coincidence.fPts[1] = pt1;
33 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +000034}
35
36SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
37 int segmentCount = fSortedSegments.count();
38 SkASSERT(segmentCount > 0);
39 for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
40 SkOpSegment* testSegment = fSortedSegments[sortedIndex];
41 if (testSegment->done()) {
42 continue;
43 }
44 *start = *end = 0;
45 while (testSegment->nextCandidate(start, end)) {
46 if (!testSegment->isVertical(*start, *end)) {
47 return testSegment;
48 }
49 }
50 }
51 return NULL;
52}
53
54// first pass, add missing T values
55// second pass, determine winding values of overlaps
56void SkOpContour::addCoincidentPoints() {
57 int count = fCoincidences.count();
58 for (int index = 0; index < count; ++index) {
59 SkCoincidence& coincidence = fCoincidences[index];
caryclark@google.com07393ca2013-04-08 11:47:37 +000060 int thisIndex = coincidence.fSegments[0];
61 SkOpSegment& thisOne = fSegments[thisIndex];
caryclark@google.com570863f2013-09-16 15:55:01 +000062 SkOpContour* otherContour = coincidence.fOther;
caryclark@google.com07393ca2013-04-08 11:47:37 +000063 int otherIndex = coincidence.fSegments[1];
64 SkOpSegment& other = otherContour->fSegments[otherIndex];
65 if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
66 // OPTIMIZATION: remove from array
67 continue;
68 }
69 #if DEBUG_CONCIDENT
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000070 thisOne.debugShowTs("-");
71 other.debugShowTs("o");
caryclark@google.com07393ca2013-04-08 11:47:37 +000072 #endif
73 double startT = coincidence.fTs[0][0];
74 double endT = coincidence.fTs[0][1];
caryclark@google.coma5e55922013-05-07 18:51:31 +000075 bool startSwapped, oStartSwapped, cancelers;
76 if ((cancelers = startSwapped = startT > endT)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000077 SkTSwap(startT, endT);
caryclark@google.com07393ca2013-04-08 11:47:37 +000078 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000079 if (startT == endT) { // if one is very large the smaller may have collapsed to nothing
80 if (endT <= 1 - FLT_EPSILON) {
81 endT += FLT_EPSILON;
82 SkASSERT(endT <= 1);
83 } else {
84 startT -= FLT_EPSILON;
85 SkASSERT(startT >= 0);
86 }
87 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000088 SkASSERT(!approximately_negative(endT - startT));
89 double oStartT = coincidence.fTs[1][0];
90 double oEndT = coincidence.fTs[1][1];
caryclark@google.coma5e55922013-05-07 18:51:31 +000091 if ((oStartSwapped = oStartT > oEndT)) {
92 SkTSwap(oStartT, oEndT);
caryclark@google.com07393ca2013-04-08 11:47:37 +000093 cancelers ^= true;
94 }
95 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.coma5e55922013-05-07 18:51:31 +000096 if (cancelers) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000097 // make sure startT and endT have t entries
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000098 const SkPoint& startPt = coincidence.fPts[startSwapped];
caryclark@google.com07393ca2013-04-08 11:47:37 +000099 if (startT > 0 || oEndT < 1
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000100 || thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) {
101 thisOne.addTPair(startT, &other, oEndT, true, startPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000102 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000103 const SkPoint& oStartPt = coincidence.fPts[oStartSwapped];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000104 if (oStartT > 0 || endT < 1
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000105 || thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) {
106 other.addTPair(oStartT, &thisOne, endT, true, oStartPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000107 }
108 } else {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000109 const SkPoint& startPt = coincidence.fPts[startSwapped];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000110 if (startT > 0 || oStartT > 0
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000111 || thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) {
112 thisOne.addTPair(startT, &other, oStartT, true, startPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000113 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000114 const SkPoint& oEndPt = coincidence.fPts[!oStartSwapped];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000115 if (endT < 1 || oEndT < 1
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000116 || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) {
117 other.addTPair(oEndT, &thisOne, endT, true, oEndPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000118 }
119 }
120 #if DEBUG_CONCIDENT
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000121 thisOne.debugShowTs("+");
122 other.debugShowTs("o");
caryclark@google.com07393ca2013-04-08 11:47:37 +0000123 #endif
124 }
125}
126
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000127bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex,
caryclark@google.com570863f2013-09-16 15:55:01 +0000128 const SkIntersections& ts, int ptIndex, bool swap) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000129 SkPoint pt0 = ts.pt(ptIndex).asSkPoint();
130 SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint();
131 if (SkDPoint::ApproximatelyEqual(pt0, pt1)) {
132 // FIXME: one could imagine a case where it would be incorrect to ignore this
133 // suppose two self-intersecting cubics overlap to form a partial coincidence --
134 // although it isn't clear why the regular coincidence could wouldn't pick this up
135 // this is exceptional enough to ignore for now
136 return false;
137 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000138 SkCoincidence& coincidence = fPartialCoincidences.push_back();
139 coincidence.fOther = other;
140 coincidence.fSegments[0] = index;
141 coincidence.fSegments[1] = otherIndex;
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000142 coincidence.fTs[swap][0] = ts[0][ptIndex];
143 coincidence.fTs[swap][1] = ts[0][ptIndex + 1];
144 coincidence.fTs[!swap][0] = ts[1][ptIndex];
145 coincidence.fTs[!swap][1] = ts[1][ptIndex + 1];
146 coincidence.fPts[0] = pt0;
147 coincidence.fPts[1] = pt1;
148 return true;
caryclark@google.com570863f2013-09-16 15:55:01 +0000149}
150
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000151bool SkOpContour::calcAngles() {
152 int segmentCount = fSegments.count();
153 for (int test = 0; test < segmentCount; ++test) {
154 if (!fSegments[test].calcAngles()) {
155 return false;
156 }
157 }
158 return true;
159}
160
caryclark@google.com07393ca2013-04-08 11:47:37 +0000161void SkOpContour::calcCoincidentWinding() {
162 int count = fCoincidences.count();
caryclark@google.com570863f2013-09-16 15:55:01 +0000163#if DEBUG_CONCIDENT
164 if (count > 0) {
165 SkDebugf("%s count=%d\n", __FUNCTION__, count);
166 }
167#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000168 for (int index = 0; index < count; ++index) {
169 SkCoincidence& coincidence = fCoincidences[index];
caryclark@google.com570863f2013-09-16 15:55:01 +0000170 calcCommonCoincidentWinding(coincidence);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000171 }
172}
173
caryclark@google.com570863f2013-09-16 15:55:01 +0000174void SkOpContour::calcPartialCoincidentWinding() {
175 int count = fPartialCoincidences.count();
176#if DEBUG_CONCIDENT
177 if (count > 0) {
178 SkDebugf("%s count=%d\n", __FUNCTION__, count);
179 }
180#endif
181 for (int index = 0; index < count; ++index) {
182 SkCoincidence& coincidence = fPartialCoincidences[index];
183 calcCommonCoincidentWinding(coincidence);
184 }
185}
186
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000187void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) {
188 int count = coincidences.count();
189#if DEBUG_CONCIDENT
190 if (count > 0) {
191 SkDebugf("%s count=%d\n", __FUNCTION__, count);
192 }
193#endif
194 // look for a lineup where the partial implies another adjoining coincidence
195 for (int index = 0; index < count; ++index) {
196 const SkCoincidence& coincidence = coincidences[index];
197 int thisIndex = coincidence.fSegments[0];
198 SkOpSegment& thisOne = fSegments[thisIndex];
199 SkOpContour* otherContour = coincidence.fOther;
200 int otherIndex = coincidence.fSegments[1];
201 SkOpSegment& other = otherContour->fSegments[otherIndex];
202 double startT = coincidence.fTs[0][0];
203 double endT = coincidence.fTs[0][1];
204 if (startT == endT) { // this can happen in very large compares
205 continue;
206 }
207 double oStartT = coincidence.fTs[1][0];
208 double oEndT = coincidence.fTs[1][1];
209 if (oStartT == oEndT) {
210 continue;
211 }
212 bool swapStart = startT > endT;
213 bool swapOther = oStartT > oEndT;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000214 const SkPoint* startPt = &coincidence.fPts[0];
215 const SkPoint* endPt = &coincidence.fPts[1];
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000216 if (swapStart) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000217 SkTSwap(startT, endT);
218 SkTSwap(oStartT, oEndT);
219 SkTSwap(startPt, endPt);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000220 }
221 bool cancel = swapOther != swapStart;
222 int step = swapStart ? -1 : 1;
223 int oStep = swapOther ? -1 : 1;
224 double oMatchStart = cancel ? oEndT : oStartT;
225 if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) {
226 bool added = false;
227 if (oMatchStart != 0) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000228 const SkPoint& oMatchStartPt = cancel ? *endPt : *startPt;
229 added = thisOne.joinCoincidence(&other, oMatchStart, oMatchStartPt, oStep, cancel);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000230 }
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000231 if (!cancel && startT != 0 && !added) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000232 (void) other.joinCoincidence(&thisOne, startT, *startPt, step, cancel);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000233 }
234 }
235 double oMatchEnd = cancel ? oStartT : oEndT;
236 if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) {
237 bool added = false;
commit-bot@chromium.org866f4e32013-11-21 17:04:29 +0000238 if (cancel && endT != 1 && !added) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000239 (void) other.joinCoincidence(&thisOne, endT, *endPt, -step, cancel);
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000240 }
241 }
242 }
243}
244
caryclark@google.com570863f2013-09-16 15:55:01 +0000245void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
246 int thisIndex = coincidence.fSegments[0];
247 SkOpSegment& thisOne = fSegments[thisIndex];
248 if (thisOne.done()) {
249 return;
250 }
251 SkOpContour* otherContour = coincidence.fOther;
252 int otherIndex = coincidence.fSegments[1];
253 SkOpSegment& other = otherContour->fSegments[otherIndex];
254 if (other.done()) {
255 return;
256 }
257 double startT = coincidence.fTs[0][0];
258 double endT = coincidence.fTs[0][1];
259 const SkPoint* startPt = &coincidence.fPts[0];
260 const SkPoint* endPt = &coincidence.fPts[1];
261 bool cancelers;
262 if ((cancelers = startT > endT)) {
263 SkTSwap<double>(startT, endT);
264 SkTSwap<const SkPoint*>(startPt, endPt);
265 }
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000266 if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
267 if (endT <= 1 - FLT_EPSILON) {
268 endT += FLT_EPSILON;
269 SkASSERT(endT <= 1);
270 } else {
271 startT -= FLT_EPSILON;
272 SkASSERT(startT >= 0);
273 }
274 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000275 SkASSERT(!approximately_negative(endT - startT));
276 double oStartT = coincidence.fTs[1][0];
277 double oEndT = coincidence.fTs[1][1];
278 if (oStartT > oEndT) {
279 SkTSwap<double>(oStartT, oEndT);
280 cancelers ^= true;
281 }
282 SkASSERT(!approximately_negative(oEndT - oStartT));
283 if (cancelers) {
284 thisOne.addTCancel(*startPt, *endPt, &other);
285 } else {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000286 thisOne.addTCoincident(*startPt, *endPt, endT, &other);
caryclark@google.com570863f2013-09-16 15:55:01 +0000287 }
288#if DEBUG_CONCIDENT
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000289 thisOne.debugShowTs("p");
290 other.debugShowTs("o");
caryclark@google.com570863f2013-09-16 15:55:01 +0000291#endif
292}
293
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000294void SkOpContour::sortAngles() {
295 int segmentCount = fSegments.count();
296 for (int test = 0; test < segmentCount; ++test) {
297 fSegments[test].sortAngles();
298 }
299}
300
caryclark@google.com07393ca2013-04-08 11:47:37 +0000301void SkOpContour::sortSegments() {
302 int segmentCount = fSegments.count();
caryclark@google.comd892bd82013-06-17 14:10:36 +0000303 fSortedSegments.push_back_n(segmentCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000304 for (int test = 0; test < segmentCount; ++test) {
caryclark@google.comd892bd82013-06-17 14:10:36 +0000305 fSortedSegments[test] = &fSegments[test];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000306 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000307 SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000308 fFirstSorted = 0;
309}
310
311void SkOpContour::toPath(SkPathWriter* path) const {
312 int segmentCount = fSegments.count();
313 const SkPoint& pt = fSegments.front().pts()[0];
314 path->deferredMove(pt);
315 for (int test = 0; test < segmentCount; ++test) {
316 fSegments[test].addCurveTo(0, 1, path, true);
317 }
318 path->close();
319}
320
321void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
322 SkOpSegment** topStart) {
323 int segmentCount = fSortedSegments.count();
324 SkASSERT(segmentCount > 0);
325 int sortedIndex = fFirstSorted;
326 fDone = true; // may be cleared below
327 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
328 SkOpSegment* testSegment = fSortedSegments[sortedIndex];
329 if (testSegment->done()) {
330 if (sortedIndex == fFirstSorted) {
331 ++fFirstSorted;
332 }
333 continue;
334 }
335 fDone = false;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000336 SkPoint testXY = testSegment->activeLeftTop(NULL);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000337 if (*topStart) {
338 if (testXY.fY < topLeft.fY) {
339 continue;
340 }
341 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
342 continue;
343 }
344 if (bestXY->fY < testXY.fY) {
345 continue;
346 }
347 if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) {
348 continue;
349 }
350 }
351 *topStart = testSegment;
352 *bestXY = testXY;
353 }
354}
355
356SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
357 int segmentCount = fSegments.count();
358 for (int test = 0; test < segmentCount; ++test) {
359 SkOpSegment* testSegment = &fSegments[test];
360 if (testSegment->done()) {
361 continue;
362 }
363 testSegment->undoneSpan(start, end);
364 return testSegment;
365 }
366 return NULL;
367}
368
369#if DEBUG_SHOW_WINDING
370int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
371 int count = fSegments.count();
372 int sum = 0;
373 for (int index = 0; index < count; ++index) {
374 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
375 }
376// SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
377 return sum;
378}
379
caryclark@google.com570863f2013-09-16 15:55:01 +0000380void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000381// int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
382// int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
383 int ofInterest = 1 << 5 | 1 << 8;
384 int total = 0;
385 int index;
386 for (index = 0; index < contourList.count(); ++index) {
387 total += contourList[index]->segments().count();
388 }
389 int sum = 0;
390 for (index = 0; index < contourList.count(); ++index) {
391 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
392 }
393// SkDebugf("%s total=%d\n", __FUNCTION__, sum);
394}
395#endif
396
397void SkOpContour::setBounds() {
398 int count = fSegments.count();
399 if (count == 0) {
400 SkDebugf("%s empty contour\n", __FUNCTION__);
401 SkASSERT(0);
402 // FIXME: delete empty contour?
403 return;
404 }
405 fBounds = fSegments.front().bounds();
406 for (int index = 1; index < count; ++index) {
407 fBounds.add(fSegments[index].bounds());
408 }
409}