OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 #include "SkIntersections.h" | 7 #include "SkIntersections.h" |
8 #include "SkOpContour.h" | 8 #include "SkOpContour.h" |
9 #include "SkPathWriter.h" | 9 #include "SkPathWriter.h" |
10 #include "SkTSort.h" | 10 #include "SkTSort.h" |
11 | 11 |
12 void SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex, | 12 bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex, |
13 const SkIntersections& ts, bool swap) { | 13 const SkIntersections& ts, bool swap) { |
| 14 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 } |
14 SkCoincidence& coincidence = fCoincidences.push_back(); | 23 SkCoincidence& coincidence = fCoincidences.push_back(); |
15 coincidence.fOther = other; | 24 coincidence.fOther = other; |
16 coincidence.fSegments[0] = index; | 25 coincidence.fSegments[0] = index; |
17 coincidence.fSegments[1] = otherIndex; | 26 coincidence.fSegments[1] = otherIndex; |
18 coincidence.fTs[swap][0] = ts[0][0]; | 27 coincidence.fTs[swap][0] = ts[0][0]; |
19 coincidence.fTs[swap][1] = ts[0][1]; | 28 coincidence.fTs[swap][1] = ts[0][1]; |
20 coincidence.fTs[!swap][0] = ts[1][0]; | 29 coincidence.fTs[!swap][0] = ts[1][0]; |
21 coincidence.fTs[!swap][1] = ts[1][1]; | 30 coincidence.fTs[!swap][1] = ts[1][1]; |
22 coincidence.fPts[0] = ts.pt(0).asSkPoint(); | 31 coincidence.fPts[0] = pt0; |
23 coincidence.fPts[1] = ts.pt(1).asSkPoint(); | 32 coincidence.fPts[1] = pt1; |
| 33 return true; |
24 } | 34 } |
25 | 35 |
26 SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) { | 36 SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) { |
27 int segmentCount = fSortedSegments.count(); | 37 int segmentCount = fSortedSegments.count(); |
28 SkASSERT(segmentCount > 0); | 38 SkASSERT(segmentCount > 0); |
29 for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedInd
ex) { | 39 for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedInd
ex) { |
30 SkOpSegment* testSegment = fSortedSegments[sortedIndex]; | 40 SkOpSegment* testSegment = fSortedSegments[sortedIndex]; |
31 if (testSegment->done()) { | 41 if (testSegment->done()) { |
32 continue; | 42 continue; |
33 } | 43 } |
(...skipping 16 matching lines...) Expand all Loading... |
50 int thisIndex = coincidence.fSegments[0]; | 60 int thisIndex = coincidence.fSegments[0]; |
51 SkOpSegment& thisOne = fSegments[thisIndex]; | 61 SkOpSegment& thisOne = fSegments[thisIndex]; |
52 SkOpContour* otherContour = coincidence.fOther; | 62 SkOpContour* otherContour = coincidence.fOther; |
53 int otherIndex = coincidence.fSegments[1]; | 63 int otherIndex = coincidence.fSegments[1]; |
54 SkOpSegment& other = otherContour->fSegments[otherIndex]; | 64 SkOpSegment& other = otherContour->fSegments[otherIndex]; |
55 if ((thisOne.done() || other.done()) && thisOne.complete() && other.comp
lete()) { | 65 if ((thisOne.done() || other.done()) && thisOne.complete() && other.comp
lete()) { |
56 // OPTIMIZATION: remove from array | 66 // OPTIMIZATION: remove from array |
57 continue; | 67 continue; |
58 } | 68 } |
59 #if DEBUG_CONCIDENT | 69 #if DEBUG_CONCIDENT |
60 thisOne.debugShowTs(); | 70 thisOne.debugShowTs("-"); |
61 other.debugShowTs(); | 71 other.debugShowTs("o"); |
62 #endif | 72 #endif |
63 double startT = coincidence.fTs[0][0]; | 73 double startT = coincidence.fTs[0][0]; |
64 double endT = coincidence.fTs[0][1]; | 74 double endT = coincidence.fTs[0][1]; |
65 bool startSwapped, oStartSwapped, cancelers; | 75 bool startSwapped, oStartSwapped, cancelers; |
66 if ((cancelers = startSwapped = startT > endT)) { | 76 if ((cancelers = startSwapped = startT > endT)) { |
67 SkTSwap(startT, endT); | 77 SkTSwap(startT, endT); |
68 } | 78 } |
| 79 if (startT == endT) { // if one is very large the smaller may have colla
psed 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 } |
69 SkASSERT(!approximately_negative(endT - startT)); | 88 SkASSERT(!approximately_negative(endT - startT)); |
70 double oStartT = coincidence.fTs[1][0]; | 89 double oStartT = coincidence.fTs[1][0]; |
71 double oEndT = coincidence.fTs[1][1]; | 90 double oEndT = coincidence.fTs[1][1]; |
72 if ((oStartSwapped = oStartT > oEndT)) { | 91 if ((oStartSwapped = oStartT > oEndT)) { |
73 SkTSwap(oStartT, oEndT); | 92 SkTSwap(oStartT, oEndT); |
74 cancelers ^= true; | 93 cancelers ^= true; |
75 } | 94 } |
76 SkASSERT(!approximately_negative(oEndT - oStartT)); | 95 SkASSERT(!approximately_negative(oEndT - oStartT)); |
77 if (cancelers) { | 96 if (cancelers) { |
78 // make sure startT and endT have t entries | 97 // make sure startT and endT have t entries |
| 98 const SkPoint& startPt = coincidence.fPts[startSwapped]; |
79 if (startT > 0 || oEndT < 1 | 99 if (startT > 0 || oEndT < 1 |
80 || thisOne.isMissing(startT) || other.isMissing(oEndT)) { | 100 || thisOne.isMissing(startT, startPt) || other.isMissing(oEn
dT, startPt)) { |
81 thisOne.addTPair(startT, &other, oEndT, true, coincidence.fPts[s
tartSwapped]); | 101 thisOne.addTPair(startT, &other, oEndT, true, startPt); |
82 } | 102 } |
| 103 const SkPoint& oStartPt = coincidence.fPts[oStartSwapped]; |
83 if (oStartT > 0 || endT < 1 | 104 if (oStartT > 0 || endT < 1 |
84 || thisOne.isMissing(endT) || other.isMissing(oStartT)) { | 105 || thisOne.isMissing(endT, oStartPt) || other.isMissing(oSta
rtT, oStartPt)) { |
85 other.addTPair(oStartT, &thisOne, endT, true, coincidence.fPts[o
StartSwapped]); | 106 other.addTPair(oStartT, &thisOne, endT, true, oStartPt); |
86 } | 107 } |
87 } else { | 108 } else { |
| 109 const SkPoint& startPt = coincidence.fPts[startSwapped]; |
88 if (startT > 0 || oStartT > 0 | 110 if (startT > 0 || oStartT > 0 |
89 || thisOne.isMissing(startT) || other.isMissing(oStartT)) { | 111 || thisOne.isMissing(startT, startPt) || other.isMissing(oSt
artT, startPt)) { |
90 thisOne.addTPair(startT, &other, oStartT, true, coincidence.fPts
[startSwapped]); | 112 thisOne.addTPair(startT, &other, oStartT, true, startPt); |
91 } | 113 } |
| 114 const SkPoint& oEndPt = coincidence.fPts[!oStartSwapped]; |
92 if (endT < 1 || oEndT < 1 | 115 if (endT < 1 || oEndT < 1 |
93 || thisOne.isMissing(endT) || other.isMissing(oEndT)) { | 116 || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT,
oEndPt)) { |
94 other.addTPair(oEndT, &thisOne, endT, true, coincidence.fPts[!oS
tartSwapped]); | 117 other.addTPair(oEndT, &thisOne, endT, true, oEndPt); |
95 } | 118 } |
96 } | 119 } |
97 #if DEBUG_CONCIDENT | 120 #if DEBUG_CONCIDENT |
98 thisOne.debugShowTs(); | 121 thisOne.debugShowTs("+"); |
99 other.debugShowTs(); | 122 other.debugShowTs("o"); |
100 #endif | 123 #endif |
101 } | 124 } |
102 } | 125 } |
103 | 126 |
104 void SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherI
ndex, | 127 bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherI
ndex, |
105 const SkIntersections& ts, int ptIndex, bool swap) { | 128 const SkIntersections& ts, int ptIndex, bool swap) { |
| 129 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 coinci
dence -- |
| 134 // although it isn't clear why the regular coincidence could wouldn't pi
ck this up |
| 135 // this is exceptional enough to ignore for now |
| 136 return false; |
| 137 } |
106 SkCoincidence& coincidence = fPartialCoincidences.push_back(); | 138 SkCoincidence& coincidence = fPartialCoincidences.push_back(); |
107 coincidence.fOther = other; | 139 coincidence.fOther = other; |
108 coincidence.fSegments[0] = index; | 140 coincidence.fSegments[0] = index; |
109 coincidence.fSegments[1] = otherIndex; | 141 coincidence.fSegments[1] = otherIndex; |
110 coincidence.fTs[swap][0] = ts[0][index]; | 142 coincidence.fTs[swap][0] = ts[0][ptIndex]; |
111 coincidence.fTs[swap][1] = ts[0][index + 1]; | 143 coincidence.fTs[swap][1] = ts[0][ptIndex + 1]; |
112 coincidence.fTs[!swap][0] = ts[1][index]; | 144 coincidence.fTs[!swap][0] = ts[1][ptIndex]; |
113 coincidence.fTs[!swap][1] = ts[1][index + 1]; | 145 coincidence.fTs[!swap][1] = ts[1][ptIndex + 1]; |
114 coincidence.fPts[0] = ts.pt(index).asSkPoint(); | 146 coincidence.fPts[0] = pt0; |
115 coincidence.fPts[1] = ts.pt(index + 1).asSkPoint(); | 147 coincidence.fPts[1] = pt1; |
| 148 return true; |
116 } | 149 } |
117 | 150 |
118 void SkOpContour::calcCoincidentWinding() { | 151 void SkOpContour::calcCoincidentWinding() { |
119 int count = fCoincidences.count(); | 152 int count = fCoincidences.count(); |
120 #if DEBUG_CONCIDENT | 153 #if DEBUG_CONCIDENT |
121 if (count > 0) { | 154 if (count > 0) { |
122 SkDebugf("%s count=%d\n", __FUNCTION__, count); | 155 SkDebugf("%s count=%d\n", __FUNCTION__, count); |
123 } | 156 } |
124 #endif | 157 #endif |
125 for (int index = 0; index < count; ++index) { | 158 for (int index = 0; index < count; ++index) { |
(...skipping 29 matching lines...) Expand all Loading... |
155 } | 188 } |
156 double startT = coincidence.fTs[0][0]; | 189 double startT = coincidence.fTs[0][0]; |
157 double endT = coincidence.fTs[0][1]; | 190 double endT = coincidence.fTs[0][1]; |
158 const SkPoint* startPt = &coincidence.fPts[0]; | 191 const SkPoint* startPt = &coincidence.fPts[0]; |
159 const SkPoint* endPt = &coincidence.fPts[1]; | 192 const SkPoint* endPt = &coincidence.fPts[1]; |
160 bool cancelers; | 193 bool cancelers; |
161 if ((cancelers = startT > endT)) { | 194 if ((cancelers = startT > endT)) { |
162 SkTSwap<double>(startT, endT); | 195 SkTSwap<double>(startT, endT); |
163 SkTSwap<const SkPoint*>(startPt, endPt); | 196 SkTSwap<const SkPoint*>(startPt, endPt); |
164 } | 197 } |
| 198 if (startT == endT) { // if span is very large, the smaller may have collaps
ed to nothing |
| 199 if (endT <= 1 - FLT_EPSILON) { |
| 200 endT += FLT_EPSILON; |
| 201 SkASSERT(endT <= 1); |
| 202 } else { |
| 203 startT -= FLT_EPSILON; |
| 204 SkASSERT(startT >= 0); |
| 205 } |
| 206 } |
165 SkASSERT(!approximately_negative(endT - startT)); | 207 SkASSERT(!approximately_negative(endT - startT)); |
166 double oStartT = coincidence.fTs[1][0]; | 208 double oStartT = coincidence.fTs[1][0]; |
167 double oEndT = coincidence.fTs[1][1]; | 209 double oEndT = coincidence.fTs[1][1]; |
168 if (oStartT > oEndT) { | 210 if (oStartT > oEndT) { |
169 SkTSwap<double>(oStartT, oEndT); | 211 SkTSwap<double>(oStartT, oEndT); |
170 cancelers ^= true; | 212 cancelers ^= true; |
171 } | 213 } |
172 SkASSERT(!approximately_negative(oEndT - oStartT)); | 214 SkASSERT(!approximately_negative(oEndT - oStartT)); |
173 if (cancelers) { | 215 if (cancelers) { |
174 thisOne.addTCancel(*startPt, *endPt, &other); | 216 thisOne.addTCancel(*startPt, *endPt, &other); |
175 } else { | 217 } else { |
176 thisOne.addTCoincident(*startPt, *endPt, &other); | 218 thisOne.addTCoincident(*startPt, *endPt, endT, &other); |
177 } | 219 } |
178 #if DEBUG_CONCIDENT | 220 #if DEBUG_CONCIDENT |
179 thisOne.debugShowTs(); | 221 thisOne.debugShowTs("p"); |
180 other.debugShowTs(); | 222 other.debugShowTs("o"); |
181 #endif | 223 #endif |
182 } | 224 } |
183 | 225 |
184 void SkOpContour::sortSegments() { | 226 void SkOpContour::sortSegments() { |
185 int segmentCount = fSegments.count(); | 227 int segmentCount = fSegments.count(); |
186 fSortedSegments.push_back_n(segmentCount); | 228 fSortedSegments.push_back_n(segmentCount); |
187 for (int test = 0; test < segmentCount; ++test) { | 229 for (int test = 0; test < segmentCount; ++test) { |
188 fSortedSegments[test] = &fSegments[test]; | 230 fSortedSegments[test] = &fSegments[test]; |
189 } | 231 } |
190 SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1); | 232 SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1); |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
283 SkDebugf("%s empty contour\n", __FUNCTION__); | 325 SkDebugf("%s empty contour\n", __FUNCTION__); |
284 SkASSERT(0); | 326 SkASSERT(0); |
285 // FIXME: delete empty contour? | 327 // FIXME: delete empty contour? |
286 return; | 328 return; |
287 } | 329 } |
288 fBounds = fSegments.front().bounds(); | 330 fBounds = fSegments.front().bounds(); |
289 for (int index = 1; index < count; ++index) { | 331 for (int index = 1; index < count; ++index) { |
290 fBounds.add(fSegments[index].bounds()); | 332 fBounds.add(fSegments[index].bounds()); |
291 } | 333 } |
292 } | 334 } |
OLD | NEW |