2015-02-25 17:04:04 +00:00
|
|
|
/*
|
|
|
|
* 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:
|
2015-03-04 20:32:22 +00:00
|
|
|
FuzzPath()
|
2015-02-25 17:04:04 +00:00
|
|
|
: fFloatMin(0)
|
|
|
|
, fFloatMax(800)
|
|
|
|
, fAddCount(0)
|
|
|
|
, fPrintName(false)
|
2015-03-04 20:32:22 +00:00
|
|
|
, fStrokeOnly(false)
|
2015-02-25 17:04:04 +00:00
|
|
|
, 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;
|
|
|
|
}
|
|
|
|
|
2015-03-04 20:32:22 +00:00
|
|
|
void setSeed(int seed) {
|
|
|
|
fRand.setSeed(seed);
|
|
|
|
}
|
|
|
|
|
|
|
|
void setStrokeOnly() {
|
|
|
|
fStrokeOnly = true;
|
|
|
|
}
|
|
|
|
|
2015-02-25 17:04:04 +00:00
|
|
|
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);
|
2015-03-04 20:32:22 +00:00
|
|
|
SkPaint::Style style = fStrokeOnly ? SkPaint::kStroke_Style :
|
|
|
|
(SkPaint::Style) fRand.nextRangeU(SkPaint::kFill_Style, SkPaint::kStrokeAndFill_Style);
|
2015-02-25 17:04:04 +00:00
|
|
|
paint.setStyle(style);
|
|
|
|
SkColor color = (SkColor) fRand.nextU();
|
|
|
|
paint.setColor(color);
|
2015-03-04 20:32:22 +00:00
|
|
|
SkScalar width = fRand.nextRangeF(0, 10);
|
2015-02-25 17:04:04 +00:00
|
|
|
paint.setStrokeWidth(width);
|
2015-03-04 20:32:22 +00:00
|
|
|
SkScalar miter = makeScalar();
|
2015-02-25 17:04:04 +00:00
|
|
|
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);
|
2015-03-04 20:32:22 +00:00
|
|
|
}
|
2015-02-25 17:04:04 +00:00
|
|
|
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;
|
2015-03-04 20:32:22 +00:00
|
|
|
bool fStrokeOnly;
|
2015-02-25 17:04:04 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-03-04 20:32:22 +00:00
|
|
|
#include "SkGraphics.h"
|
|
|
|
#include "SkSurface.h"
|
|
|
|
#include "SkTaskGroup.h"
|
|
|
|
#include "SkTDArray.h"
|
|
|
|
|
|
|
|
struct ThreadState {
|
|
|
|
int fSeed;
|
|
|
|
const SkBitmap* fBitmap;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void test_fuzz(ThreadState* data) {
|
|
|
|
FuzzPath fuzzPath;
|
|
|
|
fuzzPath.setStrokeOnly();
|
|
|
|
fuzzPath.setSeed(data->fSeed);
|
|
|
|
fuzzPath.randomize();
|
|
|
|
const SkPath& path = fuzzPath.getPath();
|
|
|
|
const SkPaint& paint = fuzzPath.getPaint();
|
|
|
|
const SkImageInfo& info = data->fBitmap->info();
|
|
|
|
SkCanvas* canvas(SkCanvas::NewRasterDirect(info, data->fBitmap->getPixels(),
|
|
|
|
data->fBitmap->rowBytes()));
|
|
|
|
int w = info.width() / 4;
|
|
|
|
int h = info.height() / 4;
|
|
|
|
int x = data->fSeed / 4 % 4;
|
|
|
|
int y = data->fSeed % 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();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void path_fuzz_stroker(SkBitmap* bitmap, int seed) {
|
|
|
|
ThreadState states[100];
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(states); i++) {
|
|
|
|
states[i].fSeed = seed + (int) i;
|
|
|
|
states[i].fBitmap = bitmap;
|
|
|
|
}
|
|
|
|
SkTaskGroup tg;
|
|
|
|
tg.batch(test_fuzz, states, SK_ARRAY_COUNT(states));
|
|
|
|
}
|
|
|
|
|
2015-02-25 17:04:04 +00:00
|
|
|
class PathFuzzView : public SampleView {
|
|
|
|
public:
|
2015-03-04 20:32:22 +00:00
|
|
|
PathFuzzView()
|
|
|
|
: fOneDraw(false)
|
|
|
|
{
|
2015-02-25 17:04:04 +00:00
|
|
|
}
|
|
|
|
protected:
|
|
|
|
// overrides from SkEventSink
|
|
|
|
virtual bool onQuery(SkEvent* evt) {
|
|
|
|
if (SampleCode::TitleQ(*evt)) {
|
|
|
|
SampleCode::TitleR(evt, "PathFuzzer");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return this->INHERITED::onQuery(evt);
|
|
|
|
}
|
|
|
|
|
2015-03-26 01:17:31 +00:00
|
|
|
void onOnceBeforeDraw() override {
|
2015-03-04 20:32:22 +00:00
|
|
|
fIndex = 0;
|
|
|
|
SkImageInfo info(SkImageInfo::MakeN32Premul(SkScalarRoundToInt(width()),
|
|
|
|
SkScalarRoundToInt(height())));
|
|
|
|
offscreen.allocPixels(info);
|
|
|
|
path_fuzz_stroker(&offscreen, fIndex);
|
|
|
|
}
|
|
|
|
|
2015-02-25 17:04:04 +00:00
|
|
|
virtual void onDrawContent(SkCanvas* canvas) {
|
2015-03-04 20:32:22 +00:00
|
|
|
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);
|
2015-02-25 17:04:04 +00:00
|
|
|
}
|
|
|
|
this->inval(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2015-03-04 20:32:22 +00:00
|
|
|
int fIndex;
|
|
|
|
SkBitmap offscreen;
|
2015-02-25 17:04:04 +00:00
|
|
|
FuzzPath fuzzPath;
|
2015-03-04 20:32:22 +00:00
|
|
|
bool fOneDraw;
|
2015-02-25 17:04:04 +00:00
|
|
|
typedef SkView INHERITED;
|
|
|
|
};
|
|
|
|
|
|
|
|
static SkView* MyFactory() { return new PathFuzzView; }
|
|
|
|
static SkViewRegister reg(MyFactory);
|
2015-03-04 20:32:22 +00:00
|
|
|
|
|
|
|
|