|  | /* | 
|  | * Copyright 2015 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "SampleCode.h" | 
|  | #include "SkView.h" | 
|  | #include "SkCanvas.h" | 
|  | #include "SkPaint.h" | 
|  | #include "SkPath.h" | 
|  | #include "SkMatrix.h" | 
|  | #include "SkColor.h" | 
|  | #include "SkTDArray.h" | 
|  | #include "SkRandom.h" | 
|  | #include "SkRRect.h" | 
|  |  | 
|  | enum RandomAddPath { | 
|  | kMoveToPath, | 
|  | kRMoveToPath, | 
|  | kLineToPath, | 
|  | kRLineToPath, | 
|  | kQuadToPath, | 
|  | kRQuadToPath, | 
|  | kConicToPath, | 
|  | kRConicToPath, | 
|  | kCubicToPath, | 
|  | kRCubicToPath, | 
|  | kArcToPath, | 
|  | kArcTo2Path, | 
|  | kClosePath, | 
|  | kAddArc, | 
|  | kAddRoundRect1, | 
|  | kAddRoundRect2, | 
|  | kAddRRect, | 
|  | kAddPoly, | 
|  | kAddPath1, | 
|  | kAddPath2, | 
|  | kAddPath3, | 
|  | kReverseAddPath, | 
|  | }; | 
|  |  | 
|  | const int kRandomAddPath_Last = kReverseAddPath; | 
|  |  | 
|  | const char* gRandomAddPathNames[] = { | 
|  | "kMoveToPath", | 
|  | "kRMoveToPath", | 
|  | "kLineToPath", | 
|  | "kRLineToPath", | 
|  | "kQuadToPath", | 
|  | "kRQuadToPath", | 
|  | "kConicToPath", | 
|  | "kRConicToPath", | 
|  | "kCubicToPath", | 
|  | "kRCubicToPath", | 
|  | "kArcToPath", | 
|  | "kArcTo2Path", | 
|  | "kClosePath", | 
|  | "kAddArc", | 
|  | "kAddRoundRect1", | 
|  | "kAddRoundRect2", | 
|  | "kAddRRect", | 
|  | "kAddPoly", | 
|  | "kAddPath1", | 
|  | "kAddPath2", | 
|  | "kAddPath3", | 
|  | "kReverseAddPath", | 
|  | }; | 
|  |  | 
|  | enum RandomSetRRect { | 
|  | kSetEmpty, | 
|  | kSetRect, | 
|  | kSetOval, | 
|  | kSetRectXY, | 
|  | kSetNinePatch, | 
|  | kSetRectRadii, | 
|  | }; | 
|  |  | 
|  | const char* gRandomSetRRectNames[] = { | 
|  | "kSetEmpty", | 
|  | "kSetRect", | 
|  | "kSetOval", | 
|  | "kSetRectXY", | 
|  | "kSetNinePatch", | 
|  | "kSetRectRadii", | 
|  | }; | 
|  |  | 
|  | int kRandomSetRRect_Last = kSetRectRadii; | 
|  |  | 
|  | enum RandomSetMatrix { | 
|  | kSetIdentity, | 
|  | kSetTranslate, | 
|  | kSetTranslateX, | 
|  | kSetTranslateY, | 
|  | kSetScale, | 
|  | kSetScaleTranslate, | 
|  | kSetScaleX, | 
|  | kSetScaleY, | 
|  | kSetSkew, | 
|  | kSetSkewTranslate, | 
|  | kSetSkewX, | 
|  | kSetSkewY, | 
|  | kSetRotate, | 
|  | kSetRotateTranslate, | 
|  | kSetPerspectiveX, | 
|  | kSetPerspectiveY, | 
|  | kSetAll, | 
|  | }; | 
|  |  | 
|  | int kRandomSetMatrix_Last = kSetAll; | 
|  |  | 
|  | const char* gRandomSetMatrixNames[] = { | 
|  | "kSetIdentity", | 
|  | "kSetTranslate", | 
|  | "kSetTranslateX", | 
|  | "kSetTranslateY", | 
|  | "kSetScale", | 
|  | "kSetScaleTranslate", | 
|  | "kSetScaleX", | 
|  | "kSetScaleY", | 
|  | "kSetSkew", | 
|  | "kSetSkewTranslate", | 
|  | "kSetSkewX", | 
|  | "kSetSkewY", | 
|  | "kSetRotate", | 
|  | "kSetRotateTranslate", | 
|  | "kSetPerspectiveX", | 
|  | "kSetPerspectiveY", | 
|  | "kSetAll", | 
|  | }; | 
|  |  | 
|  | class FuzzPath { | 
|  | public: | 
|  | FuzzPath() | 
|  | : fFloatMin(0) | 
|  | , fFloatMax(800) | 
|  | , fAddCount(0) | 
|  | , fPrintName(false) | 
|  | , fStrokeOnly(false) | 
|  | , fValidate(false) | 
|  | { | 
|  | fTab = "                                                                                  "; | 
|  | } | 
|  | void randomize() { | 
|  | fPathDepth = 0; | 
|  | fPathDepthLimit = fRand.nextRangeU(1, 2); | 
|  | fPathContourCount = fRand.nextRangeU(1, 4); | 
|  | fPathSegmentLimit = fRand.nextRangeU(1, 8); | 
|  | fClip = makePath(); | 
|  | SkASSERT(!fPathDepth); | 
|  | fMatrix = makeMatrix(); | 
|  | fPaint = makePaint(); | 
|  | fPathDepthLimit = fRand.nextRangeU(1, 3); | 
|  | fPathContourCount = fRand.nextRangeU(1, 6); | 
|  | fPathSegmentLimit = fRand.nextRangeU(1, 16); | 
|  | fPath = makePath(); | 
|  | SkASSERT(!fPathDepth); | 
|  | } | 
|  |  | 
|  | const SkPath& getClip() const { | 
|  | return fClip; | 
|  | } | 
|  |  | 
|  | const SkMatrix& getMatrix() const { | 
|  | return fMatrix; | 
|  | } | 
|  |  | 
|  | const SkPaint& getPaint() const { | 
|  | return fPaint; | 
|  | } | 
|  |  | 
|  | const SkPath& getPath() const { | 
|  | return fPath; | 
|  | } | 
|  |  | 
|  | void setSeed(int seed) { | 
|  | fRand.setSeed(seed); | 
|  | } | 
|  |  | 
|  | void setStrokeOnly() { | 
|  | fStrokeOnly = true; | 
|  | } | 
|  |  | 
|  | private: | 
|  |  | 
|  | SkPath::AddPathMode makeAddPathMode() { | 
|  | return (SkPath::AddPathMode) fRand.nextRangeU(SkPath::kAppend_AddPathMode, | 
|  | SkPath::kExtend_AddPathMode); | 
|  | } | 
|  |  | 
|  | RandomAddPath makeAddPathType() { | 
|  | return (RandomAddPath) fRand.nextRangeU(0, kRandomAddPath_Last); | 
|  | } | 
|  |  | 
|  | SkScalar makeAngle() { | 
|  | SkScalar angle; | 
|  | angle = fRand.nextF(); | 
|  | return angle; | 
|  | } | 
|  |  | 
|  | bool makeBool() { | 
|  | return fRand.nextBool(); | 
|  | } | 
|  |  | 
|  | SkPath::Direction makeDirection() { | 
|  | return (SkPath::Direction) fRand.nextRangeU(SkPath::kCW_Direction, SkPath::kCCW_Direction); | 
|  | } | 
|  |  | 
|  | SkMatrix makeMatrix() { | 
|  | SkMatrix matrix; | 
|  | matrix.reset(); | 
|  | RandomSetMatrix setMatrix = (RandomSetMatrix) fRand.nextRangeU(0, kRandomSetMatrix_Last); | 
|  | if (fPrintName) { | 
|  | SkDebugf("%.*s%s\n", fPathDepth * 3, fTab, gRandomSetMatrixNames[setMatrix]); | 
|  | } | 
|  | switch (setMatrix) { | 
|  | case kSetIdentity: | 
|  | break; | 
|  | case kSetTranslateX: | 
|  | matrix.setTranslateX(makeScalar()); | 
|  | break; | 
|  | case kSetTranslateY: | 
|  | matrix.setTranslateY(makeScalar()); | 
|  | break; | 
|  | case kSetTranslate: | 
|  | matrix.setTranslate(makeScalar(), makeScalar()); | 
|  | break; | 
|  | case kSetScaleX: | 
|  | matrix.setScaleX(makeScalar()); | 
|  | break; | 
|  | case kSetScaleY: | 
|  | matrix.setScaleY(makeScalar()); | 
|  | break; | 
|  | case kSetScale: | 
|  | matrix.setScale(makeScalar(), makeScalar()); | 
|  | break; | 
|  | case kSetScaleTranslate: | 
|  | matrix.setScale(makeScalar(), makeScalar(), makeScalar(), makeScalar()); | 
|  | break; | 
|  | case kSetSkewX: | 
|  | matrix.setSkewX(makeScalar()); | 
|  | break; | 
|  | case kSetSkewY: | 
|  | matrix.setSkewY(makeScalar()); | 
|  | break; | 
|  | case kSetSkew: | 
|  | matrix.setSkew(makeScalar(), makeScalar()); | 
|  | break; | 
|  | case kSetSkewTranslate: | 
|  | matrix.setSkew(makeScalar(), makeScalar(), makeScalar(), makeScalar()); | 
|  | break; | 
|  | case kSetRotate: | 
|  | matrix.setRotate(makeScalar()); | 
|  | break; | 
|  | case kSetRotateTranslate: | 
|  | matrix.setRotate(makeScalar(), makeScalar(), makeScalar()); | 
|  | break; | 
|  | case kSetPerspectiveX: | 
|  | matrix.setPerspX(makeScalar()); | 
|  | break; | 
|  | case kSetPerspectiveY: | 
|  | matrix.setPerspY(makeScalar()); | 
|  | break; | 
|  | case kSetAll: | 
|  | matrix.setAll(makeScalar(), makeScalar(), makeScalar(), | 
|  | makeScalar(), makeScalar(), makeScalar(), | 
|  | makeScalar(), makeScalar(), makeScalar()); | 
|  | break; | 
|  | } | 
|  | return matrix; | 
|  | } | 
|  |  | 
|  | SkPaint makePaint() { | 
|  | SkPaint paint; | 
|  | bool antiAlias = fRand.nextBool(); | 
|  | paint.setAntiAlias(antiAlias); | 
|  | SkPaint::Style style = fStrokeOnly ? SkPaint::kStroke_Style : | 
|  | (SkPaint::Style) fRand.nextRangeU(SkPaint::kFill_Style, SkPaint::kStrokeAndFill_Style); | 
|  | paint.setStyle(style); | 
|  | SkColor color = (SkColor) fRand.nextU(); | 
|  | paint.setColor(color); | 
|  | SkScalar width = fRand.nextRangeF(0, 10); | 
|  | paint.setStrokeWidth(width); | 
|  | SkScalar miter = makeScalar(); | 
|  | paint.setStrokeMiter(miter); | 
|  | SkPaint::Cap cap = (SkPaint::Cap) fRand.nextRangeU(SkPaint::kButt_Cap, SkPaint::kSquare_Cap); | 
|  | paint.setStrokeCap(cap); | 
|  | SkPaint::Join join = (SkPaint::Join) fRand.nextRangeU(SkPaint::kMiter_Join, | 
|  | SkPaint::kBevel_Join); | 
|  | paint.setStrokeJoin(join); | 
|  | return paint; | 
|  | } | 
|  |  | 
|  | SkPoint makePoint() { | 
|  | SkPoint result; | 
|  | makeScalarArray(2, &result.fX); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void makePointArray(size_t arrayCount, SkPoint* points) { | 
|  | for (size_t index = 0; index < arrayCount; ++index) { | 
|  | points[index] = makePoint(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void makePointArray(SkTDArray<SkPoint>* points) { | 
|  | size_t arrayCount = fRand.nextRangeU(1, 10); | 
|  | for (size_t index = 0; index < arrayCount; ++index) { | 
|  | *points->append() = makePoint(); | 
|  | } | 
|  | } | 
|  |  | 
|  | SkRect makeRect() { | 
|  | SkRect result; | 
|  | makeScalarArray(4, &result.fLeft); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | SkRRect makeRRect() { | 
|  | SkRRect rrect; | 
|  | RandomSetRRect rrectType = makeSetRRectType(); | 
|  | if (fPrintName) { | 
|  | SkDebugf("%.*s%s\n", fPathDepth * 3, fTab, gRandomSetRRectNames[rrectType]); | 
|  | } | 
|  | switch (rrectType) { | 
|  | case kSetEmpty: | 
|  | rrect.setEmpty(); | 
|  | break; | 
|  | case kSetRect: { | 
|  | SkRect rect = makeRect(); | 
|  | rrect.setRect(rect); | 
|  | } break; | 
|  | case kSetOval: { | 
|  | SkRect oval = makeRect(); | 
|  | rrect.setOval(oval); | 
|  | } break; | 
|  | case kSetRectXY: { | 
|  | SkRect rect = makeRect(); | 
|  | SkScalar xRad = makeScalar(); | 
|  | SkScalar yRad = makeScalar(); | 
|  | rrect.setRectXY(rect, xRad, yRad); | 
|  | } break; | 
|  | case kSetNinePatch: { | 
|  | SkRect rect = makeRect(); | 
|  | SkScalar leftRad = makeScalar(); | 
|  | SkScalar topRad = makeScalar(); | 
|  | SkScalar rightRad = makeScalar(); | 
|  | SkScalar bottomRad = makeScalar(); | 
|  | rrect.setNinePatch(rect, leftRad, topRad, rightRad, bottomRad); | 
|  | SkDebugf("");  // keep locals in scope | 
|  | } break; | 
|  | case kSetRectRadii: { | 
|  | SkRect rect = makeRect(); | 
|  | SkVector radii[4]; | 
|  | makeVectorArray(SK_ARRAY_COUNT(radii), radii); | 
|  | rrect.setRectRadii(rect, radii); | 
|  | } break; | 
|  | } | 
|  | return rrect; | 
|  | } | 
|  |  | 
|  | SkPath makePath() { | 
|  | SkPath path; | 
|  | for (uint32_t cIndex = 0; cIndex < fPathContourCount; ++cIndex) { | 
|  | uint32_t segments = makeSegmentCount(); | 
|  | for (uint32_t sIndex = 0; sIndex < segments; ++sIndex) { | 
|  | RandomAddPath addPathType = makeAddPathType(); | 
|  | ++fAddCount; | 
|  | if (fPrintName) { | 
|  | SkDebugf("%.*s%s\n", fPathDepth * 3, fTab, | 
|  | gRandomAddPathNames[addPathType]); | 
|  | } | 
|  | switch (addPathType) { | 
|  | case kAddArc: { | 
|  | SkRect oval = makeRect(); | 
|  | SkScalar startAngle = makeAngle(); | 
|  | SkScalar sweepAngle = makeAngle(); | 
|  | path.addArc(oval, startAngle, sweepAngle); | 
|  | validate(path); | 
|  | } break; | 
|  | case kAddRoundRect1: { | 
|  | SkRect rect = makeRect(); | 
|  | SkScalar rx = makeScalar(), ry = makeScalar(); | 
|  | SkPath::Direction dir = makeDirection(); | 
|  | path.addRoundRect(rect, rx, ry, dir); | 
|  | validate(path); | 
|  | } break; | 
|  | case kAddRoundRect2: { | 
|  | SkRect rect = makeRect(); | 
|  | SkScalar radii[8]; | 
|  | makeScalarArray(SK_ARRAY_COUNT(radii), radii); | 
|  | SkPath::Direction dir = makeDirection(); | 
|  | path.addRoundRect(rect, radii, dir); | 
|  | validate(path); | 
|  | } break; | 
|  | case kAddRRect: { | 
|  | SkRRect rrect = makeRRect(); | 
|  | SkPath::Direction dir = makeDirection(); | 
|  | path.addRRect(rrect, dir); | 
|  | validate(path); | 
|  | } break; | 
|  | case kAddPoly: { | 
|  | SkTDArray<SkPoint> points; | 
|  | makePointArray(&points); | 
|  | bool close = makeBool(); | 
|  | path.addPoly(&points[0], points.count(), close); | 
|  | validate(path); | 
|  | } break; | 
|  | case kAddPath1: | 
|  | if (fPathDepth < fPathDepthLimit) { | 
|  | ++fPathDepth; | 
|  | SkPath src = makePath(); | 
|  | validate(src); | 
|  | SkScalar dx = makeScalar(); | 
|  | SkScalar dy = makeScalar(); | 
|  | SkPath::AddPathMode mode = makeAddPathMode(); | 
|  | path.addPath(src, dx, dy, mode); | 
|  | --fPathDepth; | 
|  | validate(path); | 
|  | } | 
|  | break; | 
|  | case kAddPath2: | 
|  | if (fPathDepth < fPathDepthLimit) { | 
|  | ++fPathDepth; | 
|  | SkPath src = makePath(); | 
|  | validate(src); | 
|  | SkPath::AddPathMode mode = makeAddPathMode(); | 
|  | path.addPath(src, mode); | 
|  | --fPathDepth; | 
|  | validate(path); | 
|  | } | 
|  | break; | 
|  | case kAddPath3: | 
|  | if (fPathDepth < fPathDepthLimit) { | 
|  | ++fPathDepth; | 
|  | SkPath src = makePath(); | 
|  | validate(src); | 
|  | SkMatrix matrix = makeMatrix(); | 
|  | SkPath::AddPathMode mode = makeAddPathMode(); | 
|  | path.addPath(src, matrix, mode); | 
|  | --fPathDepth; | 
|  | validate(path); | 
|  | } | 
|  | break; | 
|  | case kReverseAddPath: | 
|  | if (fPathDepth < fPathDepthLimit) { | 
|  | ++fPathDepth; | 
|  | SkPath src = makePath(); | 
|  | validate(src); | 
|  | path.reverseAddPath(src); | 
|  | --fPathDepth; | 
|  | validate(path); | 
|  | } | 
|  | break; | 
|  | case kMoveToPath: { | 
|  | SkScalar x = makeScalar(); | 
|  | SkScalar y = makeScalar(); | 
|  | path.moveTo(x, y); | 
|  | validate(path); | 
|  | } break; | 
|  | case kRMoveToPath: { | 
|  | SkScalar x = makeScalar(); | 
|  | SkScalar y = makeScalar(); | 
|  | path.rMoveTo(x, y); | 
|  | validate(path); | 
|  | } break; | 
|  | case kLineToPath: { | 
|  | SkScalar x = makeScalar(); | 
|  | SkScalar y = makeScalar(); | 
|  | path.lineTo(x, y); | 
|  | validate(path); | 
|  | } break; | 
|  | case kRLineToPath: { | 
|  | SkScalar x = makeScalar(); | 
|  | SkScalar y = makeScalar(); | 
|  | path.rLineTo(x, y); | 
|  | validate(path); | 
|  | } break; | 
|  | case kQuadToPath: { | 
|  | SkPoint pt[2]; | 
|  | makePointArray(SK_ARRAY_COUNT(pt), pt); | 
|  | path.quadTo(pt[0], pt[1]); | 
|  | validate(path); | 
|  | } break; | 
|  | case kRQuadToPath: { | 
|  | SkPoint pt[2]; | 
|  | makePointArray(SK_ARRAY_COUNT(pt), pt); | 
|  | path.rQuadTo(pt[0].fX, pt[0].fY, pt[1].fX, pt[1].fY); | 
|  | validate(path); | 
|  | } break; | 
|  | case kConicToPath: { | 
|  | SkPoint pt[2]; | 
|  | makePointArray(SK_ARRAY_COUNT(pt), pt); | 
|  | SkScalar weight = makeScalar(); | 
|  | path.conicTo(pt[0], pt[1], weight); | 
|  | validate(path); | 
|  | } break; | 
|  | case kRConicToPath: { | 
|  | SkPoint pt[2]; | 
|  | makePointArray(SK_ARRAY_COUNT(pt), pt); | 
|  | SkScalar weight = makeScalar(); | 
|  | path.rConicTo(pt[0].fX, pt[0].fY, pt[1].fX, pt[1].fY, weight); | 
|  | validate(path); | 
|  | } break; | 
|  | case kCubicToPath: { | 
|  | SkPoint pt[3]; | 
|  | makePointArray(SK_ARRAY_COUNT(pt), pt); | 
|  | path.cubicTo(pt[0], pt[1], pt[2]); | 
|  | validate(path); | 
|  | } break; | 
|  | case kRCubicToPath: { | 
|  | SkPoint pt[3]; | 
|  | makePointArray(SK_ARRAY_COUNT(pt), pt); | 
|  | path.rCubicTo(pt[0].fX, pt[0].fY, pt[1].fX, pt[1].fY, pt[2].fX, pt[2].fY); | 
|  | validate(path); | 
|  | } break; | 
|  | case kArcToPath: { | 
|  | SkPoint pt[2]; | 
|  | makePointArray(SK_ARRAY_COUNT(pt), pt); | 
|  | SkScalar radius = makeScalar(); | 
|  | path.arcTo(pt[0], pt[1], radius); | 
|  | validate(path); | 
|  | } break; | 
|  | case kArcTo2Path: { | 
|  | SkRect oval = makeRect(); | 
|  | SkScalar startAngle = makeAngle(); | 
|  | SkScalar sweepAngle = makeAngle(); | 
|  | bool forceMoveTo = makeBool(); | 
|  | path.arcTo(oval, startAngle, sweepAngle, forceMoveTo); | 
|  | validate(path); | 
|  | } break; | 
|  | case kClosePath: | 
|  | path.close(); | 
|  | validate(path); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | return path; | 
|  | } | 
|  |  | 
|  | uint32_t makeSegmentCount() { | 
|  | return fRand.nextRangeU(1, fPathSegmentLimit); | 
|  | } | 
|  |  | 
|  | RandomSetRRect makeSetRRectType() { | 
|  | return (RandomSetRRect) fRand.nextRangeU(0, kRandomSetRRect_Last); | 
|  | } | 
|  |  | 
|  | SkScalar makeScalar() { | 
|  | SkScalar scalar; | 
|  | scalar = fRand.nextRangeF(fFloatMin, fFloatMax); | 
|  | return scalar; | 
|  | } | 
|  |  | 
|  | void makeScalarArray(size_t arrayCount, SkScalar* array) { | 
|  | for (size_t index = 0; index < arrayCount; ++index) { | 
|  | array[index] = makeScalar(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void makeVectorArray(size_t arrayCount, SkVector* array) { | 
|  | for (size_t index = 0; index < arrayCount; ++index) { | 
|  | array[index] = makeVector(); | 
|  | } | 
|  | } | 
|  |  | 
|  | SkVector makeVector() { | 
|  | SkVector result; | 
|  | makeScalarArray(2, &result.fX); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void validate(const SkPath& path) { | 
|  | if (fValidate) { | 
|  | SkDEBUGCODE(path.experimentalValidateRef()); | 
|  | } | 
|  | } | 
|  |  | 
|  | SkRandom fRand; | 
|  | SkMatrix fMatrix; | 
|  | SkPath fClip; | 
|  | SkPaint fPaint; | 
|  | SkPath fPath; | 
|  | SkScalar fFloatMin; | 
|  | SkScalar fFloatMax; | 
|  | uint32_t fPathContourCount; | 
|  | int fPathDepth; | 
|  | int fPathDepthLimit; | 
|  | uint32_t fPathSegmentLimit; | 
|  | int fAddCount; | 
|  | bool fPrintName; | 
|  | bool fStrokeOnly; | 
|  | bool fValidate; | 
|  | const char* fTab; | 
|  | }; | 
|  |  | 
|  | static bool contains_only_moveTo(const SkPath& path) { | 
|  | int verbCount = path.countVerbs(); | 
|  | if (verbCount == 0) { | 
|  | return true; | 
|  | } | 
|  | SkTDArray<uint8_t> verbs; | 
|  | verbs.setCount(verbCount); | 
|  | SkDEBUGCODE(int getVerbResult = ) path.getVerbs(verbs.begin(), verbCount); | 
|  | SkASSERT(getVerbResult == verbCount); | 
|  | for (int index = 0; index < verbCount; ++index) { | 
|  | if (verbs[index] != SkPath::kMove_Verb) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | #include "SkGraphics.h" | 
|  | #include "SkSurface.h" | 
|  | #include "SkTaskGroup.h" | 
|  | #include "SkTDArray.h" | 
|  |  | 
|  | static void path_fuzz_stroker(SkBitmap* bitmap, int seed) { | 
|  | SkTaskGroup().batch(100, [&](int i) { | 
|  | int localSeed = seed + i; | 
|  |  | 
|  | FuzzPath fuzzPath; | 
|  | fuzzPath.setStrokeOnly(); | 
|  | fuzzPath.setSeed(localSeed); | 
|  | fuzzPath.randomize(); | 
|  | const SkPath& path = fuzzPath.getPath(); | 
|  | const SkPaint& paint = fuzzPath.getPaint(); | 
|  | const SkImageInfo& info = bitmap->info(); | 
|  | SkCanvas* canvas( | 
|  | SkCanvas::NewRasterDirect(info, bitmap->getPixels(), bitmap->rowBytes())); | 
|  | int w = info.width() / 4; | 
|  | int h = info.height() / 4; | 
|  | int x = localSeed / 4 % 4; | 
|  | int y = localSeed % 4; | 
|  | SkRect clipBounds = SkRect::MakeXYWH(SkIntToScalar(x) * w, SkIntToScalar(y) * h, | 
|  | SkIntToScalar(w), SkIntToScalar(h)); | 
|  | canvas->save(); | 
|  | canvas->clipRect(clipBounds); | 
|  | canvas->translate(SkIntToScalar(x) * w, SkIntToScalar(y) * h); | 
|  | canvas->drawPath(path, paint); | 
|  | canvas->restore(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | class PathFuzzView : public SampleView { | 
|  | public: | 
|  | PathFuzzView() | 
|  | : fOneDraw(false) | 
|  | { | 
|  | } | 
|  | protected: | 
|  | // overrides from SkEventSink | 
|  | bool onQuery(SkEvent* evt) override { | 
|  | if (SampleCode::TitleQ(*evt)) { | 
|  | SampleCode::TitleR(evt, "PathFuzzer"); | 
|  | return true; | 
|  | } | 
|  | return this->INHERITED::onQuery(evt); | 
|  | } | 
|  |  | 
|  | void onOnceBeforeDraw() override { | 
|  | fIndex = 0; | 
|  | SkImageInfo info(SkImageInfo::MakeN32Premul(SkScalarRoundToInt(width()), | 
|  | SkScalarRoundToInt(height()))); | 
|  | offscreen.allocPixels(info); | 
|  | path_fuzz_stroker(&offscreen, fIndex); | 
|  | } | 
|  |  | 
|  | void onDrawContent(SkCanvas* canvas) override { | 
|  | if (fOneDraw) { | 
|  | fuzzPath.randomize(); | 
|  | const SkPath& path = fuzzPath.getPath(); | 
|  | const SkPaint& paint = fuzzPath.getPaint(); | 
|  | const SkPath& clip = fuzzPath.getClip(); | 
|  | const SkMatrix& matrix = fuzzPath.getMatrix(); | 
|  | if (!contains_only_moveTo(clip)) { | 
|  | canvas->clipPath(clip); | 
|  | } | 
|  | canvas->setMatrix(matrix); | 
|  | canvas->drawPath(path, paint); | 
|  | } else { | 
|  | path_fuzz_stroker(&offscreen, fIndex += 100); | 
|  | canvas->drawBitmap(offscreen, 0, 0); | 
|  | } | 
|  | this->inval(nullptr); | 
|  | } | 
|  |  | 
|  | private: | 
|  | int fIndex; | 
|  | SkBitmap offscreen; | 
|  | FuzzPath fuzzPath; | 
|  | bool fOneDraw; | 
|  | typedef SkView INHERITED; | 
|  | }; | 
|  |  | 
|  | static SkView* MyFactory() { return new PathFuzzView; } | 
|  | static SkViewRegister reg(MyFactory); |