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:
FuzzPath()135 FuzzPath()
136 : fFloatMin(0)
137 , fFloatMax(800)
138 , fAddCount(0)
139 , fPrintName(false)
140 , fStrokeOnly(false)
141 , fValidate(false)
142 {
143 fTab = " ";
144 }
randomize()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
getClip() const161 const SkPath& getClip() const {
162 return fClip;
163 }
164
getMatrix() const165 const SkMatrix& getMatrix() const {
166 return fMatrix;
167 }
168
getPaint() const169 const SkPaint& getPaint() const {
170 return fPaint;
171 }
172
getPath() const173 const SkPath& getPath() const {
174 return fPath;
175 }
176
setSeed(int seed)177 void setSeed(int seed) {
178 fRand.setSeed(seed);
179 }
180
setStrokeOnly()181 void setStrokeOnly() {
182 fStrokeOnly = true;
183 }
184
185 private:
186
makeAddPathMode()187 SkPath::AddPathMode makeAddPathMode() {
188 return (SkPath::AddPathMode) fRand.nextRangeU(SkPath::kAppend_AddPathMode,
189 SkPath::kExtend_AddPathMode);
190 }
191
makeAddPathType()192 RandomAddPath makeAddPathType() {
193 return (RandomAddPath) fRand.nextRangeU(0, kRandomAddPath_Last);
194 }
195
makeAngle()196 SkScalar makeAngle() {
197 SkScalar angle;
198 angle = fRand.nextF();
199 return angle;
200 }
201
makeBool()202 bool makeBool() {
203 return fRand.nextBool();
204 }
205
makeDirection()206 SkPath::Direction makeDirection() {
207 return (SkPath::Direction) fRand.nextRangeU(SkPath::kCW_Direction, SkPath::kCCW_Direction);
208 }
209
makeMatrix()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
makePaint()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
makePoint()295 SkPoint makePoint() {
296 SkPoint result;
297 makeScalarArray(2, &result.fX);
298 return result;
299 }
300
makePointArray(size_t arrayCount,SkPoint * points)301 void makePointArray(size_t arrayCount, SkPoint* points) {
302 for (size_t index = 0; index < arrayCount; ++index) {
303 points[index] = makePoint();
304 }
305 }
306
makePointArray(SkTDArray<SkPoint> * points)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
makeRect()314 SkRect makeRect() {
315 SkRect result;
316 makeScalarArray(4, &result.fLeft);
317 return result;
318 }
319
makeRRect()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
makePath()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
makeSegmentCount()543 uint32_t makeSegmentCount() {
544 return fRand.nextRangeU(1, fPathSegmentLimit);
545 }
546
makeSetRRectType()547 RandomSetRRect makeSetRRectType() {
548 return (RandomSetRRect) fRand.nextRangeU(0, kRandomSetRRect_Last);
549 }
550
makeScalar()551 SkScalar makeScalar() {
552 SkScalar scalar;
553 scalar = fRand.nextRangeF(fFloatMin, fFloatMax);
554 return scalar;
555 }
556
makeScalarArray(size_t arrayCount,SkScalar * array)557 void makeScalarArray(size_t arrayCount, SkScalar* array) {
558 for (size_t index = 0; index < arrayCount; ++index) {
559 array[index] = makeScalar();
560 }
561 }
562
makeVectorArray(size_t arrayCount,SkVector * array)563 void makeVectorArray(size_t arrayCount, SkVector* array) {
564 for (size_t index = 0; index < arrayCount; ++index) {
565 array[index] = makeVector();
566 }
567 }
568
makeVector()569 SkVector makeVector() {
570 SkVector result;
571 makeScalarArray(2, &result.fX);
572 return result;
573 }
574
validate(const SkPath & path)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
contains_only_moveTo(const SkPath & path)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
path_fuzz_stroker(SkBitmap * bitmap,int seed)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:
PathFuzzView()650 PathFuzzView()
651 : fOneDraw(false)
652 {
653 }
654 protected:
655 // overrides from SkEventSink
onQuery(SkEvent * evt)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
onOnceBeforeDraw()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
onDrawContent(SkCanvas * canvas)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
MyFactory()699 static SkView* MyFactory() { return new PathFuzzView; }
700 static SkViewRegister reg(MyFactory);
701
702
703