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