OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 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 "SkAddIntersections.h" | 7 #include "SkAddIntersections.h" |
8 #include "SkOpCoincidence.h" | 8 #include "SkOpCoincidence.h" |
9 #include "SkOpEdgeBuilder.h" | 9 #include "SkOpEdgeBuilder.h" |
10 #include "SkPathOpsCommon.h" | 10 #include "SkPathOpsCommon.h" |
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
215 fprintf(file, " path.reset();\n"); | 215 fprintf(file, " path.reset();\n"); |
216 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillT
ype()); | 216 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillT
ype()); |
217 dump_path(file, two, false, true); | 217 dump_path(file, two, false, true); |
218 fprintf(file, " SkPath path2(path);\n"); | 218 fprintf(file, " SkPath path2(path);\n"); |
219 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filenam
e);\n", op); | 219 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filenam
e);\n", op); |
220 fprintf(file, "}\n"); | 220 fprintf(file, "}\n"); |
221 fclose(file); | 221 fclose(file); |
222 } | 222 } |
223 #endif | 223 #endif |
224 | 224 |
| 225 |
| 226 #if DEBUG_T_SECT_LOOP_COUNT |
| 227 |
| 228 #include "SkMutex.h" |
| 229 |
| 230 SK_DECLARE_STATIC_MUTEX(debugWorstLoop); |
| 231 |
| 232 SkOpGlobalState debugWorstState(nullptr, nullptr SkDEBUGPARAMS(nullptr)); |
| 233 |
| 234 void ReportPathOpsDebugging() { |
| 235 debugWorstState.debugLoopReport(); |
| 236 } |
| 237 |
| 238 extern void (*gVerboseFinalize)(); |
| 239 |
| 240 #endif |
| 241 |
225 bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result, | 242 bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result, |
226 bool expectSuccess SkDEBUGPARAMS(const char* testName)) { | 243 bool expectSuccess SkDEBUGPARAMS(const char* testName)) { |
227 SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tun
e | 244 SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tun
e |
228 SkOpContour contour; | 245 SkOpContour contour; |
229 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour); | 246 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour); |
230 SkOpCoincidence coincidence; | 247 SkOpCoincidence coincidence; |
231 SkOpGlobalState globalState(&coincidence, contourList SkDEBUGPARAMS(testNam
e)); | 248 SkOpGlobalState globalState(&coincidence, contourList SkDEBUGPARAMS(testNam
e)); |
232 #if DEBUGGING_PATHOPS_FROM_HOST | 249 #if DEBUGGING_PATHOPS_FROM_HOST |
233 dump_op(one, two, op); | 250 dump_op(one, two, op); |
234 #endif | 251 #endif |
(...skipping 21 matching lines...) Expand all Loading... |
256 SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState); | 273 SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState); |
257 if (builder.unparseable()) { | 274 if (builder.unparseable()) { |
258 return false; | 275 return false; |
259 } | 276 } |
260 const int xorMask = builder.xorMask(); | 277 const int xorMask = builder.xorMask(); |
261 builder.addOperand(*subtrahend); | 278 builder.addOperand(*subtrahend); |
262 if (!builder.finish(&allocator)) { | 279 if (!builder.finish(&allocator)) { |
263 return false; | 280 return false; |
264 } | 281 } |
265 #if DEBUG_DUMP_SEGMENTS | 282 #if DEBUG_DUMP_SEGMENTS |
266 contour.dumpSegments(op); | 283 contourList->dumpSegments("seg", op); |
267 #endif | 284 #endif |
268 | 285 |
269 const int xorOpMask = builder.xorMask(); | 286 const int xorOpMask = builder.xorMask(); |
270 if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask, | 287 if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask, |
271 xorOpMask == kEvenOdd_PathOpsMask)) { | 288 xorOpMask == kEvenOdd_PathOpsMask)) { |
272 result->reset(); | 289 result->reset(); |
273 result->setFillType(fillType); | 290 result->setFillType(fillType); |
274 return true; | 291 return true; |
275 } | 292 } |
276 // find all intersections between segments | 293 // find all intersections between segments |
277 SkOpContour* current = contourList; | 294 SkOpContour* current = contourList; |
278 do { | 295 do { |
279 SkOpContour* next = current; | 296 SkOpContour* next = current; |
280 while (AddIntersectTs(current, next, &coincidence, &allocator) | 297 while (AddIntersectTs(current, next, &coincidence, &allocator) |
281 && (next = next->next())) | 298 && (next = next->next())) |
282 ; | 299 ; |
283 } while ((current = current->next())); | 300 } while ((current = current->next())); |
284 #if DEBUG_VALIDATE | 301 #if DEBUG_VALIDATE |
285 globalState.setPhase(SkOpGlobalState::kWalking); | 302 globalState.setPhase(SkOpGlobalState::kWalking); |
286 #endif | 303 #endif |
287 if (!HandleCoincidence(contourList, &coincidence, &allocator)) { | 304 if (!HandleCoincidence(contourList, &coincidence, &allocator)) { |
288 return false; | 305 return false; |
289 } | 306 } |
| 307 #if DEBUG_ALIGNMENT |
| 308 contourList->dumpSegments("aligned"); |
| 309 #endif |
290 // construct closed contours | 310 // construct closed contours |
291 result->reset(); | 311 result->reset(); |
292 result->setFillType(fillType); | 312 result->setFillType(fillType); |
293 SkPathWriter wrapper(*result); | 313 SkPathWriter wrapper(*result); |
294 bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator); | 314 bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator); |
295 { // if some edges could not be resolved, assemble remaining fragments | 315 { // if some edges could not be resolved, assemble remaining fragments |
296 SkPath temp; | 316 SkPath temp; |
297 temp.setFillType(fillType); | 317 temp.setFillType(fillType); |
298 SkPathWriter assembled(temp); | 318 SkPathWriter assembled(temp); |
299 Assemble(wrapper, &assembled); | 319 Assemble(wrapper, &assembled); |
300 *result = *assembled.nativePath(); | 320 *result = *assembled.nativePath(); |
301 result->setFillType(fillType); | 321 result->setFillType(fillType); |
302 } | 322 } |
| 323 #if DEBUG_T_SECT_LOOP_COUNT |
| 324 { |
| 325 SkAutoMutexAcquire autoM(debugWorstLoop); |
| 326 if (!gVerboseFinalize) { |
| 327 gVerboseFinalize = &ReportPathOpsDebugging; |
| 328 } |
| 329 debugWorstState.debugDoYourWorst(&globalState); |
| 330 } |
| 331 #endif |
303 return true; | 332 return true; |
304 } | 333 } |
305 | 334 |
| 335 #define DEBUG_VERIFY 0 |
| 336 |
| 337 #if DEBUG_VERIFY |
| 338 #include "SkBitmap.h" |
| 339 #include "SkCanvas.h" |
| 340 #include "SkPaint.h" |
| 341 |
| 342 const int bitWidth = 64; |
| 343 const int bitHeight = 64; |
| 344 |
| 345 static void debug_scale_matrix(const SkPath& one, const SkPath& two, SkMatrix& s
cale) { |
| 346 SkRect larger = one.getBounds(); |
| 347 larger.join(two.getBounds()); |
| 348 SkScalar largerWidth = larger.width(); |
| 349 if (largerWidth < 4) { |
| 350 largerWidth = 4; |
| 351 } |
| 352 SkScalar largerHeight = larger.height(); |
| 353 if (largerHeight < 4) { |
| 354 largerHeight = 4; |
| 355 } |
| 356 SkScalar hScale = (bitWidth - 2) / largerWidth; |
| 357 SkScalar vScale = (bitHeight - 2) / largerHeight; |
| 358 scale.reset(); |
| 359 scale.preScale(hScale, vScale); |
| 360 larger.fLeft *= hScale; |
| 361 larger.fRight *= hScale; |
| 362 larger.fTop *= vScale; |
| 363 larger.fBottom *= vScale; |
| 364 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft |
| 365 : 16000 < larger.fRight ? 16000 - larger.fRight : 0; |
| 366 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop |
| 367 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0; |
| 368 scale.preTranslate(dx, dy); |
| 369 } |
| 370 |
| 371 static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBit
map& bits) { |
| 372 if (bits.width() == 0) { |
| 373 bits.allocN32Pixels(bitWidth * 2, bitHeight); |
| 374 } |
| 375 SkCanvas canvas(bits); |
| 376 canvas.drawColor(SK_ColorWHITE); |
| 377 SkPaint paint; |
| 378 canvas.save(); |
| 379 const SkRect& bounds1 = one.getBounds(); |
| 380 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1); |
| 381 canvas.drawPath(one, paint); |
| 382 canvas.restore(); |
| 383 canvas.save(); |
| 384 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1); |
| 385 canvas.drawPath(two, paint); |
| 386 canvas.restore(); |
| 387 int errors = 0; |
| 388 for (int y = 0; y < bitHeight - 1; ++y) { |
| 389 uint32_t* addr1 = bits.getAddr32(0, y); |
| 390 uint32_t* addr2 = bits.getAddr32(0, y + 1); |
| 391 uint32_t* addr3 = bits.getAddr32(bitWidth, y); |
| 392 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1); |
| 393 for (int x = 0; x < bitWidth - 1; ++x) { |
| 394 // count 2x2 blocks |
| 395 bool err = addr1[x] != addr3[x]; |
| 396 if (err) { |
| 397 errors += addr1[x + 1] != addr3[x + 1] |
| 398 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1]; |
| 399 } |
| 400 } |
| 401 } |
| 402 return errors; |
| 403 } |
| 404 |
| 405 #endif |
| 406 |
306 bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) { | 407 bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) { |
| 408 #if DEBUG_VERIFY |
| 409 if (!OpDebug(one, two, op, result, true SkDEBUGPARAMS(nullptr))) { |
| 410 SkDebugf("%s did not expect failure\none: fill=%d\n", __FUNCTION__, one.
getFillType()); |
| 411 one.dumpHex(); |
| 412 SkDebugf("two: fill=%d\n", two.getFillType()); |
| 413 two.dumpHex(); |
| 414 SkASSERT(0); |
| 415 return false; |
| 416 } |
| 417 SkPath pathOut, scaledPathOut; |
| 418 SkRegion rgnA, rgnB, openClip, rgnOut; |
| 419 openClip.setRect(-16000, -16000, 16000, 16000); |
| 420 rgnA.setPath(one, openClip); |
| 421 rgnB.setPath(two, openClip); |
| 422 rgnOut.op(rgnA, rgnB, (SkRegion::Op) op); |
| 423 rgnOut.getBoundaryPath(&pathOut); |
| 424 SkMatrix scale; |
| 425 debug_scale_matrix(one, two, scale); |
| 426 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut; |
| 427 SkPath scaledA, scaledB; |
| 428 scaledA.addPath(one, scale); |
| 429 scaledA.setFillType(one.getFillType()); |
| 430 scaledB.addPath(two, scale); |
| 431 scaledB.setFillType(two.getFillType()); |
| 432 scaledRgnA.setPath(scaledA, openClip); |
| 433 scaledRgnB.setPath(scaledB, openClip); |
| 434 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op); |
| 435 scaledRgnOut.getBoundaryPath(&scaledPathOut); |
| 436 SkBitmap bitmap; |
| 437 SkPath scaledOut; |
| 438 scaledOut.addPath(*result, scale); |
| 439 scaledOut.setFillType(result->getFillType()); |
| 440 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap); |
| 441 const int MAX_ERRORS = 9; |
| 442 if (errors > MAX_ERRORS) { |
| 443 SkDebugf("%s did not expect failure\none: fill=%d\n", __FUNCTION__, one.
getFillType()); |
| 444 one.dumpHex(); |
| 445 SkDebugf("two: fill=%d\n", two.getFillType()); |
| 446 two.dumpHex(); |
| 447 SkASSERT(0); |
| 448 } |
| 449 return true; |
| 450 #else |
307 return OpDebug(one, two, op, result, true SkDEBUGPARAMS(nullptr)); | 451 return OpDebug(one, two, op, result, true SkDEBUGPARAMS(nullptr)); |
| 452 #endif |
308 } | 453 } |
OLD | NEW |