| /* | |
| * 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) { | |
| sk_parallel_for(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); | |