SkPathOpsOp.cpp (14542B)
1 /* 2 * Copyright 2012 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 "include/core/SkPath.h" 8 #include "include/core/SkPathTypes.h" 9 #include "include/core/SkRect.h" 10 #include "include/core/SkTypes.h" 11 #include "include/pathops/SkPathOps.h" 12 #include "include/private/base/SkMath.h" 13 #include "include/private/base/SkTDArray.h" 14 #include "src/base/SkArenaAlloc.h" 15 #include "src/pathops/SkAddIntersections.h" 16 #include "src/pathops/SkOpAngle.h" 17 #include "src/pathops/SkOpCoincidence.h" 18 #include "src/pathops/SkOpContour.h" 19 #include "src/pathops/SkOpEdgeBuilder.h" 20 #include "src/pathops/SkOpSegment.h" 21 #include "src/pathops/SkOpSpan.h" 22 #include "src/pathops/SkPathOpsCommon.h" 23 #include "src/pathops/SkPathOpsTypes.h" 24 #include "src/pathops/SkPathWriter.h" 25 26 #include <utility> 27 28 static bool findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr, 29 SkOpSpanBase** endPtr, SkOpSegment** result) { 30 while (!chase.empty()) { 31 SkOpSpanBase* span = chase.back(); 32 chase.pop_back(); 33 // OPTIMIZE: prev makes this compatible with old code -- but is it necessary? 34 *startPtr = span->ptT()->prev()->span(); 35 SkOpSegment* segment = (*startPtr)->segment(); 36 bool done = true; 37 *endPtr = nullptr; 38 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) { 39 *startPtr = last->start(); 40 *endPtr = last->end(); 41 #if TRY_ROTATE 42 *chase.insert(0) = span; 43 #else 44 *chase.append() = span; 45 #endif 46 *result = last->segment(); 47 return true; 48 } 49 if (done) { 50 continue; 51 } 52 int winding; 53 bool sortable; 54 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable); 55 if (!angle) { 56 *result = nullptr; 57 return true; 58 } 59 if (winding == SK_MinS32) { 60 continue; 61 } 62 int sumMiWinding, sumSuWinding; 63 if (sortable) { 64 segment = angle->segment(); 65 sumMiWinding = segment->updateWindingReverse(angle); 66 if (sumMiWinding == SK_MinS32) { 67 SkASSERT(segment->globalState()->debugSkipAssert()); 68 *result = nullptr; 69 return true; 70 } 71 sumSuWinding = segment->updateOppWindingReverse(angle); 72 if (sumSuWinding == SK_MinS32) { 73 SkASSERT(segment->globalState()->debugSkipAssert()); 74 *result = nullptr; 75 return true; 76 } 77 if (segment->operand()) { 78 using std::swap; 79 swap(sumMiWinding, sumSuWinding); 80 } 81 } 82 SkOpSegment* first = nullptr; 83 const SkOpAngle* firstAngle = angle; 84 while ((angle = angle->next()) != firstAngle) { 85 segment = angle->segment(); 86 SkOpSpanBase* start = angle->start(); 87 SkOpSpanBase* end = angle->end(); 88 int maxWinding = 0, sumWinding = 0, oppMaxWinding = 0, oppSumWinding = 0; 89 if (sortable) { 90 segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding, 91 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding); 92 } 93 if (!segment->done(angle)) { 94 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) { 95 first = segment; 96 *startPtr = start; 97 *endPtr = end; 98 } 99 // OPTIMIZATION: should this also add to the chase? 100 if (sortable) { 101 if (!segment->markAngle(maxWinding, sumWinding, oppMaxWinding, 102 oppSumWinding, angle, nullptr)) { 103 return false; 104 } 105 } 106 } 107 } 108 if (first) { 109 #if TRY_ROTATE 110 *chase.insert(0) = span; 111 #else 112 *chase.append() = span; 113 #endif 114 *result = first; 115 return true; 116 } 117 } 118 *result = nullptr; 119 return true; 120 } 121 122 static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, 123 const int xorMask, const int xorOpMask, SkPathWriter* writer) { 124 bool unsortable = false; 125 bool lastSimple = false; 126 bool simple = false; 127 do { 128 SkOpSpan* span = FindSortableTop(contourList); 129 if (!span) { 130 break; 131 } 132 SkOpSegment* current = span->segment(); 133 SkOpSpanBase* start = span->next(); 134 SkOpSpanBase* end = span; 135 SkTDArray<SkOpSpanBase*> chase; 136 do { 137 if (current->activeOp(start, end, xorMask, xorOpMask, op)) { 138 do { 139 if (!unsortable && current->done()) { 140 break; 141 } 142 SkASSERT(unsortable || !current->done()); 143 SkOpSpanBase* nextStart = start; 144 SkOpSpanBase* nextEnd = end; 145 lastSimple = simple; 146 SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd, 147 &unsortable, &simple, op, xorMask, xorOpMask); 148 if (!next) { 149 if (!unsortable && writer->hasMove() 150 && current->verb() != SkPath::kLine_Verb 151 && !writer->isClosed()) { 152 if (!current->addCurveTo(start, end, writer)) { 153 return false; 154 } 155 if (!writer->isClosed()) { 156 SkPathOpsDebug::ShowActiveSpans(contourList); 157 } 158 } else if (lastSimple) { 159 if (!current->addCurveTo(start, end, writer)) { 160 return false; 161 } 162 } 163 break; 164 } 165 #if DEBUG_FLOW 166 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__, 167 current->debugID(), start->pt().fX, start->pt().fY, 168 end->pt().fX, end->pt().fY); 169 #endif 170 if (!current->addCurveTo(start, end, writer)) { 171 return false; 172 } 173 current = next; 174 start = nextStart; 175 end = nextEnd; 176 } while (!writer->isClosed() && (!unsortable || !start->starter(end)->done())); 177 if (current->activeWinding(start, end) && !writer->isClosed()) { 178 SkOpSpan* spanStart = start->starter(end); 179 if (!spanStart->done()) { 180 if (!current->addCurveTo(start, end, writer)) { 181 return false; 182 } 183 current->markDone(spanStart); 184 } 185 } 186 writer->finishContour(); 187 } else { 188 SkOpSpanBase* last; 189 if (!current->markAndChaseDone(start, end, &last)) { 190 return false; 191 } 192 if (last && !last->chased()) { 193 last->setChased(true); 194 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last)); 195 *chase.append() = last; 196 #if DEBUG_WINDING 197 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID()); 198 if (!last->final()) { 199 SkDebugf(" windSum=%d", last->upCast()->windSum()); 200 } 201 SkDebugf("\n"); 202 #endif 203 } 204 } 205 if (!findChaseOp(chase, &start, &end, ¤t)) { 206 return false; 207 } 208 SkPathOpsDebug::ShowActiveSpans(contourList); 209 if (!current) { 210 break; 211 } 212 } while (true); 213 } while (true); 214 return true; 215 } 216 217 // diagram of why this simplifcation is possible is here: 218 // https://skia.org/dev/present/pathops link at bottom of the page 219 // https://drive.google.com/file/d/0BwoLUwz9PYkHLWpsaXd0UDdaN00/view?usp=sharing 220 static const SkPathOp gOpInverse[kReverseDifference_SkPathOp + 1][2][2] = { 221 // inside minuend outside minuend 222 // inside subtrahend outside subtrahend inside subtrahend outside subtrahend 223 {{ kDifference_SkPathOp, kIntersect_SkPathOp }, { kUnion_SkPathOp, kReverseDifference_SkPathOp }}, 224 {{ kIntersect_SkPathOp, kDifference_SkPathOp }, { kReverseDifference_SkPathOp, kUnion_SkPathOp }}, 225 {{ kUnion_SkPathOp, kReverseDifference_SkPathOp }, { kDifference_SkPathOp, kIntersect_SkPathOp }}, 226 {{ kXOR_SkPathOp, kXOR_SkPathOp }, { kXOR_SkPathOp, kXOR_SkPathOp }}, 227 {{ kReverseDifference_SkPathOp, kUnion_SkPathOp }, { kIntersect_SkPathOp, kDifference_SkPathOp }}, 228 }; 229 230 static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = { 231 {{ false, false }, { true, false }}, // diff 232 {{ false, false }, { false, true }}, // sect 233 {{ false, true }, { true, true }}, // union 234 {{ false, true }, { true, false }}, // xor 235 {{ false, true }, { false, false }}, // rev diff 236 }; 237 238 #if DEBUG_T_SECT_LOOP_COUNT 239 240 #include "include/private/base/SkMutex.h" 241 242 SkOpGlobalState debugWorstState(nullptr, nullptr SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr)); 243 244 void ReportPathOpsDebugging() { 245 debugWorstState.debugLoopReport(); 246 } 247 248 extern void (*gVerboseFinalize)(); 249 250 #endif 251 252 std::optional<SkPath> OpDebug(const SkPath& one, const SkPath& two, SkPathOp op 253 SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) { 254 #if DEBUG_DUMP_VERIFY 255 #ifndef SK_DEBUG 256 const char* testName = "release"; 257 #endif 258 if (SkPathOpsDebug::gDumpOp) { 259 DumpOp(one, two, op, testName); 260 } 261 #endif 262 op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()]; 263 bool inverseFill = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]; 264 SkPathFillType fillType = inverseFill ? SkPathFillType::kInverseEvenOdd : 265 SkPathFillType::kEvenOdd; 266 SkRect rect1, rect2; 267 if (kIntersect_SkPathOp == op && one.isRect(&rect1) && two.isRect(&rect2)) { 268 SkPath result = rect1.intersect(rect2) ? SkPath::Rect(rect1) 269 : SkPath(); 270 return result.makeFillType(fillType); 271 } 272 if (one.isEmpty() || two.isEmpty()) { 273 SkPath work; 274 switch (op) { 275 case kIntersect_SkPathOp: 276 break; 277 case kUnion_SkPathOp: 278 case kXOR_SkPathOp: 279 work = one.isEmpty() ? two : one; 280 break; 281 case kDifference_SkPathOp: 282 if (!one.isEmpty()) { 283 work = one; 284 } 285 break; 286 case kReverseDifference_SkPathOp: 287 if (!two.isEmpty()) { 288 work = two; 289 } 290 break; 291 default: 292 SkASSERT(0); // unhandled case 293 } 294 if (inverseFill != work.isInverseFillType()) { 295 work.toggleInverseFillType(); 296 } 297 return Simplify(work); 298 } 299 SkSTArenaAlloc<4096> allocator; // FIXME: add a constant expression here, tune 300 SkOpContour contour; 301 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour); 302 SkOpGlobalState globalState(contourList, &allocator 303 SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName)); 304 SkOpCoincidence coincidence(&globalState); 305 const SkPath* minuend = &one; 306 const SkPath* subtrahend = &two; 307 if (op == kReverseDifference_SkPathOp) { 308 using std::swap; 309 swap(minuend, subtrahend); 310 op = kDifference_SkPathOp; 311 } 312 #if DEBUG_SORT 313 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault; 314 #endif 315 // turn path into list of segments 316 SkOpEdgeBuilder builder(*minuend, contourList, &globalState); 317 if (builder.unparseable()) { 318 return {}; 319 } 320 const int xorMask = builder.xorMask(); 321 builder.addOperand(*subtrahend); 322 if (!builder.finish()) { 323 return {}; 324 } 325 #if DEBUG_DUMP_SEGMENTS 326 contourList->dumpSegments("seg", op); 327 #endif 328 329 const int xorOpMask = builder.xorMask(); 330 if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask, 331 xorOpMask == kEvenOdd_PathOpsMask)) { 332 return SkPath().makeFillType(fillType); 333 } 334 // find all intersections between segments 335 SkOpContour* current = contourList; 336 do { 337 SkOpContour* next = current; 338 while (AddIntersectTs(current, next, &coincidence) 339 && (next = next->next())) 340 ; 341 } while ((current = current->next())); 342 #if DEBUG_VALIDATE 343 globalState.setPhase(SkOpPhase::kWalking); 344 #endif 345 bool success = HandleCoincidence(contourList, &coincidence); 346 #if DEBUG_COIN 347 globalState.debugAddToGlobalCoinDicts(); 348 #endif 349 if (!success) { 350 return {}; 351 } 352 #if DEBUG_ALIGNMENT 353 contourList->dumpSegments("aligned"); 354 #endif 355 // construct closed contours 356 SkPathWriter wrapper(fillType); 357 if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) { 358 return {}; 359 } 360 wrapper.assemble(); // if some edges could not be resolved, assemble remaining 361 SkPath result = wrapper.nativePath(); 362 #if DEBUG_T_SECT_LOOP_COUNT 363 static SkMutex& debugWorstLoop = *(new SkMutex); 364 { 365 SkAutoMutexExclusive autoM(debugWorstLoop); 366 if (!gVerboseFinalize) { 367 gVerboseFinalize = &ReportPathOpsDebugging; 368 } 369 debugWorstState.debugDoYourWorst(&globalState); 370 } 371 #endif 372 return result; 373 } 374 375 std::optional<SkPath> Op(const SkPath& one, const SkPath& two, SkPathOp op) { 376 #if DEBUG_DUMP_VERIFY 377 if (SkPathOpsDebug::gVerifyOp) { 378 if (auto result = OpDebug(one, two, op SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) { 379 VerifyOp(one, two, op, &result.value()); 380 return *result; 381 } else { 382 ReportOpFail(one, two, op); 383 return {}; 384 } 385 } 386 #endif 387 if (auto result = OpDebug(one, two, op SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr))) { 388 return *result; 389 } 390 return {}; 391 }