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 "SkAddIntersections.h" 8 #include "SkOpCoincidence.h" 9 #include "SkOpEdgeBuilder.h" 10 #include "SkPathOpsCommon.h" 11 #include "SkPathWriter.h" 12 13 static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr, 14 SkOpSpanBase** endPtr) { 15 while (chase.count()) { 16 SkOpSpanBase* span; 17 chase.pop(&span); 18 // OPTIMIZE: prev makes this compatible with old code -- but is it necessary? 19 *startPtr = span->ptT()->prev()->span(); 20 SkOpSegment* segment = (*startPtr)->segment(); 21 bool done = true; 22 *endPtr = NULL; 23 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) { 24 *startPtr = last->start(); 25 *endPtr = last->end(); 26 #if TRY_ROTATE 27 *chase.insert(0) = span; 28 #else 29 *chase.append() = span; 30 #endif 31 return last->segment(); 32 } 33 if (done) { 34 continue; 35 } 36 int winding; 37 bool sortable; 38 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable); 39 if (winding == SK_MinS32) { 40 continue; 41 } 42 int sumMiWinding, sumSuWinding; 43 if (sortable) { 44 segment = angle->segment(); 45 sumMiWinding = segment->updateWindingReverse(angle); 46 sumSuWinding = segment->updateOppWindingReverse(angle); 47 if (segment->operand()) { 48 SkTSwap<int>(sumMiWinding, sumSuWinding); 49 } 50 } 51 SkOpSegment* first = NULL; 52 const SkOpAngle* firstAngle = angle; 53 while ((angle = angle->next()) != firstAngle) { 54 segment = angle->segment(); 55 SkOpSpanBase* start = angle->start(); 56 SkOpSpanBase* end = angle->end(); 57 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding; 58 if (sortable) { 59 segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding, 60 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding); 61 } 62 if (!segment->done(angle)) { 63 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) { 64 first = segment; 65 *startPtr = start; 66 *endPtr = end; 67 } 68 // OPTIMIZATION: should this also add to the chase? 69 if (sortable) { 70 (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding, 71 oppSumWinding, angle); 72 } 73 } 74 } 75 if (first) { 76 #if TRY_ROTATE 77 *chase.insert(0) = span; 78 #else 79 *chase.append() = span; 80 #endif 81 return first; 82 } 83 } 84 return NULL; 85 } 86 87 static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, 88 const int xorMask, const int xorOpMask, SkPathWriter* simple, SkChunkAlloc* allocator) { 89 bool unsortable = false; 90 do { 91 SkOpSpan* span = FindSortableTop(contourList); 92 if (!span) { 93 break; 94 } 95 SkOpSegment* current = span->segment(); 96 SkOpSpanBase* start = span->next(); 97 SkOpSpanBase* end = span; 98 SkTDArray<SkOpSpanBase*> chase; 99 do { 100 if (current->activeOp(start, end, xorMask, xorOpMask, op)) { 101 do { 102 if (!unsortable && current->done()) { 103 break; 104 } 105 SkASSERT(unsortable || !current->done()); 106 SkOpSpanBase* nextStart = start; 107 SkOpSpanBase* nextEnd = end; 108 SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd, 109 &unsortable, op, xorMask, xorOpMask); 110 if (!next) { 111 if (!unsortable && simple->hasMove() 112 && current->verb() != SkPath::kLine_Verb 113 && !simple->isClosed()) { 114 current->addCurveTo(start, end, simple, true); 115 #if DEBUG_ACTIVE_SPANS 116 if (!simple->isClosed()) { 117 DebugShowActiveSpans(contourList); 118 } 119 #endif 120 } 121 break; 122 } 123 #if DEBUG_FLOW 124 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__, 125 current->debugID(), start->pt().fX, start->pt().fY, 126 end->pt().fX, end->pt().fY); 127 #endif 128 current->addCurveTo(start, end, simple, true); 129 current = next; 130 start = nextStart; 131 end = nextEnd; 132 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done())); 133 if (current->activeWinding(start, end) && !simple->isClosed()) { 134 SkOpSpan* spanStart = start->starter(end); 135 if (!spanStart->done()) { 136 current->addCurveTo(start, end, simple, true); 137 current->markDone(spanStart); 138 } 139 } 140 simple->close(); 141 } else { 142 SkOpSpanBase* last = current->markAndChaseDone(start, end); 143 if (last && !last->chased()) { 144 last->setChased(true); 145 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last)); 146 *chase.append() = last; 147 #if DEBUG_WINDING 148 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID()); 149 if (!last->final()) { 150 SkDebugf(" windSum=%d", last->upCast()->windSum()); 151 } 152 SkDebugf("\n"); 153 #endif 154 } 155 } 156 current = findChaseOp(chase, &start, &end); 157 #if DEBUG_ACTIVE_SPANS 158 DebugShowActiveSpans(contourList); 159 #endif 160 if (!current) { 161 break; 162 } 163 } while (true); 164 } while (true); 165 return simple->someAssemblyRequired(); 166 } 167 168 // pretty picture: 169 // https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b-7vD9JP2V-kn9Ss/edit?usp=sharing 170 static const SkPathOp gOpInverse[kReverseDifference_SkPathOp + 1][2][2] = { 171 // inside minuend outside minuend 172 // inside subtrahend outside subtrahend inside subtrahend outside subtrahend 173 {{ kDifference_SkPathOp, kIntersect_SkPathOp }, { kUnion_SkPathOp, kReverseDifference_SkPathOp }}, 174 {{ kIntersect_SkPathOp, kDifference_SkPathOp }, { kReverseDifference_SkPathOp, kUnion_SkPathOp }}, 175 {{ kUnion_SkPathOp, kReverseDifference_SkPathOp }, { kDifference_SkPathOp, kIntersect_SkPathOp }}, 176 {{ kXOR_SkPathOp, kXOR_SkPathOp }, { kXOR_SkPathOp, kXOR_SkPathOp }}, 177 {{ kReverseDifference_SkPathOp, kUnion_SkPathOp }, { kIntersect_SkPathOp, kDifference_SkPathOp }}, 178 }; 179 180 static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = { 181 {{ false, false }, { true, false }}, // diff 182 {{ false, false }, { false, true }}, // sect 183 {{ false, true }, { true, true }}, // union 184 {{ false, true }, { true, false }}, // xor 185 {{ false, true }, { false, false }}, // rev diff 186 }; 187 188 #define DEBUGGING_PATHOPS_FROM_HOST 0 // enable to debug svg in chrome -- note path hardcoded below 189 #if DEBUGGING_PATHOPS_FROM_HOST 190 #include "SkData.h" 191 #include "SkStream.h" 192 193 static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) { 194 SkDynamicMemoryWStream wStream; 195 path.dump(&wStream, force, dumpAsHex); 196 SkAutoDataUnref data(wStream.copyToData()); 197 fprintf(file, "%.*s\n", (int) data->size(), data->data()); 198 } 199 200 static int dumpID = 0; 201 202 static void dump_op(const SkPath& one, const SkPath& two, SkPathOp op) { 203 #if SK_BUILD_FOR_MAC 204 FILE* file = fopen("/Users/caryclark/Documents/svgop.txt", "w"); 205 #else 206 FILE* file = fopen("/usr/local/google/home/caryclark/Documents/svgop.txt", "w"); 207 #endif 208 fprintf(file, 209 "\nstatic void fuzz763_%d(skiatest::Reporter* reporter, const char* filename) {\n", 210 ++dumpID); 211 fprintf(file, " SkPath path;\n"); 212 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType()); 213 dump_path(file, one, false, true); 214 fprintf(file, " SkPath path1(path);\n"); 215 fprintf(file, " path.reset();\n"); 216 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType()); 217 dump_path(file, two, false, true); 218 fprintf(file, " SkPath path2(path);\n"); 219 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op); 220 fprintf(file, "}\n"); 221 fclose(file); 222 } 223 #endif 224 225 bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result, 226 bool expectSuccess) { 227 SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tune 228 SkOpContour contour; 229 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour); 230 SkOpCoincidence coincidence; 231 SkOpGlobalState globalState(&coincidence, contourList); 232 #if DEBUGGING_PATHOPS_FROM_HOST 233 dump_op(one, two, op); 234 #endif 235 #if 0 && DEBUG_SHOW_TEST_NAME 236 char* debugName = DEBUG_FILENAME_STRING; 237 if (debugName && debugName[0]) { 238 SkPathOpsDebug::BumpTestName(debugName); 239 SkPathOpsDebug::ShowPath(one, two, op, debugName); 240 } 241 #endif 242 op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()]; 243 SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()] 244 ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType; 245 const SkPath* minuend = &one; 246 const SkPath* subtrahend = &two; 247 if (op == kReverseDifference_SkPathOp) { 248 minuend = &two; 249 subtrahend = &one; 250 op = kDifference_SkPathOp; 251 } 252 #if DEBUG_SORT 253 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault; 254 #endif 255 // turn path into list of segments 256 SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState); 257 if (builder.unparseable()) { 258 return false; 259 } 260 const int xorMask = builder.xorMask(); 261 builder.addOperand(*subtrahend); 262 if (!builder.finish(&allocator)) { 263 return false; 264 } 265 #if DEBUG_DUMP_SEGMENTS 266 contour.dumpSegments(op); 267 #endif 268 269 const int xorOpMask = builder.xorMask(); 270 if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask, 271 xorOpMask == kEvenOdd_PathOpsMask)) { 272 result->reset(); 273 result->setFillType(fillType); 274 return true; 275 } 276 // find all intersections between segments 277 SkOpContour* current = contourList; 278 do { 279 SkOpContour* next = current; 280 while (AddIntersectTs(current, next, &coincidence, &allocator) 281 && (next = next->next())) 282 ; 283 } while ((current = current->next())); 284 #if DEBUG_VALIDATE 285 globalState.setPhase(SkOpGlobalState::kWalking); 286 #endif 287 if (!HandleCoincidence(contourList, &coincidence, &allocator)) { 288 return false; 289 } 290 // construct closed contours 291 result->reset(); 292 result->setFillType(fillType); 293 SkPathWriter wrapper(*result); 294 bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator); 295 { // if some edges could not be resolved, assemble remaining fragments 296 SkPath temp; 297 temp.setFillType(fillType); 298 SkPathWriter assembled(temp); 299 Assemble(wrapper, &assembled); 300 *result = *assembled.nativePath(); 301 result->setFillType(fillType); 302 } 303 return true; 304 } 305 306 bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) { 307 return OpDebug(one, two, op, result, true); 308 } 309