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" | |
8 #include "SkOpContour.h" | 7 #include "SkOpContour.h" |
| 8 #include "SkOpTAllocator.h" |
9 #include "SkPathWriter.h" | 9 #include "SkPathWriter.h" |
| 10 #include "SkReduceOrder.h" |
10 #include "SkTSort.h" | 11 #include "SkTSort.h" |
11 | 12 |
12 bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex, | 13 void SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc
* allocator) { |
13 const SkIntersections& ts, bool swap) { | 14 switch (verb) { |
14 SkPoint pt0 = ts.pt(0).asSkPoint(); | 15 case SkPath::kLine_Verb: { |
15 SkPoint pt1 = ts.pt(1).asSkPoint(); | 16 SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocato
r, 2); |
16 if (pt0 == pt1 || ts[0][0] == ts[0][1] || ts[1][0] == ts[1][1]) { | 17 memcpy(ptStorage, pts, sizeof(SkPoint) * 2); |
17 // FIXME: one could imagine a case where it would be incorrect to ignore
this | 18 appendSegment(allocator).addLine(ptStorage, this); |
18 // suppose two self-intersecting cubics overlap to be coincident -- | 19 } break; |
19 // this needs to check that by some measure the t values are far enough
apart | 20 case SkPath::kQuad_Verb: { |
20 // or needs to check to see if the self-intersection bit was set on the
cubic segment | 21 SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocato
r, 3); |
21 return false; | 22 memcpy(ptStorage, pts, sizeof(SkPoint) * 3); |
| 23 appendSegment(allocator).addQuad(ptStorage, this); |
| 24 } break; |
| 25 case SkPath::kCubic_Verb: { |
| 26 SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocato
r, 4); |
| 27 memcpy(ptStorage, pts, sizeof(SkPoint) * 4); |
| 28 appendSegment(allocator).addCubic(ptStorage, this); |
| 29 } break; |
| 30 default: |
| 31 SkASSERT(0); |
22 } | 32 } |
23 SkCoincidence& coincidence = fCoincidences.push_back(); | |
24 coincidence.fOther = other; | |
25 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]; | |
31 coincidence.fPts[swap][0] = pt0; | |
32 coincidence.fPts[swap][1] = pt1; | |
33 bool nearStart = ts.nearlySame(0); | |
34 bool nearEnd = ts.nearlySame(1); | |
35 coincidence.fPts[!swap][0] = nearStart ? ts.pt2(0).asSkPoint() : pt0; | |
36 coincidence.fPts[!swap][1] = nearEnd ? ts.pt2(1).asSkPoint() : pt1; | |
37 coincidence.fNearly[0] = nearStart; | |
38 coincidence.fNearly[1] = nearEnd; | |
39 return true; | |
40 } | 33 } |
41 | 34 |
42 SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) { | 35 SkOpSegment* SkOpContour::nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase*
* end) { |
43 int segmentCount = fSortedSegments.count(); | 36 int segmentCount = fSortedSegments.count(); |
44 SkASSERT(segmentCount > 0); | 37 SkASSERT(segmentCount > 0); |
45 for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedInd
ex) { | 38 for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedInd
ex) { |
46 SkOpSegment* testSegment = fSortedSegments[sortedIndex]; | 39 SkOpSegment* testSegment = fSortedSegments[sortedIndex]; |
47 if (testSegment->done()) { | 40 if (testSegment->done()) { |
48 continue; | 41 continue; |
49 } | 42 } |
50 *start = *end = 0; | 43 SkOpSpanBase* span = testSegment->head(); |
51 while (testSegment->nextCandidate(start, end)) { | 44 SkOpSpanBase* testS, * testE; |
52 if (!testSegment->isVertical(*start, *end)) { | 45 while (SkOpSegment::NextCandidate(span, &testS, &testE)) { |
| 46 if (!testSegment->isVertical(testS, testE)) { |
| 47 *start = testS; |
| 48 *end = testE; |
53 return testSegment; | 49 return testSegment; |
54 } | 50 } |
| 51 span = span->upCast()->next(); |
55 } | 52 } |
56 } | 53 } |
57 return NULL; | 54 return NULL; |
58 } | 55 } |
59 | 56 |
60 // if one is very large the smaller may have collapsed to nothing | |
61 static void bump_out_close_span(double* startTPtr, double* endTPtr) { | |
62 double startT = *startTPtr; | |
63 double endT = *endTPtr; | |
64 if (approximately_negative(endT - startT)) { | |
65 if (endT <= 1 - FLT_EPSILON) { | |
66 *endTPtr += FLT_EPSILON; | |
67 SkASSERT(*endTPtr <= 1); | |
68 } else { | |
69 *startTPtr -= FLT_EPSILON; | |
70 SkASSERT(*startTPtr >= 0); | |
71 } | |
72 } | |
73 } | |
74 | |
75 // first pass, add missing T values | |
76 // second pass, determine winding values of overlaps | |
77 void SkOpContour::addCoincidentPoints() { | |
78 int count = fCoincidences.count(); | |
79 for (int index = 0; index < count; ++index) { | |
80 SkCoincidence& coincidence = fCoincidences[index]; | |
81 int thisIndex = coincidence.fSegments[0]; | |
82 SkOpSegment& thisOne = fSegments[thisIndex]; | |
83 SkOpContour* otherContour = coincidence.fOther; | |
84 int otherIndex = coincidence.fSegments[1]; | |
85 SkOpSegment& other = otherContour->fSegments[otherIndex]; | |
86 if ((thisOne.done() || other.done()) && thisOne.complete() && other.comp
lete()) { | |
87 // OPTIMIZATION: remove from array | |
88 continue; | |
89 } | |
90 #if DEBUG_CONCIDENT | |
91 thisOne.debugShowTs("-"); | |
92 other.debugShowTs("o"); | |
93 #endif | |
94 double startT = coincidence.fTs[0][0]; | |
95 double endT = coincidence.fTs[0][1]; | |
96 bool startSwapped, oStartSwapped, cancelers; | |
97 if ((cancelers = startSwapped = startT > endT)) { | |
98 SkTSwap(startT, endT); | |
99 } | |
100 bump_out_close_span(&startT, &endT); | |
101 SkASSERT(!approximately_negative(endT - startT)); | |
102 double oStartT = coincidence.fTs[1][0]; | |
103 double oEndT = coincidence.fTs[1][1]; | |
104 if ((oStartSwapped = oStartT > oEndT)) { | |
105 SkTSwap(oStartT, oEndT); | |
106 cancelers ^= true; | |
107 } | |
108 bump_out_close_span(&oStartT, &oEndT); | |
109 SkASSERT(!approximately_negative(oEndT - oStartT)); | |
110 const SkPoint& startPt = coincidence.fPts[0][startSwapped]; | |
111 if (cancelers) { | |
112 // make sure startT and endT have t entries | |
113 if (startT > 0 || oEndT < 1 | |
114 || thisOne.isMissing(startT, startPt) || other.isMissing(oEn
dT, startPt)) { | |
115 thisOne.addTPair(startT, &other, oEndT, true, startPt, | |
116 coincidence.fPts[1][startSwapped]); | |
117 } | |
118 const SkPoint& oStartPt = coincidence.fPts[1][oStartSwapped]; | |
119 if (oStartT > 0 || endT < 1 | |
120 || thisOne.isMissing(endT, oStartPt) || other.isMissing(oSta
rtT, oStartPt)) { | |
121 other.addTPair(oStartT, &thisOne, endT, true, oStartPt, | |
122 coincidence.fPts[0][oStartSwapped]); | |
123 } | |
124 } else { | |
125 if (startT > 0 || oStartT > 0 | |
126 || thisOne.isMissing(startT, startPt) || other.isMissing(oSt
artT, startPt)) { | |
127 thisOne.addTPair(startT, &other, oStartT, true, startPt, | |
128 coincidence.fPts[1][startSwapped]); | |
129 } | |
130 const SkPoint& oEndPt = coincidence.fPts[1][!oStartSwapped]; | |
131 if (endT < 1 || oEndT < 1 | |
132 || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT,
oEndPt)) { | |
133 other.addTPair(oEndT, &thisOne, endT, true, oEndPt, | |
134 coincidence.fPts[0][!oStartSwapped]); | |
135 } | |
136 } | |
137 #if DEBUG_CONCIDENT | |
138 thisOne.debugShowTs("+"); | |
139 other.debugShowTs("o"); | |
140 #endif | |
141 } | |
142 // if there are multiple pairs of coincidence that share an edge, see if the
opposite | |
143 // are also coincident | |
144 for (int index = 0; index < count - 1; ++index) { | |
145 const SkCoincidence& coincidence = fCoincidences[index]; | |
146 int thisIndex = coincidence.fSegments[0]; | |
147 SkOpContour* otherContour = coincidence.fOther; | |
148 int otherIndex = coincidence.fSegments[1]; | |
149 for (int idx2 = 1; idx2 < count; ++idx2) { | |
150 const SkCoincidence& innerCoin = fCoincidences[idx2]; | |
151 int innerThisIndex = innerCoin.fSegments[0]; | |
152 if (thisIndex == innerThisIndex) { | |
153 checkCoincidentPair(coincidence, 1, innerCoin, 1, false); | |
154 } | |
155 if (this == otherContour && otherIndex == innerThisIndex) { | |
156 checkCoincidentPair(coincidence, 0, innerCoin, 1, false); | |
157 } | |
158 SkOpContour* innerOtherContour = innerCoin.fOther; | |
159 innerThisIndex = innerCoin.fSegments[1]; | |
160 if (this == innerOtherContour && thisIndex == innerThisIndex) { | |
161 checkCoincidentPair(coincidence, 1, innerCoin, 0, false); | |
162 } | |
163 if (otherContour == innerOtherContour && otherIndex == innerThisInde
x) { | |
164 checkCoincidentPair(coincidence, 0, innerCoin, 0, false); | |
165 } | |
166 } | |
167 } | |
168 } | |
169 | |
170 bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherI
ndex, | |
171 const SkIntersections& ts, int ptIndex, bool swap) { | |
172 SkPoint pt0 = ts.pt(ptIndex).asSkPoint(); | |
173 SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint(); | |
174 if (SkDPoint::ApproximatelyEqual(pt0, pt1)) { | |
175 // FIXME: one could imagine a case where it would be incorrect to ignore
this | |
176 // suppose two self-intersecting cubics overlap to form a partial coinci
dence -- | |
177 // although it isn't clear why the regular coincidence could wouldn't pi
ck this up | |
178 // this is exceptional enough to ignore for now | |
179 return false; | |
180 } | |
181 SkCoincidence& coincidence = fPartialCoincidences.push_back(); | |
182 coincidence.fOther = other; | |
183 coincidence.fSegments[0] = index; | |
184 coincidence.fSegments[1] = otherIndex; | |
185 coincidence.fTs[swap][0] = ts[0][ptIndex]; | |
186 coincidence.fTs[swap][1] = ts[0][ptIndex + 1]; | |
187 coincidence.fTs[!swap][0] = ts[1][ptIndex]; | |
188 coincidence.fTs[!swap][1] = ts[1][ptIndex + 1]; | |
189 coincidence.fPts[0][0] = coincidence.fPts[1][0] = pt0; | |
190 coincidence.fPts[0][1] = coincidence.fPts[1][1] = pt1; | |
191 coincidence.fNearly[0] = 0; | |
192 coincidence.fNearly[1] = 0; | |
193 return true; | |
194 } | |
195 | |
196 void SkOpContour::align(const SkOpSegment::AlignedSpan& aligned, bool swap, | |
197 SkCoincidence* coincidence) { | |
198 for (int idx2 = 0; idx2 < 2; ++idx2) { | |
199 if (coincidence->fPts[0][idx2] == aligned.fOldPt | |
200 && coincidence->fTs[swap][idx2] == aligned.fOldT) { | |
201 SkASSERT(SkDPoint::RoughlyEqual(coincidence->fPts[0][idx2], aligned.
fPt)); | |
202 coincidence->fPts[0][idx2] = aligned.fPt; | |
203 SkASSERT(way_roughly_equal(coincidence->fTs[swap][idx2], aligned.fT)
); | |
204 coincidence->fTs[swap][idx2] = aligned.fT; | |
205 } | |
206 } | |
207 } | |
208 | |
209 void SkOpContour::alignCoincidence(const SkOpSegment::AlignedSpan& aligned, | |
210 SkTArray<SkCoincidence, true>* coincidences) { | |
211 int count = coincidences->count(); | |
212 for (int index = 0; index < count; ++index) { | |
213 SkCoincidence& coincidence = (*coincidences)[index]; | |
214 int thisIndex = coincidence.fSegments[0]; | |
215 const SkOpSegment* thisOne = &fSegments[thisIndex]; | |
216 const SkOpContour* otherContour = coincidence.fOther; | |
217 int otherIndex = coincidence.fSegments[1]; | |
218 const SkOpSegment* other = &otherContour->fSegments[otherIndex]; | |
219 if (thisOne == aligned.fOther1 && other == aligned.fOther2) { | |
220 align(aligned, false, &coincidence); | |
221 } else if (thisOne == aligned.fOther2 && other == aligned.fOther1) { | |
222 align(aligned, true, &coincidence); | |
223 } | |
224 } | |
225 } | |
226 | |
227 void SkOpContour::alignTPt(int segmentIndex, const SkOpContour* other, int other
Index, | |
228 bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const { | |
229 int zeroPt; | |
230 if ((zeroPt = alignT(swap, tIndex, ts)) >= 0) { | |
231 alignPt(segmentIndex, point, zeroPt); | |
232 } | |
233 if ((zeroPt = other->alignT(!swap, tIndex, ts)) >= 0) { | |
234 other->alignPt(otherIndex, point, zeroPt); | |
235 } | |
236 } | |
237 | |
238 void SkOpContour::alignPt(int index, SkPoint* point, int zeroPt) const { | |
239 const SkOpSegment& segment = fSegments[index]; | |
240 if (0 == zeroPt) { | |
241 *point = segment.pts()[0]; | |
242 } else { | |
243 *point = segment.pts()[SkPathOpsVerbToPoints(segment.verb())]; | |
244 } | |
245 } | |
246 | |
247 int SkOpContour::alignT(bool swap, int tIndex, SkIntersections* ts) const { | |
248 double tVal = (*ts)[swap][tIndex]; | |
249 if (tVal != 0 && precisely_zero(tVal)) { | |
250 ts->set(swap, tIndex, 0); | |
251 return 0; | |
252 } | |
253 if (tVal != 1 && precisely_equal(tVal, 1)) { | |
254 ts->set(swap, tIndex, 1); | |
255 return 1; | |
256 } | |
257 return -1; | |
258 } | |
259 | |
260 bool SkOpContour::calcAngles() { | |
261 int segmentCount = fSegments.count(); | |
262 for (int test = 0; test < segmentCount; ++test) { | |
263 if (!fSegments[test].calcAngles()) { | |
264 return false; | |
265 } | |
266 } | |
267 return true; | |
268 } | |
269 | |
270 bool SkOpContour::calcCoincidentWinding() { | |
271 int count = fCoincidences.count(); | |
272 #if DEBUG_CONCIDENT | |
273 if (count > 0) { | |
274 SkDebugf("%s count=%d\n", __FUNCTION__, count); | |
275 } | |
276 #endif | |
277 for (int index = 0; index < count; ++index) { | |
278 SkCoincidence& coincidence = fCoincidences[index]; | |
279 if (!calcCommonCoincidentWinding(coincidence)) { | |
280 return false; | |
281 } | |
282 } | |
283 return true; | |
284 } | |
285 | |
286 void SkOpContour::calcPartialCoincidentWinding() { | |
287 int count = fPartialCoincidences.count(); | |
288 #if DEBUG_CONCIDENT | |
289 if (count > 0) { | |
290 SkDebugf("%s count=%d\n", __FUNCTION__, count); | |
291 } | |
292 #endif | |
293 for (int index = 0; index < count; ++index) { | |
294 SkCoincidence& coincidence = fPartialCoincidences[index]; | |
295 calcCommonCoincidentWinding(coincidence); | |
296 } | |
297 // if there are multiple pairs of partial coincidence that share an edge, se
e if the opposite | |
298 // are also coincident | |
299 for (int index = 0; index < count - 1; ++index) { | |
300 const SkCoincidence& coincidence = fPartialCoincidences[index]; | |
301 int thisIndex = coincidence.fSegments[0]; | |
302 SkOpContour* otherContour = coincidence.fOther; | |
303 int otherIndex = coincidence.fSegments[1]; | |
304 for (int idx2 = 1; idx2 < count; ++idx2) { | |
305 const SkCoincidence& innerCoin = fPartialCoincidences[idx2]; | |
306 int innerThisIndex = innerCoin.fSegments[0]; | |
307 if (thisIndex == innerThisIndex) { | |
308 checkCoincidentPair(coincidence, 1, innerCoin, 1, true); | |
309 } | |
310 if (this == otherContour && otherIndex == innerThisIndex) { | |
311 checkCoincidentPair(coincidence, 0, innerCoin, 1, true); | |
312 } | |
313 SkOpContour* innerOtherContour = innerCoin.fOther; | |
314 innerThisIndex = innerCoin.fSegments[1]; | |
315 if (this == innerOtherContour && thisIndex == innerThisIndex) { | |
316 checkCoincidentPair(coincidence, 1, innerCoin, 0, true); | |
317 } | |
318 if (otherContour == innerOtherContour && otherIndex == innerThisInde
x) { | |
319 checkCoincidentPair(coincidence, 0, innerCoin, 0, true); | |
320 } | |
321 } | |
322 } | |
323 } | |
324 | |
325 void SkOpContour::checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx, | |
326 const SkCoincidence& twoCoin, int twoIdx, bool partial) { | |
327 SkASSERT((oneIdx ? this : oneCoin.fOther) == (twoIdx ? this : twoCoin.fOther
)); | |
328 SkASSERT(oneCoin.fSegments[!oneIdx] == twoCoin.fSegments[!twoIdx]); | |
329 // look for common overlap | |
330 double min = SK_ScalarMax; | |
331 double max = SK_ScalarMin; | |
332 double min1 = oneCoin.fTs[!oneIdx][0]; | |
333 double max1 = oneCoin.fTs[!oneIdx][1]; | |
334 double min2 = twoCoin.fTs[!twoIdx][0]; | |
335 double max2 = twoCoin.fTs[!twoIdx][1]; | |
336 bool cancelers = (min1 < max1) != (min2 < max2); | |
337 if (min1 > max1) { | |
338 SkTSwap(min1, max1); | |
339 } | |
340 if (min2 > max2) { | |
341 SkTSwap(min2, max2); | |
342 } | |
343 if (between(min1, min2, max1)) { | |
344 min = min2; | |
345 } | |
346 if (between(min1, max2, max1)) { | |
347 max = max2; | |
348 } | |
349 if (between(min2, min1, max2)) { | |
350 min = SkTMin(min, min1); | |
351 } | |
352 if (between(min2, max1, max2)) { | |
353 max = SkTMax(max, max1); | |
354 } | |
355 if (min >= max) { | |
356 return; // no overlap | |
357 } | |
358 // look to see if opposite are different segments | |
359 int seg1Index = oneCoin.fSegments[oneIdx]; | |
360 int seg2Index = twoCoin.fSegments[twoIdx]; | |
361 if (seg1Index == seg2Index) { | |
362 return; | |
363 } | |
364 SkOpContour* contour1 = oneIdx ? oneCoin.fOther : this; | |
365 SkOpContour* contour2 = twoIdx ? twoCoin.fOther : this; | |
366 SkOpSegment* segment1 = &contour1->fSegments[seg1Index]; | |
367 SkOpSegment* segment2 = &contour2->fSegments[seg2Index]; | |
368 // find opposite t value ranges corresponding to reference min/max range | |
369 const SkOpContour* refContour = oneIdx ? this : oneCoin.fOther; | |
370 const int refSegIndex = oneCoin.fSegments[!oneIdx]; | |
371 const SkOpSegment* refSegment = &refContour->fSegments[refSegIndex]; | |
372 int seg1Start = segment1->findOtherT(min, refSegment); | |
373 int seg1End = segment1->findOtherT(max, refSegment); | |
374 int seg2Start = segment2->findOtherT(min, refSegment); | |
375 int seg2End = segment2->findOtherT(max, refSegment); | |
376 // if the opposite pairs already contain min/max, we're done | |
377 if (seg1Start >= 0 && seg1End >= 0 && seg2Start >= 0 && seg2End >= 0) { | |
378 return; | |
379 } | |
380 double loEnd = SkTMin(min1, min2); | |
381 double hiEnd = SkTMax(max1, max2); | |
382 // insert the missing coincident point(s) | |
383 double missingT1 = -1; | |
384 double otherT1 = -1; | |
385 if (seg1Start < 0) { | |
386 if (seg2Start < 0) { | |
387 return; | |
388 } | |
389 missingT1 = segment1->calcMissingTStart(refSegment, loEnd, min, max, hiE
nd, | |
390 segment2, seg1End); | |
391 if (missingT1 < 0) { | |
392 return; | |
393 } | |
394 const SkOpSpan* missingSpan = &segment2->span(seg2Start); | |
395 otherT1 = missingSpan->fT; | |
396 } else if (seg2Start < 0) { | |
397 SkASSERT(seg1Start >= 0); | |
398 missingT1 = segment2->calcMissingTStart(refSegment, loEnd, min, max, hiE
nd, | |
399 segment1, seg2End); | |
400 if (missingT1 < 0) { | |
401 return; | |
402 } | |
403 const SkOpSpan* missingSpan = &segment1->span(seg1Start); | |
404 otherT1 = missingSpan->fT; | |
405 } | |
406 SkPoint missingPt1; | |
407 SkOpSegment* addTo1 = NULL; | |
408 SkOpSegment* addOther1 = seg1Start < 0 ? segment2 : segment1; | |
409 int minTIndex = refSegment->findExactT(min, addOther1); | |
410 SkASSERT(minTIndex >= 0); | |
411 if (missingT1 >= 0) { | |
412 missingPt1 = refSegment->span(minTIndex).fPt; | |
413 addTo1 = seg1Start < 0 ? segment1 : segment2; | |
414 } | |
415 double missingT2 = -1; | |
416 double otherT2 = -1; | |
417 if (seg1End < 0) { | |
418 if (seg2End < 0) { | |
419 return; | |
420 } | |
421 missingT2 = segment1->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd
, | |
422 segment2, seg1Start); | |
423 if (missingT2 < 0) { | |
424 return; | |
425 } | |
426 const SkOpSpan* missingSpan = &segment2->span(seg2End); | |
427 otherT2 = missingSpan->fT; | |
428 } else if (seg2End < 0) { | |
429 SkASSERT(seg1End >= 0); | |
430 missingT2 = segment2->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd
, | |
431 segment1, seg2Start); | |
432 if (missingT2 < 0) { | |
433 return; | |
434 } | |
435 const SkOpSpan* missingSpan = &segment1->span(seg1End); | |
436 otherT2 = missingSpan->fT; | |
437 } | |
438 SkPoint missingPt2; | |
439 SkOpSegment* addTo2 = NULL; | |
440 SkOpSegment* addOther2 = seg1End < 0 ? segment2 : segment1; | |
441 int maxTIndex = refSegment->findExactT(max, addOther2); | |
442 SkASSERT(maxTIndex >= 0); | |
443 if (missingT2 >= 0) { | |
444 missingPt2 = refSegment->span(maxTIndex).fPt; | |
445 addTo2 = seg1End < 0 ? segment1 : segment2; | |
446 } | |
447 if (missingT1 >= 0) { | |
448 addTo1->pinT(missingPt1, &missingT1); | |
449 addTo1->addTPair(missingT1, addOther1, otherT1, false, missingPt1); | |
450 } else { | |
451 SkASSERT(minTIndex >= 0); | |
452 missingPt1 = refSegment->span(minTIndex).fPt; | |
453 } | |
454 if (missingT2 >= 0) { | |
455 addTo2->pinT(missingPt2, &missingT2); | |
456 addTo2->addTPair(missingT2, addOther2, otherT2, false, missingPt2); | |
457 } else { | |
458 SkASSERT(minTIndex >= 0); | |
459 missingPt2 = refSegment->span(maxTIndex).fPt; | |
460 } | |
461 if (!partial) { | |
462 return; | |
463 } | |
464 if (cancelers) { | |
465 if (missingT1 >= 0) { | |
466 if (addTo1->reversePoints(missingPt1, missingPt2)) { | |
467 SkTSwap(missingPt1, missingPt2); | |
468 } | |
469 addTo1->addTCancel(missingPt1, missingPt2, addOther1); | |
470 } else { | |
471 if (addTo2->reversePoints(missingPt1, missingPt2)) { | |
472 SkTSwap(missingPt1, missingPt2); | |
473 } | |
474 addTo2->addTCancel(missingPt1, missingPt2, addOther2); | |
475 } | |
476 } else if (missingT1 >= 0) { | |
477 SkAssertResult(addTo1->addTCoincident(missingPt1, missingPt2, | |
478 addTo1 == addTo2 ? missingT2 : otherT2, addOther1)); | |
479 } else { | |
480 SkAssertResult(addTo2->addTCoincident(missingPt2, missingPt1, | |
481 addTo2 == addTo1 ? missingT1 : otherT1, addOther2)); | |
482 } | |
483 } | |
484 | |
485 void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coinciden
ces, bool partial) { | |
486 int count = coincidences.count(); | |
487 #if DEBUG_CONCIDENT | |
488 if (count > 0) { | |
489 SkDebugf("%s count=%d\n", __FUNCTION__, count); | |
490 } | |
491 #endif | |
492 // look for a lineup where the partial implies another adjoining coincidence | |
493 for (int index = 0; index < count; ++index) { | |
494 const SkCoincidence& coincidence = coincidences[index]; | |
495 int thisIndex = coincidence.fSegments[0]; | |
496 SkOpSegment& thisOne = fSegments[thisIndex]; | |
497 if (thisOne.done()) { | |
498 continue; | |
499 } | |
500 SkOpContour* otherContour = coincidence.fOther; | |
501 int otherIndex = coincidence.fSegments[1]; | |
502 SkOpSegment& other = otherContour->fSegments[otherIndex]; | |
503 if (other.done()) { | |
504 continue; | |
505 } | |
506 double startT = coincidence.fTs[0][0]; | |
507 double endT = coincidence.fTs[0][1]; | |
508 if (startT == endT) { // this can happen in very large compares | |
509 continue; | |
510 } | |
511 double oStartT = coincidence.fTs[1][0]; | |
512 double oEndT = coincidence.fTs[1][1]; | |
513 if (oStartT == oEndT) { | |
514 continue; | |
515 } | |
516 bool swapStart = startT > endT; | |
517 bool swapOther = oStartT > oEndT; | |
518 const SkPoint* startPt = &coincidence.fPts[0][0]; | |
519 const SkPoint* endPt = &coincidence.fPts[0][1]; | |
520 if (swapStart) { | |
521 SkTSwap(startT, endT); | |
522 SkTSwap(oStartT, oEndT); | |
523 SkTSwap(startPt, endPt); | |
524 } | |
525 bool cancel = swapOther != swapStart; | |
526 int step = swapStart ? -1 : 1; | |
527 int oStep = swapOther ? -1 : 1; | |
528 double oMatchStart = cancel ? oEndT : oStartT; | |
529 if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatch
Start == 0)) { | |
530 bool added = false; | |
531 if (oMatchStart != 0) { | |
532 const SkPoint& oMatchStartPt = cancel ? *endPt : *startPt; | |
533 added = thisOne.joinCoincidence(&other, oMatchStart, oMatchStart
Pt, oStep, cancel); | |
534 } | |
535 if (!cancel && startT != 0 && !added) { | |
536 (void) other.joinCoincidence(&thisOne, startT, *startPt, step, c
ancel); | |
537 } | |
538 } | |
539 double oMatchEnd = cancel ? oStartT : oEndT; | |
540 if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd ==
1)) { | |
541 bool added = false; | |
542 if (cancel && endT != 1 && !added) { | |
543 (void) other.joinCoincidence(&thisOne, endT, *endPt, -step, canc
el); | |
544 } | |
545 } | |
546 } | |
547 } | |
548 | |
549 bool SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence)
{ | |
550 if (coincidence.fNearly[0] && coincidence.fNearly[1]) { | |
551 return true; | |
552 } | |
553 int thisIndex = coincidence.fSegments[0]; | |
554 SkOpSegment& thisOne = fSegments[thisIndex]; | |
555 if (thisOne.done()) { | |
556 return true; | |
557 } | |
558 SkOpContour* otherContour = coincidence.fOther; | |
559 int otherIndex = coincidence.fSegments[1]; | |
560 SkOpSegment& other = otherContour->fSegments[otherIndex]; | |
561 if (other.done()) { | |
562 return true; | |
563 } | |
564 double startT = coincidence.fTs[0][0]; | |
565 double endT = coincidence.fTs[0][1]; | |
566 const SkPoint* startPt = &coincidence.fPts[0][0]; | |
567 const SkPoint* endPt = &coincidence.fPts[0][1]; | |
568 bool cancelers; | |
569 if ((cancelers = startT > endT)) { | |
570 SkTSwap<double>(startT, endT); | |
571 SkTSwap<const SkPoint*>(startPt, endPt); | |
572 } | |
573 bump_out_close_span(&startT, &endT); | |
574 SkASSERT(!approximately_negative(endT - startT)); | |
575 double oStartT = coincidence.fTs[1][0]; | |
576 double oEndT = coincidence.fTs[1][1]; | |
577 if (oStartT > oEndT) { | |
578 SkTSwap<double>(oStartT, oEndT); | |
579 cancelers ^= true; | |
580 } | |
581 bump_out_close_span(&oStartT, &oEndT); | |
582 SkASSERT(!approximately_negative(oEndT - oStartT)); | |
583 bool success = true; | |
584 if (cancelers) { | |
585 thisOne.addTCancel(*startPt, *endPt, &other); | |
586 } else { | |
587 success = thisOne.addTCoincident(*startPt, *endPt, endT, &other); | |
588 } | |
589 #if DEBUG_CONCIDENT | |
590 thisOne.debugShowTs("p"); | |
591 other.debugShowTs("o"); | |
592 #endif | |
593 return success; | |
594 } | |
595 | |
596 void SkOpContour::resolveNearCoincidence() { | |
597 int count = fCoincidences.count(); | |
598 for (int index = 0; index < count; ++index) { | |
599 SkCoincidence& coincidence = fCoincidences[index]; | |
600 if (!coincidence.fNearly[0] || !coincidence.fNearly[1]) { | |
601 continue; | |
602 } | |
603 int thisIndex = coincidence.fSegments[0]; | |
604 SkOpSegment& thisOne = fSegments[thisIndex]; | |
605 SkOpContour* otherContour = coincidence.fOther; | |
606 int otherIndex = coincidence.fSegments[1]; | |
607 SkOpSegment& other = otherContour->fSegments[otherIndex]; | |
608 if ((thisOne.done() || other.done()) && thisOne.complete() && other.comp
lete()) { | |
609 // OPTIMIZATION: remove from coincidence array | |
610 continue; | |
611 } | |
612 #if DEBUG_CONCIDENT | |
613 thisOne.debugShowTs("-"); | |
614 other.debugShowTs("o"); | |
615 #endif | |
616 double startT = coincidence.fTs[0][0]; | |
617 double endT = coincidence.fTs[0][1]; | |
618 bool cancelers; | |
619 if ((cancelers = startT > endT)) { | |
620 SkTSwap<double>(startT, endT); | |
621 } | |
622 if (startT == endT) { // if span is very large, the smaller may have col
lapsed to nothing | |
623 if (endT <= 1 - FLT_EPSILON) { | |
624 endT += FLT_EPSILON; | |
625 SkASSERT(endT <= 1); | |
626 } else { | |
627 startT -= FLT_EPSILON; | |
628 SkASSERT(startT >= 0); | |
629 } | |
630 } | |
631 SkASSERT(!approximately_negative(endT - startT)); | |
632 double oStartT = coincidence.fTs[1][0]; | |
633 double oEndT = coincidence.fTs[1][1]; | |
634 if (oStartT > oEndT) { | |
635 SkTSwap<double>(oStartT, oEndT); | |
636 cancelers ^= true; | |
637 } | |
638 SkASSERT(!approximately_negative(oEndT - oStartT)); | |
639 if (cancelers) { | |
640 thisOne.blindCancel(coincidence, &other); | |
641 } else { | |
642 thisOne.blindCoincident(coincidence, &other); | |
643 } | |
644 } | |
645 } | |
646 | |
647 void SkOpContour::sortAngles() { | |
648 int segmentCount = fSegments.count(); | |
649 for (int test = 0; test < segmentCount; ++test) { | |
650 fSegments[test].sortAngles(); | |
651 } | |
652 } | |
653 | |
654 void SkOpContour::sortSegments() { | |
655 int segmentCount = fSegments.count(); | |
656 fSortedSegments.push_back_n(segmentCount); | |
657 for (int test = 0; test < segmentCount; ++test) { | |
658 fSortedSegments[test] = &fSegments[test]; | |
659 } | |
660 SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1); | |
661 fFirstSorted = 0; | |
662 } | |
663 | |
664 void SkOpContour::toPath(SkPathWriter* path) const { | 57 void SkOpContour::toPath(SkPathWriter* path) const { |
665 int segmentCount = fSegments.count(); | 58 const SkPoint& pt = fHead.pts()[0]; |
666 const SkPoint& pt = fSegments.front().pts()[0]; | |
667 path->deferredMove(pt); | 59 path->deferredMove(pt); |
668 for (int test = 0; test < segmentCount; ++test) { | 60 const SkOpSegment* segment = &fHead; |
669 fSegments[test].addCurveTo(0, 1, path, true); | 61 do { |
670 } | 62 segment->addCurveTo(segment->head(), segment->tail(), path, true); |
| 63 } while ((segment = segment->next())); |
671 path->close(); | 64 path->close(); |
672 } | 65 } |
673 | 66 |
674 void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, | 67 void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, |
675 SkOpSegment** topStart) { | 68 SkOpSegment** topStart) { |
676 int segmentCount = fSortedSegments.count(); | 69 int segmentCount = fSortedSegments.count(); |
677 SkASSERT(segmentCount > 0); | 70 SkASSERT(segmentCount > 0); |
678 int sortedIndex = fFirstSorted; | 71 int sortedIndex = fFirstSorted; |
679 fDone = true; // may be cleared below | 72 fDone = true; // may be cleared below |
680 for ( ; sortedIndex < segmentCount; ++sortedIndex) { | 73 for ( ; sortedIndex < segmentCount; ++sortedIndex) { |
(...skipping 18 matching lines...) Expand all Loading... |
699 } | 92 } |
700 if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) { | 93 if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) { |
701 continue; | 94 continue; |
702 } | 95 } |
703 } | 96 } |
704 *topStart = testSegment; | 97 *topStart = testSegment; |
705 *bestXY = testXY; | 98 *bestXY = testXY; |
706 } | 99 } |
707 } | 100 } |
708 | 101 |
709 SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) { | 102 SkOpSegment* SkOpContour::undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase**
endPtr) { |
710 int segmentCount = fSegments.count(); | 103 SkOpSegment* segment = &fHead; |
711 for (int test = 0; test < segmentCount; ++test) { | 104 do { |
712 SkOpSegment* testSegment = &fSegments[test]; | 105 if (segment->done()) { |
713 if (testSegment->done()) { | |
714 continue; | 106 continue; |
715 } | 107 } |
716 testSegment->undoneSpan(start, end); | 108 segment->undoneSpan(startPtr, endPtr); |
717 return testSegment; | 109 return segment; |
718 } | 110 } while ((segment = segment->next())); |
719 return NULL; | 111 return NULL; |
720 } | 112 } |
721 | |
722 #if DEBUG_SHOW_WINDING | |
723 int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) { | |
724 int count = fSegments.count(); | |
725 int sum = 0; | |
726 for (int index = 0; index < count; ++index) { | |
727 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest
); | |
728 } | |
729 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum); | |
730 return sum; | |
731 } | |
732 | |
733 void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& con
tourList) { | |
734 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13; | |
735 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16; | |
736 int ofInterest = 1 << 5 | 1 << 8; | |
737 int total = 0; | |
738 int index; | |
739 for (index = 0; index < contourList.count(); ++index) { | |
740 total += contourList[index]->segments().count(); | |
741 } | |
742 int sum = 0; | |
743 for (index = 0; index < contourList.count(); ++index) { | |
744 sum += contourList[index]->debugShowWindingValues(total, ofInterest); | |
745 } | |
746 // SkDebugf("%s total=%d\n", __FUNCTION__, sum); | |
747 } | |
748 #endif | |
749 | |
750 void SkOpContour::setBounds() { | |
751 int count = fSegments.count(); | |
752 if (count == 0) { | |
753 SkDebugf("%s empty contour\n", __FUNCTION__); | |
754 SkASSERT(0); | |
755 // FIXME: delete empty contour? | |
756 return; | |
757 } | |
758 fBounds = fSegments.front().bounds(); | |
759 for (int index = 1; index < count; ++index) { | |
760 fBounds.add(fSegments[index].bounds()); | |
761 } | |
762 } | |
OLD | NEW |