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
19enum 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
44const int kRandomAddPath_Last = kReverseAddPath;
45
46const 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
71enum RandomSetRRect {
72    kSetEmpty,
73    kSetRect,
74    kSetOval,
75    kSetRectXY,
76    kSetNinePatch,
77    kSetRectRadii,
78};
79
80const char* gRandomSetRRectNames[] = {
81    "kSetEmpty",
82    "kSetRect",
83    "kSetOval",
84    "kSetRectXY",
85    "kSetNinePatch",
86    "kSetRectRadii",
87};
88
89int kRandomSetRRect_Last = kSetRectRadii;
90
91enum 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
111int kRandomSetMatrix_Last = kSetAll;
112
113const 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
133class FuzzPath {
134public:
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
185private:
186
187SkPath::AddPathMode makeAddPathMode() {
188    return (SkPath::AddPathMode) fRand.nextRangeU(SkPath::kAppend_AddPathMode,
189        SkPath::kExtend_AddPathMode);
190}
191
192RandomAddPath makeAddPathType() {
193    return (RandomAddPath) fRand.nextRangeU(0, kRandomAddPath_Last);
194}
195
196SkScalar makeAngle() {
197    SkScalar angle;
198    angle = fRand.nextF();
199    return angle;
200}
201
202bool makeBool() {
203    return fRand.nextBool();
204}
205
206SkPath::Direction makeDirection() {
207    return (SkPath::Direction) fRand.nextRangeU(SkPath::kCW_Direction, SkPath::kCCW_Direction);
208}
209
210SkMatrix 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
274SkPaint 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
295SkPoint makePoint() {
296    SkPoint result;
297    makeScalarArray(2, &result.fX);
298    return result;
299}
300
301void makePointArray(size_t arrayCount, SkPoint* points) {
302    for (size_t index = 0; index < arrayCount; ++index) {
303        points[index] = makePoint();
304    }
305}
306
307void 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
314SkRect makeRect() {
315    SkRect result;
316    makeScalarArray(4, &result.fLeft);
317    return result;
318}
319
320SkRRect 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
363SkPath 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
543uint32_t makeSegmentCount() {
544    return fRand.nextRangeU(1, fPathSegmentLimit);
545}
546
547RandomSetRRect makeSetRRectType() {
548    return (RandomSetRRect) fRand.nextRangeU(0, kRandomSetRRect_Last);
549}
550
551SkScalar makeScalar() {
552    SkScalar scalar;
553    scalar = fRand.nextRangeF(fFloatMin, fFloatMax);
554    return scalar;
555}
556
557void makeScalarArray(size_t arrayCount, SkScalar* array) {
558    for (size_t index = 0; index < arrayCount; ++index) {
559        array[index] = makeScalar();
560    }
561}
562
563void makeVectorArray(size_t arrayCount, SkVector* array) {
564    for (size_t index = 0; index < arrayCount; ++index) {
565        array[index] = makeVector();
566    }
567}
568
569SkVector makeVector() {
570    SkVector result;
571    makeScalarArray(2, &result.fX);
572    return result;
573}
574
575void validate(const SkPath& path) {
576    if (fValidate) {
577        // FIXME: this could probably assert on path.isValid() instead
578        SkDEBUGCODE(path.validateRef());
579    }
580}
581
582SkRandom fRand;
583SkMatrix fMatrix;
584SkPath fClip;
585SkPaint fPaint;
586SkPath fPath;
587SkScalar fFloatMin;
588SkScalar fFloatMax;
589uint32_t fPathContourCount;
590int fPathDepth;
591int fPathDepthLimit;
592uint32_t fPathSegmentLimit;
593int fAddCount;
594bool fPrintName;
595bool fStrokeOnly;
596bool fValidate;
597const char* fTab;
598};
599
600static bool contains_only_moveTo(const SkPath& path) {
601    int verbCount = path.countVerbs();
602    if (verbCount == 0) {
603        return true;
604    }
605    SkTDArray<uint8_t> verbs;
606    verbs.setCount(verbCount);
607    SkDEBUGCODE(int getVerbResult = ) path.getVerbs(verbs.begin(), verbCount);
608    SkASSERT(getVerbResult == verbCount);
609    for (int index = 0; index < verbCount; ++index) {
610        if (verbs[index] != SkPath::kMove_Verb) {
611            return false;
612        }
613    }
614    return true;
615}
616
617#include "SkGraphics.h"
618#include "SkSurface.h"
619#include "SkTaskGroup.h"
620#include "SkTDArray.h"
621
622static void path_fuzz_stroker(SkBitmap* bitmap, int seed) {
623    SkTaskGroup().batch(100, [&](int i) {
624        int localSeed = seed + i;
625
626        FuzzPath fuzzPath;
627        fuzzPath.setStrokeOnly();
628        fuzzPath.setSeed(localSeed);
629        fuzzPath.randomize();
630        const SkPath& path = fuzzPath.getPath();
631        const SkPaint& paint = fuzzPath.getPaint();
632        const SkImageInfo& info = bitmap->info();
633        std::unique_ptr<SkCanvas> canvas(
634            SkCanvas::MakeRasterDirect(info, bitmap->getPixels(), bitmap->rowBytes()));
635        int w = info.width() / 4;
636        int h = info.height() / 4;
637        int x = localSeed / 4 % 4;
638        int y = localSeed % 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
649class PathFuzzView : public SampleView {
650public:
651    PathFuzzView()
652        : fOneDraw(false)
653    {
654    }
655protected:
656    // overrides from SkEventSink
657    bool onQuery(SkEvent* evt) override {
658        if (SampleCode::TitleQ(*evt)) {
659            SampleCode::TitleR(evt, "PathFuzzer");
660            return true;
661        }
662        return this->INHERITED::onQuery(evt);
663    }
664
665    void onOnceBeforeDraw() override {
666        fIndex = 0;
667        SkImageInfo info(SkImageInfo::MakeN32Premul(SkScalarRoundToInt(width()),
668                SkScalarRoundToInt(height())));
669        offscreen.allocPixels(info);
670        path_fuzz_stroker(&offscreen, fIndex);
671    }
672
673    void onDrawContent(SkCanvas* canvas) override {
674        if (fOneDraw) {
675            fuzzPath.randomize();
676            const SkPath& path = fuzzPath.getPath();
677            const SkPaint& paint = fuzzPath.getPaint();
678            const SkPath& clip = fuzzPath.getClip();
679            const SkMatrix& matrix = fuzzPath.getMatrix();
680            if (!contains_only_moveTo(clip)) {
681                canvas->clipPath(clip);
682            }
683            canvas->setMatrix(matrix);
684            canvas->drawPath(path, paint);
685        } else {
686            path_fuzz_stroker(&offscreen, fIndex += 100);
687            canvas->drawBitmap(offscreen, 0, 0);
688        }
689    }
690
691private:
692    int fIndex;
693    SkBitmap offscreen;
694    FuzzPath fuzzPath;
695    bool fOneDraw;
696    typedef SkView INHERITED;
697};
698
699static SkView* MyFactory() { return new PathFuzzView; }
700static SkViewRegister reg(MyFactory);
701