C++11 override should now be supported by all of {bots,Chrome,Android,Mozilla}
[platform/upstream/libSkiaSharp.git] / samplecode / SamplePathFuzz.cpp
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
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:
134     FuzzPath()
135         : fFloatMin(0)
136         , fFloatMax(800)
137         , fAddCount(0)
138         , fPrintName(false)
139         , fStrokeOnly(false)
140         , fValidate(false)
141     {
142         fTab = "                                                                                  ";
143     }
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
160     const SkPath& getClip() const {
161         return fClip;
162     }
163
164     const SkMatrix& getMatrix() const {
165         return fMatrix;
166     }
167
168     const SkPaint& getPaint() const {
169         return fPaint;
170     }
171
172     const SkPath& getPath() const {
173         return fPath;
174     }
175
176     void setSeed(int seed) {
177         fRand.setSeed(seed);
178     }
179
180     void setStrokeOnly() {
181         fStrokeOnly = true;
182     }
183
184 private:
185
186 SkPath::AddPathMode makeAddPathMode() {
187     return (SkPath::AddPathMode) fRand.nextRangeU(SkPath::kAppend_AddPathMode,
188         SkPath::kExtend_AddPathMode);
189 }
190
191 RandomAddPath makeAddPathType() {
192     return (RandomAddPath) fRand.nextRangeU(0, kRandomAddPath_Last);
193 }
194
195 SkScalar makeAngle() {
196     SkScalar angle;
197     angle = fRand.nextF();
198     return angle;
199 }
200
201 bool makeBool() {
202     return fRand.nextBool();
203 }
204
205 SkPath::Direction makeDirection() {
206     return (SkPath::Direction) fRand.nextRangeU(SkPath::kCW_Direction, SkPath::kCCW_Direction);
207 }
208
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
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
294 SkPoint makePoint() {
295     SkPoint result;
296     makeScalarArray(2, &result.fX);
297     return result;
298 }
299
300 void makePointArray(size_t arrayCount, SkPoint* points) {
301     for (size_t index = 0; index < arrayCount; ++index) {
302         points[index] = makePoint();
303     }
304 }
305
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
313 SkRect makeRect() {
314     SkRect result;
315     makeScalarArray(4, &result.fLeft);
316     return result;
317 }
318
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
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
542 uint32_t makeSegmentCount() {
543     return fRand.nextRangeU(1, fPathSegmentLimit);
544 }
545
546 RandomSetRRect makeSetRRectType() {
547     return (RandomSetRRect) fRand.nextRangeU(0, kRandomSetRRect_Last);
548 }
549
550 SkScalar makeScalar() {
551     SkScalar scalar;
552     scalar = fRand.nextRangeF(fFloatMin, fFloatMax);
553     return scalar;
554 }
555
556 void makeScalarArray(size_t arrayCount, SkScalar* array) {
557     for (size_t index = 0; index < arrayCount; ++index) {
558         array[index] = makeScalar();
559     }
560 }
561
562 void makeVectorArray(size_t arrayCount, SkVector* array) {
563     for (size_t index = 0; index < arrayCount; ++index) {
564         array[index] = makeVector();
565     }
566 }
567
568 SkVector makeVector() {
569     SkVector result;
570     makeScalarArray(2, &result.fX);
571     return result;
572 }
573
574 void validate(const SkPath& path) {
575     if (fValidate) {
576         SkDEBUGCODE(path.experimentalValidateRef());
577     }
578 }
579
580 SkRandom fRand;
581 SkMatrix fMatrix;
582 SkPath fClip;
583 SkPaint fPaint;
584 SkPath fPath;
585 SkScalar fFloatMin;
586 SkScalar fFloatMax;
587 uint32_t fPathContourCount;
588 int fPathDepth;
589 int fPathDepthLimit;
590 uint32_t fPathSegmentLimit;
591 int fAddCount;
592 bool fPrintName;
593 bool fStrokeOnly;
594 bool fValidate;
595 const char* fTab;
596 };
597
598 static bool contains_only_moveTo(const SkPath& path) {
599     int verbCount = path.countVerbs();
600     if (verbCount == 0) {
601         return true;
602     }
603     SkTDArray<uint8_t> verbs;
604     verbs.setCount(verbCount);
605     SkDEBUGCODE(int getVerbResult = ) path.getVerbs(verbs.begin(), verbCount);
606     SkASSERT(getVerbResult == verbCount);
607     for (int index = 0; index < verbCount; ++index) {
608         if (verbs[index] != SkPath::kMove_Verb) {
609             return false;
610         }
611     }
612     return true;
613 }
614
615 #include "SkGraphics.h"
616 #include "SkSurface.h"
617 #include "SkTaskGroup.h"
618 #include "SkTDArray.h"
619
620 struct ThreadState {
621     int fSeed;
622     const SkBitmap* fBitmap;
623 };
624
625 static void test_fuzz(ThreadState* data) {
626     FuzzPath fuzzPath;
627     fuzzPath.setStrokeOnly();
628     fuzzPath.setSeed(data->fSeed);
629     fuzzPath.randomize();
630     const SkPath& path = fuzzPath.getPath();
631     const SkPaint& paint = fuzzPath.getPaint();
632     const SkImageInfo& info = data->fBitmap->info();
633     SkCanvas* canvas(SkCanvas::NewRasterDirect(info, data->fBitmap->getPixels(),
634             data->fBitmap->rowBytes()));
635     int w = info.width() / 4;
636     int h = info.height() / 4;
637     int x = data->fSeed / 4 % 4;
638     int y = data->fSeed % 4;
639     SkRect clipBounds = SkRect::MakeXYWH(SkIntToScalar(x) * w, SkIntToScalar(y) * h, 
640         SkIntToScalar(w), SkIntToScalar(h));
641     canvas->save();
642         canvas->clipRect(clipBounds);
643         canvas->translate(SkIntToScalar(x) * w, SkIntToScalar(y) * h);
644         canvas->drawPath(path, paint);
645     canvas->restore();
646 }
647
648 static void path_fuzz_stroker(SkBitmap* bitmap, int seed) {
649     ThreadState states[100];
650     for (size_t i = 0; i < SK_ARRAY_COUNT(states); i++) {
651         states[i].fSeed   = seed + (int) i;
652         states[i].fBitmap = bitmap;
653     }
654     SkTaskGroup tg;
655     tg.batch(test_fuzz, states, SK_ARRAY_COUNT(states));
656 }
657
658 class PathFuzzView : public SampleView {
659 public:
660     PathFuzzView()
661         : fOneDraw(false)
662     {
663     }
664 protected:
665     // overrides from SkEventSink
666     virtual bool onQuery(SkEvent* evt) {
667         if (SampleCode::TitleQ(*evt)) {
668             SampleCode::TitleR(evt, "PathFuzzer");
669             return true;
670         }
671         return this->INHERITED::onQuery(evt);
672     }
673
674     void onOnceBeforeDraw() override {
675         fIndex = 0;
676         SkImageInfo info(SkImageInfo::MakeN32Premul(SkScalarRoundToInt(width()), 
677                 SkScalarRoundToInt(height())));
678         offscreen.allocPixels(info);
679         path_fuzz_stroker(&offscreen, fIndex);
680     }
681
682     virtual void onDrawContent(SkCanvas* canvas) {
683         if (fOneDraw) {
684             fuzzPath.randomize();
685             const SkPath& path = fuzzPath.getPath();
686             const SkPaint& paint = fuzzPath.getPaint();
687             const SkPath& clip = fuzzPath.getClip();
688             const SkMatrix& matrix = fuzzPath.getMatrix();
689             if (!contains_only_moveTo(clip)) {
690                 canvas->clipPath(clip);
691             }
692             canvas->setMatrix(matrix);
693             canvas->drawPath(path, paint);
694         } else {
695             path_fuzz_stroker(&offscreen, fIndex += 100);
696             canvas->drawBitmap(offscreen, 0, 0);
697         }
698         this->inval(NULL);
699     }
700
701 private:
702     int fIndex;
703     SkBitmap offscreen;
704     FuzzPath fuzzPath;
705     bool fOneDraw;
706     typedef SkView INHERITED;
707 };
708
709 static SkView* MyFactory() { return new PathFuzzView; }
710 static SkViewRegister reg(MyFactory);
711
712