| 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 |