Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: src/pathops/SkPathOpsOp.cpp

Issue 1394503003: fix some pathops bugs found in 1M skps (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: init to avoid warning Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pathops/SkPathOpsDebug.cpp ('k') | src/pathops/SkPathOpsSimplify.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « src/pathops/SkPathOpsDebug.cpp ('k') | src/pathops/SkPathOpsSimplify.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698