Index: samplecode/SamplePathFuzz.cpp |
diff --git a/samplecode/SamplePathFuzz.cpp b/samplecode/SamplePathFuzz.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d6b11c5971012ad51158ab21e6c896a2ba4677e1 |
--- /dev/null |
+++ b/samplecode/SamplePathFuzz.cpp |
@@ -0,0 +1,651 @@ |
+/* |
+ * 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" |
+ |
+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) |
+ , 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; |
+ } |
+ |
+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 = (SkPaint::Style) fRand.nextRangeU(SkPaint::kFill_Style, |
+ SkPaint::kStrokeAndFill_Style); |
+ paint.setStyle(style); |
+ SkColor color = (SkColor) fRand.nextU(); |
+ paint.setColor(color); |
+ SkScalar width = fRand.nextF(); |
+ paint.setStrokeWidth(width); |
+ SkScalar miter = fRand.nextF(); |
+ 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 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; |
+} |
+ |
+class PathFuzzView : public SampleView { |
+public: |
+ PathFuzzView() { |
+ fDots = 0; |
+ } |
+protected: |
+ // overrides from SkEventSink |
+ virtual bool onQuery(SkEvent* evt) { |
+ if (SampleCode::TitleQ(*evt)) { |
+ SampleCode::TitleR(evt, "PathFuzzer"); |
+ return true; |
+ } |
+ return this->INHERITED::onQuery(evt); |
+ } |
+ |
+ virtual void onDrawContent(SkCanvas* canvas) { |
+ 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); |
+ this->inval(NULL); |
+ if (++fDots == 8000) { |
+ SkDebugf("\n"); |
+ fDots = 0; |
+ } |
+ if ((fDots % 100) == 99) { |
+ SkDebugf("."); |
+ } |
+ } |
+ |
+private: |
+ FuzzPath fuzzPath; |
+ int fDots; |
+ typedef SkView INHERITED; |
+}; |
+ |
+////////////////////////////////////////////////////////////////////////////// |
+ |
+static SkView* MyFactory() { return new PathFuzzView; } |
+static SkViewRegister reg(MyFactory); |