1 /* 2 * Copyright 2015 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 "SampleCode.h" 9 #include "SkView.h" 10 #include "SkCanvas.h" 11 #include "SkPaint.h" 12 #include "SkPath.h" 13 #include "SkMatrix.h" 14 #include "SkColor.h" 15 #include "SkTDArray.h" 16 #include "SkRandom.h" 17 #include "SkRRect.h" 18 19 enum RandomAddPath { 20 kMoveToPath, 21 kRMoveToPath, 22 kLineToPath, 23 kRLineToPath, 24 kQuadToPath, 25 kRQuadToPath, 26 kConicToPath, 27 kRConicToPath, 28 kCubicToPath, 29 kRCubicToPath, 30 kArcToPath, 31 kArcTo2Path, 32 kClosePath, 33 kAddArc, 34 kAddRoundRect1, 35 kAddRoundRect2, 36 kAddRRect, 37 kAddPoly, 38 kAddPath1, 39 kAddPath2, 40 kAddPath3, 41 kReverseAddPath, 42 }; 43 44 const int kRandomAddPath_Last = kReverseAddPath; 45 46 const char* gRandomAddPathNames[] = { 47 "kMoveToPath", 48 "kRMoveToPath", 49 "kLineToPath", 50 "kRLineToPath", 51 "kQuadToPath", 52 "kRQuadToPath", 53 "kConicToPath", 54 "kRConicToPath", 55 "kCubicToPath", 56 "kRCubicToPath", 57 "kArcToPath", 58 "kArcTo2Path", 59 "kClosePath", 60 "kAddArc", 61 "kAddRoundRect1", 62 "kAddRoundRect2", 63 "kAddRRect", 64 "kAddPoly", 65 "kAddPath1", 66 "kAddPath2", 67 "kAddPath3", 68 "kReverseAddPath", 69 }; 70 71 enum RandomSetRRect { 72 kSetEmpty, 73 kSetRect, 74 kSetOval, 75 kSetRectXY, 76 kSetNinePatch, 77 kSetRectRadii, 78 }; 79 80 const char* gRandomSetRRectNames[] = { 81 "kSetEmpty", 82 "kSetRect", 83 "kSetOval", 84 "kSetRectXY", 85 "kSetNinePatch", 86 "kSetRectRadii", 87 }; 88 89 int kRandomSetRRect_Last = kSetRectRadii; 90 91 enum RandomSetMatrix { 92 kSetIdentity, 93 kSetTranslate, 94 kSetTranslateX, 95 kSetTranslateY, 96 kSetScale, 97 kSetScaleTranslate, 98 kSetScaleX, 99 kSetScaleY, 100 kSetSkew, 101 kSetSkewTranslate, 102 kSetSkewX, 103 kSetSkewY, 104 kSetRotate, 105 kSetRotateTranslate, 106 kSetPerspectiveX, 107 kSetPerspectiveY, 108 kSetAll, 109 }; 110 111 int kRandomSetMatrix_Last = kSetAll; 112 113 const char* gRandomSetMatrixNames[] = { 114 "kSetIdentity", 115 "kSetTranslate", 116 "kSetTranslateX", 117 "kSetTranslateY", 118 "kSetScale", 119 "kSetScaleTranslate", 120 "kSetScaleX", 121 "kSetScaleY", 122 "kSetSkew", 123 "kSetSkewTranslate", 124 "kSetSkewX", 125 "kSetSkewY", 126 "kSetRotate", 127 "kSetRotateTranslate", 128 "kSetPerspectiveX", 129 "kSetPerspectiveY", 130 "kSetAll", 131 }; 132 133 class FuzzPath { 134 public: 135 FuzzPath() 136 : fFloatMin(0) 137 , fFloatMax(800) 138 , fAddCount(0) 139 , fPrintName(false) 140 , fStrokeOnly(false) 141 , fValidate(false) 142 { 143 fTab = " "; 144 } 145 void randomize() { 146 fPathDepth = 0; 147 fPathDepthLimit = fRand.nextRangeU(1, 2); 148 fPathContourCount = fRand.nextRangeU(1, 4); 149 fPathSegmentLimit = fRand.nextRangeU(1, 8); 150 fClip = makePath(); 151 SkASSERT(!fPathDepth); 152 fMatrix = makeMatrix(); 153 fPaint = makePaint(); 154 fPathDepthLimit = fRand.nextRangeU(1, 3); 155 fPathContourCount = fRand.nextRangeU(1, 6); 156 fPathSegmentLimit = fRand.nextRangeU(1, 16); 157 fPath = makePath(); 158 SkASSERT(!fPathDepth); 159 } 160 161 const SkPath& getClip() const { 162 return fClip; 163 } 164 165 const SkMatrix& getMatrix() const { 166 return fMatrix; 167 } 168 169 const SkPaint& getPaint() const { 170 return fPaint; 171 } 172 173 const SkPath& getPath() const { 174 return fPath; 175 } 176 177 void setSeed(int seed) { 178 fRand.setSeed(seed); 179 } 180 181 void setStrokeOnly() { 182 fStrokeOnly = true; 183 } 184 185 private: 186 187 SkPath::AddPathMode makeAddPathMode() { 188 return (SkPath::AddPathMode) fRand.nextRangeU(SkPath::kAppend_AddPathMode, 189 SkPath::kExtend_AddPathMode); 190 } 191 192 RandomAddPath makeAddPathType() { 193 return (RandomAddPath) fRand.nextRangeU(0, kRandomAddPath_Last); 194 } 195 196 SkScalar makeAngle() { 197 SkScalar angle; 198 angle = fRand.nextF(); 199 return angle; 200 } 201 202 bool makeBool() { 203 return fRand.nextBool(); 204 } 205 206 SkPath::Direction makeDirection() { 207 return (SkPath::Direction) fRand.nextRangeU(SkPath::kCW_Direction, SkPath::kCCW_Direction); 208 } 209 210 SkMatrix makeMatrix() { 211 SkMatrix matrix; 212 matrix.reset(); 213 RandomSetMatrix setMatrix = (RandomSetMatrix) fRand.nextRangeU(0, kRandomSetMatrix_Last); 214 if (fPrintName) { 215 SkDebugf("%.*s%s\n", fPathDepth * 3, fTab, gRandomSetMatrixNames[setMatrix]); 216 } 217 switch (setMatrix) { 218 case kSetIdentity: 219 break; 220 case kSetTranslateX: 221 matrix.setTranslateX(makeScalar()); 222 break; 223 case kSetTranslateY: 224 matrix.setTranslateY(makeScalar()); 225 break; 226 case kSetTranslate: 227 matrix.setTranslate(makeScalar(), makeScalar()); 228 break; 229 case kSetScaleX: 230 matrix.setScaleX(makeScalar()); 231 break; 232 case kSetScaleY: 233 matrix.setScaleY(makeScalar()); 234 break; 235 case kSetScale: 236 matrix.setScale(makeScalar(), makeScalar()); 237 break; 238 case kSetScaleTranslate: 239 matrix.setScale(makeScalar(), makeScalar(), makeScalar(), makeScalar()); 240 break; 241 case kSetSkewX: 242 matrix.setSkewX(makeScalar()); 243 break; 244 case kSetSkewY: 245 matrix.setSkewY(makeScalar()); 246 break; 247 case kSetSkew: 248 matrix.setSkew(makeScalar(), makeScalar()); 249 break; 250 case kSetSkewTranslate: 251 matrix.setSkew(makeScalar(), makeScalar(), makeScalar(), makeScalar()); 252 break; 253 case kSetRotate: 254 matrix.setRotate(makeScalar()); 255 break; 256 case kSetRotateTranslate: 257 matrix.setRotate(makeScalar(), makeScalar(), makeScalar()); 258 break; 259 case kSetPerspectiveX: 260 matrix.setPerspX(makeScalar()); 261 break; 262 case kSetPerspectiveY: 263 matrix.setPerspY(makeScalar()); 264 break; 265 case kSetAll: 266 matrix.setAll(makeScalar(), makeScalar(), makeScalar(), 267 makeScalar(), makeScalar(), makeScalar(), 268 makeScalar(), makeScalar(), makeScalar()); 269 break; 270 } 271 return matrix; 272 } 273 274 SkPaint makePaint() { 275 SkPaint paint; 276 bool antiAlias = fRand.nextBool(); 277 paint.setAntiAlias(antiAlias); 278 SkPaint::Style style = fStrokeOnly ? SkPaint::kStroke_Style : 279 (SkPaint::Style) fRand.nextRangeU(SkPaint::kFill_Style, SkPaint::kStrokeAndFill_Style); 280 paint.setStyle(style); 281 SkColor color = (SkColor) fRand.nextU(); 282 paint.setColor(color); 283 SkScalar width = fRand.nextRangeF(0, 10); 284 paint.setStrokeWidth(width); 285 SkScalar miter = makeScalar(); 286 paint.setStrokeMiter(miter); 287 SkPaint::Cap cap = (SkPaint::Cap) fRand.nextRangeU(SkPaint::kButt_Cap, SkPaint::kSquare_Cap); 288 paint.setStrokeCap(cap); 289 SkPaint::Join join = (SkPaint::Join) fRand.nextRangeU(SkPaint::kMiter_Join, 290 SkPaint::kBevel_Join); 291 paint.setStrokeJoin(join); 292 return paint; 293 } 294 295 SkPoint makePoint() { 296 SkPoint result; 297 makeScalarArray(2, &result.fX); 298 return result; 299 } 300 301 void makePointArray(size_t arrayCount, SkPoint* points) { 302 for (size_t index = 0; index < arrayCount; ++index) { 303 points[index] = makePoint(); 304 } 305 } 306 307 void makePointArray(SkTDArray<SkPoint>* points) { 308 size_t arrayCount = fRand.nextRangeU(1, 10); 309 for (size_t index = 0; index < arrayCount; ++index) { 310 *points->append() = makePoint(); 311 } 312 } 313 314 SkRect makeRect() { 315 SkRect result; 316 makeScalarArray(4, &result.fLeft); 317 return result; 318 } 319 320 SkRRect makeRRect() { 321 SkRRect rrect; 322 RandomSetRRect rrectType = makeSetRRectType(); 323 if (fPrintName) { 324 SkDebugf("%.*s%s\n", fPathDepth * 3, fTab, gRandomSetRRectNames[rrectType]); 325 } 326 switch (rrectType) { 327 case kSetEmpty: 328 rrect.setEmpty(); 329 break; 330 case kSetRect: { 331 SkRect rect = makeRect(); 332 rrect.setRect(rect); 333 } break; 334 case kSetOval: { 335 SkRect oval = makeRect(); 336 rrect.setOval(oval); 337 } break; 338 case kSetRectXY: { 339 SkRect rect = makeRect(); 340 SkScalar xRad = makeScalar(); 341 SkScalar yRad = makeScalar(); 342 rrect.setRectXY(rect, xRad, yRad); 343 } break; 344 case kSetNinePatch: { 345 SkRect rect = makeRect(); 346 SkScalar leftRad = makeScalar(); 347 SkScalar topRad = makeScalar(); 348 SkScalar rightRad = makeScalar(); 349 SkScalar bottomRad = makeScalar(); 350 rrect.setNinePatch(rect, leftRad, topRad, rightRad, bottomRad); 351 SkDebugf(""); // keep locals in scope 352 } break; 353 case kSetRectRadii: { 354 SkRect rect = makeRect(); 355 SkVector radii[4]; 356 makeVectorArray(SK_ARRAY_COUNT(radii), radii); 357 rrect.setRectRadii(rect, radii); 358 } break; 359 } 360 return rrect; 361 } 362 363 SkPath makePath() { 364 SkPath path; 365 for (uint32_t cIndex = 0; cIndex < fPathContourCount; ++cIndex) { 366 uint32_t segments = makeSegmentCount(); 367 for (uint32_t sIndex = 0; sIndex < segments; ++sIndex) { 368 RandomAddPath addPathType = makeAddPathType(); 369 ++fAddCount; 370 if (fPrintName) { 371 SkDebugf("%.*s%s\n", fPathDepth * 3, fTab, 372 gRandomAddPathNames[addPathType]); 373 } 374 switch (addPathType) { 375 case kAddArc: { 376 SkRect oval = makeRect(); 377 SkScalar startAngle = makeAngle(); 378 SkScalar sweepAngle = makeAngle(); 379 path.addArc(oval, startAngle, sweepAngle); 380 validate(path); 381 } break; 382 case kAddRoundRect1: { 383 SkRect rect = makeRect(); 384 SkScalar rx = makeScalar(), ry = makeScalar(); 385 SkPath::Direction dir = makeDirection(); 386 path.addRoundRect(rect, rx, ry, dir); 387 validate(path); 388 } break; 389 case kAddRoundRect2: { 390 SkRect rect = makeRect(); 391 SkScalar radii[8]; 392 makeScalarArray(SK_ARRAY_COUNT(radii), radii); 393 SkPath::Direction dir = makeDirection(); 394 path.addRoundRect(rect, radii, dir); 395 validate(path); 396 } break; 397 case kAddRRect: { 398 SkRRect rrect = makeRRect(); 399 SkPath::Direction dir = makeDirection(); 400 path.addRRect(rrect, dir); 401 validate(path); 402 } break; 403 case kAddPoly: { 404 SkTDArray<SkPoint> points; 405 makePointArray(&points); 406 bool close = makeBool(); 407 path.addPoly(&points[0], points.count(), close); 408 validate(path); 409 } break; 410 case kAddPath1: 411 if (fPathDepth < fPathDepthLimit) { 412 ++fPathDepth; 413 SkPath src = makePath(); 414 validate(src); 415 SkScalar dx = makeScalar(); 416 SkScalar dy = makeScalar(); 417 SkPath::AddPathMode mode = makeAddPathMode(); 418 path.addPath(src, dx, dy, mode); 419 --fPathDepth; 420 validate(path); 421 } 422 break; 423 case kAddPath2: 424 if (fPathDepth < fPathDepthLimit) { 425 ++fPathDepth; 426 SkPath src = makePath(); 427 validate(src); 428 SkPath::AddPathMode mode = makeAddPathMode(); 429 path.addPath(src, mode); 430 --fPathDepth; 431 validate(path); 432 } 433 break; 434 case kAddPath3: 435 if (fPathDepth < fPathDepthLimit) { 436 ++fPathDepth; 437 SkPath src = makePath(); 438 validate(src); 439 SkMatrix matrix = makeMatrix(); 440 SkPath::AddPathMode mode = makeAddPathMode(); 441 path.addPath(src, matrix, mode); 442 --fPathDepth; 443 validate(path); 444 } 445 break; 446 case kReverseAddPath: 447 if (fPathDepth < fPathDepthLimit) { 448 ++fPathDepth; 449 SkPath src = makePath(); 450 validate(src); 451 path.reverseAddPath(src); 452 --fPathDepth; 453 validate(path); 454 } 455 break; 456 case kMoveToPath: { 457 SkScalar x = makeScalar(); 458 SkScalar y = makeScalar(); 459 path.moveTo(x, y); 460 validate(path); 461 } break; 462 case kRMoveToPath: { 463 SkScalar x = makeScalar(); 464 SkScalar y = makeScalar(); 465 path.rMoveTo(x, y); 466 validate(path); 467 } break; 468 case kLineToPath: { 469 SkScalar x = makeScalar(); 470 SkScalar y = makeScalar(); 471 path.lineTo(x, y); 472 validate(path); 473 } break; 474 case kRLineToPath: { 475 SkScalar x = makeScalar(); 476 SkScalar y = makeScalar(); 477 path.rLineTo(x, y); 478 validate(path); 479 } break; 480 case kQuadToPath: { 481 SkPoint pt[2]; 482 makePointArray(SK_ARRAY_COUNT(pt), pt); 483 path.quadTo(pt[0], pt[1]); 484 validate(path); 485 } break; 486 case kRQuadToPath: { 487 SkPoint pt[2]; 488 makePointArray(SK_ARRAY_COUNT(pt), pt); 489 path.rQuadTo(pt[0].fX, pt[0].fY, pt[1].fX, pt[1].fY); 490 validate(path); 491 } break; 492 case kConicToPath: { 493 SkPoint pt[2]; 494 makePointArray(SK_ARRAY_COUNT(pt), pt); 495 SkScalar weight = makeScalar(); 496 path.conicTo(pt[0], pt[1], weight); 497 validate(path); 498 } break; 499 case kRConicToPath: { 500 SkPoint pt[2]; 501 makePointArray(SK_ARRAY_COUNT(pt), pt); 502 SkScalar weight = makeScalar(); 503 path.rConicTo(pt[0].fX, pt[0].fY, pt[1].fX, pt[1].fY, weight); 504 validate(path); 505 } break; 506 case kCubicToPath: { 507 SkPoint pt[3]; 508 makePointArray(SK_ARRAY_COUNT(pt), pt); 509 path.cubicTo(pt[0], pt[1], pt[2]); 510 validate(path); 511 } break; 512 case kRCubicToPath: { 513 SkPoint pt[3]; 514 makePointArray(SK_ARRAY_COUNT(pt), pt); 515 path.rCubicTo(pt[0].fX, pt[0].fY, pt[1].fX, pt[1].fY, pt[2].fX, pt[2].fY); 516 validate(path); 517 } break; 518 case kArcToPath: { 519 SkPoint pt[2]; 520 makePointArray(SK_ARRAY_COUNT(pt), pt); 521 SkScalar radius = makeScalar(); 522 path.arcTo(pt[0], pt[1], radius); 523 validate(path); 524 } break; 525 case kArcTo2Path: { 526 SkRect oval = makeRect(); 527 SkScalar startAngle = makeAngle(); 528 SkScalar sweepAngle = makeAngle(); 529 bool forceMoveTo = makeBool(); 530 path.arcTo(oval, startAngle, sweepAngle, forceMoveTo); 531 validate(path); 532 } break; 533 case kClosePath: 534 path.close(); 535 validate(path); 536 break; 537 } 538 } 539 } 540 return path; 541 } 542 543 uint32_t makeSegmentCount() { 544 return fRand.nextRangeU(1, fPathSegmentLimit); 545 } 546 547 RandomSetRRect makeSetRRectType() { 548 return (RandomSetRRect) fRand.nextRangeU(0, kRandomSetRRect_Last); 549 } 550 551 SkScalar makeScalar() { 552 SkScalar scalar; 553 scalar = fRand.nextRangeF(fFloatMin, fFloatMax); 554 return scalar; 555 } 556 557 void makeScalarArray(size_t arrayCount, SkScalar* array) { 558 for (size_t index = 0; index < arrayCount; ++index) { 559 array[index] = makeScalar(); 560 } 561 } 562 563 void makeVectorArray(size_t arrayCount, SkVector* array) { 564 for (size_t index = 0; index < arrayCount; ++index) { 565 array[index] = makeVector(); 566 } 567 } 568 569 SkVector makeVector() { 570 SkVector result; 571 makeScalarArray(2, &result.fX); 572 return result; 573 } 574 575 void validate(const SkPath& path) { 576 if (fValidate) { 577 SkDEBUGCODE(path.experimentalValidateRef()); 578 } 579 } 580 581 SkRandom fRand; 582 SkMatrix fMatrix; 583 SkPath fClip; 584 SkPaint fPaint; 585 SkPath fPath; 586 SkScalar fFloatMin; 587 SkScalar fFloatMax; 588 uint32_t fPathContourCount; 589 int fPathDepth; 590 int fPathDepthLimit; 591 uint32_t fPathSegmentLimit; 592 int fAddCount; 593 bool fPrintName; 594 bool fStrokeOnly; 595 bool fValidate; 596 const char* fTab; 597 }; 598 599 static bool contains_only_moveTo(const SkPath& path) { 600 int verbCount = path.countVerbs(); 601 if (verbCount == 0) { 602 return true; 603 } 604 SkTDArray<uint8_t> verbs; 605 verbs.setCount(verbCount); 606 SkDEBUGCODE(int getVerbResult = ) path.getVerbs(verbs.begin(), verbCount); 607 SkASSERT(getVerbResult == verbCount); 608 for (int index = 0; index < verbCount; ++index) { 609 if (verbs[index] != SkPath::kMove_Verb) { 610 return false; 611 } 612 } 613 return true; 614 } 615 616 #include "SkGraphics.h" 617 #include "SkSurface.h" 618 #include "SkTaskGroup.h" 619 #include "SkTDArray.h" 620 621 static void path_fuzz_stroker(SkBitmap* bitmap, int seed) { 622 SkTaskGroup().batch(100, [&](int i) { 623 int localSeed = seed + i; 624 625 FuzzPath fuzzPath; 626 fuzzPath.setStrokeOnly(); 627 fuzzPath.setSeed(localSeed); 628 fuzzPath.randomize(); 629 const SkPath& path = fuzzPath.getPath(); 630 const SkPaint& paint = fuzzPath.getPaint(); 631 const SkImageInfo& info = bitmap->info(); 632 SkCanvas* canvas( 633 SkCanvas::NewRasterDirect(info, bitmap->getPixels(), bitmap->rowBytes())); 634 int w = info.width() / 4; 635 int h = info.height() / 4; 636 int x = localSeed / 4 % 4; 637 int y = localSeed % 4; 638 SkRect clipBounds = SkRect::MakeXYWH(SkIntToScalar(x) * w, SkIntToScalar(y) * h, 639 SkIntToScalar(w), SkIntToScalar(h)); 640 canvas->save(); 641 canvas->clipRect(clipBounds); 642 canvas->translate(SkIntToScalar(x) * w, SkIntToScalar(y) * h); 643 canvas->drawPath(path, paint); 644 canvas->restore(); 645 }); 646 } 647 648 class PathFuzzView : public SampleView { 649 public: 650 PathFuzzView() 651 : fOneDraw(false) 652 { 653 } 654 protected: 655 // overrides from SkEventSink 656 bool onQuery(SkEvent* evt) override { 657 if (SampleCode::TitleQ(*evt)) { 658 SampleCode::TitleR(evt, "PathFuzzer"); 659 return true; 660 } 661 return this->INHERITED::onQuery(evt); 662 } 663 664 void onOnceBeforeDraw() override { 665 fIndex = 0; 666 SkImageInfo info(SkImageInfo::MakeN32Premul(SkScalarRoundToInt(width()), 667 SkScalarRoundToInt(height()))); 668 offscreen.allocPixels(info); 669 path_fuzz_stroker(&offscreen, fIndex); 670 } 671 672 void onDrawContent(SkCanvas* canvas) override { 673 if (fOneDraw) { 674 fuzzPath.randomize(); 675 const SkPath& path = fuzzPath.getPath(); 676 const SkPaint& paint = fuzzPath.getPaint(); 677 const SkPath& clip = fuzzPath.getClip(); 678 const SkMatrix& matrix = fuzzPath.getMatrix(); 679 if (!contains_only_moveTo(clip)) { 680 canvas->clipPath(clip); 681 } 682 canvas->setMatrix(matrix); 683 canvas->drawPath(path, paint); 684 } else { 685 path_fuzz_stroker(&offscreen, fIndex += 100); 686 canvas->drawBitmap(offscreen, 0, 0); 687 } 688 this->inval(nullptr); 689 } 690 691 private: 692 int fIndex; 693 SkBitmap offscreen; 694 FuzzPath fuzzPath; 695 bool fOneDraw; 696 typedef SkView INHERITED; 697 }; 698 699 static SkView* MyFactory() { return new PathFuzzView; } 700 static SkViewRegister reg(MyFactory); 701 702 703