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)) { 15 SkPaint p(paint); 16 17 const SkScalar intervals[] = { 18 SkIntToScalar(on), 19 SkIntToScalar(off), 20 }; 21 22 p.setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref(); 23 canvas->drawLine(0, 0, finalX, 0, p); 24 } 25 26 // earlier bug stopped us from drawing very long single-segment dashes, because 27 // SkPathMeasure was skipping very small delta-T values (nearlyzero). This is 28 // now fixes, so this giant dash should appear. 29 static void show_giant_dash(SkCanvas* canvas) { 30 SkPaint paint; 31 32 drawline(canvas, 1, 1, paint, SkIntToScalar(20 * 1000)); 33 } 34 35 class DashingGM : public skiagm::GM { 36 public: 37 DashingGM() {} 38 39 protected: 40 SkString onShortName() { 41 return SkString("dashing"); 42 } 43 44 SkISize onISize() { return skiagm::make_isize(640, 300); } 45 46 virtual void onDraw(SkCanvas* canvas) { 47 static const struct { 48 int fOnInterval; 49 int fOffInterval; 50 } gData[] = { 51 { 1, 1 }, 52 { 4, 1 }, 53 }; 54 55 SkPaint paint; 56 paint.setStyle(SkPaint::kStroke_Style); 57 58 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 59 canvas->translate(0, SK_ScalarHalf); 60 61 for (int width = 0; width <= 2; ++width) { 62 for (size_t data = 0; data < SK_ARRAY_COUNT(gData); ++data) { 63 for (int aa = 0; aa <= 1; ++aa) { 64 int w = width * width * width; 65 paint.setAntiAlias(SkToBool(aa)); 66 paint.setStrokeWidth(SkIntToScalar(w)); 67 68 int scale = w ? w : 1; 69 70 drawline(canvas, gData[data].fOnInterval * scale, 71 gData[data].fOffInterval * scale, 72 paint); 73 canvas->translate(0, SkIntToScalar(20)); 74 } 75 } 76 } 77 78 show_giant_dash(canvas); 79 } 80 }; 81 82 /////////////////////////////////////////////////////////////////////////////// 83 84 static void make_unit_star(SkPath* path, int n) { 85 SkScalar rad = -SK_ScalarPI / 2; 86 const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n; 87 88 path->moveTo(0, -SK_Scalar1); 89 for (int i = 1; i < n; i++) { 90 rad += drad; 91 SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV); 92 path->lineTo(cosV, sinV); 93 } 94 path->close(); 95 } 96 97 static void make_path_line(SkPath* path, const SkRect& bounds) { 98 path->moveTo(bounds.left(), bounds.top()); 99 path->lineTo(bounds.right(), bounds.bottom()); 100 } 101 102 static void make_path_rect(SkPath* path, const SkRect& bounds) { 103 path->addRect(bounds); 104 } 105 106 static void make_path_oval(SkPath* path, const SkRect& bounds) { 107 path->addOval(bounds); 108 } 109 110 static void make_path_star(SkPath* path, const SkRect& bounds) { 111 make_unit_star(path, 5); 112 SkMatrix matrix; 113 matrix.setRectToRect(path->getBounds(), bounds, SkMatrix::kCenter_ScaleToFit); 114 path->transform(matrix); 115 } 116 117 class Dashing2GM : public skiagm::GM { 118 public: 119 Dashing2GM() {} 120 121 protected: 122 SkString onShortName() { 123 return SkString("dashing2"); 124 } 125 126 SkISize onISize() { return skiagm::make_isize(640, 480); } 127 128 virtual void onDraw(SkCanvas* canvas) { 129 static const int gIntervals[] = { 130 3, // 3 dashes: each count [0] followed by intervals [1..count] 131 2, 10, 10, 132 4, 20, 5, 5, 5, 133 2, 2, 2 134 }; 135 136 void (*gProc[])(SkPath*, const SkRect&) = { 137 make_path_line, make_path_rect, make_path_oval, make_path_star, 138 }; 139 140 SkPaint paint; 141 paint.setAntiAlias(true); 142 paint.setStyle(SkPaint::kStroke_Style); 143 paint.setStrokeWidth(SkIntToScalar(6)); 144 145 SkRect bounds = SkRect::MakeWH(SkIntToScalar(120), SkIntToScalar(120)); 146 bounds.offset(SkIntToScalar(20), SkIntToScalar(20)); 147 SkScalar dx = bounds.width() * 4 / 3; 148 SkScalar dy = bounds.height() * 4 / 3; 149 150 const int* intervals = &gIntervals[1]; 151 for (int y = 0; y < gIntervals[0]; ++y) { 152 SkScalar vals[SK_ARRAY_COUNT(gIntervals)]; // more than enough 153 int count = *intervals++; 154 for (int i = 0; i < count; ++i) { 155 vals[i] = SkIntToScalar(*intervals++); 156 } 157 SkScalar phase = vals[0] / 2; 158 paint.setPathEffect(new SkDashPathEffect(vals, count, phase))->unref(); 159 160 for (size_t x = 0; x < SK_ARRAY_COUNT(gProc); ++x) { 161 SkPath path; 162 SkRect r = bounds; 163 r.offset(x * dx, y * dy); 164 gProc[x](&path, r); 165 166 canvas->drawPath(path, paint); 167 } 168 } 169 } 170 }; 171 172 ////////////////////////////////////////////////////////////////////////////// 173 174 // Test out the on/off line dashing Chrome if fond of 175 class Dashing3GM : public skiagm::GM { 176 public: 177 Dashing3GM() {} 178 179 protected: 180 SkString onShortName() { 181 return SkString("dashing3"); 182 } 183 184 SkISize onISize() { return skiagm::make_isize(640, 480); } 185 186 // Draw a 100x100 block of dashed lines. The horizontal ones are BW 187 // while the vertical ones are AA. 188 void drawDashedLines(SkCanvas* canvas, 189 SkScalar lineLength, 190 SkScalar phase, 191 SkScalar dashLength, 192 int strokeWidth, 193 bool circles) { 194 SkPaint p; 195 p.setColor(SK_ColorBLACK); 196 p.setStyle(SkPaint::kStroke_Style); 197 p.setStrokeWidth(SkIntToScalar(strokeWidth)); 198 199 if (circles) { 200 p.setStrokeCap(SkPaint::kRound_Cap); 201 } 202 203 SkScalar intervals[2] = { dashLength, dashLength }; 204 205 p.setPathEffect(new SkDashPathEffect(intervals, 2, phase, false)); 206 207 SkPoint pts[2]; 208 209 for (int y = 0; y < 100; y += 10*strokeWidth) { 210 pts[0].set(0, SkIntToScalar(y)); 211 pts[1].set(lineLength, SkIntToScalar(y)); 212 213 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p); 214 } 215 216 p.setAntiAlias(true); 217 218 for (int x = 0; x < 100; x += 14*strokeWidth) { 219 pts[0].set(SkIntToScalar(x), 0); 220 pts[1].set(SkIntToScalar(x), lineLength); 221 222 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p); 223 } 224 } 225 226 virtual void onDraw(SkCanvas* canvas) { 227 // 1on/1off 1x1 squares with phase of 0 - points fastpath 228 canvas->save(); 229 canvas->translate(2, 0); 230 this->drawDashedLines(canvas, 100, 0, SK_Scalar1, 1, false); 231 canvas->restore(); 232 233 // 1on/1off 1x1 squares with phase of .5 - rects fastpath (due to partial squares) 234 canvas->save(); 235 canvas->translate(112, 0); 236 this->drawDashedLines(canvas, 100, SK_ScalarHalf, SK_Scalar1, 1, false); 237 canvas->restore(); 238 239 // 1on/1off 1x1 squares with phase of 1 - points fastpath 240 canvas->save(); 241 canvas->translate(222, 0); 242 this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, false); 243 canvas->restore(); 244 245 // 1on/1off 1x1 squares with phase of 1 and non-integer length - rects fastpath 246 canvas->save(); 247 canvas->translate(332, 0); 248 this->drawDashedLines(canvas, 99.5f, SK_ScalarHalf, SK_Scalar1, 1, false); 249 canvas->restore(); 250 251 // 255on/255off 1x1 squares with phase of 0 - rects fast path 252 canvas->save(); 253 canvas->translate(446, 0); 254 this->drawDashedLines(canvas, 100, 0, SkIntToScalar(255), 1, false); 255 canvas->restore(); 256 257 // 1on/1off 3x3 squares with phase of 0 - points fast path 258 canvas->save(); 259 canvas->translate(2, 110); 260 this->drawDashedLines(canvas, 100, 0, SkIntToScalar(3), 3, false); 261 canvas->restore(); 262 263 // 1on/1off 3x3 squares with phase of 1.5 - rects fast path 264 canvas->save(); 265 canvas->translate(112, 110); 266 this->drawDashedLines(canvas, 100, SkFloatToScalar(1.5f), SkIntToScalar(3), 3, false); 267 canvas->restore(); 268 269 // 1on/1off 1x1 circles with phase of 1 - no fast path yet 270 canvas->save(); 271 canvas->translate(2, 220); 272 this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, true); 273 canvas->restore(); 274 275 // 1on/1off 3x3 circles with phase of 1 - no fast path yet 276 canvas->save(); 277 canvas->translate(112, 220); 278 this->drawDashedLines(canvas, 100, 0, SkIntToScalar(3), 3, true); 279 canvas->restore(); 280 281 // 1on/1off 1x1 squares with rotation - should break fast path 282 canvas->save(); 283 canvas->translate(332+SK_ScalarRoot2Over2*100, 110+SK_ScalarRoot2Over2*100); 284 canvas->rotate(45); 285 canvas->translate(-50, -50); 286 287 this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, false); 288 canvas->restore(); 289 290 // 3on/3off 3x1 rects - should use rect fast path regardless of phase 291 for (int phase = 0; phase <= 3; ++phase) { 292 canvas->save(); 293 canvas->translate(SkIntToScalar(phase*110+2), 294 SkIntToScalar(330)); 295 this->drawDashedLines(canvas, 100, SkIntToScalar(phase), SkIntToScalar(3), 1, false); 296 canvas->restore(); 297 } 298 } 299 300 }; 301 302 ////////////////////////////////////////////////////////////////////////////// 303 304 static skiagm::GM* F0(void*) { return new DashingGM; } 305 static skiagm::GM* F1(void*) { return new Dashing2GM; } 306 static skiagm::GM* F2(void*) { return new Dashing3GM; } 307 308 static skiagm::GMRegistry gR0(F0); 309 static skiagm::GMRegistry gR1(F1); 310 static skiagm::GMRegistry gR2(F2); 311