1 2/* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8#include "gm.h" 9#include "SkRandom.h" 10#include "SkTArray.h" 11 12class SkOnce : SkNoncopyable { 13public: 14 SkOnce() { fDidOnce = false; } 15 16 bool needToDo() const { return !fDidOnce; } 17 bool alreadyDone() const { return fDidOnce; } 18 void accomplished() { 19 SkASSERT(!fDidOnce); 20 fDidOnce = true; 21 } 22 23private: 24 bool fDidOnce; 25}; 26 27namespace skiagm { 28 29class ConvexPathsGM : public GM { 30 SkOnce fOnce; 31public: 32 ConvexPathsGM() { 33 this->setBGColor(0xFF000000); 34 } 35 36protected: 37 virtual SkString onShortName() { 38 return SkString("convexpaths"); 39 } 40 41 42 virtual SkISize onISize() { 43 return make_isize(1200, 1100); 44 } 45 46 void makePaths() { 47 if (fOnce.alreadyDone()) { 48 return; 49 } 50 fOnce.accomplished(); 51 // CW 52 fPaths.push_back().moveTo(0, 0); 53 fPaths.back().quadTo(50 * SK_Scalar1, 100 * SK_Scalar1, 54 0, 100 * SK_Scalar1); 55 fPaths.back().lineTo(0, 0); 56 57 // CCW 58 fPaths.push_back().moveTo(0, 0); 59 fPaths.back().lineTo(0, 100 * SK_Scalar1); 60 fPaths.back().quadTo(50 * SK_Scalar1, 100 * SK_Scalar1, 61 0, 0); 62 63 // CW 64 fPaths.push_back().moveTo(0, 50 * SK_Scalar1); 65 fPaths.back().quadTo(50 * SK_Scalar1, 0, 66 100 * SK_Scalar1, 50 * SK_Scalar1); 67 fPaths.back().quadTo(50 * SK_Scalar1, 100 * SK_Scalar1, 68 0, 50 * SK_Scalar1); 69 70 // CCW 71 fPaths.push_back().moveTo(0, 50 * SK_Scalar1); 72 fPaths.back().quadTo(50 * SK_Scalar1, 100 * SK_Scalar1, 73 100 * SK_Scalar1, 50 * SK_Scalar1); 74 fPaths.back().quadTo(50 * SK_Scalar1, 0, 75 0, 50 * SK_Scalar1); 76 77 fPaths.push_back().addRect(0, 0, 78 100 * SK_Scalar1, 100 * SK_Scalar1, 79 SkPath::kCW_Direction); 80 81 fPaths.push_back().addRect(0, 0, 82 100 * SK_Scalar1, 100 * SK_Scalar1, 83 SkPath::kCCW_Direction); 84 85 fPaths.push_back().addCircle(50 * SK_Scalar1, 50 * SK_Scalar1, 86 50 * SK_Scalar1, SkPath::kCW_Direction); 87 88 fPaths.push_back().addCircle(50 * SK_Scalar1, 50 * SK_Scalar1, 89 40 * SK_Scalar1, SkPath::kCCW_Direction); 90 91 fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0, 92 50 * SK_Scalar1, 93 100 * SK_Scalar1), 94 SkPath::kCW_Direction); 95 96 fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0, 97 100 * SK_Scalar1, 98 50 * SK_Scalar1), 99 SkPath::kCCW_Direction); 100 101 fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0, 102 100 * SK_Scalar1, 103 5 * SK_Scalar1), 104 SkPath::kCCW_Direction); 105 106 fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0, 107 SK_Scalar1, 108 100 * SK_Scalar1), 109 SkPath::kCCW_Direction); 110 111 fPaths.push_back().addRoundRect(SkRect::MakeXYWH(0, 0, 112 SK_Scalar1 * 100, 113 SK_Scalar1 * 100), 114 40 * SK_Scalar1, 20 * SK_Scalar1, 115 SkPath::kCW_Direction); 116 117 fPaths.push_back().addRoundRect(SkRect::MakeXYWH(0, 0, 118 SK_Scalar1 * 100, 119 SK_Scalar1 * 100), 120 20 * SK_Scalar1, 40 * SK_Scalar1, 121 SkPath::kCCW_Direction); 122 123 // large number of points 124 enum { 125 kLength = 100, 126 kPtsPerSide = (1 << 12), 127 }; 128 fPaths.push_back().moveTo(0, 0); 129 for (int i = 1; i < kPtsPerSide; ++i) { // skip the first point due to moveTo. 130 fPaths.back().lineTo(kLength * SkIntToScalar(i) / kPtsPerSide, 0); 131 } 132 for (int i = 0; i < kPtsPerSide; ++i) { 133 fPaths.back().lineTo(kLength, kLength * SkIntToScalar(i) / kPtsPerSide); 134 } 135 for (int i = kPtsPerSide; i > 0; --i) { 136 fPaths.back().lineTo(kLength * SkIntToScalar(i) / kPtsPerSide, kLength); 137 } 138 for (int i = kPtsPerSide; i > 0; --i) { 139 fPaths.back().lineTo(0, kLength * SkIntToScalar(i) / kPtsPerSide); 140 } 141 142 // shallow diagonals 143 fPaths.push_back().lineTo(100 * SK_Scalar1, SK_Scalar1); 144 fPaths.back().lineTo(98 * SK_Scalar1, 100 * SK_Scalar1); 145 fPaths.back().lineTo(3 * SK_Scalar1, 96 * SK_Scalar1); 146 147 //It turns out arcTos are not automatically marked as convex and they 148 //may in fact be ever so slightly concave. 149 //fPaths.push_back().arcTo(SkRect::MakeXYWH(0, 0, 150 // 50 * SK_Scalar1, 151 // 100 * SK_Scalar1), 152 // 25 * SK_Scalar1, 130 * SK_Scalar1, false); 153 154 // cubics 155 fPaths.push_back().cubicTo( 1 * SK_Scalar1, 1 * SK_Scalar1, 156 10 * SK_Scalar1, 90 * SK_Scalar1, 157 0 * SK_Scalar1, 100 * SK_Scalar1); 158 fPaths.push_back().cubicTo(100 * SK_Scalar1, 50 * SK_Scalar1, 159 20 * SK_Scalar1, 100 * SK_Scalar1, 160 0 * SK_Scalar1, 0 * SK_Scalar1); 161 162 // path that has a cubic with a repeated first control point and 163 // a repeated last control point. 164 fPaths.push_back().moveTo(SK_Scalar1 * 10, SK_Scalar1 * 10); 165 fPaths.back().cubicTo(10 * SK_Scalar1, 10 * SK_Scalar1, 166 10 * SK_Scalar1, 0, 167 20 * SK_Scalar1, 0); 168 fPaths.back().lineTo(40 * SK_Scalar1, 0); 169 fPaths.back().cubicTo(40 * SK_Scalar1, 0, 170 50 * SK_Scalar1, 0, 171 50 * SK_Scalar1, 10 * SK_Scalar1); 172 173 // path that has two cubics with repeated middle control points. 174 fPaths.push_back().moveTo(SK_Scalar1 * 10, SK_Scalar1 * 10); 175 fPaths.back().cubicTo(10 * SK_Scalar1, 0, 176 10 * SK_Scalar1, 0, 177 20 * SK_Scalar1, 0); 178 fPaths.back().lineTo(40 * SK_Scalar1, 0); 179 fPaths.back().cubicTo(50 * SK_Scalar1, 0, 180 50 * SK_Scalar1, 0, 181 50 * SK_Scalar1, 10 * SK_Scalar1); 182 183 // cubic where last three points are almost a line 184 fPaths.push_back().moveTo(0, 228 * SK_Scalar1 / 8); 185 fPaths.back().cubicTo(628 * SK_Scalar1 / 8, 82 * SK_Scalar1 / 8, 186 1255 * SK_Scalar1 / 8, 141 * SK_Scalar1 / 8, 187 1883 * SK_Scalar1 / 8, 202 * SK_Scalar1 / 8); 188 189 // flat cubic where the at end point tangents both point outward. 190 fPaths.push_back().moveTo(10 * SK_Scalar1, 0); 191 fPaths.back().cubicTo(0, SK_Scalar1, 192 30 * SK_Scalar1, SK_Scalar1, 193 20 * SK_Scalar1, 0); 194 195 // flat cubic where initial tangent is in, end tangent out 196 fPaths.push_back().moveTo(0, 0 * SK_Scalar1); 197 fPaths.back().cubicTo(10 * SK_Scalar1, SK_Scalar1, 198 30 * SK_Scalar1, SK_Scalar1, 199 20 * SK_Scalar1, 0); 200 201 // flat cubic where initial tangent is out, end tangent in 202 fPaths.push_back().moveTo(10 * SK_Scalar1, 0); 203 fPaths.back().cubicTo(0, SK_Scalar1, 204 20 * SK_Scalar1, SK_Scalar1, 205 30 * SK_Scalar1, 0); 206 207 // triangle where one edge is a degenerate quad 208 fPaths.push_back().moveTo(SkFloatToScalar(8.59375f), 45 * SK_Scalar1); 209 fPaths.back().quadTo(SkFloatToScalar(16.9921875f), 45 * SK_Scalar1, 210 SkFloatToScalar(31.25f), 45 * SK_Scalar1); 211 fPaths.back().lineTo(100 * SK_Scalar1, 100 * SK_Scalar1); 212 fPaths.back().lineTo(SkFloatToScalar(8.59375f), 45 * SK_Scalar1); 213 214 // point degenerate 215 fPaths.push_back().moveTo(50 * SK_Scalar1, 50 * SK_Scalar1); 216 fPaths.back().lineTo(50 * SK_Scalar1, 50 * SK_Scalar1); 217 218 fPaths.push_back().moveTo(50 * SK_Scalar1, 50 * SK_Scalar1); 219 fPaths.back().quadTo(50 * SK_Scalar1, 50 * SK_Scalar1, 220 50 * SK_Scalar1, 50 * SK_Scalar1); 221 fPaths.push_back().moveTo(50 * SK_Scalar1, 50 * SK_Scalar1); 222 fPaths.back().cubicTo(50 * SK_Scalar1, 50 * SK_Scalar1, 223 50 * SK_Scalar1, 50 * SK_Scalar1, 224 50 * SK_Scalar1, 50 * SK_Scalar1); 225 226 // moveTo only paths 227 fPaths.push_back().moveTo(0, 0); 228 fPaths.back().moveTo(0, 0); 229 fPaths.back().moveTo(SK_Scalar1, SK_Scalar1); 230 fPaths.back().moveTo(SK_Scalar1, SK_Scalar1); 231 fPaths.back().moveTo(10 * SK_Scalar1, 10 * SK_Scalar1); 232 233 fPaths.push_back().moveTo(0, 0); 234 fPaths.back().moveTo(0, 0); 235 236 // line degenerate 237 fPaths.push_back().lineTo(100 * SK_Scalar1, 100 * SK_Scalar1); 238 fPaths.push_back().quadTo(100 * SK_Scalar1, 100 * SK_Scalar1, 0, 0); 239 fPaths.push_back().quadTo(100 * SK_Scalar1, 100 * SK_Scalar1, 240 50 * SK_Scalar1, 50 * SK_Scalar1); 241 fPaths.push_back().quadTo(50 * SK_Scalar1, 50 * SK_Scalar1, 242 100 * SK_Scalar1, 100 * SK_Scalar1); 243 fPaths.push_back().cubicTo(0, 0, 244 0, 0, 245 100 * SK_Scalar1, 100 * SK_Scalar1); 246 247 // small circle. This is listed last so that it has device coords far 248 // from the origin (small area relative to x,y values). 249 fPaths.push_back().addCircle(0, 0, SkFloatToScalar(0.8f)); 250 } 251 252 virtual void onDraw(SkCanvas* canvas) { 253 this->makePaths(); 254 255 SkPaint paint; 256 paint.setAntiAlias(true); 257 SkRandom rand; 258 canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1); 259 for (int i = 0; i < fPaths.count(); ++i) { 260 canvas->save(); 261 // position the path, and make it at off-integer coords. 262 canvas->translate(SK_Scalar1 * 200 * (i % 5) + SK_Scalar1 / 4, 263 SK_Scalar1 * 200 * (i / 5) + 3 * SK_Scalar1 / 4); 264 SkColor color = rand.nextU(); 265 color |= 0xff000000; 266 paint.setColor(color); 267 SkASSERT(fPaths[i].isConvex()); 268 canvas->drawPath(fPaths[i], paint); 269 canvas->restore(); 270 } 271 } 272 273private: 274 typedef GM INHERITED; 275 SkTArray<SkPath> fPaths; 276}; 277 278////////////////////////////////////////////////////////////////////////////// 279 280static GM* MyFactory(void*) { return new ConvexPathsGM; } 281static GMRegistry reg(MyFactory); 282 283} 284