| /* |
| * Copyright 2011 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "gm/gm.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkColor.h" |
| #include "include/core/SkMatrix.h" |
| #include "include/core/SkPaint.h" |
| #include "include/core/SkPathBuilder.h" |
| #include "include/core/SkRect.h" |
| #include "include/core/SkScalar.h" |
| #include "include/core/SkSize.h" |
| #include "include/core/SkString.h" |
| #include "include/core/SkTypes.h" |
| #include "include/private/SkNoncopyable.h" |
| #include "include/private/SkTArray.h" |
| #include "include/utils/SkRandom.h" |
| |
| namespace { |
| |
| class SkDoOnce : SkNoncopyable { |
| public: |
| SkDoOnce() { fDidOnce = false; } |
| |
| bool needToDo() const { return !fDidOnce; } |
| bool alreadyDone() const { return fDidOnce; } |
| void accomplished() { |
| SkASSERT(!fDidOnce); |
| fDidOnce = true; |
| } |
| |
| private: |
| bool fDidOnce; |
| }; |
| |
| class ConvexPathsGM : public skiagm::GM { |
| SkDoOnce fOnce; |
| |
| void onOnceBeforeDraw() override { this->setBGColor(0xFF000000); } |
| |
| SkString onShortName() override { return SkString("convexpaths"); } |
| |
| |
| SkISize onISize() override { return {1200, 1100}; } |
| |
| void makePaths() { |
| if (fOnce.alreadyDone()) { |
| return; |
| } |
| fOnce.accomplished(); |
| |
| SkPathBuilder b; |
| fPaths.push_back(b.moveTo(0, 0) |
| .quadTo(50, 100, 0, 100) |
| .lineTo(0, 0) |
| .detach()); |
| |
| fPaths.push_back(b.moveTo(0, 50) |
| .quadTo(50, 0, 100, 50) |
| .quadTo(50, 100, 0, 50) |
| .detach()); |
| |
| fPaths.push_back(SkPath::Rect({0, 0, 100, 100}, SkPathDirection::kCW)); |
| fPaths.push_back(SkPath::Rect({0, 0, 100, 100}, SkPathDirection::kCCW)); |
| fPaths.push_back(SkPath::Circle(50, 50, 50, SkPathDirection::kCW)); |
| fPaths.push_back(SkPath::Oval(SkRect::MakeXYWH(0, 0, 50, 100), SkPathDirection::kCW)); |
| fPaths.push_back(SkPath::Oval(SkRect::MakeXYWH(0, 0, 100, 5), SkPathDirection::kCCW)); |
| fPaths.push_back(SkPath::Oval(SkRect::MakeXYWH(0, 0, 1, 100), SkPathDirection::kCCW)); |
| fPaths.push_back(SkPath::RRect(SkRRect::MakeRectXY({0, 0, 100, 100}, 40, 20), |
| SkPathDirection::kCW)); |
| |
| // large number of points |
| enum { |
| kLength = 100, |
| kPtsPerSide = (1 << 12), |
| }; |
| b.moveTo(0, 0); |
| for (int i = 1; i < kPtsPerSide; ++i) { // skip the first point due to moveTo. |
| b.lineTo(kLength * SkIntToScalar(i) / kPtsPerSide, 0); |
| } |
| for (int i = 0; i < kPtsPerSide; ++i) { |
| b.lineTo(kLength, kLength * SkIntToScalar(i) / kPtsPerSide); |
| } |
| for (int i = kPtsPerSide; i > 0; --i) { |
| b.lineTo(kLength * SkIntToScalar(i) / kPtsPerSide, kLength); |
| } |
| for (int i = kPtsPerSide; i > 0; --i) { |
| b.lineTo(0, kLength * SkIntToScalar(i) / kPtsPerSide); |
| } |
| fPaths.push_back(b.detach()); |
| |
| // shallow diagonals |
| fPaths.push_back(SkPath::Polygon({{0,0}, {100,1}, {98,100}, {3,96}}, false)); |
| |
| fPaths.push_back(b.arcTo(SkRect::MakeXYWH(0, 0, 50, 100), 25, 130, false) |
| .detach()); |
| |
| // cubics |
| fPaths.push_back(b.cubicTo( 1, 1, 10, 90, 0, 100).detach()); |
| fPaths.push_back(b.cubicTo(100, 50, 20, 100, 0, 0).detach()); |
| |
| // path that has a cubic with a repeated first control point and |
| // a repeated last control point. |
| fPaths.push_back(b.moveTo(10, 10) |
| .cubicTo(10, 10, 10, 0, 20, 0) |
| .lineTo(40, 0) |
| .cubicTo(40, 0, 50, 0, 50, 10) |
| .detach()); |
| |
| // path that has two cubics with repeated middle control points. |
| fPaths.push_back(b.moveTo(10, 10) |
| .cubicTo(10, 0, 10, 0, 20, 0) |
| .lineTo(40, 0) |
| .cubicTo(50, 0, 50, 0, 50, 10) |
| .detach()); |
| |
| // cubic where last three points are almost a line |
| fPaths.push_back(b.moveTo(0, 228.0f/8) |
| .cubicTo( 628.0f/ 8, 82.0f/8, |
| 1255.0f/ 8, 141.0f/8, |
| 1883.0f/ 8, 202.0f/8) |
| .detach()); |
| |
| // flat cubic where the at end point tangents both point outward. |
| fPaths.push_back(b.moveTo(10, 0) |
| .cubicTo(0, 1, 30, 1, 20, 0) |
| .detach()); |
| |
| // flat cubic where initial tangent is in, end tangent out |
| fPaths.push_back(b.moveTo(0, 0) |
| .cubicTo(10, 1, 30, 1, 20, 0) |
| .detach()); |
| |
| // flat cubic where initial tangent is out, end tangent in |
| fPaths.push_back(b.moveTo(10, 0) |
| .cubicTo(0, 1, 20, 1, 30, 0) |
| .detach()); |
| |
| // triangle where one edge is a degenerate quad |
| fPaths.push_back(b.moveTo(8.59375f, 45) |
| .quadTo(16.9921875f, 45, |
| 31.25f, 45) |
| .lineTo(100, 100) |
| .lineTo(8.59375f, 45) |
| .detach()); |
| |
| // triangle where one edge is a quad with a repeated point |
| fPaths.push_back(b.moveTo(0, 25) |
| .lineTo(50, 0) |
| .quadTo(50, 50, 50, 50) |
| .detach()); |
| |
| // triangle where one edge is a cubic with a 2x repeated point |
| fPaths.push_back(b.moveTo(0, 25) |
| .lineTo(50, 0) |
| .cubicTo(50, 0, 50, 50, 50, 50) |
| .detach()); |
| |
| // triangle where one edge is a quad with a nearly repeated point |
| fPaths.push_back(b.moveTo(0, 25) |
| .lineTo(50, 0) |
| .quadTo(50, 49.95f, 50, 50) |
| .detach()); |
| |
| // triangle where one edge is a cubic with a 3x nearly repeated point |
| fPaths.push_back(b.moveTo(0, 25) |
| .lineTo(50, 0) |
| .cubicTo(50, 49.95f, 50, 49.97f, 50, 50) |
| .detach()); |
| |
| // triangle where there is a point degenerate cubic at one corner |
| fPaths.push_back(b.moveTo(0, 25) |
| .lineTo(50, 0) |
| .lineTo(50, 50) |
| .cubicTo(50, 50, 50, 50, 50, 50) |
| .detach()); |
| |
| // point line |
| fPaths.push_back(SkPath::Line({50, 50}, {50, 50})); |
| |
| // point quad |
| fPaths.push_back(b.moveTo(50, 50) |
| .quadTo(50, 50, 50, 50) |
| .detach()); |
| |
| // point cubic |
| fPaths.push_back(b.moveTo(50, 50) |
| .cubicTo(50, 50, 50, 50, 50, 50) |
| .detach()); |
| |
| // moveTo only paths |
| fPaths.push_back(b.moveTo(0, 0) |
| .moveTo(0, 0) |
| .moveTo(1, 1) |
| .moveTo(1, 1) |
| .moveTo(10, 10) |
| .detach()); |
| |
| fPaths.push_back(b.moveTo(0, 0) |
| .moveTo(0, 0) |
| .detach()); |
| |
| // line degenerate |
| fPaths.push_back(b.lineTo(100, 100).detach()); |
| fPaths.push_back(b.quadTo(100, 100, 0, 0).detach()); |
| fPaths.push_back(b.quadTo(100, 100, 50, 50).detach()); |
| fPaths.push_back(b.quadTo(50, 50, 100, 100).detach()); |
| fPaths.push_back(b.cubicTo(0, 0, 0, 0, 100, 100).detach()); |
| |
| // skbug.com/8928 |
| fPaths.push_back(b.moveTo(16.875f, 192.594f) |
| .cubicTo(45.625f, 192.594f, 74.375f, 192.594f, 103.125f, 192.594f) |
| .cubicTo(88.75f, 167.708f, 74.375f, 142.823f, 60, 117.938f) |
| .cubicTo(45.625f, 142.823f, 31.25f, 167.708f, 16.875f, 192.594f) |
| .close() |
| .detach()); |
| SkMatrix m; |
| m.setAll(0.1f, 0, -1, 0, 0.115207f, -2.64977f, 0, 0, 1); |
| fPaths.back().transform(m); |
| |
| // small circle. This is listed last so that it has device coords far |
| // from the origin (small area relative to x,y values). |
| fPaths.push_back(SkPath::Circle(0, 0, 1.2f)); |
| } |
| |
| void onDraw(SkCanvas* canvas) override { |
| this->makePaths(); |
| |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| SkRandom rand; |
| canvas->translate(20, 20); |
| |
| // As we've added more paths this has gotten pretty big. Scale the whole thing down. |
| canvas->scale(2.0f/3, 2.0f/3); |
| |
| for (int i = 0; i < fPaths.count(); ++i) { |
| canvas->save(); |
| // position the path, and make it at off-integer coords. |
| canvas->translate(200.0f * (i % 5) + 1.0f/10, |
| 200.0f * (i / 5) + 9.0f/10); |
| SkColor color = rand.nextU(); |
| color |= 0xff000000; |
| paint.setColor(color); |
| #if 0 // This hitting on 32bit Linux builds for some paths. Temporarily disabling while it is |
| // debugged. |
| SkASSERT(fPaths[i].isConvex()); |
| #endif |
| canvas->drawPath(fPaths[i], paint); |
| canvas->restore(); |
| } |
| } |
| |
| SkTArray<SkPath> fPaths; |
| }; |
| } // namespace |
| |
| DEF_GM( return new ConvexPathsGM; ) |