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