1/* 2 * Copyright 2012 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 "SkCanvas.h" 10#include "SkPaint.h" 11#include "SkDashPathEffect.h" 12 13static void drawline(SkCanvas* canvas, int on, int off, const SkPaint& paint, 14 SkScalar finalX = SkIntToScalar(600), SkScalar finalY = SkIntToScalar(0), 15 SkScalar phase = SkIntToScalar(0), 16 SkScalar startX = SkIntToScalar(0), SkScalar startY = SkIntToScalar(0)) { 17 SkPaint p(paint); 18 19 const SkScalar intervals[] = { 20 SkIntToScalar(on), 21 SkIntToScalar(off), 22 }; 23 24 p.setPathEffect(SkDashPathEffect::Create(intervals, 2, phase))->unref(); 25 canvas->drawLine(startX, startY, finalX, finalY, p); 26} 27 28// earlier bug stopped us from drawing very long single-segment dashes, because 29// SkPathMeasure was skipping very small delta-T values (nearlyzero). This is 30// now fixes, so this giant dash should appear. 31static void show_giant_dash(SkCanvas* canvas) { 32 SkPaint paint; 33 34 drawline(canvas, 1, 1, paint, SkIntToScalar(20 * 1000)); 35} 36 37static void show_zero_len_dash(SkCanvas* canvas) { 38 SkPaint paint; 39 40 drawline(canvas, 2, 2, paint, SkIntToScalar(0)); 41 paint.setStyle(SkPaint::kStroke_Style); 42 paint.setStrokeWidth(SkIntToScalar(2)); 43 canvas->translate(0, SkIntToScalar(20)); 44 drawline(canvas, 4, 4, paint, SkIntToScalar(0)); 45} 46 47class DashingGM : public skiagm::GM { 48public: 49 DashingGM() {} 50 51protected: 52 53 SkString onShortName() { 54 return SkString("dashing"); 55 } 56 57 SkISize onISize() { return SkISize::Make(640, 340); } 58 59 virtual void onDraw(SkCanvas* canvas) { 60 static const struct { 61 int fOnInterval; 62 int fOffInterval; 63 } gData[] = { 64 { 1, 1 }, 65 { 4, 1 }, 66 }; 67 68 SkPaint paint; 69 paint.setStyle(SkPaint::kStroke_Style); 70 71 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 72 canvas->translate(0, SK_ScalarHalf); 73 for (int width = 0; width <= 2; ++width) { 74 for (size_t data = 0; data < SK_ARRAY_COUNT(gData); ++data) { 75 for (int aa = 0; aa <= 1; ++aa) { 76 int w = width * width * width; 77 paint.setAntiAlias(SkToBool(aa)); 78 paint.setStrokeWidth(SkIntToScalar(w)); 79 80 int scale = w ? w : 1; 81 82 drawline(canvas, gData[data].fOnInterval * scale, 83 gData[data].fOffInterval * scale, 84 paint); 85 canvas->translate(0, SkIntToScalar(20)); 86 } 87 } 88 } 89 90 show_giant_dash(canvas); 91 canvas->translate(0, SkIntToScalar(20)); 92 show_zero_len_dash(canvas); 93 canvas->translate(0, SkIntToScalar(20)); 94 // Draw 0 on, 0 off dashed line 95 paint.setStrokeWidth(SkIntToScalar(8)); 96 drawline(canvas, 0, 0, paint); 97 } 98}; 99 100/////////////////////////////////////////////////////////////////////////////// 101 102static void make_unit_star(SkPath* path, int n) { 103 SkScalar rad = -SK_ScalarPI / 2; 104 const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n; 105 106 path->moveTo(0, -SK_Scalar1); 107 for (int i = 1; i < n; i++) { 108 rad += drad; 109 SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV); 110 path->lineTo(cosV, sinV); 111 } 112 path->close(); 113} 114 115static void make_path_line(SkPath* path, const SkRect& bounds) { 116 path->moveTo(bounds.left(), bounds.top()); 117 path->lineTo(bounds.right(), bounds.bottom()); 118} 119 120static void make_path_rect(SkPath* path, const SkRect& bounds) { 121 path->addRect(bounds); 122} 123 124static void make_path_oval(SkPath* path, const SkRect& bounds) { 125 path->addOval(bounds); 126} 127 128static void make_path_star(SkPath* path, const SkRect& bounds) { 129 make_unit_star(path, 5); 130 SkMatrix matrix; 131 matrix.setRectToRect(path->getBounds(), bounds, SkMatrix::kCenter_ScaleToFit); 132 path->transform(matrix); 133} 134 135class Dashing2GM : public skiagm::GM { 136public: 137 Dashing2GM() {} 138 139protected: 140 141 SkString onShortName() { 142 return SkString("dashing2"); 143 } 144 145 SkISize onISize() { return SkISize::Make(640, 480); } 146 147 virtual void onDraw(SkCanvas* canvas) { 148 static const int gIntervals[] = { 149 3, // 3 dashes: each count [0] followed by intervals [1..count] 150 2, 10, 10, 151 4, 20, 5, 5, 5, 152 2, 2, 2 153 }; 154 155 void (*gProc[])(SkPath*, const SkRect&) = { 156 make_path_line, make_path_rect, make_path_oval, make_path_star, 157 }; 158 159 SkPaint paint; 160 paint.setAntiAlias(true); 161 paint.setStyle(SkPaint::kStroke_Style); 162 paint.setStrokeWidth(SkIntToScalar(6)); 163 164 SkRect bounds = SkRect::MakeWH(SkIntToScalar(120), SkIntToScalar(120)); 165 bounds.offset(SkIntToScalar(20), SkIntToScalar(20)); 166 SkScalar dx = bounds.width() * 4 / 3; 167 SkScalar dy = bounds.height() * 4 / 3; 168 169 const int* intervals = &gIntervals[1]; 170 for (int y = 0; y < gIntervals[0]; ++y) { 171 SkScalar vals[SK_ARRAY_COUNT(gIntervals)]; // more than enough 172 int count = *intervals++; 173 for (int i = 0; i < count; ++i) { 174 vals[i] = SkIntToScalar(*intervals++); 175 } 176 SkScalar phase = vals[0] / 2; 177 paint.setPathEffect(SkDashPathEffect::Create(vals, count, phase))->unref(); 178 179 for (size_t x = 0; x < SK_ARRAY_COUNT(gProc); ++x) { 180 SkPath path; 181 SkRect r = bounds; 182 r.offset(x * dx, y * dy); 183 gProc[x](&path, r); 184 185 canvas->drawPath(path, paint); 186 } 187 } 188 } 189}; 190 191////////////////////////////////////////////////////////////////////////////// 192 193// Test out the on/off line dashing Chrome if fond of 194class Dashing3GM : public skiagm::GM { 195public: 196 Dashing3GM() {} 197 198protected: 199 200 SkString onShortName() { 201 return SkString("dashing3"); 202 } 203 204 SkISize onISize() { return SkISize::Make(640, 480); } 205 206 // Draw a 100x100 block of dashed lines. The horizontal ones are BW 207 // while the vertical ones are AA. 208 void drawDashedLines(SkCanvas* canvas, 209 SkScalar lineLength, 210 SkScalar phase, 211 SkScalar dashLength, 212 int strokeWidth, 213 bool circles) { 214 SkPaint p; 215 p.setColor(SK_ColorBLACK); 216 p.setStyle(SkPaint::kStroke_Style); 217 p.setStrokeWidth(SkIntToScalar(strokeWidth)); 218 219 if (circles) { 220 p.setStrokeCap(SkPaint::kRound_Cap); 221 } 222 223 SkScalar intervals[2] = { dashLength, dashLength }; 224 225 p.setPathEffect(SkDashPathEffect::Create(intervals, 2, phase))->unref(); 226 227 SkPoint pts[2]; 228 229 for (int y = 0; y < 100; y += 10*strokeWidth) { 230 pts[0].set(0, SkIntToScalar(y)); 231 pts[1].set(lineLength, SkIntToScalar(y)); 232 233 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p); 234 } 235 236 p.setAntiAlias(true); 237 238 for (int x = 0; x < 100; x += 14*strokeWidth) { 239 pts[0].set(SkIntToScalar(x), 0); 240 pts[1].set(SkIntToScalar(x), lineLength); 241 242 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p); 243 } 244 } 245 246 virtual void onDraw(SkCanvas* canvas) { 247 // 1on/1off 1x1 squares with phase of 0 - points fastpath 248 canvas->save(); 249 canvas->translate(2, 0); 250 this->drawDashedLines(canvas, 100, 0, SK_Scalar1, 1, false); 251 canvas->restore(); 252 253 // 1on/1off 1x1 squares with phase of .5 - rects fastpath (due to partial squares) 254 canvas->save(); 255 canvas->translate(112, 0); 256 this->drawDashedLines(canvas, 100, SK_ScalarHalf, SK_Scalar1, 1, false); 257 canvas->restore(); 258 259 // 1on/1off 1x1 squares with phase of 1 - points fastpath 260 canvas->save(); 261 canvas->translate(222, 0); 262 this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, false); 263 canvas->restore(); 264 265 // 1on/1off 1x1 squares with phase of 1 and non-integer length - rects fastpath 266 canvas->save(); 267 canvas->translate(332, 0); 268 this->drawDashedLines(canvas, 99.5f, SK_ScalarHalf, SK_Scalar1, 1, false); 269 canvas->restore(); 270 271 // 255on/255off 1x1 squares with phase of 0 - rects fast path 272 canvas->save(); 273 canvas->translate(446, 0); 274 this->drawDashedLines(canvas, 100, 0, SkIntToScalar(255), 1, false); 275 canvas->restore(); 276 277 // 1on/1off 3x3 squares with phase of 0 - points fast path 278 canvas->save(); 279 canvas->translate(2, 110); 280 this->drawDashedLines(canvas, 100, 0, SkIntToScalar(3), 3, false); 281 canvas->restore(); 282 283 // 1on/1off 3x3 squares with phase of 1.5 - rects fast path 284 canvas->save(); 285 canvas->translate(112, 110); 286 this->drawDashedLines(canvas, 100, 1.5f, SkIntToScalar(3), 3, false); 287 canvas->restore(); 288 289 // 1on/1off 1x1 circles with phase of 1 - no fast path yet 290 canvas->save(); 291 canvas->translate(2, 220); 292 this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, true); 293 canvas->restore(); 294 295 // 1on/1off 3x3 circles with phase of 1 - no fast path yet 296 canvas->save(); 297 canvas->translate(112, 220); 298 this->drawDashedLines(canvas, 100, 0, SkIntToScalar(3), 3, true); 299 canvas->restore(); 300 301 // 1on/1off 1x1 squares with rotation - should break fast path 302 canvas->save(); 303 canvas->translate(332+SK_ScalarRoot2Over2*100, 110+SK_ScalarRoot2Over2*100); 304 canvas->rotate(45); 305 canvas->translate(-50, -50); 306 307 this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, false); 308 canvas->restore(); 309 310 // 3on/3off 3x1 rects - should use rect fast path regardless of phase 311 for (int phase = 0; phase <= 3; ++phase) { 312 canvas->save(); 313 canvas->translate(SkIntToScalar(phase*110+2), 314 SkIntToScalar(330)); 315 this->drawDashedLines(canvas, 100, SkIntToScalar(phase), SkIntToScalar(3), 1, false); 316 canvas->restore(); 317 } 318 } 319 320}; 321 322////////////////////////////////////////////////////////////////////////////// 323 324class Dashing4GM : public skiagm::GM { 325public: 326 Dashing4GM() {} 327 328protected: 329 330 SkString onShortName() { 331 return SkString("dashing4"); 332 } 333 334 SkISize onISize() { return SkISize::Make(640, 950); } 335 336 virtual void onDraw(SkCanvas* canvas) { 337 static const struct { 338 int fOnInterval; 339 int fOffInterval; 340 } gData[] = { 341 { 1, 1 }, 342 { 4, 2 }, 343 { 0, 4 }, // test for zero length on interval 344 }; 345 346 SkPaint paint; 347 paint.setStyle(SkPaint::kStroke_Style); 348 349 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 350 canvas->translate(0, SK_ScalarHalf); 351 352 for (int width = 0; width <= 2; ++width) { 353 for (size_t data = 0; data < SK_ARRAY_COUNT(gData); ++data) { 354 for (int aa = 0; aa <= 1; ++aa) { 355 for (int cap = 0; cap <= 1; ++cap) { 356 int w = width * width * width; 357 paint.setAntiAlias(SkToBool(aa)); 358 paint.setStrokeWidth(SkIntToScalar(w)); 359 360 SkToBool(cap) ? paint.setStrokeCap(SkPaint::kSquare_Cap) 361 : paint.setStrokeCap(SkPaint::kRound_Cap); 362 363 int scale = w ? w : 1; 364 365 drawline(canvas, gData[data].fOnInterval * scale, 366 gData[data].fOffInterval * scale, 367 paint); 368 canvas->translate(0, SkIntToScalar(20)); 369 } 370 } 371 } 372 } 373 374 for (int aa = 0; aa <= 1; ++aa) { 375 paint.setAntiAlias(SkToBool(aa)); 376 paint.setStrokeWidth(8.f); 377 paint.setStrokeCap(SkPaint::kSquare_Cap); 378 // Single dash element that is cut off at start and end 379 drawline(canvas, 32, 16, paint, 20.f, 0, 5.f); 380 canvas->translate(0, SkIntToScalar(20)); 381 382 // Two dash elements where each one is cut off at beginning and end respectively 383 drawline(canvas, 32, 16, paint, 56.f, 0, 5.f); 384 canvas->translate(0, SkIntToScalar(20)); 385 386 // Many dash elements where first and last are cut off at beginning and end respectively 387 drawline(canvas, 32, 16, paint, 584.f, 0, 5.f); 388 canvas->translate(0, SkIntToScalar(20)); 389 390 // Diagonal dash line where src pnts are not axis aligned (as apposed to being diagonal from 391 // a canvas rotation) 392 drawline(canvas, 32, 16, paint, 600.f, 30.f); 393 canvas->translate(0, SkIntToScalar(20)); 394 395 // Case where only the off interval exists on the line. Thus nothing should be drawn 396 drawline(canvas, 32, 16, paint, 8.f, 0.f, 40.f); 397 canvas->translate(0, SkIntToScalar(20)); 398 } 399 } 400}; 401 402////////////////////////////////////////////////////////////////////////////// 403 404class Dashing5GM : public skiagm::GM { 405public: 406 Dashing5GM(bool doAA) : fDoAA(doAA) {} 407 408protected: 409 410 bool runAsBench() const override { return true; } 411 412 SkString onShortName() override { 413 if (fDoAA) { 414 return SkString("dashing5_aa"); 415 } else { 416 return SkString("dashing5_bw"); 417 } 418 } 419 420 SkISize onISize() override { return SkISize::Make(400, 200); } 421 422 void onDraw(SkCanvas* canvas) override { 423 static const int kOn = 4; 424 static const int kOff = 4; 425 static const int kIntervalLength = kOn + kOff; 426 427 static const SkColor gColors[kIntervalLength] = { 428 SK_ColorRED, 429 SK_ColorGREEN, 430 SK_ColorBLUE, 431 SK_ColorCYAN, 432 SK_ColorMAGENTA, 433 SK_ColorYELLOW, 434 SK_ColorGRAY, 435 SK_ColorDKGRAY 436 }; 437 438 SkPaint paint; 439 paint.setStyle(SkPaint::kStroke_Style); 440 441 paint.setAntiAlias(fDoAA); 442 443 SkMatrix rot; 444 rot.setRotate(90); 445 SkASSERT(rot.rectStaysRect()); 446 447 canvas->concat(rot); 448 449 int sign; // used to toggle the direction of the lines 450 int phase = 0; 451 452 for (int x = 0; x < 200; x += 10) { 453 paint.setStrokeWidth(SkIntToScalar(phase+1)); 454 paint.setColor(gColors[phase]); 455 sign = (x % 20) ? 1 : -1; 456 drawline(canvas, kOn, kOff, paint, 457 SkIntToScalar(x), -sign * SkIntToScalar(10003), 458 SkIntToScalar(phase), 459 SkIntToScalar(x), sign * SkIntToScalar(10003)); 460 phase = (phase + 1) % kIntervalLength; 461 } 462 463 for (int y = -400; y < 0; y += 10) { 464 paint.setStrokeWidth(SkIntToScalar(phase+1)); 465 paint.setColor(gColors[phase]); 466 sign = (y % 20) ? 1 : -1; 467 drawline(canvas, kOn, kOff, paint, 468 -sign * SkIntToScalar(10003), SkIntToScalar(y), 469 SkIntToScalar(phase), 470 sign * SkIntToScalar(10003), SkIntToScalar(y)); 471 phase = (phase + 1) % kIntervalLength; 472 } 473 } 474 475private: 476 bool fDoAA; 477}; 478 479DEF_SIMPLE_GM(longpathdash, canvas, 512, 512) { 480 SkPath lines; 481 for (int x = 32; x < 256; x += 16) { 482 for (SkScalar a = 0; a < 3.141592f * 2; a += 0.03141592f) { 483 SkPoint pts[2] = { 484 { 256 + (float) sin(a) * x, 485 256 + (float) cos(a) * x }, 486 { 256 + (float) sin(a + 3.141592 / 3) * (x + 64), 487 256 + (float) cos(a + 3.141592 / 3) * (x + 64) } 488 }; 489 lines.moveTo(pts[0]); 490 for (SkScalar i = 0; i < 1; i += 0.05f) { 491 lines.lineTo(pts[0].fX * (1 - i) + pts[1].fX * i, 492 pts[0].fY * (1 - i) + pts[1].fY * i); 493 } 494 } 495 } 496 SkPaint p; 497 p.setAntiAlias(true); 498 p.setStyle(SkPaint::kStroke_Style); 499 p.setStrokeWidth(1); 500 const SkScalar intervals[] = { 1, 1 }; 501 p.setPathEffect(SkDashPathEffect::Create(intervals, SK_ARRAY_COUNT(intervals), 0))->unref(); 502 canvas->drawPath(lines, p); 503} 504 505DEF_SIMPLE_GM(longlinedash, canvas, 512, 512) { 506 SkPaint p; 507 p.setAntiAlias(true); 508 p.setStyle(SkPaint::kStroke_Style); 509 p.setStrokeWidth(80); 510 511 const SkScalar intervals[] = { 2, 2 }; 512 p.setPathEffect(SkDashPathEffect::Create(intervals, SK_ARRAY_COUNT(intervals), 0))->unref(); 513 canvas->drawRect(SkRect::MakeXYWH(-10000, 100, 20000, 20), p); 514} 515 516DEF_SIMPLE_GM(longwavyline, canvas, 512, 512) { 517 SkPaint p; 518 p.setAntiAlias(true); 519 p.setStyle(SkPaint::kStroke_Style); 520 p.setStrokeWidth(2); 521 522 SkPath wavy; 523 wavy.moveTo(-10000, 100); 524 for (SkScalar i = -10000; i < 10000; i += 20) { 525 wavy.quadTo(i + 5, 95, i + 10, 100); 526 wavy.quadTo(i + 15, 105, i + 20, 100); 527 } 528 canvas->drawPath(wavy, p); 529} 530 531////////////////////////////////////////////////////////////////////////////// 532 533DEF_GM(return new DashingGM;) 534DEF_GM(return new Dashing2GM;) 535DEF_GM(return new Dashing3GM;) 536DEF_GM(return new Dashing4GM;) 537DEF_GM(return new Dashing5GM(true);) 538DEF_GM(return new Dashing5GM(false);) 539