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