SkPathOpsDebug.cpp revision ccec0f958ffc71a9986d236bc2eb335cb2111119
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 8#include "SkPathOpsDebug.h" 9#include "SkPath.h" 10#if DEBUG_ANGLE 11#include "SkString.h" 12#endif 13 14#if DEBUG_VALIDATE 15extern bool FLAGS_runFail; 16#endif 17 18#if defined SK_DEBUG || !FORCE_RELEASE 19 20const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"}; 21 22#if defined(SK_DEBUG) || !FORCE_RELEASE 23int SkPathOpsDebug::gContourID = 0; 24int SkPathOpsDebug::gSegmentID = 0; 25#endif 26 27#if DEBUG_SORT || DEBUG_SWAP_TOP 28int SkPathOpsDebug::gSortCountDefault = SK_MaxS32; 29int SkPathOpsDebug::gSortCount; 30#endif 31 32#if DEBUG_ACTIVE_OP 33const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"}; 34#endif 35 36bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray, 37 const SkOpSpanBase* span) { 38 for (int index = 0; index < chaseArray.count(); ++index) { 39 const SkOpSpanBase* entry = chaseArray[index]; 40 if (entry == span) { 41 return true; 42 } 43 } 44 return false; 45} 46 47void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) { 48 size_t len = strlen(str); 49 bool num = false; 50 for (size_t idx = 0; idx < len; ++idx) { 51 if (num && str[idx] == 'e') { 52 if (len + 2 >= bufferLen) { 53 return; 54 } 55 memmove(&str[idx + 2], &str[idx + 1], len - idx); 56 str[idx] = '*'; 57 str[idx + 1] = '^'; 58 ++len; 59 } 60 num = str[idx] >= '0' && str[idx] <= '9'; 61 } 62} 63 64bool SkPathOpsDebug::ValidWind(int wind) { 65 return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF; 66} 67 68void SkPathOpsDebug::WindingPrintf(int wind) { 69 if (wind == SK_MinS32) { 70 SkDebugf("?"); 71 } else { 72 SkDebugf("%d", wind); 73 } 74} 75#endif // defined SK_DEBUG || !FORCE_RELEASE 76 77 78#if DEBUG_SHOW_TEST_NAME 79void* SkPathOpsDebug::CreateNameStr() { 80 return SkNEW_ARRAY(char, DEBUG_FILENAME_STRING_LENGTH); 81} 82 83void SkPathOpsDebug::DeleteNameStr(void* v) { 84 SkDELETE_ARRAY(reinterpret_cast<char* >(v)); 85} 86 87void SkPathOpsDebug::BumpTestName(char* test) { 88 char* num = test + strlen(test); 89 while (num[-1] >= '0' && num[-1] <= '9') { 90 --num; 91 } 92 if (num[0] == '\0') { 93 return; 94 } 95 int dec = atoi(num); 96 if (dec == 0) { 97 return; 98 } 99 ++dec; 100 SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec); 101} 102#endif 103 104#if !DEBUG_SHOW_TEST_NAME // enable when building without extended test 105void SkPathOpsDebug::ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name) { 106} 107#endif 108 109#include "SkOpAngle.h" 110#include "SkOpSegment.h" 111 112#if DEBUG_SWAP_TOP 113int SkOpSegment::debugInflections(const SkOpSpanBase* start, const SkOpSpanBase* end) const { 114 if (fVerb != SkPath::kCubic_Verb) { 115 return false; 116 } 117 SkDCubic dst = SkDCubic::SubDivide(fPts, start->t(), end->t()); 118 double inflections[2]; 119 return dst.findInflections(inflections); 120} 121#endif 122 123SkOpAngle* SkOpSegment::debugLastAngle() { 124 SkOpAngle* result = NULL; 125 SkOpSpan* span = this->head(); 126 do { 127 if (span->toAngle()) { 128 SkASSERT(!result); 129 result = span->toAngle(); 130 } 131 } while ((span = span->next()->upCastable())); 132 SkASSERT(result); 133 return result; 134} 135 136void SkOpSegment::debugReset() { 137 this->init(this->fPts, this->contour(), this->verb()); 138} 139 140#if DEBUG_ACTIVE_SPANS 141void SkOpSegment::debugShowActiveSpans() const { 142 debugValidate(); 143 if (done()) { 144 return; 145 } 146 int lastId = -1; 147 double lastT = -1; 148 const SkOpSpan* span = &fHead; 149 do { 150 if (span->done()) { 151 continue; 152 } 153 if (lastId == fID && lastT == span->t()) { 154 continue; 155 } 156 lastId = fID; 157 lastT = span->t(); 158 SkDebugf("%s id=%d", __FUNCTION__, fID); 159 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); 160 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { 161 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); 162 } 163 const SkOpPtT* ptT = span->ptT(); 164 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", ptT->fT, ptT->fPt.fX, ptT->fPt.fY); 165 SkDebugf(" tEnd=%1.9g", span->next()->t()); 166 SkDebugf(" windSum="); 167 if (span->windSum() == SK_MinS32) { 168 SkDebugf("?"); 169 } else { 170 SkDebugf("%d", span->windSum()); 171 } 172 SkDebugf(" windValue=%d oppValue=%d", span->windValue(), span->oppValue()); 173 SkDebugf("\n"); 174 } while ((span = span->next()->upCastable())); 175} 176#endif 177 178#if DEBUG_MARK_DONE 179void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) { 180 const SkPoint& pt = span->ptT()->fPt; 181 SkDebugf("%s id=%d", fun, fID); 182 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); 183 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { 184 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); 185 } 186 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=", 187 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t()); 188 if (winding == SK_MinS32) { 189 SkDebugf("?"); 190 } else { 191 SkDebugf("%d", winding); 192 } 193 SkDebugf(" windSum="); 194 if (span->windSum() == SK_MinS32) { 195 SkDebugf("?"); 196 } else { 197 SkDebugf("%d", span->windSum()); 198 } 199 SkDebugf(" windValue=%d\n", span->windValue()); 200} 201 202void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding, 203 int oppWinding) { 204 const SkPoint& pt = span->ptT()->fPt; 205 SkDebugf("%s id=%d", fun, fID); 206 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); 207 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { 208 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); 209 } 210 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=", 211 span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding); 212 if (winding == SK_MinS32) { 213 SkDebugf("?"); 214 } else { 215 SkDebugf("%d", winding); 216 } 217 SkDebugf(" newOppSum="); 218 if (oppWinding == SK_MinS32) { 219 SkDebugf("?"); 220 } else { 221 SkDebugf("%d", oppWinding); 222 } 223 SkDebugf(" oppSum="); 224 if (span->oppSum() == SK_MinS32) { 225 SkDebugf("?"); 226 } else { 227 SkDebugf("%d", span->oppSum()); 228 } 229 SkDebugf(" windSum="); 230 if (span->windSum() == SK_MinS32) { 231 SkDebugf("?"); 232 } else { 233 SkDebugf("%d", span->windSum()); 234 } 235 SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue()); 236} 237 238#endif 239 240#if DEBUG_ANGLE 241SkString SkOpAngle::debugPart() const { 242 SkString result; 243 switch (this->segment()->verb()) { 244 case SkPath::kLine_Verb: 245 result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart), 246 this->segment()->debugID()); 247 break; 248 case SkPath::kQuad_Verb: 249 result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart), 250 this->segment()->debugID()); 251 break; 252 case SkPath::kCubic_Verb: 253 result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fCurvePart), 254 this->segment()->debugID()); 255 break; 256 default: 257 SkASSERT(0); 258 } 259 return result; 260} 261#endif 262 263#if DEBUG_SORT 264void SkOpAngle::debugLoop() const { 265 const SkOpAngle* first = this; 266 const SkOpAngle* next = this; 267 do { 268 next->dumpOne(true); 269 SkDebugf("\n"); 270 next = next->fNext; 271 } while (next && next != first); 272 next = first; 273 do { 274 next->debugValidate(); 275 next = next->fNext; 276 } while (next && next != first); 277} 278#endif 279 280void SkOpAngle::debugValidate() const { 281#if DEBUG_VALIDATE 282 const SkOpAngle* first = this; 283 const SkOpAngle* next = this; 284 int wind = 0; 285 int opp = 0; 286 int lastXor = -1; 287 int lastOppXor = -1; 288 do { 289 if (next->unorderable()) { 290 return; 291 } 292 const SkOpSpan* minSpan = next->start()->starter(next->end()); 293 if (minSpan->windValue() == SK_MinS32) { 294 return; 295 } 296 bool op = next->segment()->operand(); 297 bool isXor = next->segment()->isXor(); 298 bool oppXor = next->segment()->oppXor(); 299 SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM)); 300 SkASSERT(!DEBUG_LIMIT_WIND_SUM 301 || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM)); 302 bool useXor = op ? oppXor : isXor; 303 SkASSERT(lastXor == -1 || lastXor == (int) useXor); 304 lastXor = (int) useXor; 305 wind += next->sign() * (op ? minSpan->oppValue() : minSpan->windValue()); 306 if (useXor) { 307 wind &= 1; 308 } 309 useXor = op ? isXor : oppXor; 310 SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor); 311 lastOppXor = (int) useXor; 312 opp += next->sign() * (op ? minSpan->windValue() : minSpan->oppValue()); 313 if (useXor) { 314 opp &= 1; 315 } 316 next = next->fNext; 317 } while (next && next != first); 318 SkASSERT(wind == 0); 319 SkASSERT(opp == 0 || !FLAGS_runFail); 320#endif 321} 322 323void SkOpAngle::debugValidateNext() const { 324#if !FORCE_RELEASE 325 const SkOpAngle* first = this; 326 const SkOpAngle* next = first; 327 SkTDArray<const SkOpAngle*>(angles); 328 do { 329// SK_ALWAYSBREAK(next->fSegment->debugContains(next)); 330 angles.push(next); 331 next = next->next(); 332 if (next == first) { 333 break; 334 } 335 SK_ALWAYSBREAK(!angles.contains(next)); 336 if (!next) { 337 return; 338 } 339 } while (true); 340#endif 341} 342 343void SkOpSegment::debugValidate() const { 344#if DEBUG_VALIDATE 345 const SkOpSpanBase* span = &fHead; 346 double lastT = -1; 347 const SkOpSpanBase* prev = NULL; 348 int count = 0; 349 int done = 0; 350 do { 351 if (!span->final()) { 352 ++count; 353 done += span->upCast()->done() ? 1 : 0; 354 } 355 SkASSERT(span->segment() == this); 356 SkASSERT(!prev || prev->upCast()->next() == span); 357 SkASSERT(!prev || prev == span->prev()); 358 prev = span; 359 double t = span->ptT()->fT; 360 SkASSERT(lastT < t); 361 lastT = t; 362 span->debugValidate(); 363 } while (!span->final() && (span = span->upCast()->next())); 364 SkASSERT(count == fCount); 365 SkASSERT(done == fDoneCount); 366 SkASSERT(span->final()); 367 span->debugValidate(); 368#endif 369} 370 371bool SkOpSpanBase::debugCoinEndLoopCheck() const { 372 int loop = 0; 373 const SkOpSpanBase* next = this; 374 SkOpSpanBase* nextCoin; 375 do { 376 nextCoin = next->fCoinEnd; 377 SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin); 378 for (int check = 1; check < loop - 1; ++check) { 379 const SkOpSpanBase* checkCoin = this->fCoinEnd; 380 const SkOpSpanBase* innerCoin = checkCoin; 381 for (int inner = check + 1; inner < loop; ++inner) { 382 innerCoin = innerCoin->fCoinEnd; 383 if (checkCoin == innerCoin) { 384 SkDebugf("*** bad coincident end loop ***\n"); 385 return false; 386 } 387 } 388 } 389 ++loop; 390 } while ((next = nextCoin) && next != this); 391 return true; 392} 393 394void SkOpSpanBase::debugValidate() const { 395#if DEBUG_VALIDATE 396 const SkOpPtT* ptT = &fPtT; 397 SkASSERT(ptT->span() == this); 398 do { 399// SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt)); 400 ptT->debugValidate(); 401 ptT = ptT->next(); 402 } while (ptT != &fPtT); 403 SkASSERT(this->debugCoinEndLoopCheck()); 404 if (!this->final()) { 405 SkASSERT(this->upCast()->debugCoinLoopCheck()); 406 } 407 if (fFromAngle) { 408 fFromAngle->debugValidate(); 409 } 410 if (!this->final() && this->upCast()->toAngle()) { 411 this->upCast()->toAngle()->debugValidate(); 412 } 413#endif 414} 415 416bool SkOpSpan::debugCoinLoopCheck() const { 417 int loop = 0; 418 const SkOpSpan* next = this; 419 SkOpSpan* nextCoin; 420 do { 421 nextCoin = next->fCoincident; 422 SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin); 423 for (int check = 1; check < loop - 1; ++check) { 424 const SkOpSpan* checkCoin = this->fCoincident; 425 const SkOpSpan* innerCoin = checkCoin; 426 for (int inner = check + 1; inner < loop; ++inner) { 427 innerCoin = innerCoin->fCoincident; 428 if (checkCoin == innerCoin) { 429 SkDebugf("*** bad coincident loop ***\n"); 430 return false; 431 } 432 } 433 } 434 ++loop; 435 } while ((next = nextCoin) && next != this); 436 return true; 437} 438 439#include "SkOpContour.h" 440 441int SkOpPtT::debugLoopLimit(bool report) const { 442 int loop = 0; 443 const SkOpPtT* next = this; 444 do { 445 for (int check = 1; check < loop - 1; ++check) { 446 const SkOpPtT* checkPtT = this->fNext; 447 const SkOpPtT* innerPtT = checkPtT; 448 for (int inner = check + 1; inner < loop; ++inner) { 449 innerPtT = innerPtT->fNext; 450 if (checkPtT == innerPtT) { 451 if (report) { 452 SkDebugf("*** bad ptT loop ***\n"); 453 } 454 return loop; 455 } 456 } 457 } 458 ++loop; 459 } while ((next = next->fNext) && next != this); 460 return 0; 461} 462 463void SkOpPtT::debugValidate() const { 464#if DEBUG_VALIDATE 465 if (contour()->globalState()->phase() == SkOpGlobalState::kIntersecting) { 466 return; 467 } 468 SkASSERT(fNext); 469 SkASSERT(fNext != this); 470 SkASSERT(fNext->fNext); 471 SkASSERT(debugLoopLimit(false) == 0); 472#endif 473} 474