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 "SkPath.h" 10 #include "SkRandom.h" 11 #include "SkDashPathEffect.h" 12 #include "SkParsePath.h" 13 14 #define W 400 15 #define H 400 16 #define N 50 17 18 static const SkScalar SW = SkIntToScalar(W); 19 static const SkScalar SH = SkIntToScalar(H); 20 21 static void rnd_rect(SkRect* r, SkPaint* paint, SkRandom& rand) { 22 SkScalar x = rand.nextUScalar1() * W; 23 SkScalar y = rand.nextUScalar1() * H; 24 SkScalar w = rand.nextUScalar1() * (W >> 2); 25 SkScalar h = rand.nextUScalar1() * (H >> 2); 26 SkScalar hoffset = rand.nextSScalar1(); 27 SkScalar woffset = rand.nextSScalar1(); 28 29 r->set(x, y, x + w, y + h); 30 r->offset(-w/2 + woffset, -h/2 + hoffset); 31 32 paint->setColor(rand.nextU()); 33 paint->setAlpha(0xFF); 34 } 35 36 37 class StrokesGM : public skiagm::GM { 38 public: 39 StrokesGM() {} 40 41 protected: 42 43 SkString onShortName() override { 44 return SkString("strokes_round"); 45 } 46 47 SkISize onISize() override { 48 return SkISize::Make(W, H*2); 49 } 50 51 void onDraw(SkCanvas* canvas) override { 52 SkPaint paint; 53 paint.setStyle(SkPaint::kStroke_Style); 54 paint.setStrokeWidth(SkIntToScalar(9)/2); 55 56 for (int y = 0; y < 2; y++) { 57 paint.setAntiAlias(!!y); 58 SkAutoCanvasRestore acr(canvas, true); 59 canvas->translate(0, SH * y); 60 canvas->clipRect(SkRect::MakeLTRB( 61 SkIntToScalar(2), SkIntToScalar(2) 62 , SW - SkIntToScalar(2), SH - SkIntToScalar(2) 63 )); 64 65 SkRandom rand; 66 for (int i = 0; i < N; i++) { 67 SkRect r; 68 rnd_rect(&r, &paint, rand); 69 canvas->drawOval(r, paint); 70 rnd_rect(&r, &paint, rand); 71 canvas->drawRoundRect(r, r.width()/4, r.height()/4, paint); 72 rnd_rect(&r, &paint, rand); 73 } 74 } 75 } 76 77 private: 78 typedef skiagm::GM INHERITED; 79 }; 80 81 /* See 82 https://code.google.com/p/chromium/issues/detail?id=422974 and 83 http://jsfiddle.net/1xnku3sg/2/ 84 */ 85 class ZeroLenStrokesGM : public skiagm::GM { 86 SkPath fMoveHfPath, fMoveZfPath, fDashedfPath, fRefPath[4]; 87 SkPath fCubicPath, fQuadPath, fLinePath; 88 protected: 89 void onOnceBeforeDraw() override { 90 91 SkAssertResult(SkParsePath::FromSVGString("M0,0h0M10,0h0M20,0h0", &fMoveHfPath)); 92 SkAssertResult(SkParsePath::FromSVGString("M0,0zM10,0zM20,0z", &fMoveZfPath)); 93 SkAssertResult(SkParsePath::FromSVGString("M0,0h25", &fDashedfPath)); 94 SkAssertResult(SkParsePath::FromSVGString("M 0 0 C 0 0 0 0 0 0", &fCubicPath)); 95 SkAssertResult(SkParsePath::FromSVGString("M 0 0 Q 0 0 0 0", &fQuadPath)); 96 SkAssertResult(SkParsePath::FromSVGString("M 0 0 L 0 0", &fLinePath)); 97 98 for (int i = 0; i < 3; ++i) { 99 fRefPath[0].addCircle(i * 10.f, 0, 5); 100 fRefPath[1].addCircle(i * 10.f, 0, 10); 101 fRefPath[2].addRect(i * 10.f - 4, -2, i * 10.f + 4, 6); 102 fRefPath[3].addRect(i * 10.f - 10, -10, i * 10.f + 10, 10); 103 } 104 } 105 106 SkString onShortName() override { 107 return SkString("zeroPath"); 108 } 109 110 SkISize onISize() override { 111 return SkISize::Make(W, H*2); 112 } 113 114 void onDraw(SkCanvas* canvas) override { 115 SkPaint fillPaint, strokePaint, dashPaint; 116 fillPaint.setAntiAlias(true); 117 strokePaint = fillPaint; 118 strokePaint.setStyle(SkPaint::kStroke_Style); 119 for (int i = 0; i < 2; ++i) { 120 fillPaint.setAlpha(255); 121 strokePaint.setAlpha(255); 122 strokePaint.setStrokeWidth(i ? 8.f : 10.f); 123 strokePaint.setStrokeCap(i ? SkPaint::kSquare_Cap : SkPaint::kRound_Cap); 124 canvas->save(); 125 canvas->translate(10 + i * 100.f, 10); 126 canvas->drawPath(fMoveHfPath, strokePaint); 127 canvas->translate(0, 20); 128 canvas->drawPath(fMoveZfPath, strokePaint); 129 dashPaint = strokePaint; 130 const SkScalar intervals[] = { 0, 10 }; 131 dashPaint.setPathEffect(SkDashPathEffect::Create(intervals, 2, 0))->unref(); 132 SkPath fillPath; 133 dashPaint.getFillPath(fDashedfPath, &fillPath); 134 canvas->translate(0, 20); 135 canvas->drawPath(fDashedfPath, dashPaint); 136 canvas->translate(0, 20); 137 canvas->drawPath(fRefPath[i * 2], fillPaint); 138 strokePaint.setStrokeWidth(20); 139 strokePaint.setAlpha(127); 140 canvas->translate(0, 50); 141 canvas->drawPath(fMoveHfPath, strokePaint); 142 canvas->translate(0, 30); 143 canvas->drawPath(fMoveZfPath, strokePaint); 144 canvas->translate(0, 30); 145 fillPaint.setAlpha(127); 146 canvas->drawPath(fRefPath[1 + i * 2], fillPaint); 147 canvas->translate(0, 30); 148 canvas->drawPath(fCubicPath, strokePaint); 149 canvas->translate(0, 30); 150 canvas->drawPath(fQuadPath, strokePaint); 151 canvas->translate(0, 30); 152 canvas->drawPath(fLinePath, strokePaint); 153 canvas->restore(); 154 } 155 } 156 157 private: 158 typedef skiagm::GM INHERITED; 159 }; 160 161 class TeenyStrokesGM : public skiagm::GM { 162 163 SkString onShortName() override { 164 return SkString("teenyStrokes"); 165 } 166 167 SkISize onISize() override { 168 return SkISize::Make(W, H*2); 169 } 170 171 static void line(SkScalar scale, SkCanvas* canvas, SkColor color) { 172 SkPaint p; 173 p.setAntiAlias(true); 174 p.setStyle(SkPaint::kStroke_Style); 175 p.setColor(color); 176 canvas->translate(50, 0); 177 canvas->save(); 178 p.setStrokeWidth(scale * 5); 179 canvas->scale(1 / scale, 1 / scale); 180 canvas->drawLine(20 * scale, 20 * scale, 20 * scale, 100 * scale, p); 181 canvas->drawLine(20 * scale, 20 * scale, 100 * scale, 100 * scale, p); 182 canvas->restore(); 183 } 184 185 void onDraw(SkCanvas* canvas) override { 186 line(0.00005f, canvas, SK_ColorBLACK); 187 line(0.000045f, canvas, SK_ColorRED); 188 line(0.0000035f, canvas, SK_ColorGREEN); 189 line(0.000003f, canvas, SK_ColorBLUE); 190 line(0.000002f, canvas, SK_ColorBLACK); 191 } 192 private: 193 typedef skiagm::GM INHERITED; 194 }; 195 196 DEF_SIMPLE_GM(CubicStroke, canvas, 384, 384) { 197 SkPaint p; 198 p.setAntiAlias(true); 199 p.setStyle(SkPaint::kStroke_Style); 200 p.setStrokeWidth(1.0720f); 201 SkPath path; 202 path.moveTo(-6000,-6000); 203 path.cubicTo(-3500,5500,-500,5500,2500,-6500); 204 canvas->drawPath(path, p); 205 p.setStrokeWidth(1.0721f); 206 canvas->translate(10, 10); 207 canvas->drawPath(path, p); 208 p.setStrokeWidth(1.0722f); 209 canvas->translate(10, 10); 210 canvas->drawPath(path, p); 211 } 212 213 DEF_SIMPLE_GM(zerolinestroke, canvas, 90, 120) { 214 SkPaint paint; 215 paint.setStyle(SkPaint::kStroke_Style); 216 paint.setStrokeWidth(20); 217 paint.setAntiAlias(true); 218 paint.setStrokeCap(SkPaint::kRound_Cap); 219 220 SkPath path; 221 path.moveTo(30, 90); 222 path.lineTo(30, 90); 223 path.lineTo(60, 90); 224 path.lineTo(60, 90); 225 canvas->drawPath(path, paint); 226 227 path.reset(); 228 path.moveTo(30, 30); 229 path.lineTo(60, 30); 230 canvas->drawPath(path, paint); 231 232 path.reset(); 233 path.moveTo(30, 60); 234 path.lineTo(30, 60); 235 path.lineTo(60, 60); 236 canvas->drawPath(path, paint); 237 } 238 239 DEF_SIMPLE_GM(quadcap, canvas, 200, 200) { 240 SkPaint p; 241 p.setAntiAlias(true); 242 p.setStyle(SkPaint::kStroke_Style); 243 p.setStrokeWidth(0); 244 SkPath path; 245 SkPoint pts[] = {{105.738571f,13.126318f}, 246 {105.738571f,13.126318f}, 247 {123.753784f,1.f}}; 248 SkVector tangent = pts[1] - pts[2]; 249 tangent.normalize(); 250 SkPoint pts2[3]; 251 memcpy(pts2, pts, sizeof(pts)); 252 const SkScalar capOutset = SK_ScalarPI / 8; 253 pts2[0].fX += tangent.fX * capOutset; 254 pts2[0].fY += tangent.fY * capOutset; 255 pts2[1].fX += tangent.fX * capOutset; 256 pts2[1].fY += tangent.fY * capOutset; 257 pts2[2].fX += -tangent.fX * capOutset; 258 pts2[2].fY += -tangent.fY * capOutset; 259 path.moveTo(pts2[0]); 260 path.quadTo(pts2[1], pts2[2]); 261 canvas->drawPath(path, p); 262 263 path.reset(); 264 path.moveTo(pts[0]); 265 path.quadTo(pts[1], pts[2]); 266 p.setStrokeCap(SkPaint::kRound_Cap); 267 canvas->translate(30, 0); 268 canvas->drawPath(path, p); 269 } 270 271 class Strokes2GM : public skiagm::GM { 272 SkPath fPath; 273 protected: 274 void onOnceBeforeDraw() override { 275 SkRandom rand; 276 fPath.moveTo(0, 0); 277 for (int i = 0; i < 13; i++) { 278 SkScalar x = rand.nextUScalar1() * (W >> 1); 279 SkScalar y = rand.nextUScalar1() * (H >> 1); 280 fPath.lineTo(x, y); 281 } 282 } 283 284 285 SkString onShortName() override { 286 return SkString("strokes_poly"); 287 } 288 289 SkISize onISize() override { 290 return SkISize::Make(W, H*2); 291 } 292 293 static void rotate(SkScalar angle, SkScalar px, SkScalar py, SkCanvas* canvas) { 294 SkMatrix matrix; 295 matrix.setRotate(angle, px, py); 296 canvas->concat(matrix); 297 } 298 299 void onDraw(SkCanvas* canvas) override { 300 canvas->drawColor(SK_ColorWHITE); 301 302 SkPaint paint; 303 paint.setStyle(SkPaint::kStroke_Style); 304 paint.setStrokeWidth(SkIntToScalar(9)/2); 305 306 for (int y = 0; y < 2; y++) { 307 paint.setAntiAlias(!!y); 308 SkAutoCanvasRestore acr(canvas, true); 309 canvas->translate(0, SH * y); 310 canvas->clipRect(SkRect::MakeLTRB(SkIntToScalar(2), 311 SkIntToScalar(2), 312 SW - SkIntToScalar(2), 313 SH - SkIntToScalar(2))); 314 315 SkRandom rand; 316 for (int i = 0; i < N/2; i++) { 317 SkRect r; 318 rnd_rect(&r, &paint, rand); 319 rotate(SkIntToScalar(15), SW/2, SH/2, canvas); 320 canvas->drawPath(fPath, paint); 321 } 322 } 323 } 324 325 private: 326 typedef skiagm::GM INHERITED; 327 }; 328 329 ////////////////////////////////////////////////////////////////////////////// 330 331 static SkRect inset(const SkRect& r) { 332 SkRect rr(r); 333 rr.inset(r.width()/10, r.height()/10); 334 return rr; 335 } 336 337 class Strokes3GM : public skiagm::GM { 338 static void make0(SkPath* path, const SkRect& bounds, SkString* title) { 339 path->addRect(bounds, SkPath::kCW_Direction); 340 path->addRect(inset(bounds), SkPath::kCW_Direction); 341 title->set("CW CW"); 342 } 343 344 static void make1(SkPath* path, const SkRect& bounds, SkString* title) { 345 path->addRect(bounds, SkPath::kCW_Direction); 346 path->addRect(inset(bounds), SkPath::kCCW_Direction); 347 title->set("CW CCW"); 348 } 349 350 static void make2(SkPath* path, const SkRect& bounds, SkString* title) { 351 path->addOval(bounds, SkPath::kCW_Direction); 352 path->addOval(inset(bounds), SkPath::kCW_Direction); 353 title->set("CW CW"); 354 } 355 356 static void make3(SkPath* path, const SkRect& bounds, SkString* title) { 357 path->addOval(bounds, SkPath::kCW_Direction); 358 path->addOval(inset(bounds), SkPath::kCCW_Direction); 359 title->set("CW CCW"); 360 } 361 362 static void make4(SkPath* path, const SkRect& bounds, SkString* title) { 363 path->addRect(bounds, SkPath::kCW_Direction); 364 SkRect r = bounds; 365 r.inset(bounds.width() / 10, -bounds.height() / 10); 366 path->addOval(r, SkPath::kCW_Direction); 367 title->set("CW CW"); 368 } 369 370 static void make5(SkPath* path, const SkRect& bounds, SkString* title) { 371 path->addRect(bounds, SkPath::kCW_Direction); 372 SkRect r = bounds; 373 r.inset(bounds.width() / 10, -bounds.height() / 10); 374 path->addOval(r, SkPath::kCCW_Direction); 375 title->set("CW CCW"); 376 } 377 378 public: 379 Strokes3GM() {} 380 381 protected: 382 383 SkString onShortName() override { 384 return SkString("strokes3"); 385 } 386 387 SkISize onISize() override { 388 return SkISize::Make(1500, 1500); 389 } 390 391 void onDraw(SkCanvas* canvas) override { 392 SkPaint origPaint; 393 origPaint.setAntiAlias(true); 394 origPaint.setStyle(SkPaint::kStroke_Style); 395 SkPaint fillPaint(origPaint); 396 fillPaint.setColor(SK_ColorRED); 397 SkPaint strokePaint(origPaint); 398 strokePaint.setColor(sk_tool_utils::color_to_565(0xFF4444FF)); 399 400 void (*procs[])(SkPath*, const SkRect&, SkString*) = { 401 make0, make1, make2, make3, make4, make5 402 }; 403 404 canvas->translate(SkIntToScalar(20), SkIntToScalar(80)); 405 406 SkRect bounds = SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(50)); 407 SkScalar dx = bounds.width() * 4/3; 408 SkScalar dy = bounds.height() * 5; 409 410 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) { 411 SkPath orig; 412 SkString str; 413 procs[i](&orig, bounds, &str); 414 415 canvas->save(); 416 for (int j = 0; j < 13; ++j) { 417 strokePaint.setStrokeWidth(SK_Scalar1 * j * j); 418 canvas->drawPath(orig, strokePaint); 419 canvas->drawPath(orig, origPaint); 420 SkPath fill; 421 strokePaint.getFillPath(orig, &fill); 422 canvas->drawPath(fill, fillPaint); 423 canvas->translate(dx + strokePaint.getStrokeWidth(), 0); 424 } 425 canvas->restore(); 426 canvas->translate(0, dy); 427 } 428 } 429 430 private: 431 typedef skiagm::GM INHERITED; 432 }; 433 434 class Strokes4GM : public skiagm::GM { 435 public: 436 Strokes4GM() {} 437 438 protected: 439 440 SkString onShortName() override { 441 return SkString("strokes_zoomed"); 442 } 443 444 SkISize onISize() override { 445 return SkISize::Make(W, H*2); 446 } 447 448 void onDraw(SkCanvas* canvas) override { 449 SkPaint paint; 450 paint.setStyle(SkPaint::kStroke_Style); 451 paint.setStrokeWidth(0.055f); 452 453 canvas->scale(1000, 1000); 454 canvas->drawCircle(0, 2, 1.97f, paint); 455 } 456 457 private: 458 typedef skiagm::GM INHERITED; 459 }; 460 461 // Test stroking for curves that produce degenerate tangents when t is 0 or 1 (see bug 4191) 462 class Strokes5GM : public skiagm::GM { 463 public: 464 Strokes5GM() {} 465 466 protected: 467 468 SkString onShortName() override { 469 return SkString("zero_control_stroke"); 470 } 471 472 SkISize onISize() override { 473 return SkISize::Make(W, H*2); 474 } 475 476 void onDraw(SkCanvas* canvas) override { 477 SkPaint p; 478 p.setColor(SK_ColorRED); 479 p.setAntiAlias(true); 480 p.setStyle(SkPaint::kStroke_Style); 481 p.setStrokeWidth(40); 482 p.setStrokeCap(SkPaint::kButt_Cap); 483 484 SkPath path; 485 path.moveTo(157.474f,111.753f); 486 path.cubicTo(128.5f,111.5f,35.5f,29.5f,35.5f,29.5f); 487 canvas->drawPath(path, p); 488 path.reset(); 489 path.moveTo(250, 50); 490 path.quadTo(280, 80, 280, 80); 491 canvas->drawPath(path, p); 492 path.reset(); 493 path.moveTo(150, 50); 494 path.conicTo(180, 80, 180, 80, 0.707f); 495 canvas->drawPath(path, p); 496 497 path.reset(); 498 path.moveTo(157.474f,311.753f); 499 path.cubicTo(157.474f,311.753f,85.5f,229.5f,35.5f,229.5f); 500 canvas->drawPath(path, p); 501 path.reset(); 502 path.moveTo(280, 250); 503 path.quadTo(280, 250, 310, 280); 504 canvas->drawPath(path, p); 505 path.reset(); 506 path.moveTo(180, 250); 507 path.conicTo(180, 250, 210, 280, 0.707f); 508 canvas->drawPath(path, p); 509 } 510 511 private: 512 typedef skiagm::GM INHERITED; 513 }; 514 515 516 ////////////////////////////////////////////////////////////////////////////// 517 518 DEF_GM( return new StrokesGM; ) 519 DEF_GM( return new Strokes2GM; ) 520 DEF_GM( return new Strokes3GM; ) 521 DEF_GM( return new Strokes4GM; ) 522 DEF_GM( return new Strokes5GM; ) 523 524 DEF_GM( return new ZeroLenStrokesGM; ) 525 DEF_GM( return new TeenyStrokesGM; ) 526