blob: f3861a1e0b230131c2d47794d4cd17ae7fc6d6e6 [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
12void SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
13 const SkIntersections& ts, bool swap) {
caryclark@google.comd892bd82013-06-17 14:10:36 +000014 SkCoincidence& coincidence = fCoincidences.push_back();
caryclark@google.com07393ca2013-04-08 11:47:37 +000015 coincidence.fContours[0] = this; // FIXME: no need to store
16 coincidence.fContours[1] = other;
17 coincidence.fSegments[0] = index;
18 coincidence.fSegments[1] = otherIndex;
19 coincidence.fTs[swap][0] = ts[0][0];
20 coincidence.fTs[swap][1] = ts[0][1];
21 coincidence.fTs[!swap][0] = ts[1][0];
22 coincidence.fTs[!swap][1] = ts[1][1];
23 coincidence.fPts[0] = ts.pt(0).asSkPoint();
24 coincidence.fPts[1] = ts.pt(1).asSkPoint();
25}
26
27SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
28 int segmentCount = fSortedSegments.count();
29 SkASSERT(segmentCount > 0);
30 for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
31 SkOpSegment* testSegment = fSortedSegments[sortedIndex];
32 if (testSegment->done()) {
33 continue;
34 }
35 *start = *end = 0;
36 while (testSegment->nextCandidate(start, end)) {
37 if (!testSegment->isVertical(*start, *end)) {
38 return testSegment;
39 }
40 }
41 }
42 return NULL;
43}
44
45// first pass, add missing T values
46// second pass, determine winding values of overlaps
47void SkOpContour::addCoincidentPoints() {
48 int count = fCoincidences.count();
49 for (int index = 0; index < count; ++index) {
50 SkCoincidence& coincidence = fCoincidences[index];
51 SkASSERT(coincidence.fContours[0] == this);
52 int thisIndex = coincidence.fSegments[0];
53 SkOpSegment& thisOne = fSegments[thisIndex];
54 SkOpContour* otherContour = coincidence.fContours[1];
55 int otherIndex = coincidence.fSegments[1];
56 SkOpSegment& other = otherContour->fSegments[otherIndex];
57 if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
58 // OPTIMIZATION: remove from array
59 continue;
60 }
61 #if DEBUG_CONCIDENT
62 thisOne.debugShowTs();
63 other.debugShowTs();
64 #endif
65 double startT = coincidence.fTs[0][0];
66 double endT = coincidence.fTs[0][1];
caryclark@google.coma5e55922013-05-07 18:51:31 +000067 bool startSwapped, oStartSwapped, cancelers;
68 if ((cancelers = startSwapped = startT > endT)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000069 SkTSwap(startT, endT);
caryclark@google.com07393ca2013-04-08 11:47:37 +000070 }
71 SkASSERT(!approximately_negative(endT - startT));
72 double oStartT = coincidence.fTs[1][0];
73 double oEndT = coincidence.fTs[1][1];
caryclark@google.coma5e55922013-05-07 18:51:31 +000074 if ((oStartSwapped = oStartT > oEndT)) {
75 SkTSwap(oStartT, oEndT);
caryclark@google.com07393ca2013-04-08 11:47:37 +000076 cancelers ^= true;
77 }
78 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.coma5e55922013-05-07 18:51:31 +000079 if (cancelers) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000080 // make sure startT and endT have t entries
81 if (startT > 0 || oEndT < 1
82 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
caryclark@google.coma5e55922013-05-07 18:51:31 +000083 thisOne.addTPair(startT, &other, oEndT, true, coincidence.fPts[startSwapped]);
caryclark@google.com07393ca2013-04-08 11:47:37 +000084 }
85 if (oStartT > 0 || endT < 1
86 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
caryclark@google.coma5e55922013-05-07 18:51:31 +000087 other.addTPair(oStartT, &thisOne, endT, true, coincidence.fPts[oStartSwapped]);
caryclark@google.com07393ca2013-04-08 11:47:37 +000088 }
89 } else {
90 if (startT > 0 || oStartT > 0
91 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.coma5e55922013-05-07 18:51:31 +000092 thisOne.addTPair(startT, &other, oStartT, true, coincidence.fPts[startSwapped]);
caryclark@google.com07393ca2013-04-08 11:47:37 +000093 }
94 if (endT < 1 || oEndT < 1
95 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.coma5e55922013-05-07 18:51:31 +000096 other.addTPair(oEndT, &thisOne, endT, true, coincidence.fPts[!oStartSwapped]);
caryclark@google.com07393ca2013-04-08 11:47:37 +000097 }
98 }
99 #if DEBUG_CONCIDENT
100 thisOne.debugShowTs();
101 other.debugShowTs();
102 #endif
103 }
104}
105
106void SkOpContour::calcCoincidentWinding() {
107 int count = fCoincidences.count();
108 for (int index = 0; index < count; ++index) {
109 SkCoincidence& coincidence = fCoincidences[index];
110 SkASSERT(coincidence.fContours[0] == this);
111 int thisIndex = coincidence.fSegments[0];
112 SkOpSegment& thisOne = fSegments[thisIndex];
113 if (thisOne.done()) {
114 continue;
115 }
116 SkOpContour* otherContour = coincidence.fContours[1];
117 int otherIndex = coincidence.fSegments[1];
118 SkOpSegment& other = otherContour->fSegments[otherIndex];
119 if (other.done()) {
120 continue;
121 }
122 double startT = coincidence.fTs[0][0];
123 double endT = coincidence.fTs[0][1];
124 bool cancelers;
125 if ((cancelers = startT > endT)) {
126 SkTSwap<double>(startT, endT);
127 }
128 SkASSERT(!approximately_negative(endT - startT));
129 double oStartT = coincidence.fTs[1][0];
130 double oEndT = coincidence.fTs[1][1];
131 if (oStartT > oEndT) {
132 SkTSwap<double>(oStartT, oEndT);
133 cancelers ^= true;
134 }
135 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.coma5e55922013-05-07 18:51:31 +0000136 if (cancelers) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000137 // make sure startT and endT have t entries
138 if (!thisOne.done() && !other.done()) {
139 thisOne.addTCancel(startT, endT, &other, oStartT, oEndT);
140 }
141 } else {
142 if (!thisOne.done() && !other.done()) {
143 thisOne.addTCoincident(startT, endT, &other, oStartT, oEndT);
144 }
145 }
146 #if DEBUG_CONCIDENT
147 thisOne.debugShowTs();
148 other.debugShowTs();
149 #endif
150 }
151}
152
153void SkOpContour::sortSegments() {
154 int segmentCount = fSegments.count();
caryclark@google.comd892bd82013-06-17 14:10:36 +0000155 fSortedSegments.push_back_n(segmentCount);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000156 for (int test = 0; test < segmentCount; ++test) {
caryclark@google.comd892bd82013-06-17 14:10:36 +0000157 fSortedSegments[test] = &fSegments[test];
caryclark@google.com07393ca2013-04-08 11:47:37 +0000158 }
commit-bot@chromium.orgb76d3b62013-04-22 19:55:19 +0000159 SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000160 fFirstSorted = 0;
161}
162
163void SkOpContour::toPath(SkPathWriter* path) const {
164 int segmentCount = fSegments.count();
165 const SkPoint& pt = fSegments.front().pts()[0];
166 path->deferredMove(pt);
167 for (int test = 0; test < segmentCount; ++test) {
168 fSegments[test].addCurveTo(0, 1, path, true);
169 }
170 path->close();
171}
172
173void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
174 SkOpSegment** topStart) {
175 int segmentCount = fSortedSegments.count();
176 SkASSERT(segmentCount > 0);
177 int sortedIndex = fFirstSorted;
178 fDone = true; // may be cleared below
179 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
180 SkOpSegment* testSegment = fSortedSegments[sortedIndex];
181 if (testSegment->done()) {
182 if (sortedIndex == fFirstSorted) {
183 ++fFirstSorted;
184 }
185 continue;
186 }
187 fDone = false;
188 SkPoint testXY = testSegment->activeLeftTop(true, NULL);
189 if (*topStart) {
190 if (testXY.fY < topLeft.fY) {
191 continue;
192 }
193 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
194 continue;
195 }
196 if (bestXY->fY < testXY.fY) {
197 continue;
198 }
199 if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) {
200 continue;
201 }
202 }
203 *topStart = testSegment;
204 *bestXY = testXY;
205 }
206}
207
208SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
209 int segmentCount = fSegments.count();
210 for (int test = 0; test < segmentCount; ++test) {
211 SkOpSegment* testSegment = &fSegments[test];
212 if (testSegment->done()) {
213 continue;
214 }
215 testSegment->undoneSpan(start, end);
216 return testSegment;
217 }
218 return NULL;
219}
220
221#if DEBUG_SHOW_WINDING
222int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
223 int count = fSegments.count();
224 int sum = 0;
225 for (int index = 0; index < count; ++index) {
226 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
227 }
228// SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
229 return sum;
230}
231
caryclark@google.comd892bd82013-06-17 14:10:36 +0000232static void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000233// int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
234// int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
235 int ofInterest = 1 << 5 | 1 << 8;
236 int total = 0;
237 int index;
238 for (index = 0; index < contourList.count(); ++index) {
239 total += contourList[index]->segments().count();
240 }
241 int sum = 0;
242 for (index = 0; index < contourList.count(); ++index) {
243 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
244 }
245// SkDebugf("%s total=%d\n", __FUNCTION__, sum);
246}
247#endif
248
249void SkOpContour::setBounds() {
250 int count = fSegments.count();
251 if (count == 0) {
252 SkDebugf("%s empty contour\n", __FUNCTION__);
253 SkASSERT(0);
254 // FIXME: delete empty contour?
255 return;
256 }
257 fBounds = fSegments.front().bounds();
258 for (int index = 1; index < count; ++index) {
259 fBounds.add(fSegments[index].bounds());
260 }
261}