Home | History | Annotate | Download | only in samplecode
      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