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