1 /* 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" 10 #include "SkTSort.h" 11 12 bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex, 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 } 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[0] = pt0; 32 coincidence.fPts[1] = pt1; 33 return true; 34 } 35 36 SkOpSegment* 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 56 void SkOpContour::addCoincidentPoints() { 57 int count = fCoincidences.count(); 58 for (int index = 0; index < count; ++index) { 59 SkCoincidence& coincidence = fCoincidences[index]; 60 int thisIndex = coincidence.fSegments[0]; 61 SkOpSegment& thisOne = fSegments[thisIndex]; 62 SkOpContour* otherContour = coincidence.fOther; 63 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 70 thisOne.debugShowTs("-"); 71 other.debugShowTs("o"); 72 #endif 73 double startT = coincidence.fTs[0][0]; 74 double endT = coincidence.fTs[0][1]; 75 bool startSwapped, oStartSwapped, cancelers; 76 if ((cancelers = startSwapped = startT > endT)) { 77 SkTSwap(startT, endT); 78 } 79 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 } 88 SkASSERT(!approximately_negative(endT - startT)); 89 double oStartT = coincidence.fTs[1][0]; 90 double oEndT = coincidence.fTs[1][1]; 91 if ((oStartSwapped = oStartT > oEndT)) { 92 SkTSwap(oStartT, oEndT); 93 cancelers ^= true; 94 } 95 SkASSERT(!approximately_negative(oEndT - oStartT)); 96 if (cancelers) { 97 // make sure startT and endT have t entries 98 const SkPoint& startPt = coincidence.fPts[startSwapped]; 99 if (startT > 0 || oEndT < 1 100 || thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) { 101 thisOne.addTPair(startT, &other, oEndT, true, startPt); 102 } 103 const SkPoint& oStartPt = coincidence.fPts[oStartSwapped]; 104 if (oStartT > 0 || endT < 1 105 || thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) { 106 other.addTPair(oStartT, &thisOne, endT, true, oStartPt); 107 } 108 } else { 109 const SkPoint& startPt = coincidence.fPts[startSwapped]; 110 if (startT > 0 || oStartT > 0 111 || thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) { 112 thisOne.addTPair(startT, &other, oStartT, true, startPt); 113 } 114 const SkPoint& oEndPt = coincidence.fPts[!oStartSwapped]; 115 if (endT < 1 || oEndT < 1 116 || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) { 117 other.addTPair(oEndT, &thisOne, endT, true, oEndPt); 118 } 119 } 120 #if DEBUG_CONCIDENT 121 thisOne.debugShowTs("+"); 122 other.debugShowTs("o"); 123 #endif 124 } 125 } 126 127 bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex, 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 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 } 138 SkCoincidence& coincidence = fPartialCoincidences.push_back(); 139 coincidence.fOther = other; 140 coincidence.fSegments[0] = index; 141 coincidence.fSegments[1] = otherIndex; 142 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; 149 } 150 151 void SkOpContour::calcCoincidentWinding() { 152 int count = fCoincidences.count(); 153 #if DEBUG_CONCIDENT 154 if (count > 0) { 155 SkDebugf("%s count=%d\n", __FUNCTION__, count); 156 } 157 #endif 158 for (int index = 0; index < count; ++index) { 159 SkCoincidence& coincidence = fCoincidences[index]; 160 calcCommonCoincidentWinding(coincidence); 161 } 162 } 163 164 void SkOpContour::calcPartialCoincidentWinding() { 165 int count = fPartialCoincidences.count(); 166 #if DEBUG_CONCIDENT 167 if (count > 0) { 168 SkDebugf("%s count=%d\n", __FUNCTION__, count); 169 } 170 #endif 171 for (int index = 0; index < count; ++index) { 172 SkCoincidence& coincidence = fPartialCoincidences[index]; 173 calcCommonCoincidentWinding(coincidence); 174 } 175 } 176 177 void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) { 178 int count = coincidences.count(); 179 #if DEBUG_CONCIDENT 180 if (count > 0) { 181 SkDebugf("%s count=%d\n", __FUNCTION__, count); 182 } 183 #endif 184 // look for a lineup where the partial implies another adjoining coincidence 185 for (int index = 0; index < count; ++index) { 186 const SkCoincidence& coincidence = coincidences[index]; 187 int thisIndex = coincidence.fSegments[0]; 188 SkOpSegment& thisOne = fSegments[thisIndex]; 189 SkOpContour* otherContour = coincidence.fOther; 190 int otherIndex = coincidence.fSegments[1]; 191 SkOpSegment& other = otherContour->fSegments[otherIndex]; 192 double startT = coincidence.fTs[0][0]; 193 double endT = coincidence.fTs[0][1]; 194 if (startT == endT) { // this can happen in very large compares 195 continue; 196 } 197 double oStartT = coincidence.fTs[1][0]; 198 double oEndT = coincidence.fTs[1][1]; 199 if (oStartT == oEndT) { 200 continue; 201 } 202 bool swapStart = startT > endT; 203 bool swapOther = oStartT > oEndT; 204 if (swapStart) { 205 SkTSwap<double>(startT, endT); 206 SkTSwap<double>(oStartT, oEndT); 207 } 208 bool cancel = swapOther != swapStart; 209 int step = swapStart ? -1 : 1; 210 int oStep = swapOther ? -1 : 1; 211 double oMatchStart = cancel ? oEndT : oStartT; 212 if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) { 213 bool added = false; 214 if (oMatchStart != 0) { 215 added = thisOne.joinCoincidence(&other, oMatchStart, oStep, cancel); 216 } 217 if (!cancel && startT != 0 && !added) { 218 (void) other.joinCoincidence(&thisOne, startT, step, cancel); 219 } 220 } 221 double oMatchEnd = cancel ? oStartT : oEndT; 222 if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) { 223 bool added = false; 224 if (cancel && endT != 1 && !added) { 225 (void) other.joinCoincidence(&thisOne, endT, -step, cancel); 226 } 227 } 228 } 229 } 230 231 void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) { 232 int thisIndex = coincidence.fSegments[0]; 233 SkOpSegment& thisOne = fSegments[thisIndex]; 234 if (thisOne.done()) { 235 return; 236 } 237 SkOpContour* otherContour = coincidence.fOther; 238 int otherIndex = coincidence.fSegments[1]; 239 SkOpSegment& other = otherContour->fSegments[otherIndex]; 240 if (other.done()) { 241 return; 242 } 243 double startT = coincidence.fTs[0][0]; 244 double endT = coincidence.fTs[0][1]; 245 const SkPoint* startPt = &coincidence.fPts[0]; 246 const SkPoint* endPt = &coincidence.fPts[1]; 247 bool cancelers; 248 if ((cancelers = startT > endT)) { 249 SkTSwap<double>(startT, endT); 250 SkTSwap<const SkPoint*>(startPt, endPt); 251 } 252 if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing 253 if (endT <= 1 - FLT_EPSILON) { 254 endT += FLT_EPSILON; 255 SkASSERT(endT <= 1); 256 } else { 257 startT -= FLT_EPSILON; 258 SkASSERT(startT >= 0); 259 } 260 } 261 SkASSERT(!approximately_negative(endT - startT)); 262 double oStartT = coincidence.fTs[1][0]; 263 double oEndT = coincidence.fTs[1][1]; 264 if (oStartT > oEndT) { 265 SkTSwap<double>(oStartT, oEndT); 266 cancelers ^= true; 267 } 268 SkASSERT(!approximately_negative(oEndT - oStartT)); 269 if (cancelers) { 270 thisOne.addTCancel(*startPt, *endPt, &other); 271 } else { 272 thisOne.addTCoincident(*startPt, *endPt, endT, &other); 273 } 274 #if DEBUG_CONCIDENT 275 thisOne.debugShowTs("p"); 276 other.debugShowTs("o"); 277 #endif 278 } 279 280 void SkOpContour::sortSegments() { 281 int segmentCount = fSegments.count(); 282 fSortedSegments.push_back_n(segmentCount); 283 for (int test = 0; test < segmentCount; ++test) { 284 fSortedSegments[test] = &fSegments[test]; 285 } 286 SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1); 287 fFirstSorted = 0; 288 } 289 290 void SkOpContour::toPath(SkPathWriter* path) const { 291 int segmentCount = fSegments.count(); 292 const SkPoint& pt = fSegments.front().pts()[0]; 293 path->deferredMove(pt); 294 for (int test = 0; test < segmentCount; ++test) { 295 fSegments[test].addCurveTo(0, 1, path, true); 296 } 297 path->close(); 298 } 299 300 void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, 301 SkOpSegment** topStart) { 302 int segmentCount = fSortedSegments.count(); 303 SkASSERT(segmentCount > 0); 304 int sortedIndex = fFirstSorted; 305 fDone = true; // may be cleared below 306 for ( ; sortedIndex < segmentCount; ++sortedIndex) { 307 SkOpSegment* testSegment = fSortedSegments[sortedIndex]; 308 if (testSegment->done()) { 309 if (sortedIndex == fFirstSorted) { 310 ++fFirstSorted; 311 } 312 continue; 313 } 314 fDone = false; 315 SkPoint testXY = testSegment->activeLeftTop(true, NULL); 316 if (*topStart) { 317 if (testXY.fY < topLeft.fY) { 318 continue; 319 } 320 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) { 321 continue; 322 } 323 if (bestXY->fY < testXY.fY) { 324 continue; 325 } 326 if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) { 327 continue; 328 } 329 } 330 *topStart = testSegment; 331 *bestXY = testXY; 332 } 333 } 334 335 SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) { 336 int segmentCount = fSegments.count(); 337 for (int test = 0; test < segmentCount; ++test) { 338 SkOpSegment* testSegment = &fSegments[test]; 339 if (testSegment->done()) { 340 continue; 341 } 342 testSegment->undoneSpan(start, end); 343 return testSegment; 344 } 345 return NULL; 346 } 347 348 #if DEBUG_SHOW_WINDING 349 int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) { 350 int count = fSegments.count(); 351 int sum = 0; 352 for (int index = 0; index < count; ++index) { 353 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest); 354 } 355 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum); 356 return sum; 357 } 358 359 void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) { 360 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13; 361 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16; 362 int ofInterest = 1 << 5 | 1 << 8; 363 int total = 0; 364 int index; 365 for (index = 0; index < contourList.count(); ++index) { 366 total += contourList[index]->segments().count(); 367 } 368 int sum = 0; 369 for (index = 0; index < contourList.count(); ++index) { 370 sum += contourList[index]->debugShowWindingValues(total, ofInterest); 371 } 372 // SkDebugf("%s total=%d\n", __FUNCTION__, sum); 373 } 374 #endif 375 376 void SkOpContour::setBounds() { 377 int count = fSegments.count(); 378 if (count == 0) { 379 SkDebugf("%s empty contour\n", __FUNCTION__); 380 SkASSERT(0); 381 // FIXME: delete empty contour? 382 return; 383 } 384 fBounds = fSegments.front().bounds(); 385 for (int index = 1; index < count; ++index) { 386 fBounds.add(fSegments[index].bounds()); 387 } 388 } 389