1 /* 2 * Copyright 2011 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 "gm/gm.h" 9 #include "include/core/SkCanvas.h" 10 #include "include/core/SkColor.h" 11 #include "include/core/SkMatrix.h" 12 #include "include/core/SkPaint.h" 13 #include "include/core/SkPathBuilder.h" 14 #include "include/core/SkRRect.h" 15 #include "include/core/SkRect.h" 16 #include "include/core/SkScalar.h" 17 #include "include/core/SkSize.h" 18 #include "include/core/SkString.h" 19 #include "include/core/SkTypes.h" 20 #include "include/private/base/SkTArray.h" 21 #include "src/base/SkRandom.h" 22 23 using namespace skia_private; 24 25 namespace { 26 27 class SkDoOnce { 28 public: SkDoOnce()29 SkDoOnce() { fDidOnce = false; } 30 // Make noncopyable 31 SkDoOnce(SkDoOnce&) = delete; 32 SkDoOnce& operator=(SkDoOnce&) = delete; 33 needToDo() const34 bool needToDo() const { return !fDidOnce; } alreadyDone() const35 bool alreadyDone() const { return fDidOnce; } accomplished()36 void accomplished() { 37 SkASSERT(!fDidOnce); 38 fDidOnce = true; 39 } 40 41 private: 42 bool fDidOnce; 43 }; 44 45 class ConvexPathsGM : public skiagm::GM { 46 SkDoOnce fOnce; 47 onOnceBeforeDraw()48 void onOnceBeforeDraw() override { this->setBGColor(0xFF000000); } 49 getName() const50 SkString getName() const override { return SkString("convexpaths"); } 51 getISize()52 SkISize getISize() override { return {1200, 1100}; } 53 makePaths()54 void makePaths() { 55 if (fOnce.alreadyDone()) { 56 return; 57 } 58 fOnce.accomplished(); 59 60 SkPathBuilder b; 61 fPaths.push_back(b.moveTo(0, 0) 62 .quadTo(50, 100, 0, 100) 63 .lineTo(0, 0) 64 .detach()); 65 66 fPaths.push_back(b.moveTo(0, 50) 67 .quadTo(50, 0, 100, 50) 68 .quadTo(50, 100, 0, 50) 69 .detach()); 70 71 fPaths.push_back(SkPath::Rect({0, 0, 100, 100}, SkPathDirection::kCW)); 72 fPaths.push_back(SkPath::Rect({0, 0, 100, 100}, SkPathDirection::kCCW)); 73 fPaths.push_back(SkPath::Circle(50, 50, 50, SkPathDirection::kCW)); 74 fPaths.push_back(SkPath::Oval(SkRect::MakeXYWH(0, 0, 50, 100), SkPathDirection::kCW)); 75 fPaths.push_back(SkPath::Oval(SkRect::MakeXYWH(0, 0, 100, 5), SkPathDirection::kCCW)); 76 fPaths.push_back(SkPath::Oval(SkRect::MakeXYWH(0, 0, 1, 100), SkPathDirection::kCCW)); 77 fPaths.push_back(SkPath::RRect(SkRRect::MakeRectXY({0, 0, 100, 100}, 40, 20), 78 SkPathDirection::kCW)); 79 80 // large number of points 81 static constexpr int kLength = 100; 82 static constexpr int kPtsPerSide = (1 << 12); 83 84 b.moveTo(0, 0); 85 for (int i = 1; i < kPtsPerSide; ++i) { // skip the first point due to moveTo. 86 b.lineTo(kLength * SkIntToScalar(i) / kPtsPerSide, 0); 87 } 88 for (int i = 0; i < kPtsPerSide; ++i) { 89 b.lineTo(kLength, kLength * SkIntToScalar(i) / kPtsPerSide); 90 } 91 for (int i = kPtsPerSide; i > 0; --i) { 92 b.lineTo(kLength * SkIntToScalar(i) / kPtsPerSide, kLength); 93 } 94 for (int i = kPtsPerSide; i > 0; --i) { 95 b.lineTo(0, kLength * SkIntToScalar(i) / kPtsPerSide); 96 } 97 fPaths.push_back(b.detach()); 98 99 // shallow diagonals 100 fPaths.push_back(SkPath::Polygon({{0,0}, {100,1}, {98,100}, {3,96}}, false)); 101 102 fPaths.push_back(b.arcTo(SkRect::MakeXYWH(0, 0, 50, 100), 25, 130, false) 103 .detach()); 104 105 // cubics 106 fPaths.push_back(b.cubicTo( 1, 1, 10, 90, 0, 100).detach()); 107 fPaths.push_back(b.cubicTo(100, 50, 20, 100, 0, 0).detach()); 108 109 // path that has a cubic with a repeated first control point and 110 // a repeated last control point. 111 fPaths.push_back(b.moveTo(10, 10) 112 .cubicTo(10, 10, 10, 0, 20, 0) 113 .lineTo(40, 0) 114 .cubicTo(40, 0, 50, 0, 50, 10) 115 .detach()); 116 117 // path that has two cubics with repeated middle control points. 118 fPaths.push_back(b.moveTo(10, 10) 119 .cubicTo(10, 0, 10, 0, 20, 0) 120 .lineTo(40, 0) 121 .cubicTo(50, 0, 50, 0, 50, 10) 122 .detach()); 123 124 // cubic where last three points are almost a line 125 fPaths.push_back(b.moveTo(0, 228.0f/8) 126 .cubicTo( 628.0f/ 8, 82.0f/8, 127 1255.0f/ 8, 141.0f/8, 128 1883.0f/ 8, 202.0f/8) 129 .detach()); 130 131 // flat cubic where the at end point tangents both point outward. 132 fPaths.push_back(b.moveTo(10, 0) 133 .cubicTo(0, 1, 30, 1, 20, 0) 134 .detach()); 135 136 // flat cubic where initial tangent is in, end tangent out 137 fPaths.push_back(b.moveTo(0, 0) 138 .cubicTo(10, 1, 30, 1, 20, 0) 139 .detach()); 140 141 // flat cubic where initial tangent is out, end tangent in 142 fPaths.push_back(b.moveTo(10, 0) 143 .cubicTo(0, 1, 20, 1, 30, 0) 144 .detach()); 145 146 // triangle where one edge is a degenerate quad 147 fPaths.push_back(b.moveTo(8.59375f, 45) 148 .quadTo(16.9921875f, 45, 149 31.25f, 45) 150 .lineTo(100, 100) 151 .lineTo(8.59375f, 45) 152 .detach()); 153 154 // triangle where one edge is a quad with a repeated point 155 fPaths.push_back(b.moveTo(0, 25) 156 .lineTo(50, 0) 157 .quadTo(50, 50, 50, 50) 158 .detach()); 159 160 // triangle where one edge is a cubic with a 2x repeated point 161 fPaths.push_back(b.moveTo(0, 25) 162 .lineTo(50, 0) 163 .cubicTo(50, 0, 50, 50, 50, 50) 164 .detach()); 165 166 // triangle where one edge is a quad with a nearly repeated point 167 fPaths.push_back(b.moveTo(0, 25) 168 .lineTo(50, 0) 169 .quadTo(50, 49.95f, 50, 50) 170 .detach()); 171 172 // triangle where one edge is a cubic with a 3x nearly repeated point 173 fPaths.push_back(b.moveTo(0, 25) 174 .lineTo(50, 0) 175 .cubicTo(50, 49.95f, 50, 49.97f, 50, 50) 176 .detach()); 177 178 // triangle where there is a point degenerate cubic at one corner 179 fPaths.push_back(b.moveTo(0, 25) 180 .lineTo(50, 0) 181 .lineTo(50, 50) 182 .cubicTo(50, 50, 50, 50, 50, 50) 183 .detach()); 184 185 // point line 186 fPaths.push_back(SkPath::Line({50, 50}, {50, 50})); 187 188 // point quad 189 fPaths.push_back(b.moveTo(50, 50) 190 .quadTo(50, 50, 50, 50) 191 .detach()); 192 193 // point cubic 194 fPaths.push_back(b.moveTo(50, 50) 195 .cubicTo(50, 50, 50, 50, 50, 50) 196 .detach()); 197 198 // moveTo only paths 199 fPaths.push_back(b.moveTo(0, 0) 200 .moveTo(0, 0) 201 .moveTo(1, 1) 202 .moveTo(1, 1) 203 .moveTo(10, 10) 204 .detach()); 205 206 fPaths.push_back(b.moveTo(0, 0) 207 .moveTo(0, 0) 208 .detach()); 209 210 // line degenerate 211 fPaths.push_back(b.lineTo(100, 100).detach()); 212 fPaths.push_back(b.quadTo(100, 100, 0, 0).detach()); 213 fPaths.push_back(b.quadTo(100, 100, 50, 50).detach()); 214 fPaths.push_back(b.quadTo(50, 50, 100, 100).detach()); 215 fPaths.push_back(b.cubicTo(0, 0, 0, 0, 100, 100).detach()); 216 217 // skbug.com/8928 218 fPaths.push_back(b.moveTo(16.875f, 192.594f) 219 .cubicTo(45.625f, 192.594f, 74.375f, 192.594f, 103.125f, 192.594f) 220 .cubicTo(88.75f, 167.708f, 74.375f, 142.823f, 60, 117.938f) 221 .cubicTo(45.625f, 142.823f, 31.25f, 167.708f, 16.875f, 192.594f) 222 .close() 223 .detach()); 224 SkMatrix m; 225 m.setAll(0.1f, 0, -1, 0, 0.115207f, -2.64977f, 0, 0, 1); 226 fPaths.back().transform(m); 227 228 // small circle. This is listed last so that it has device coords far 229 // from the origin (small area relative to x,y values). 230 fPaths.push_back(SkPath::Circle(0, 0, 1.2f)); 231 } 232 onDraw(SkCanvas * canvas)233 void onDraw(SkCanvas* canvas) override { 234 this->makePaths(); 235 236 SkPaint paint; 237 paint.setAntiAlias(true); 238 SkRandom rand; 239 canvas->translate(20, 20); 240 241 // As we've added more paths this has gotten pretty big. Scale the whole thing down. 242 canvas->scale(2.0f/3, 2.0f/3); 243 244 for (int i = 0; i < fPaths.size(); ++i) { 245 canvas->save(); 246 // position the path, and make it at off-integer coords. 247 canvas->translate(200.0f * (i % 5) + 1.0f/10, 248 200.0f * (i / 5) + 9.0f/10); 249 SkColor color = rand.nextU(); 250 color |= 0xff000000; 251 paint.setColor(color); 252 #if 0 // This hitting on 32bit Linux builds for some paths. Temporarily disabling while it is 253 // debugged. 254 SkASSERT(fPaths[i].isConvex()); 255 #endif 256 canvas->drawPath(fPaths[i], paint); 257 canvas->restore(); 258 } 259 } 260 261 TArray<SkPath> fPaths; 262 }; 263 } // namespace 264 265 DEF_GM( return new ConvexPathsGM; ) 266