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