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#include "gm.h" 8#include "SkCanvas.h" 9#include "SkPaint.h" 10#include "SkPath.h" 11#include "SkRandom.h" 12 13namespace skiagm { 14 15class DegenerateSegmentsGM : public GM { 16public: 17 DegenerateSegmentsGM() {} 18 19protected: 20 struct PathAndName { 21 SkPath fPath; 22 const char* fName1; 23 const char* fName2; 24 }; 25 26 SkString onShortName() { 27 return SkString("degeneratesegments"); 28 } 29 30 SkISize onISize() { return SkISize::Make(896, 930); } 31 32 typedef SkPoint (*AddSegmentFunc)(SkPath&, SkPoint&); 33 34 // We need to use explicit commands here, instead of addPath, because we 35 // do not want the moveTo that is added at the beginning of a path to 36 // appear in the appended path. 37 static SkPoint AddMove(SkPath& path, SkPoint& startPt) { 38 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 39 path.moveTo(moveToPt); 40 return moveToPt; 41 } 42 43 static SkPoint AddMoveClose(SkPath& path, SkPoint& startPt) { 44 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 45 path.moveTo(moveToPt); 46 path.close(); 47 return moveToPt; 48 } 49 50 static SkPoint AddDegenLine(SkPath& path, SkPoint& startPt) { 51 path.lineTo(startPt); 52 return startPt; 53 } 54 55 static SkPoint AddMoveDegenLine(SkPath& path, SkPoint& startPt) { 56 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 57 path.moveTo(moveToPt); 58 path.lineTo(moveToPt); 59 return moveToPt; 60 } 61 62 static SkPoint AddMoveDegenLineClose(SkPath& path, SkPoint& startPt) { 63 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 64 path.moveTo(moveToPt); 65 path.lineTo(moveToPt); 66 path.close(); 67 return moveToPt; 68 } 69 70 static SkPoint AddDegenQuad(SkPath& path, SkPoint& startPt) { 71 path.quadTo(startPt, startPt); 72 return startPt; 73 } 74 75 static SkPoint AddMoveDegenQuad(SkPath& path, SkPoint& startPt) { 76 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 77 path.moveTo(moveToPt); 78 path.quadTo(moveToPt, moveToPt); 79 return moveToPt; 80 } 81 82 static SkPoint AddMoveDegenQuadClose(SkPath& path, SkPoint& startPt) { 83 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 84 path.moveTo(moveToPt); 85 path.quadTo(moveToPt, moveToPt); 86 path.close(); 87 return moveToPt; 88 } 89 90 static SkPoint AddDegenCubic(SkPath& path, SkPoint& startPt) { 91 path.cubicTo(startPt, startPt, startPt); 92 return startPt; 93 } 94 95 static SkPoint AddMoveDegenCubic(SkPath& path, SkPoint& startPt) { 96 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 97 path.moveTo(moveToPt); 98 path.cubicTo(moveToPt, moveToPt, moveToPt); 99 return moveToPt; 100 } 101 102 static SkPoint AddMoveDegenCubicClose(SkPath& path, SkPoint& startPt) { 103 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 104 path.moveTo(moveToPt); 105 path.cubicTo(moveToPt, moveToPt, moveToPt); 106 path.close(); 107 return moveToPt; 108 } 109 110 static SkPoint AddClose(SkPath& path, SkPoint& startPt) { 111 path.close(); 112 return startPt; 113 } 114 115 static SkPoint AddLine(SkPath& path, SkPoint& startPt) { 116 SkPoint endPt = startPt + SkPoint::Make(40*SK_Scalar1, 0); 117 path.lineTo(endPt); 118 return endPt; 119 } 120 121 static SkPoint AddMoveLine(SkPath& path, SkPoint& startPt) { 122 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 123 SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0); 124 path.moveTo(moveToPt); 125 path.lineTo(endPt); 126 return endPt; 127 } 128 129 static SkPoint AddMoveLineClose(SkPath& path, SkPoint& startPt) { 130 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 131 SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0); 132 path.moveTo(moveToPt); 133 path.lineTo(endPt); 134 path.close(); 135 return endPt; 136 } 137 138 static SkPoint AddQuad(SkPath& path, SkPoint& startPt) { 139 SkPoint midPt = startPt + SkPoint::Make(20*SK_Scalar1, 5*SK_Scalar1); 140 SkPoint endPt = startPt + SkPoint::Make(40*SK_Scalar1, 0); 141 path.quadTo(midPt, endPt); 142 return endPt; 143 } 144 145 static SkPoint AddMoveQuad(SkPath& path, SkPoint& startPt) { 146 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 147 SkPoint midPt = moveToPt + SkPoint::Make(20*SK_Scalar1, 5*SK_Scalar1); 148 SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0); 149 path.moveTo(moveToPt); 150 path.quadTo(midPt, endPt); 151 return endPt; 152 } 153 154 static SkPoint AddMoveQuadClose(SkPath& path, SkPoint& startPt) { 155 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 156 SkPoint midPt = moveToPt + SkPoint::Make(20*SK_Scalar1, 5*SK_Scalar1); 157 SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0); 158 path.moveTo(moveToPt); 159 path.quadTo(midPt, endPt); 160 path.close(); 161 return endPt; 162 } 163 164 static SkPoint AddCubic(SkPath& path, SkPoint& startPt) { 165 SkPoint t1Pt = startPt + SkPoint::Make(15*SK_Scalar1, 5*SK_Scalar1); 166 SkPoint t2Pt = startPt + SkPoint::Make(25*SK_Scalar1, 5*SK_Scalar1); 167 SkPoint endPt = startPt + SkPoint::Make(40*SK_Scalar1, 0); 168 path.cubicTo(t1Pt, t2Pt, endPt); 169 return endPt; 170 } 171 172 static SkPoint AddMoveCubic(SkPath& path, SkPoint& startPt) { 173 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 174 SkPoint t1Pt = moveToPt + SkPoint::Make(15*SK_Scalar1, 5*SK_Scalar1); 175 SkPoint t2Pt = moveToPt + SkPoint::Make(25*SK_Scalar1, 5*SK_Scalar1); 176 SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0); 177 path.moveTo(moveToPt); 178 path.cubicTo(t1Pt, t2Pt, endPt); 179 return endPt; 180 } 181 182 static SkPoint AddMoveCubicClose(SkPath& path, SkPoint& startPt) { 183 SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1); 184 SkPoint t1Pt = moveToPt + SkPoint::Make(15*SK_Scalar1, 5*SK_Scalar1); 185 SkPoint t2Pt = moveToPt + SkPoint::Make(25*SK_Scalar1, 5*SK_Scalar1); 186 SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0); 187 path.moveTo(moveToPt); 188 path.cubicTo(t1Pt, t2Pt, endPt); 189 path.close(); 190 return endPt; 191 } 192 193 void drawPath(SkPath& path, SkCanvas* canvas, SkColor color, 194 const SkRect& clip, SkPaint::Cap cap, SkPaint::Join join, 195 SkPaint::Style style, SkPath::FillType fill, 196 SkScalar strokeWidth) { 197 path.setFillType(fill); 198 SkPaint paint; 199 paint.setStrokeCap(cap); 200 paint.setStrokeWidth(strokeWidth); 201 paint.setStrokeJoin(join); 202 paint.setColor(color); 203 paint.setStyle(style); 204 canvas->save(); 205 canvas->clipRect(clip); 206 canvas->drawPath(path, paint); 207 canvas->restore(); 208 } 209 210 virtual void onDraw(SkCanvas* canvas) { 211 static const AddSegmentFunc gSegmentFunctions[] = { 212 AddMove, 213 AddMoveClose, 214 AddDegenLine, 215 AddMoveDegenLine, 216 AddMoveDegenLineClose, 217 AddDegenQuad, 218 AddMoveDegenQuad, 219 AddMoveDegenQuadClose, 220 AddDegenCubic, 221 AddMoveDegenCubic, 222 AddMoveDegenCubicClose, 223 AddClose, 224 AddLine, 225 AddMoveLine, 226 AddMoveLineClose, 227 AddQuad, 228 AddMoveQuad, 229 AddMoveQuadClose, 230 AddCubic, 231 AddMoveCubic, 232 AddMoveCubicClose 233 }; 234 static const char* gSegmentNames[] = { 235 "Move", 236 "MoveClose", 237 "DegenLine", 238 "MoveDegenLine", 239 "MoveDegenLineClose", 240 "DegenQuad", 241 "MoveDegenQuad", 242 "MoveDegenQuadClose", 243 "DegenCubic", 244 "MoveDegenCubic", 245 "MoveDegenCubicClose", 246 "Close", 247 "Line", 248 "MoveLine", 249 "MoveLineClose", 250 "Quad", 251 "MoveQuad", 252 "MoveQuadClose", 253 "Cubic", 254 "MoveCubic", 255 "MoveCubicClose" 256 }; 257 258 struct FillAndName { 259 SkPath::FillType fFill; 260 const char* fName; 261 }; 262 static const FillAndName gFills[] = { 263 {SkPath::kWinding_FillType, "Winding"}, 264 {SkPath::kEvenOdd_FillType, "Even / Odd"}, 265 {SkPath::kInverseWinding_FillType, "Inverse Winding"}, 266 {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"} 267 }; 268 struct StyleAndName { 269 SkPaint::Style fStyle; 270 const char* fName; 271 }; 272 static const StyleAndName gStyles[] = { 273 {SkPaint::kFill_Style, "Fill"}, 274 {SkPaint::kStroke_Style, "Stroke 10"}, 275 {SkPaint::kStrokeAndFill_Style, "Stroke 10 And Fill"} 276 }; 277 struct CapAndName { 278 SkPaint::Cap fCap; 279 SkPaint::Join fJoin; 280 const char* fName; 281 }; 282 static const CapAndName gCaps[] = { 283 {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"}, 284 {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"}, 285 {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"} 286 }; 287 288 SkPaint titlePaint; 289 titlePaint.setColor(SK_ColorBLACK); 290 titlePaint.setAntiAlias(true); 291 sk_tool_utils::set_portable_typeface(&titlePaint); 292 titlePaint.setTextSize(15 * SK_Scalar1); 293 const char title[] = "Random Paths Drawn Into Rectangle Clips With " 294 "Indicated Style, Fill and Linecaps, " 295 "with Stroke width 6"; 296 canvas->drawText(title, strlen(title), 297 20 * SK_Scalar1, 298 20 * SK_Scalar1, 299 titlePaint); 300 301 SkRandom rand; 302 SkRect rect = SkRect::MakeWH(220*SK_Scalar1, 50*SK_Scalar1); 303 canvas->save(); 304 canvas->translate(2*SK_Scalar1, 30 * SK_Scalar1); // The title 305 canvas->save(); 306 unsigned numSegments = SK_ARRAY_COUNT(gSegmentFunctions); 307 unsigned numCaps = SK_ARRAY_COUNT(gCaps); 308 unsigned numStyles = SK_ARRAY_COUNT(gStyles); 309 unsigned numFills = SK_ARRAY_COUNT(gFills); 310 for (size_t row = 0; row < 6; ++row) { 311 if (0 < row) { 312 canvas->translate(0, rect.height() + 100*SK_Scalar1); 313 } 314 canvas->save(); 315 for (size_t column = 0; column < 4; ++column) { 316 if (0 < column) { 317 canvas->translate(rect.width() + 4*SK_Scalar1, 0); 318 } 319 320 SkColor color = sk_tool_utils::color_to_565(0xff007000); 321 StyleAndName style = gStyles[(rand.nextU() >> 16) % numStyles]; 322 CapAndName cap = gCaps[(rand.nextU() >> 16) % numCaps]; 323 FillAndName fill = gFills[(rand.nextU() >> 16) % numFills]; 324 SkPath path; 325 unsigned s1 = (rand.nextU() >> 16) % numSegments; 326 unsigned s2 = (rand.nextU() >> 16) % numSegments; 327 unsigned s3 = (rand.nextU() >> 16) % numSegments; 328 unsigned s4 = (rand.nextU() >> 16) % numSegments; 329 unsigned s5 = (rand.nextU() >> 16) % numSegments; 330 SkPoint pt = SkPoint::Make(10*SK_Scalar1, 0); 331 pt = gSegmentFunctions[s1](path, pt); 332 pt = gSegmentFunctions[s2](path, pt); 333 pt = gSegmentFunctions[s3](path, pt); 334 pt = gSegmentFunctions[s4](path, pt); 335 pt = gSegmentFunctions[s5](path, pt); 336 337 this->drawPath(path, canvas, color, rect, 338 cap.fCap, cap.fJoin, style.fStyle, 339 fill.fFill, SK_Scalar1*6); 340 341 SkPaint rectPaint; 342 rectPaint.setColor(SK_ColorBLACK); 343 rectPaint.setStyle(SkPaint::kStroke_Style); 344 rectPaint.setStrokeWidth(-1); 345 rectPaint.setAntiAlias(true); 346 canvas->drawRect(rect, rectPaint); 347 348 SkPaint labelPaint; 349 labelPaint.setColor(color); 350 labelPaint.setAntiAlias(true); 351 sk_tool_utils::set_portable_typeface(&labelPaint); 352 labelPaint.setTextSize(10 * SK_Scalar1); 353 canvas->drawText(style.fName, 354 strlen(style.fName), 355 0, rect.height() + 12 * SK_Scalar1, 356 labelPaint); 357 canvas->drawText(fill.fName, 358 strlen(fill.fName), 359 0, rect.height() + 24 * SK_Scalar1, 360 labelPaint); 361 canvas->drawText(cap.fName, 362 strlen(cap.fName), 363 0, rect.height() + 36 * SK_Scalar1, 364 labelPaint); 365 canvas->drawText(gSegmentNames[s1], 366 strlen(gSegmentNames[s1]), 367 0, rect.height() + 48 * SK_Scalar1, 368 labelPaint); 369 canvas->drawText(gSegmentNames[s2], 370 strlen(gSegmentNames[s2]), 371 0, rect.height() + 60 * SK_Scalar1, 372 labelPaint); 373 canvas->drawText(gSegmentNames[s3], 374 strlen(gSegmentNames[s3]), 375 0, rect.height() + 72 * SK_Scalar1, 376 labelPaint); 377 canvas->drawText(gSegmentNames[s4], 378 strlen(gSegmentNames[s4]), 379 0, rect.height() + 84 * SK_Scalar1, 380 labelPaint); 381 canvas->drawText(gSegmentNames[s5], 382 strlen(gSegmentNames[s5]), 383 0, rect.height() + 96 * SK_Scalar1, 384 labelPaint); 385 } 386 canvas->restore(); 387 } 388 canvas->restore(); 389 canvas->restore(); 390 } 391 392private: 393 typedef GM INHERITED; 394}; 395 396////////////////////////////////////////////////////////////////////////////// 397 398static GM* MyFactory(void*) { return new DegenerateSegmentsGM; } 399static GMRegistry reg(MyFactory); 400 401} 402