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 14 namespace skiagm { 15 16 class DegenerateSegmentsGM : public GM { 17 public: 18 DegenerateSegmentsGM() {} 19 20 protected: 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 SkFont font(sk_tool_utils::create_portable_typeface(), 15); 293 const char title[] = "Random Paths Drawn Into Rectangle Clips With " 294 "Indicated Style, Fill and Linecaps, " 295 "with Stroke width 6"; 296 canvas->drawString(title, 20, 20, font, titlePaint); 297 298 SkRandom rand; 299 SkRect rect = SkRect::MakeWH(220*SK_Scalar1, 50*SK_Scalar1); 300 canvas->save(); 301 canvas->translate(2*SK_Scalar1, 30 * SK_Scalar1); // The title 302 canvas->save(); 303 unsigned numSegments = SK_ARRAY_COUNT(gSegmentFunctions); 304 unsigned numCaps = SK_ARRAY_COUNT(gCaps); 305 unsigned numStyles = SK_ARRAY_COUNT(gStyles); 306 unsigned numFills = SK_ARRAY_COUNT(gFills); 307 for (size_t row = 0; row < 6; ++row) { 308 if (0 < row) { 309 canvas->translate(0, rect.height() + 100*SK_Scalar1); 310 } 311 canvas->save(); 312 for (size_t column = 0; column < 4; ++column) { 313 if (0 < column) { 314 canvas->translate(rect.width() + 4*SK_Scalar1, 0); 315 } 316 317 SkColor color = sk_tool_utils::color_to_565(0xff007000); 318 StyleAndName style = gStyles[(rand.nextU() >> 16) % numStyles]; 319 CapAndName cap = gCaps[(rand.nextU() >> 16) % numCaps]; 320 FillAndName fill = gFills[(rand.nextU() >> 16) % numFills]; 321 SkPath path; 322 unsigned s1 = (rand.nextU() >> 16) % numSegments; 323 unsigned s2 = (rand.nextU() >> 16) % numSegments; 324 unsigned s3 = (rand.nextU() >> 16) % numSegments; 325 unsigned s4 = (rand.nextU() >> 16) % numSegments; 326 unsigned s5 = (rand.nextU() >> 16) % numSegments; 327 SkPoint pt = SkPoint::Make(10*SK_Scalar1, 0); 328 pt = gSegmentFunctions[s1](path, pt); 329 pt = gSegmentFunctions[s2](path, pt); 330 pt = gSegmentFunctions[s3](path, pt); 331 pt = gSegmentFunctions[s4](path, pt); 332 pt = gSegmentFunctions[s5](path, pt); 333 334 this->drawPath(path, canvas, color, rect, 335 cap.fCap, cap.fJoin, style.fStyle, 336 fill.fFill, SK_Scalar1*6); 337 338 SkPaint rectPaint; 339 rectPaint.setColor(SK_ColorBLACK); 340 rectPaint.setStyle(SkPaint::kStroke_Style); 341 rectPaint.setStrokeWidth(-1); 342 rectPaint.setAntiAlias(true); 343 canvas->drawRect(rect, rectPaint); 344 345 SkPaint labelPaint; 346 labelPaint.setColor(color); 347 labelPaint.setAntiAlias(true); 348 font.setSize(10); 349 canvas->drawString(style.fName, 0, rect.height() + 12, font, labelPaint); 350 canvas->drawString(fill.fName, 0, rect.height() + 24, font, labelPaint); 351 canvas->drawString(cap.fName, 0, rect.height() + 36, font, labelPaint); 352 canvas->drawString(gSegmentNames[s1], 0, rect.height() + 48, font, labelPaint); 353 canvas->drawString(gSegmentNames[s2], 0, rect.height() + 60, font, labelPaint); 354 canvas->drawString(gSegmentNames[s3], 0, rect.height() + 72, font, labelPaint); 355 canvas->drawString(gSegmentNames[s4], 0, rect.height() + 84, font, labelPaint); 356 canvas->drawString(gSegmentNames[s5], 0, rect.height() + 96, font, labelPaint); 357 } 358 canvas->restore(); 359 } 360 canvas->restore(); 361 canvas->restore(); 362 } 363 364 private: 365 typedef GM INHERITED; 366 }; 367 368 ////////////////////////////////////////////////////////////////////////////// 369 370 DEF_GM( return new DegenerateSegmentsGM; ) 371 372 } 373