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. 17class ClippedCubicGM : public skiagm::GM { 18public: 19 ClippedCubicGM() {} 20 21protected: 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 53private: 54 typedef skiagm::GM INHERITED; 55}; 56 57 58class ClippedCubic2GM : public skiagm::GM { 59public: 60 ClippedCubic2GM() {} 61 62protected: 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; 123private: 124 typedef skiagm::GM INHERITED; 125}; 126 127 128class CubicPathGM : public skiagm::GM { 129public: 130 CubicPathGM() {} 131 132protected: 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->drawText(title, strlen(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->drawText(gStyles[style].fName, 248 strlen(gStyles[style].fName), 249 0, rect.height() + 12 * SK_Scalar1, 250 labelPaint); 251 canvas->drawText(gFills[fill].fName, 252 strlen(gFills[fill].fName), 253 0, rect.height() + 24 * SK_Scalar1, 254 labelPaint); 255 canvas->drawText(gCaps[cap].fName, 256 strlen(gCaps[cap].fName), 257 0, rect.height() + 36 * SK_Scalar1, 258 labelPaint); 259 } 260 canvas->restore(); 261 } 262 canvas->restore(); 263 } 264 canvas->restore(); 265 canvas->restore(); 266 } 267 268private: 269 typedef skiagm::GM INHERITED; 270}; 271 272class CubicClosePathGM : public skiagm::GM { 273public: 274 CubicClosePathGM() {} 275 276protected: 277 278 SkString onShortName() { 279 return SkString("cubicclosepath"); 280 } 281 282 SkISize onISize() { return SkISize::Make(1240, 390); } 283 284 void drawPath(SkPath& path,SkCanvas* canvas,SkColor color, 285 const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join, 286 SkPaint::Style style, SkPath::FillType fill, 287 SkScalar strokeWidth) { 288 path.setFillType(fill); 289 SkPaint paint; 290 paint.setStrokeCap(cap); 291 paint.setStrokeWidth(strokeWidth); 292 paint.setStrokeJoin(join); 293 paint.setColor(color); 294 paint.setStyle(style); 295 canvas->save(); 296 canvas->clipRect(clip); 297 canvas->drawPath(path, paint); 298 canvas->restore(); 299 } 300 301 virtual void onDraw(SkCanvas* canvas) { 302 struct FillAndName { 303 SkPath::FillType fFill; 304 const char* fName; 305 }; 306 constexpr FillAndName gFills[] = { 307 {SkPath::kWinding_FillType, "Winding"}, 308 {SkPath::kEvenOdd_FillType, "Even / Odd"}, 309 {SkPath::kInverseWinding_FillType, "Inverse Winding"}, 310 {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"}, 311 }; 312 struct StyleAndName { 313 SkPaint::Style fStyle; 314 const char* fName; 315 }; 316 constexpr StyleAndName gStyles[] = { 317 {SkPaint::kFill_Style, "Fill"}, 318 {SkPaint::kStroke_Style, "Stroke"}, 319 {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"}, 320 }; 321 struct CapAndName { 322 SkPaint::Cap fCap; 323 SkPaint::Join fJoin; 324 const char* fName; 325 }; 326 constexpr CapAndName gCaps[] = { 327 {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"}, 328 {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"}, 329 {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"} 330 }; 331 struct PathAndName { 332 SkPath fPath; 333 const char* fName; 334 }; 335 PathAndName path; 336 path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1); 337 path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1, 338 60*SK_Scalar1, 20*SK_Scalar1, 339 75*SK_Scalar1, 10*SK_Scalar1); 340 path.fPath.close(); 341 path.fName = "moveTo-cubic-close"; 342 343 SkPaint titlePaint; 344 titlePaint.setColor(SK_ColorBLACK); 345 titlePaint.setAntiAlias(true); 346 sk_tool_utils::set_portable_typeface(&titlePaint); 347 titlePaint.setTextSize(15 * SK_Scalar1); 348 const char title[] = "Cubic Closed Drawn Into Rectangle Clips With " 349 "Indicated Style, Fill and Linecaps, with stroke width 10"; 350 canvas->drawText(title, strlen(title), 351 20 * SK_Scalar1, 352 20 * SK_Scalar1, 353 titlePaint); 354 355 SkRandom rand; 356 SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1); 357 canvas->save(); 358 canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1); 359 canvas->save(); 360 for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) { 361 if (0 < cap) { 362 canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0); 363 } 364 canvas->save(); 365 for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) { 366 if (0 < fill) { 367 canvas->translate(0, rect.height() + 40 * SK_Scalar1); 368 } 369 canvas->save(); 370 for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) { 371 if (0 < style) { 372 canvas->translate(rect.width() + 40 * SK_Scalar1, 0); 373 } 374 375 SkColor color = 0xff007000; 376 this->drawPath(path.fPath, canvas, color, rect, 377 gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle, 378 gFills[fill].fFill, SK_Scalar1*10); 379 380 SkPaint rectPaint; 381 rectPaint.setColor(SK_ColorBLACK); 382 rectPaint.setStyle(SkPaint::kStroke_Style); 383 rectPaint.setStrokeWidth(-1); 384 rectPaint.setAntiAlias(true); 385 canvas->drawRect(rect, rectPaint); 386 387 SkPaint labelPaint; 388 labelPaint.setColor(color); 389 labelPaint.setAntiAlias(true); 390 sk_tool_utils::set_portable_typeface(&labelPaint); 391 labelPaint.setTextSize(10 * SK_Scalar1); 392 canvas->drawText(gStyles[style].fName, 393 strlen(gStyles[style].fName), 394 0, rect.height() + 12 * SK_Scalar1, 395 labelPaint); 396 canvas->drawText(gFills[fill].fName, 397 strlen(gFills[fill].fName), 398 0, rect.height() + 24 * SK_Scalar1, 399 labelPaint); 400 canvas->drawText(gCaps[cap].fName, 401 strlen(gCaps[cap].fName), 402 0, rect.height() + 36 * SK_Scalar1, 403 labelPaint); 404 } 405 canvas->restore(); 406 } 407 canvas->restore(); 408 } 409 canvas->restore(); 410 canvas->restore(); 411 } 412 413private: 414 typedef skiagm::GM INHERITED; 415}; 416 417DEF_SIMPLE_GM(bug5099, canvas, 50, 50) { 418 SkPaint p; 419 p.setColor(SK_ColorRED); 420 p.setAntiAlias(true); 421 p.setStyle(SkPaint::kStroke_Style); 422 p.setStrokeWidth(10); 423 424 SkPath path; 425 path.moveTo(6, 27); 426 path.cubicTo(31.5f, 1.5f, 3.5f, 4.5f, 29, 29); 427 canvas->drawPath(path, p); 428} 429 430DEF_SIMPLE_GM(bug6083, canvas, 100, 50) { 431 SkPaint p; 432 p.setColor(SK_ColorRED); 433 p.setAntiAlias(true); 434 p.setStyle(SkPaint::kStroke_Style); 435 p.setStrokeWidth(15); 436 canvas->translate(-500, -130); 437 SkPath path; 438 path.moveTo(500.988f, 155.200f); 439 path.lineTo(526.109f, 155.200f); 440 SkPoint p1 = { 526.109f, 155.200f }; 441 SkPoint p2 = { 525.968f, 212.968f }; 442 SkPoint p3 = { 526.109f, 241.840f }; 443 path.cubicTo(p1, p2, p3); 444 canvas->drawPath(path, p); 445 canvas->translate(50, 0); 446 path.reset(); 447 p2.set(525.968f, 213.172f); 448 path.moveTo(500.988f, 155.200f); 449 path.lineTo(526.109f, 155.200f); 450 path.cubicTo(p1, p2, p3); 451 canvas->drawPath(path, p); 452} 453 454////////////////////////////////////////////////////////////////////////////// 455 456DEF_GM( return new CubicPathGM; ) 457DEF_GM( return new CubicClosePathGM; ) 458DEF_GM( return new ClippedCubicGM; ) 459DEF_GM( return new ClippedCubic2GM; ) 460