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