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 SkPaint p(paint); 17 18 const SkScalar intervals[] = { 19 SkIntToScalar(on), 20 SkIntToScalar(off), 21 }; 22 23 p.setPathEffect(SkDashPathEffect::Create(intervals, 2, phase))->unref(); 24 canvas->drawLine(0, 0, finalX, finalY, p); 25 } 26 27 // earlier bug stopped us from drawing very long single-segment dashes, because 28 // SkPathMeasure was skipping very small delta-T values (nearlyzero). This is 29 // now fixes, so this giant dash should appear. 30 static void show_giant_dash(SkCanvas* canvas) { 31 SkPaint paint; 32 33 drawline(canvas, 1, 1, paint, SkIntToScalar(20 * 1000)); 34 } 35 36 class DashingGM : public skiagm::GM { 37 public: 38 DashingGM() {} 39 40 protected: 41 virtual uint32_t onGetFlags() const SK_OVERRIDE { 42 return kSkipTiled_Flag; 43 } 44 45 SkString onShortName() { 46 return SkString("dashing"); 47 } 48 49 SkISize onISize() { return SkISize::Make(640, 300); } 50 51 virtual void onDraw(SkCanvas* canvas) { 52 static const struct { 53 int fOnInterval; 54 int fOffInterval; 55 } gData[] = { 56 { 1, 1 }, 57 { 4, 1 }, 58 }; 59 60 SkPaint paint; 61 paint.setStyle(SkPaint::kStroke_Style); 62 63 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 64 canvas->translate(0, SK_ScalarHalf); 65 for (int width = 0; width <= 2; ++width) { 66 for (size_t data = 0; data < SK_ARRAY_COUNT(gData); ++data) { 67 for (int aa = 0; aa <= 1; ++aa) { 68 int w = width * width * width; 69 paint.setAntiAlias(SkToBool(aa)); 70 paint.setStrokeWidth(SkIntToScalar(w)); 71 72 int scale = w ? w : 1; 73 74 drawline(canvas, gData[data].fOnInterval * scale, 75 gData[data].fOffInterval * scale, 76 paint); 77 canvas->translate(0, SkIntToScalar(20)); 78 } 79 } 80 } 81 82 show_giant_dash(canvas); 83 } 84 }; 85 86 /////////////////////////////////////////////////////////////////////////////// 87 88 static void make_unit_star(SkPath* path, int n) { 89 SkScalar rad = -SK_ScalarPI / 2; 90 const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n; 91 92 path->moveTo(0, -SK_Scalar1); 93 for (int i = 1; i < n; i++) { 94 rad += drad; 95 SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV); 96 path->lineTo(cosV, sinV); 97 } 98 path->close(); 99 } 100 101 static void make_path_line(SkPath* path, const SkRect& bounds) { 102 path->moveTo(bounds.left(), bounds.top()); 103 path->lineTo(bounds.right(), bounds.bottom()); 104 } 105 106 static void make_path_rect(SkPath* path, const SkRect& bounds) { 107 path->addRect(bounds); 108 } 109 110 static void make_path_oval(SkPath* path, const SkRect& bounds) { 111 path->addOval(bounds); 112 } 113 114 static void make_path_star(SkPath* path, const SkRect& bounds) { 115 make_unit_star(path, 5); 116 SkMatrix matrix; 117 matrix.setRectToRect(path->getBounds(), bounds, SkMatrix::kCenter_ScaleToFit); 118 path->transform(matrix); 119 } 120 121 class Dashing2GM : public skiagm::GM { 122 public: 123 Dashing2GM() {} 124 125 protected: 126 virtual uint32_t onGetFlags() const SK_OVERRIDE { 127 return kSkipTiled_Flag; 128 } 129 130 SkString onShortName() { 131 return SkString("dashing2"); 132 } 133 134 SkISize onISize() { return SkISize::Make(640, 480); } 135 136 virtual void onDraw(SkCanvas* canvas) { 137 static const int gIntervals[] = { 138 3, // 3 dashes: each count [0] followed by intervals [1..count] 139 2, 10, 10, 140 4, 20, 5, 5, 5, 141 2, 2, 2 142 }; 143 144 void (*gProc[])(SkPath*, const SkRect&) = { 145 make_path_line, make_path_rect, make_path_oval, make_path_star, 146 }; 147 148 SkPaint paint; 149 paint.setAntiAlias(true); 150 paint.setStyle(SkPaint::kStroke_Style); 151 paint.setStrokeWidth(SkIntToScalar(6)); 152 153 SkRect bounds = SkRect::MakeWH(SkIntToScalar(120), SkIntToScalar(120)); 154 bounds.offset(SkIntToScalar(20), SkIntToScalar(20)); 155 SkScalar dx = bounds.width() * 4 / 3; 156 SkScalar dy = bounds.height() * 4 / 3; 157 158 const int* intervals = &gIntervals[1]; 159 for (int y = 0; y < gIntervals[0]; ++y) { 160 SkScalar vals[SK_ARRAY_COUNT(gIntervals)]; // more than enough 161 int count = *intervals++; 162 for (int i = 0; i < count; ++i) { 163 vals[i] = SkIntToScalar(*intervals++); 164 } 165 SkScalar phase = vals[0] / 2; 166 paint.setPathEffect(SkDashPathEffect::Create(vals, count, phase))->unref(); 167 168 for (size_t x = 0; x < SK_ARRAY_COUNT(gProc); ++x) { 169 SkPath path; 170 SkRect r = bounds; 171 r.offset(x * dx, y * dy); 172 gProc[x](&path, r); 173 174 canvas->drawPath(path, paint); 175 } 176 } 177 } 178 }; 179 180 ////////////////////////////////////////////////////////////////////////////// 181 182 // Test out the on/off line dashing Chrome if fond of 183 class Dashing3GM : public skiagm::GM { 184 public: 185 Dashing3GM() {} 186 187 protected: 188 virtual uint32_t onGetFlags() const SK_OVERRIDE { 189 return kSkipTiled_Flag; 190 } 191 192 SkString onShortName() { 193 return SkString("dashing3"); 194 } 195 196 SkISize onISize() { return SkISize::Make(640, 480); } 197 198 // Draw a 100x100 block of dashed lines. The horizontal ones are BW 199 // while the vertical ones are AA. 200 void drawDashedLines(SkCanvas* canvas, 201 SkScalar lineLength, 202 SkScalar phase, 203 SkScalar dashLength, 204 int strokeWidth, 205 bool circles) { 206 SkPaint p; 207 p.setColor(SK_ColorBLACK); 208 p.setStyle(SkPaint::kStroke_Style); 209 p.setStrokeWidth(SkIntToScalar(strokeWidth)); 210 211 if (circles) { 212 p.setStrokeCap(SkPaint::kRound_Cap); 213 } 214 215 SkScalar intervals[2] = { dashLength, dashLength }; 216 217 p.setPathEffect(SkDashPathEffect::Create(intervals, 2, phase))->unref(); 218 219 SkPoint pts[2]; 220 221 for (int y = 0; y < 100; y += 10*strokeWidth) { 222 pts[0].set(0, SkIntToScalar(y)); 223 pts[1].set(lineLength, SkIntToScalar(y)); 224 225 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p); 226 } 227 228 p.setAntiAlias(true); 229 230 for (int x = 0; x < 100; x += 14*strokeWidth) { 231 pts[0].set(SkIntToScalar(x), 0); 232 pts[1].set(SkIntToScalar(x), lineLength); 233 234 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p); 235 } 236 } 237 238 virtual void onDraw(SkCanvas* canvas) { 239 // 1on/1off 1x1 squares with phase of 0 - points fastpath 240 canvas->save(); 241 canvas->translate(2, 0); 242 this->drawDashedLines(canvas, 100, 0, SK_Scalar1, 1, false); 243 canvas->restore(); 244 245 // 1on/1off 1x1 squares with phase of .5 - rects fastpath (due to partial squares) 246 canvas->save(); 247 canvas->translate(112, 0); 248 this->drawDashedLines(canvas, 100, SK_ScalarHalf, SK_Scalar1, 1, false); 249 canvas->restore(); 250 251 // 1on/1off 1x1 squares with phase of 1 - points fastpath 252 canvas->save(); 253 canvas->translate(222, 0); 254 this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, false); 255 canvas->restore(); 256 257 // 1on/1off 1x1 squares with phase of 1 and non-integer length - rects fastpath 258 canvas->save(); 259 canvas->translate(332, 0); 260 this->drawDashedLines(canvas, 99.5f, SK_ScalarHalf, SK_Scalar1, 1, false); 261 canvas->restore(); 262 263 // 255on/255off 1x1 squares with phase of 0 - rects fast path 264 canvas->save(); 265 canvas->translate(446, 0); 266 this->drawDashedLines(canvas, 100, 0, SkIntToScalar(255), 1, false); 267 canvas->restore(); 268 269 // 1on/1off 3x3 squares with phase of 0 - points fast path 270 canvas->save(); 271 canvas->translate(2, 110); 272 this->drawDashedLines(canvas, 100, 0, SkIntToScalar(3), 3, false); 273 canvas->restore(); 274 275 // 1on/1off 3x3 squares with phase of 1.5 - rects fast path 276 canvas->save(); 277 canvas->translate(112, 110); 278 this->drawDashedLines(canvas, 100, 1.5f, SkIntToScalar(3), 3, false); 279 canvas->restore(); 280 281 // 1on/1off 1x1 circles with phase of 1 - no fast path yet 282 canvas->save(); 283 canvas->translate(2, 220); 284 this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, true); 285 canvas->restore(); 286 287 // 1on/1off 3x3 circles with phase of 1 - no fast path yet 288 canvas->save(); 289 canvas->translate(112, 220); 290 this->drawDashedLines(canvas, 100, 0, SkIntToScalar(3), 3, true); 291 canvas->restore(); 292 293 // 1on/1off 1x1 squares with rotation - should break fast path 294 canvas->save(); 295 canvas->translate(332+SK_ScalarRoot2Over2*100, 110+SK_ScalarRoot2Over2*100); 296 canvas->rotate(45); 297 canvas->translate(-50, -50); 298 299 this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, false); 300 canvas->restore(); 301 302 // 3on/3off 3x1 rects - should use rect fast path regardless of phase 303 for (int phase = 0; phase <= 3; ++phase) { 304 canvas->save(); 305 canvas->translate(SkIntToScalar(phase*110+2), 306 SkIntToScalar(330)); 307 this->drawDashedLines(canvas, 100, SkIntToScalar(phase), SkIntToScalar(3), 1, false); 308 canvas->restore(); 309 } 310 } 311 312 }; 313 314 ////////////////////////////////////////////////////////////////////////////// 315 316 class Dashing4GM : public skiagm::GM { 317 public: 318 Dashing4GM() {} 319 320 protected: 321 virtual uint32_t onGetFlags() const SK_OVERRIDE { 322 return kSkipTiled_Flag; 323 } 324 325 SkString onShortName() { 326 return SkString("dashing4"); 327 } 328 329 SkISize onISize() { return SkISize::Make(640, 950); } 330 331 virtual void onDraw(SkCanvas* canvas) { 332 static const struct { 333 int fOnInterval; 334 int fOffInterval; 335 } gData[] = { 336 { 1, 1 }, 337 { 4, 2 }, 338 { 0, 4 }, // test for zero length on interval 339 }; 340 341 SkPaint paint; 342 paint.setStyle(SkPaint::kStroke_Style); 343 344 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 345 canvas->translate(0, SK_ScalarHalf); 346 347 for (int width = 0; width <= 2; ++width) { 348 for (size_t data = 0; data < SK_ARRAY_COUNT(gData); ++data) { 349 for (int aa = 0; aa <= 1; ++aa) { 350 for (int cap = 0; cap <= 1; ++cap) { 351 int w = width * width * width; 352 paint.setAntiAlias(SkToBool(aa)); 353 paint.setStrokeWidth(SkIntToScalar(w)); 354 355 SkToBool(cap) ? paint.setStrokeCap(SkPaint::kSquare_Cap) 356 : paint.setStrokeCap(SkPaint::kRound_Cap); 357 358 int scale = w ? w : 1; 359 360 drawline(canvas, gData[data].fOnInterval * scale, 361 gData[data].fOffInterval * scale, 362 paint); 363 canvas->translate(0, SkIntToScalar(20)); 364 } 365 } 366 } 367 } 368 369 for (int aa = 0; aa <= 1; ++aa) { 370 paint.setAntiAlias(SkToBool(aa)); 371 paint.setStrokeWidth(8.f); 372 paint.setStrokeCap(SkPaint::kSquare_Cap); 373 // Single dash element that is cut off at start and end 374 drawline(canvas, 32, 16, paint, 20.f, 0, 5.f); 375 canvas->translate(0, SkIntToScalar(20)); 376 377 // Two dash elements where each one is cut off at beginning and end respectively 378 drawline(canvas, 32, 16, paint, 56.f, 0, 5.f); 379 canvas->translate(0, SkIntToScalar(20)); 380 381 // Many dash elements where first and last are cut off at beginning and end respectively 382 drawline(canvas, 32, 16, paint, 584.f, 0, 5.f); 383 canvas->translate(0, SkIntToScalar(20)); 384 385 // Diagonal dash line where src pnts are not axis aligned (as apposed to being diagonal from 386 // a canvas rotation) 387 drawline(canvas, 32, 16, paint, 600.f, 30.f); 388 canvas->translate(0, SkIntToScalar(20)); 389 390 // Case where only the off interval exists on the line. Thus nothing should be drawn 391 drawline(canvas, 32, 16, paint, 8.f, 0.f, 40.f); 392 canvas->translate(0, SkIntToScalar(20)); 393 } 394 } 395 }; 396 397 ////////////////////////////////////////////////////////////////////////////// 398 399 static skiagm::GM* F0(void*) { return new DashingGM; } 400 static skiagm::GM* F1(void*) { return new Dashing2GM; } 401 static skiagm::GM* F2(void*) { return new Dashing3GM; } 402 static skiagm::GM* F3(void*) { return new Dashing4GM; } 403 404 static skiagm::GMRegistry gR0(F0); 405 static skiagm::GMRegistry gR1(F1); 406 static skiagm::GMRegistry gR2(F2); 407 static skiagm::GMRegistry gR3(F3); 408