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.h" 9 #include "sk_tool_utils.h" 10 #include "SkCanvas.h" 11 #include "SkPaint.h" 12 #include "SkPath.h" 13 #include "SkRandom.h" 14 15 // https://bug.skia.org/1316 shows that this cubic, when slightly clipped, creates big 16 // (incorrect) changes to its control points. 17 class ClippedCubicGM : public skiagm::GM { 18 public: 19 ClippedCubicGM() {} 20 21 protected: 22 23 SkString onShortName() { 24 return SkString("clippedcubic"); 25 } 26 27 SkISize onISize() { return SkISize::Make(1240, 390); } 28 29 virtual void onDraw(SkCanvas* canvas) { 30 SkPath path; 31 path.moveTo(0, 0); 32 path.cubicTo(140, 150, 40, 10, 170, 150); 33 34 SkPaint paint; 35 SkRect bounds = path.getBounds(); 36 37 for (SkScalar dy = -1; dy <= 1; dy += 1) { 38 canvas->save(); 39 for (SkScalar dx = -1; dx <= 1; dx += 1) { 40 canvas->save(); 41 canvas->clipRect(bounds); 42 canvas->translate(dx, dy); 43 canvas->drawPath(path, paint); 44 canvas->restore(); 45 46 canvas->translate(bounds.width(), 0); 47 } 48 canvas->restore(); 49 canvas->translate(0, bounds.height()); 50 } 51 } 52 53 private: 54 typedef skiagm::GM INHERITED; 55 }; 56 57 58 class ClippedCubic2GM : public skiagm::GM { 59 public: 60 ClippedCubic2GM() {} 61 62 protected: 63 64 SkString onShortName() override { 65 return SkString("clippedcubic2"); 66 } 67 68 SkISize onISize() override { return SkISize::Make(1240, 390); } 69 70 void onDraw(SkCanvas* canvas) override { 71 canvas->save(); 72 canvas->translate(-2, 120); 73 drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 150)); 74 canvas->translate(0, 170); 75 drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 100)); 76 canvas->translate(0, 170); 77 drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 30, 150)); 78 canvas->translate(0, 170); 79 drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 10, 150)); 80 canvas->restore(); 81 canvas->save(); 82 canvas->translate(20, -2); 83 drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 150, 80)); 84 canvas->translate(170, 0); 85 drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 100, 80)); 86 canvas->translate(170, 0); 87 drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 150, 30)); 88 canvas->translate(170, 0); 89 drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 150, 10)); 90 canvas->restore(); 91 } 92 93 void drawOne(SkCanvas* canvas, const SkPath& path, const SkRect& clip) { 94 SkPaint framePaint, fillPaint; 95 framePaint.setStyle(SkPaint::kStroke_Style); 96 canvas->drawRect(clip, framePaint); 97 canvas->drawPath(path, framePaint); 98 canvas->save(); 99 canvas->clipRect(clip); 100 canvas->drawPath(path, fillPaint); 101 canvas->restore(); 102 } 103 104 void onOnceBeforeDraw() override { 105 fPath.moveTo(69.7030518991886f, 0); 106 fPath.cubicTo( 69.7030518991886f, 21.831149999999997f, 107 58.08369508178456f, 43.66448333333333f, 34.8449814469765f, 65.5f); 108 fPath.cubicTo( 11.608591683531916f, 87.33115f, -0.010765133872116195f, 109.16448333333332f, 109 -0.013089005235602302f, 131); 110 fPath.close(); 111 fFlipped = fPath; 112 SkMatrix matrix; 113 matrix.reset(); 114 matrix.setScaleX(0); 115 matrix.setScaleY(0); 116 matrix.setSkewX(1); 117 matrix.setSkewY(1); 118 fFlipped.transform(matrix); 119 } 120 121 SkPath fPath; 122 SkPath fFlipped; 123 private: 124 typedef skiagm::GM INHERITED; 125 }; 126 127 128 class CubicPathGM : public skiagm::GM { 129 public: 130 CubicPathGM() {} 131 132 protected: 133 134 SkString onShortName() { 135 return SkString("cubicpath"); 136 } 137 138 SkISize onISize() { return SkISize::Make(1240, 390); } 139 140 void drawPath(SkPath& path,SkCanvas* canvas,SkColor color, 141 const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join, 142 SkPaint::Style style, SkPath::FillType fill, 143 SkScalar strokeWidth) { 144 path.setFillType(fill); 145 SkPaint paint; 146 paint.setStrokeCap(cap); 147 paint.setStrokeWidth(strokeWidth); 148 paint.setStrokeJoin(join); 149 paint.setColor(color); 150 paint.setStyle(style); 151 canvas->save(); 152 canvas->clipRect(clip); 153 canvas->drawPath(path, paint); 154 canvas->restore(); 155 } 156 157 virtual void onDraw(SkCanvas* canvas) { 158 struct FillAndName { 159 SkPath::FillType fFill; 160 const char* fName; 161 }; 162 constexpr FillAndName gFills[] = { 163 {SkPath::kWinding_FillType, "Winding"}, 164 {SkPath::kEvenOdd_FillType, "Even / Odd"}, 165 {SkPath::kInverseWinding_FillType, "Inverse Winding"}, 166 {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"}, 167 }; 168 struct StyleAndName { 169 SkPaint::Style fStyle; 170 const char* fName; 171 }; 172 constexpr StyleAndName gStyles[] = { 173 {SkPaint::kFill_Style, "Fill"}, 174 {SkPaint::kStroke_Style, "Stroke"}, 175 {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"}, 176 }; 177 struct CapAndName { 178 SkPaint::Cap fCap; 179 SkPaint::Join fJoin; 180 const char* fName; 181 }; 182 constexpr CapAndName gCaps[] = { 183 {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"}, 184 {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"}, 185 {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"} 186 }; 187 struct PathAndName { 188 SkPath fPath; 189 const char* fName; 190 }; 191 PathAndName path; 192 path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1); 193 path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1, 194 60*SK_Scalar1, 20*SK_Scalar1, 195 75*SK_Scalar1, 10*SK_Scalar1); 196 path.fName = "moveTo-cubic"; 197 198 SkPaint titlePaint; 199 titlePaint.setColor(SK_ColorBLACK); 200 titlePaint.setAntiAlias(true); 201 sk_tool_utils::set_portable_typeface(&titlePaint); 202 titlePaint.setTextSize(15 * SK_Scalar1); 203 const char title[] = "Cubic Drawn Into Rectangle Clips With " 204 "Indicated Style, Fill and Linecaps, with stroke width 10"; 205 canvas->drawString(title, 206 20 * SK_Scalar1, 207 20 * SK_Scalar1, 208 titlePaint); 209 210 SkRandom rand; 211 SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1); 212 canvas->save(); 213 canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1); 214 canvas->save(); 215 for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) { 216 if (0 < cap) { 217 canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0); 218 } 219 canvas->save(); 220 for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) { 221 if (0 < fill) { 222 canvas->translate(0, rect.height() + 40 * SK_Scalar1); 223 } 224 canvas->save(); 225 for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) { 226 if (0 < style) { 227 canvas->translate(rect.width() + 40 * SK_Scalar1, 0); 228 } 229 230 SkColor color = 0xff007000; 231 this->drawPath(path.fPath, canvas, color, rect, 232 gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle, 233 gFills[fill].fFill, SK_Scalar1*10); 234 235 SkPaint rectPaint; 236 rectPaint.setColor(SK_ColorBLACK); 237 rectPaint.setStyle(SkPaint::kStroke_Style); 238 rectPaint.setStrokeWidth(-1); 239 rectPaint.setAntiAlias(true); 240 canvas->drawRect(rect, rectPaint); 241 242 SkPaint labelPaint; 243 labelPaint.setColor(color); 244 labelPaint.setAntiAlias(true); 245 sk_tool_utils::set_portable_typeface(&labelPaint); 246 labelPaint.setTextSize(10 * SK_Scalar1); 247 canvas->drawString(gStyles[style].fName, 248 0, rect.height() + 12 * SK_Scalar1, 249 labelPaint); 250 canvas->drawString(gFills[fill].fName, 251 0, rect.height() + 24 * SK_Scalar1, 252 labelPaint); 253 canvas->drawString(gCaps[cap].fName, 254 0, rect.height() + 36 * SK_Scalar1, 255 labelPaint); 256 } 257 canvas->restore(); 258 } 259 canvas->restore(); 260 } 261 canvas->restore(); 262 canvas->restore(); 263 } 264 265 private: 266 typedef skiagm::GM INHERITED; 267 }; 268 269 class CubicClosePathGM : public skiagm::GM { 270 public: 271 CubicClosePathGM() {} 272 273 protected: 274 275 SkString onShortName() { 276 return SkString("cubicclosepath"); 277 } 278 279 SkISize onISize() { return SkISize::Make(1240, 390); } 280 281 void drawPath(SkPath& path,SkCanvas* canvas,SkColor color, 282 const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join, 283 SkPaint::Style style, SkPath::FillType fill, 284 SkScalar strokeWidth) { 285 path.setFillType(fill); 286 SkPaint paint; 287 paint.setStrokeCap(cap); 288 paint.setStrokeWidth(strokeWidth); 289 paint.setStrokeJoin(join); 290 paint.setColor(color); 291 paint.setStyle(style); 292 canvas->save(); 293 canvas->clipRect(clip); 294 canvas->drawPath(path, paint); 295 canvas->restore(); 296 } 297 298 virtual void onDraw(SkCanvas* canvas) { 299 struct FillAndName { 300 SkPath::FillType fFill; 301 const char* fName; 302 }; 303 constexpr FillAndName gFills[] = { 304 {SkPath::kWinding_FillType, "Winding"}, 305 {SkPath::kEvenOdd_FillType, "Even / Odd"}, 306 {SkPath::kInverseWinding_FillType, "Inverse Winding"}, 307 {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"}, 308 }; 309 struct StyleAndName { 310 SkPaint::Style fStyle; 311 const char* fName; 312 }; 313 constexpr StyleAndName gStyles[] = { 314 {SkPaint::kFill_Style, "Fill"}, 315 {SkPaint::kStroke_Style, "Stroke"}, 316 {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"}, 317 }; 318 struct CapAndName { 319 SkPaint::Cap fCap; 320 SkPaint::Join fJoin; 321 const char* fName; 322 }; 323 constexpr CapAndName gCaps[] = { 324 {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"}, 325 {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"}, 326 {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"} 327 }; 328 struct PathAndName { 329 SkPath fPath; 330 const char* fName; 331 }; 332 PathAndName path; 333 path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1); 334 path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1, 335 60*SK_Scalar1, 20*SK_Scalar1, 336 75*SK_Scalar1, 10*SK_Scalar1); 337 path.fPath.close(); 338 path.fName = "moveTo-cubic-close"; 339 340 SkPaint titlePaint; 341 titlePaint.setColor(SK_ColorBLACK); 342 titlePaint.setAntiAlias(true); 343 sk_tool_utils::set_portable_typeface(&titlePaint); 344 titlePaint.setTextSize(15 * SK_Scalar1); 345 const char title[] = "Cubic Closed Drawn Into Rectangle Clips With " 346 "Indicated Style, Fill and Linecaps, with stroke width 10"; 347 canvas->drawString(title, 348 20 * SK_Scalar1, 349 20 * SK_Scalar1, 350 titlePaint); 351 352 SkRandom rand; 353 SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1); 354 canvas->save(); 355 canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1); 356 canvas->save(); 357 for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) { 358 if (0 < cap) { 359 canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0); 360 } 361 canvas->save(); 362 for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) { 363 if (0 < fill) { 364 canvas->translate(0, rect.height() + 40 * SK_Scalar1); 365 } 366 canvas->save(); 367 for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) { 368 if (0 < style) { 369 canvas->translate(rect.width() + 40 * SK_Scalar1, 0); 370 } 371 372 SkColor color = 0xff007000; 373 this->drawPath(path.fPath, canvas, color, rect, 374 gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle, 375 gFills[fill].fFill, SK_Scalar1*10); 376 377 SkPaint rectPaint; 378 rectPaint.setColor(SK_ColorBLACK); 379 rectPaint.setStyle(SkPaint::kStroke_Style); 380 rectPaint.setStrokeWidth(-1); 381 rectPaint.setAntiAlias(true); 382 canvas->drawRect(rect, rectPaint); 383 384 SkPaint labelPaint; 385 labelPaint.setColor(color); 386 labelPaint.setAntiAlias(true); 387 sk_tool_utils::set_portable_typeface(&labelPaint); 388 labelPaint.setTextSize(10 * SK_Scalar1); 389 canvas->drawString(gStyles[style].fName, 390 0, rect.height() + 12 * SK_Scalar1, 391 labelPaint); 392 canvas->drawString(gFills[fill].fName, 393 0, rect.height() + 24 * SK_Scalar1, 394 labelPaint); 395 canvas->drawString(gCaps[cap].fName, 396 0, rect.height() + 36 * SK_Scalar1, 397 labelPaint); 398 } 399 canvas->restore(); 400 } 401 canvas->restore(); 402 } 403 canvas->restore(); 404 canvas->restore(); 405 } 406 407 private: 408 typedef skiagm::GM INHERITED; 409 }; 410 411 DEF_SIMPLE_GM(bug5099, canvas, 50, 50) { 412 SkPaint p; 413 p.setColor(SK_ColorRED); 414 p.setAntiAlias(true); 415 p.setStyle(SkPaint::kStroke_Style); 416 p.setStrokeWidth(10); 417 418 SkPath path; 419 path.moveTo(6, 27); 420 path.cubicTo(31.5f, 1.5f, 3.5f, 4.5f, 29, 29); 421 canvas->drawPath(path, p); 422 } 423 424 DEF_SIMPLE_GM(bug6083, canvas, 100, 50) { 425 SkPaint p; 426 p.setColor(SK_ColorRED); 427 p.setAntiAlias(true); 428 p.setStyle(SkPaint::kStroke_Style); 429 p.setStrokeWidth(15); 430 canvas->translate(-500, -130); 431 SkPath path; 432 path.moveTo(500.988f, 155.200f); 433 path.lineTo(526.109f, 155.200f); 434 SkPoint p1 = { 526.109f, 155.200f }; 435 SkPoint p2 = { 525.968f, 212.968f }; 436 SkPoint p3 = { 526.109f, 241.840f }; 437 path.cubicTo(p1, p2, p3); 438 canvas->drawPath(path, p); 439 canvas->translate(50, 0); 440 path.reset(); 441 p2.set(525.968f, 213.172f); 442 path.moveTo(500.988f, 155.200f); 443 path.lineTo(526.109f, 155.200f); 444 path.cubicTo(p1, p2, p3); 445 canvas->drawPath(path, p); 446 } 447 448 ////////////////////////////////////////////////////////////////////////////// 449 450 DEF_GM( return new CubicPathGM; ) 451 DEF_GM( return new CubicClosePathGM; ) 452 DEF_GM( return new ClippedCubicGM; ) 453 DEF_GM( return new ClippedCubic2GM; ) 454