blob: d6b11c5971012ad51158ab21e6c896a2ba4677e1 [file] [log] [blame]
caryclark63c684a2015-02-25 09:04:04 -08001/*
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
18enum RandomAddPath {
19 kMoveToPath,
20 kRMoveToPath,
21 kLineToPath,
22 kRLineToPath,
23 kQuadToPath,
24 kRQuadToPath,
25 kConicToPath,
26 kRConicToPath,
27 kCubicToPath,
28 kRCubicToPath,
29 kArcToPath,
30 kArcTo2Path,
31 kClosePath,
32 kAddArc,
33 kAddRoundRect1,
34 kAddRoundRect2,
35 kAddRRect,
36 kAddPoly,
37 kAddPath1,
38 kAddPath2,
39 kAddPath3,
40 kReverseAddPath,
41};
42
43const int kRandomAddPath_Last = kReverseAddPath;
44
45const char* gRandomAddPathNames[] = {
46 "kMoveToPath",
47 "kRMoveToPath",
48 "kLineToPath",
49 "kRLineToPath",
50 "kQuadToPath",
51 "kRQuadToPath",
52 "kConicToPath",
53 "kRConicToPath",
54 "kCubicToPath",
55 "kRCubicToPath",
56 "kArcToPath",
57 "kArcTo2Path",
58 "kClosePath",
59 "kAddArc",
60 "kAddRoundRect1",
61 "kAddRoundRect2",
62 "kAddRRect",
63 "kAddPoly",
64 "kAddPath1",
65 "kAddPath2",
66 "kAddPath3",
67 "kReverseAddPath",
68};
69
70enum RandomSetRRect {
71 kSetEmpty,
72 kSetRect,
73 kSetOval,
74 kSetRectXY,
75 kSetNinePatch,
76 kSetRectRadii,
77};
78
79const char* gRandomSetRRectNames[] = {
80 "kSetEmpty",
81 "kSetRect",
82 "kSetOval",
83 "kSetRectXY",
84 "kSetNinePatch",
85 "kSetRectRadii",
86};
87
88int kRandomSetRRect_Last = kSetRectRadii;
89
90enum RandomSetMatrix {
91 kSetIdentity,
92 kSetTranslate,
93 kSetTranslateX,
94 kSetTranslateY,
95 kSetScale,
96 kSetScaleTranslate,
97 kSetScaleX,
98 kSetScaleY,
99 kSetSkew,
100 kSetSkewTranslate,
101 kSetSkewX,
102 kSetSkewY,
103 kSetRotate,
104 kSetRotateTranslate,
105 kSetPerspectiveX,
106 kSetPerspectiveY,
107 kSetAll,
108};
109
110int kRandomSetMatrix_Last = kSetAll;
111
112const char* gRandomSetMatrixNames[] = {
113 "kSetIdentity",
114 "kSetTranslate",
115 "kSetTranslateX",
116 "kSetTranslateY",
117 "kSetScale",
118 "kSetScaleTranslate",
119 "kSetScaleX",
120 "kSetScaleY",
121 "kSetSkew",
122 "kSetSkewTranslate",
123 "kSetSkewX",
124 "kSetSkewY",
125 "kSetRotate",
126 "kSetRotateTranslate",
127 "kSetPerspectiveX",
128 "kSetPerspectiveY",
129 "kSetAll",
130};
131
132class FuzzPath {
133public:
134 FuzzPath()
135 : fFloatMin(0)
136 , fFloatMax(800)
137 , fAddCount(0)
138 , fPrintName(false)
139 , fValidate(false)
140 {
141 fTab = " ";
142 }
143 void randomize() {
144 fPathDepth = 0;
145 fPathDepthLimit = fRand.nextRangeU(1, 2);
146 fPathContourCount = fRand.nextRangeU(1, 4);
147 fPathSegmentLimit = fRand.nextRangeU(1, 8);
148 fClip = makePath();
149 SkASSERT(!fPathDepth);
150 fMatrix = makeMatrix();
151 fPaint = makePaint();
152 fPathDepthLimit = fRand.nextRangeU(1, 3);
153 fPathContourCount = fRand.nextRangeU(1, 6);
154 fPathSegmentLimit = fRand.nextRangeU(1, 16);
155 fPath = makePath();
156 SkASSERT(!fPathDepth);
157 }
158
159 const SkPath& getClip() const {
160 return fClip;
161 }
162
163 const SkMatrix& getMatrix() const {
164 return fMatrix;
165 }
166
167 const SkPaint& getPaint() const {
168 return fPaint;
169 }
170
171 const SkPath& getPath() const {
172 return fPath;
173 }
174
175private:
176
177SkPath::AddPathMode makeAddPathMode() {
178 return (SkPath::AddPathMode) fRand.nextRangeU(SkPath::kAppend_AddPathMode,
179 SkPath::kExtend_AddPathMode);
180}
181
182RandomAddPath makeAddPathType() {
183 return (RandomAddPath) fRand.nextRangeU(0, kRandomAddPath_Last);
184}
185
186SkScalar makeAngle() {
187 SkScalar angle;
188 angle = fRand.nextF();
189 return angle;
190}
191
192bool makeBool() {
193 return fRand.nextBool();
194}
195
196SkPath::Direction makeDirection() {
197 return (SkPath::Direction) fRand.nextRangeU(SkPath::kCW_Direction, SkPath::kCCW_Direction);
198}
199
200SkMatrix makeMatrix() {
201 SkMatrix matrix;
202 matrix.reset();
203 RandomSetMatrix setMatrix = (RandomSetMatrix) fRand.nextRangeU(0, kRandomSetMatrix_Last);
204 if (fPrintName) {
205 SkDebugf("%.*s%s\n", fPathDepth * 3, fTab, gRandomSetMatrixNames[setMatrix]);
206 }
207 switch (setMatrix) {
208 case kSetIdentity:
209 break;
210 case kSetTranslateX:
211 matrix.setTranslateX(makeScalar());
212 break;
213 case kSetTranslateY:
214 matrix.setTranslateY(makeScalar());
215 break;
216 case kSetTranslate:
217 matrix.setTranslate(makeScalar(), makeScalar());
218 break;
219 case kSetScaleX:
220 matrix.setScaleX(makeScalar());
221 break;
222 case kSetScaleY:
223 matrix.setScaleY(makeScalar());
224 break;
225 case kSetScale:
226 matrix.setScale(makeScalar(), makeScalar());
227 break;
228 case kSetScaleTranslate:
229 matrix.setScale(makeScalar(), makeScalar(), makeScalar(), makeScalar());
230 break;
231 case kSetSkewX:
232 matrix.setSkewX(makeScalar());
233 break;
234 case kSetSkewY:
235 matrix.setSkewY(makeScalar());
236 break;
237 case kSetSkew:
238 matrix.setSkew(makeScalar(), makeScalar());
239 break;
240 case kSetSkewTranslate:
241 matrix.setSkew(makeScalar(), makeScalar(), makeScalar(), makeScalar());
242 break;
243 case kSetRotate:
244 matrix.setRotate(makeScalar());
245 break;
246 case kSetRotateTranslate:
247 matrix.setRotate(makeScalar(), makeScalar(), makeScalar());
248 break;
249 case kSetPerspectiveX:
250 matrix.setPerspX(makeScalar());
251 break;
252 case kSetPerspectiveY:
253 matrix.setPerspY(makeScalar());
254 break;
255 case kSetAll:
256 matrix.setAll(makeScalar(), makeScalar(), makeScalar(),
257 makeScalar(), makeScalar(), makeScalar(),
258 makeScalar(), makeScalar(), makeScalar());
259 break;
260 }
261 return matrix;
262}
263
264SkPaint makePaint() {
265 SkPaint paint;
266 bool antiAlias = fRand.nextBool();
267 paint.setAntiAlias(antiAlias);
268 SkPaint::Style style = (SkPaint::Style) fRand.nextRangeU(SkPaint::kFill_Style,
269 SkPaint::kStrokeAndFill_Style);
270 paint.setStyle(style);
271 SkColor color = (SkColor) fRand.nextU();
272 paint.setColor(color);
273 SkScalar width = fRand.nextF();
274 paint.setStrokeWidth(width);
275 SkScalar miter = fRand.nextF();
276 paint.setStrokeMiter(miter);
277 SkPaint::Cap cap = (SkPaint::Cap) fRand.nextRangeU(SkPaint::kButt_Cap, SkPaint::kSquare_Cap);
278 paint.setStrokeCap(cap);
279 SkPaint::Join join = (SkPaint::Join) fRand.nextRangeU(SkPaint::kMiter_Join,
280 SkPaint::kBevel_Join);
281 paint.setStrokeJoin(join);
282 return paint;
283}
284
285SkPoint makePoint() {
286 SkPoint result;
287 makeScalarArray(2, &result.fX);
288 return result;
289}
290
291void makePointArray(size_t arrayCount, SkPoint* points) {
292 for (size_t index = 0; index < arrayCount; ++index) {
293 points[index] = makePoint();
294 }
295}
296
297void makePointArray(SkTDArray<SkPoint>* points) {
298 size_t arrayCount = fRand.nextRangeU(1, 10);
299 for (size_t index = 0; index < arrayCount; ++index) {
300 *points->append() = makePoint();
301 }
302}
303
304SkRect makeRect() {
305 SkRect result;
306 makeScalarArray(4, &result.fLeft);
307 return result;
308}
309
310SkRRect makeRRect() {
311 SkRRect rrect;
312 RandomSetRRect rrectType = makeSetRRectType();
313 if (fPrintName) {
314 SkDebugf("%.*s%s\n", fPathDepth * 3, fTab, gRandomSetRRectNames[rrectType]);
315 }
316 switch (rrectType) {
317 case kSetEmpty:
318 rrect.setEmpty();
319 break;
320 case kSetRect: {
321 SkRect rect = makeRect();
322 rrect.setRect(rect);
323 } break;
324 case kSetOval: {
325 SkRect oval = makeRect();
326 rrect.setOval(oval);
327 } break;
328 case kSetRectXY: {
329 SkRect rect = makeRect();
330 SkScalar xRad = makeScalar();
331 SkScalar yRad = makeScalar();
332 rrect.setRectXY(rect, xRad, yRad);
333 } break;
334 case kSetNinePatch: {
335 SkRect rect = makeRect();
336 SkScalar leftRad = makeScalar();
337 SkScalar topRad = makeScalar();
338 SkScalar rightRad = makeScalar();
339 SkScalar bottomRad = makeScalar();
340 rrect.setNinePatch(rect, leftRad, topRad, rightRad, bottomRad);
341 SkDebugf(""); // keep locals in scope
342 } break;
343 case kSetRectRadii: {
344 SkRect rect = makeRect();
345 SkVector radii[4];
346 makeVectorArray(SK_ARRAY_COUNT(radii), radii);
347 rrect.setRectRadii(rect, radii);
348 } break;
349 }
350 return rrect;
351}
352
353SkPath makePath() {
354 SkPath path;
355 for (uint32_t cIndex = 0; cIndex < fPathContourCount; ++cIndex) {
356 uint32_t segments = makeSegmentCount();
357 for (uint32_t sIndex = 0; sIndex < segments; ++sIndex) {
358 RandomAddPath addPathType = makeAddPathType();
359 ++fAddCount;
360 if (fPrintName) {
361 SkDebugf("%.*s%s\n", fPathDepth * 3, fTab,
362 gRandomAddPathNames[addPathType]);
363 }
364 switch (addPathType) {
365 case kAddArc: {
366 SkRect oval = makeRect();
367 SkScalar startAngle = makeAngle();
368 SkScalar sweepAngle = makeAngle();
369 path.addArc(oval, startAngle, sweepAngle);
370 validate(path);
371 } break;
372 case kAddRoundRect1: {
373 SkRect rect = makeRect();
374 SkScalar rx = makeScalar(), ry = makeScalar();
375 SkPath::Direction dir = makeDirection();
376 path.addRoundRect(rect, rx, ry, dir);
377 validate(path);
378 } break;
379 case kAddRoundRect2: {
380 SkRect rect = makeRect();
381 SkScalar radii[8];
382 makeScalarArray(SK_ARRAY_COUNT(radii), radii);
383 SkPath::Direction dir = makeDirection();
384 path.addRoundRect(rect, radii, dir);
385 validate(path);
386 } break;
387 case kAddRRect: {
388 SkRRect rrect = makeRRect();
389 SkPath::Direction dir = makeDirection();
390 path.addRRect(rrect, dir);
391 validate(path);
392 } break;
393 case kAddPoly: {
394 SkTDArray<SkPoint> points;
395 makePointArray(&points);
396 bool close = makeBool();
397 path.addPoly(&points[0], points.count(), close);
398 validate(path);
399 } break;
400 case kAddPath1:
401 if (fPathDepth < fPathDepthLimit) {
402 ++fPathDepth;
403 SkPath src = makePath();
404 validate(src);
405 SkScalar dx = makeScalar();
406 SkScalar dy = makeScalar();
407 SkPath::AddPathMode mode = makeAddPathMode();
408 path.addPath(src, dx, dy, mode);
409 --fPathDepth;
410 validate(path);
411 }
412 break;
413 case kAddPath2:
414 if (fPathDepth < fPathDepthLimit) {
415 ++fPathDepth;
416 SkPath src = makePath();
417 validate(src);
418 SkPath::AddPathMode mode = makeAddPathMode();
419 path.addPath(src, mode);
420 --fPathDepth;
421 validate(path);
422 }
423 break;
424 case kAddPath3:
425 if (fPathDepth < fPathDepthLimit) {
426 ++fPathDepth;
427 SkPath src = makePath();
428 validate(src);
429 SkMatrix matrix = makeMatrix();
430 SkPath::AddPathMode mode = makeAddPathMode();
431 path.addPath(src, matrix, mode);
432 --fPathDepth;
433 validate(path);
434 }
435 break;
436 case kReverseAddPath:
437 if (fPathDepth < fPathDepthLimit) {
438 ++fPathDepth;
439 SkPath src = makePath();
440 validate(src);
441 path.reverseAddPath(src);
442 --fPathDepth;
443 validate(path);
444 }
445 break;
446 case kMoveToPath: {
447 SkScalar x = makeScalar();
448 SkScalar y = makeScalar();
449 path.moveTo(x, y);
450 validate(path);
451 } break;
452 case kRMoveToPath: {
453 SkScalar x = makeScalar();
454 SkScalar y = makeScalar();
455 path.rMoveTo(x, y);
456 validate(path);
457 } break;
458 case kLineToPath: {
459 SkScalar x = makeScalar();
460 SkScalar y = makeScalar();
461 path.lineTo(x, y);
462 validate(path);
463 } break;
464 case kRLineToPath: {
465 SkScalar x = makeScalar();
466 SkScalar y = makeScalar();
467 path.rLineTo(x, y);
468 validate(path);
469 } break;
470 case kQuadToPath: {
471 SkPoint pt[2];
472 makePointArray(SK_ARRAY_COUNT(pt), pt);
473 path.quadTo(pt[0], pt[1]);
474 validate(path);
475 } break;
476 case kRQuadToPath: {
477 SkPoint pt[2];
478 makePointArray(SK_ARRAY_COUNT(pt), pt);
479 path.rQuadTo(pt[0].fX, pt[0].fY, pt[1].fX, pt[1].fY);
480 validate(path);
481 } break;
482 case kConicToPath: {
483 SkPoint pt[2];
484 makePointArray(SK_ARRAY_COUNT(pt), pt);
485 SkScalar weight = makeScalar();
486 path.conicTo(pt[0], pt[1], weight);
487 validate(path);
488 } break;
489 case kRConicToPath: {
490 SkPoint pt[2];
491 makePointArray(SK_ARRAY_COUNT(pt), pt);
492 SkScalar weight = makeScalar();
493 path.rConicTo(pt[0].fX, pt[0].fY, pt[1].fX, pt[1].fY, weight);
494 validate(path);
495 } break;
496 case kCubicToPath: {
497 SkPoint pt[3];
498 makePointArray(SK_ARRAY_COUNT(pt), pt);
499 path.cubicTo(pt[0], pt[1], pt[2]);
500 validate(path);
501 } break;
502 case kRCubicToPath: {
503 SkPoint pt[3];
504 makePointArray(SK_ARRAY_COUNT(pt), pt);
505 path.rCubicTo(pt[0].fX, pt[0].fY, pt[1].fX, pt[1].fY, pt[2].fX, pt[2].fY);
506 validate(path);
507 } break;
508 case kArcToPath: {
509 SkPoint pt[2];
510 makePointArray(SK_ARRAY_COUNT(pt), pt);
511 SkScalar radius = makeScalar();
512 path.arcTo(pt[0], pt[1], radius);
513 validate(path);
514 } break;
515 case kArcTo2Path: {
516 SkRect oval = makeRect();
517 SkScalar startAngle = makeAngle();
518 SkScalar sweepAngle = makeAngle();
519 bool forceMoveTo = makeBool();
520 path.arcTo(oval, startAngle, sweepAngle, forceMoveTo);
521 validate(path);
522 } break;
523 case kClosePath:
524 path.close();
525 validate(path);
526 break;
527 }
528 }
529 }
530 return path;
531}
532
533uint32_t makeSegmentCount() {
534 return fRand.nextRangeU(1, fPathSegmentLimit);
535}
536
537RandomSetRRect makeSetRRectType() {
538 return (RandomSetRRect) fRand.nextRangeU(0, kRandomSetRRect_Last);
539}
540
541SkScalar makeScalar() {
542 SkScalar scalar;
543 scalar = fRand.nextRangeF(fFloatMin, fFloatMax);
544 return scalar;
545}
546
547void makeScalarArray(size_t arrayCount, SkScalar* array) {
548 for (size_t index = 0; index < arrayCount; ++index) {
549 array[index] = makeScalar();
550 }
551}
552
553void makeVectorArray(size_t arrayCount, SkVector* array) {
554 for (size_t index = 0; index < arrayCount; ++index) {
555 array[index] = makeVector();
556 }
557}
558
559SkVector makeVector() {
560 SkVector result;
561 makeScalarArray(2, &result.fX);
562 return result;
563}
564
565void validate(const SkPath& path) {
566 if (fValidate) {
567 SkDEBUGCODE(path.experimentalValidateRef());
568 }
569}
570
571SkRandom fRand;
572SkMatrix fMatrix;
573SkPath fClip;
574SkPaint fPaint;
575SkPath fPath;
576SkScalar fFloatMin;
577SkScalar fFloatMax;
578uint32_t fPathContourCount;
579int fPathDepth;
580int fPathDepthLimit;
581uint32_t fPathSegmentLimit;
582int fAddCount;
583bool fPrintName;
584bool fValidate;
585const char* fTab;
586};
587
588//////////////////////////////////////////////////////////////////////////////
589static bool contains_only_moveTo(const SkPath& path) {
590 int verbCount = path.countVerbs();
591 if (verbCount == 0) {
592 return true;
593 }
594 SkTDArray<uint8_t> verbs;
595 verbs.setCount(verbCount);
596 SkDEBUGCODE(int getVerbResult = ) path.getVerbs(verbs.begin(), verbCount);
597 SkASSERT(getVerbResult == verbCount);
598 for (int index = 0; index < verbCount; ++index) {
599 if (verbs[index] != SkPath::kMove_Verb) {
600 return false;
601 }
602 }
603 return true;
604}
605
606class PathFuzzView : public SampleView {
607public:
608 PathFuzzView() {
609 fDots = 0;
610 }
611protected:
612 // overrides from SkEventSink
613 virtual bool onQuery(SkEvent* evt) {
614 if (SampleCode::TitleQ(*evt)) {
615 SampleCode::TitleR(evt, "PathFuzzer");
616 return true;
617 }
618 return this->INHERITED::onQuery(evt);
619 }
620
621 virtual void onDrawContent(SkCanvas* canvas) {
622 fuzzPath.randomize();
623 const SkPath& path = fuzzPath.getPath();
624 const SkPaint& paint = fuzzPath.getPaint();
625 const SkPath& clip = fuzzPath.getClip();
626 const SkMatrix& matrix = fuzzPath.getMatrix();
627 if (!contains_only_moveTo(clip)) {
628 canvas->clipPath(clip);
629 }
630 canvas->setMatrix(matrix);
631 canvas->drawPath(path, paint);
632 this->inval(NULL);
633 if (++fDots == 8000) {
634 SkDebugf("\n");
635 fDots = 0;
636 }
637 if ((fDots % 100) == 99) {
638 SkDebugf(".");
639 }
640 }
641
642private:
643 FuzzPath fuzzPath;
644 int fDots;
645 typedef SkView INHERITED;
646};
647
648//////////////////////////////////////////////////////////////////////////////
649
650static SkView* MyFactory() { return new PathFuzzView; }
651static SkViewRegister reg(MyFactory);