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