1 2 /* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 #include "SkBenchmark.h" 9 #include "SkBitmap.h" 10 #include "SkCanvas.h" 11 #include "SkDashPathEffect.h" 12 #include "SkPaint.h" 13 #include "SkPath.h" 14 #include "SkRandom.h" 15 #include "SkString.h" 16 #include "SkTDArray.h" 17 18 19 /* 20 * Cases to consider: 21 * 22 * 1. antialiasing on/off (esp. width <= 1) 23 * 2. strokewidth == 0, 1, 2 24 * 3. hline, vline, diagonal, rect, oval 25 * 4. dots [1,1] ([N,N] where N=strokeWidth?) or arbitrary (e.g. [2,1] or [1,2,3,2]) 26 */ 27 static void path_hline(SkPath* path) { 28 path->moveTo(SkIntToScalar(10), SkIntToScalar(10)); 29 path->lineTo(SkIntToScalar(600), SkIntToScalar(10)); 30 } 31 32 class DashBench : public SkBenchmark { 33 protected: 34 SkString fName; 35 SkTDArray<SkScalar> fIntervals; 36 int fWidth; 37 SkPoint fPts[2]; 38 bool fDoClip; 39 40 enum { 41 N = SkBENCHLOOP(100) 42 }; 43 public: 44 DashBench(void* param, const SkScalar intervals[], int count, int width, 45 bool doClip = false) : INHERITED(param) { 46 fIntervals.append(count, intervals); 47 for (int i = 0; i < count; ++i) { 48 fIntervals[i] *= width; 49 } 50 fWidth = width; 51 fName.printf("dash_%d_%s", width, doClip ? "clipped" : "noclip"); 52 fDoClip = doClip; 53 54 fPts[0].set(SkIntToScalar(10), SkIntToScalar(10)); 55 fPts[1].set(SkIntToScalar(600), SkIntToScalar(10)); 56 } 57 58 virtual void makePath(SkPath* path) { 59 path_hline(path); 60 } 61 62 protected: 63 virtual const char* onGetName() SK_OVERRIDE { 64 return fName.c_str(); 65 } 66 67 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { 68 SkPaint paint; 69 this->setupPaint(&paint); 70 paint.setStyle(SkPaint::kStroke_Style); 71 paint.setStrokeWidth(SkIntToScalar(fWidth)); 72 paint.setAntiAlias(false); 73 74 SkPath path; 75 this->makePath(&path); 76 77 paint.setPathEffect(new SkDashPathEffect(fIntervals.begin(), 78 fIntervals.count(), 0))->unref(); 79 80 if (fDoClip) { 81 SkRect r = path.getBounds(); 82 r.inset(-SkIntToScalar(20), -SkIntToScalar(20)); 83 // now move it so we don't intersect 84 r.offset(0, r.height() * 3 / 2); 85 canvas->clipRect(r); 86 } 87 88 this->handlePath(canvas, path, paint, N); 89 } 90 91 virtual void handlePath(SkCanvas* canvas, const SkPath& path, 92 const SkPaint& paint, int N) { 93 for (int i = 0; i < N; ++i) { 94 // canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, paint); 95 canvas->drawPath(path, paint); 96 } 97 } 98 99 private: 100 typedef SkBenchmark INHERITED; 101 }; 102 103 class RectDashBench : public DashBench { 104 public: 105 RectDashBench(void* param, const SkScalar intervals[], int count, int width, bool doClip = false) 106 : INHERITED(param, intervals, count, width) { 107 fName.append("_rect"); 108 } 109 110 protected: 111 virtual void handlePath(SkCanvas* canvas, const SkPath& path, 112 const SkPaint& paint, int N) SK_OVERRIDE { 113 SkPoint pts[2]; 114 if (!path.isLine(pts) || pts[0].fY != pts[1].fY) { 115 this->INHERITED::handlePath(canvas, path, paint, N); 116 } else { 117 SkRect rect; 118 rect.fLeft = pts[0].fX; 119 rect.fTop = pts[0].fY - paint.getStrokeWidth() / 2; 120 rect.fRight = rect.fLeft + SkIntToScalar(fWidth); 121 rect.fBottom = rect.fTop + paint.getStrokeWidth(); 122 123 SkPaint p(paint); 124 p.setStyle(SkPaint::kFill_Style); 125 p.setPathEffect(NULL); 126 127 int count = SkScalarRoundToInt((pts[1].fX - pts[0].fX) / (2*fWidth)); 128 SkScalar dx = SkIntToScalar(2 * fWidth); 129 130 for (int i = 0; i < N*10; ++i) { 131 SkRect r = rect; 132 for (int j = 0; j < count; ++j) { 133 canvas->drawRect(r, p); 134 r.offset(dx, 0); 135 } 136 } 137 } 138 } 139 140 private: 141 typedef DashBench INHERITED; 142 }; 143 144 static void make_unit_star(SkPath* path, int n) { 145 SkScalar rad = -SK_ScalarPI / 2; 146 const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n; 147 148 path->moveTo(0, -SK_Scalar1); 149 for (int i = 1; i < n; i++) { 150 rad += drad; 151 SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV); 152 path->lineTo(cosV, sinV); 153 } 154 path->close(); 155 } 156 157 static void make_poly(SkPath* path) { 158 make_unit_star(path, 9); 159 SkMatrix matrix; 160 matrix.setScale(SkIntToScalar(100), SkIntToScalar(100)); 161 path->transform(matrix); 162 } 163 164 static void make_quad(SkPath* path) { 165 SkScalar x0 = SkIntToScalar(10); 166 SkScalar y0 = SkIntToScalar(10); 167 path->moveTo(x0, y0); 168 path->quadTo(x0, y0 + 400 * SK_Scalar1, 169 x0 + 600 * SK_Scalar1, y0 + 400 * SK_Scalar1); 170 } 171 172 static void make_cubic(SkPath* path) { 173 SkScalar x0 = SkIntToScalar(10); 174 SkScalar y0 = SkIntToScalar(10); 175 path->moveTo(x0, y0); 176 path->cubicTo(x0, y0 + 400 * SK_Scalar1, 177 x0 + 600 * SK_Scalar1, y0 + 400 * SK_Scalar1, 178 x0 + 600 * SK_Scalar1, y0); 179 } 180 181 class MakeDashBench : public SkBenchmark { 182 SkString fName; 183 SkPath fPath; 184 SkAutoTUnref<SkPathEffect> fPE; 185 186 enum { 187 N = SkBENCHLOOP(400) 188 }; 189 190 public: 191 MakeDashBench(void* param, void (*proc)(SkPath*), const char name[]) : INHERITED(param) { 192 fName.printf("makedash_%s", name); 193 proc(&fPath); 194 195 SkScalar vals[] = { SkIntToScalar(4), SkIntToScalar(4) }; 196 fPE.reset(new SkDashPathEffect(vals, 2, 0)); 197 } 198 199 protected: 200 virtual const char* onGetName() SK_OVERRIDE { 201 return fName.c_str(); 202 } 203 204 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { 205 SkPath dst; 206 for (int i = 0; i < N; ++i) { 207 SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle); 208 209 fPE->filterPath(&dst, fPath, &rec, NULL); 210 dst.rewind(); 211 } 212 } 213 214 private: 215 typedef SkBenchmark INHERITED; 216 }; 217 218 /* 219 * We try to special case square dashes (intervals are equal to strokewidth). 220 */ 221 class DashLineBench : public SkBenchmark { 222 SkString fName; 223 SkScalar fStrokeWidth; 224 bool fIsRound; 225 SkAutoTUnref<SkPathEffect> fPE; 226 227 enum { 228 N = SkBENCHLOOP(200) 229 }; 230 231 public: 232 DashLineBench(void* param, SkScalar width, bool isRound) : INHERITED(param) { 233 fName.printf("dashline_%g_%s", SkScalarToFloat(width), isRound ? "circle" : "square"); 234 fStrokeWidth = width; 235 fIsRound = isRound; 236 237 SkScalar vals[] = { SK_Scalar1, SK_Scalar1 }; 238 fPE.reset(new SkDashPathEffect(vals, 2, 0)); 239 } 240 241 protected: 242 virtual const char* onGetName() SK_OVERRIDE { 243 return fName.c_str(); 244 } 245 246 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { 247 SkPaint paint; 248 this->setupPaint(&paint); 249 paint.setStrokeWidth(fStrokeWidth); 250 paint.setStrokeCap(fIsRound ? SkPaint::kRound_Cap : SkPaint::kSquare_Cap); 251 paint.setPathEffect(fPE); 252 for (int i = 0; i < N; ++i) { 253 canvas->drawLine(10 * SK_Scalar1, 10 * SK_Scalar1, 254 640 * SK_Scalar1, 10 * SK_Scalar1, paint); 255 } 256 } 257 258 private: 259 typedef SkBenchmark INHERITED; 260 }; 261 262 class DrawPointsDashingBench : public SkBenchmark { 263 SkString fName; 264 int fStrokeWidth; 265 bool fDoAA; 266 267 SkAutoTUnref<SkPathEffect> fPathEffect; 268 269 enum { 270 N = SkBENCHLOOP(480) 271 }; 272 273 public: 274 DrawPointsDashingBench(void* param, int dashLength, int strokeWidth, bool doAA) 275 : INHERITED(param) { 276 fName.printf("drawpointsdash_%d_%d%s", dashLength, strokeWidth, doAA ? "_aa" : "_bw"); 277 fStrokeWidth = strokeWidth; 278 fDoAA = doAA; 279 280 SkScalar vals[] = { SkIntToScalar(dashLength), SkIntToScalar(dashLength) }; 281 fPathEffect.reset(new SkDashPathEffect(vals, 2, SK_Scalar1, false)); 282 } 283 284 protected: 285 virtual const char* onGetName() SK_OVERRIDE { 286 return fName.c_str(); 287 } 288 289 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { 290 291 SkPaint p; 292 this->setupPaint(&p); 293 p.setColor(SK_ColorBLACK); 294 p.setStyle(SkPaint::kStroke_Style); 295 p.setStrokeWidth(SkIntToScalar(fStrokeWidth)); 296 p.setPathEffect(fPathEffect); 297 p.setAntiAlias(fDoAA); 298 299 SkPoint pts[2] = { 300 { SkIntToScalar(10), 0 }, 301 { SkIntToScalar(640), 0 } 302 }; 303 304 for (int i = 0; i < N; ++i) { 305 306 pts[0].fY = pts[1].fY = SkIntToScalar(i % 480); 307 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p); 308 } 309 } 310 311 private: 312 typedef SkBenchmark INHERITED; 313 }; 314 315 // Want to test how we handle dashing when 99% of the dash is clipped out 316 class GiantDashBench : public SkBenchmark { 317 SkString fName; 318 SkScalar fStrokeWidth; 319 SkPoint fPts[2]; 320 SkAutoTUnref<SkPathEffect> fPathEffect; 321 322 public: 323 enum LineType { 324 kHori_LineType, 325 kVert_LineType, 326 kDiag_LineType 327 }; 328 329 static const char* LineTypeName(LineType lt) { 330 static const char* gNames[] = { "hori", "vert", "diag" }; 331 SkASSERT((size_t)lt <= SK_ARRAY_COUNT(gNames)); 332 return gNames[lt]; 333 } 334 335 GiantDashBench(void* param, LineType lt, SkScalar width) : INHERITED(param) { 336 fName.printf("giantdashline_%s_%g", LineTypeName(lt), width); 337 fStrokeWidth = width; 338 339 // deliberately pick intervals that won't be caught by asPoints(), so 340 // we can test the filterPath code-path. 341 const SkScalar intervals[] = { 2, 1, 1, 1 }; 342 fPathEffect.reset(new SkDashPathEffect(intervals, 343 SK_ARRAY_COUNT(intervals), 0)); 344 345 SkScalar cx = 640 / 2; // center X 346 SkScalar cy = 480 / 2; // center Y 347 SkMatrix matrix; 348 349 switch (lt) { 350 case kHori_LineType: 351 matrix.setIdentity(); 352 break; 353 case kVert_LineType: 354 matrix.setRotate(90, cx, cy); 355 break; 356 case kDiag_LineType: 357 matrix.setRotate(45, cx, cy); 358 break; 359 } 360 361 const SkScalar overshoot = 100*1000; 362 const SkPoint pts[2] = { 363 { -overshoot, cy }, { 640 + overshoot, cy } 364 }; 365 matrix.mapPoints(fPts, pts, 2); 366 } 367 368 protected: 369 virtual const char* onGetName() SK_OVERRIDE { 370 return fName.c_str(); 371 } 372 373 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { 374 SkPaint p; 375 this->setupPaint(&p); 376 p.setStyle(SkPaint::kStroke_Style); 377 p.setStrokeWidth(fStrokeWidth); 378 p.setPathEffect(fPathEffect); 379 380 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, p); 381 } 382 383 private: 384 typedef SkBenchmark INHERITED; 385 }; 386 387 388 /////////////////////////////////////////////////////////////////////////////// 389 390 static const SkScalar gDots[] = { SK_Scalar1, SK_Scalar1 }; 391 392 #define PARAM(array) array, SK_ARRAY_COUNT(array) 393 394 DEF_BENCH( return new DashBench(p, PARAM(gDots), 0); ) 395 DEF_BENCH( return new DashBench(p, PARAM(gDots), 1); ) 396 DEF_BENCH( return new DashBench(p, PARAM(gDots), 1, true); ) 397 DEF_BENCH( return new DashBench(p, PARAM(gDots), 4); ) 398 DEF_BENCH( return new MakeDashBench(p, make_poly, "poly"); ) 399 DEF_BENCH( return new MakeDashBench(p, make_quad, "quad"); ) 400 DEF_BENCH( return new MakeDashBench(p, make_cubic, "cubic"); ) 401 DEF_BENCH( return new DashLineBench(p, 0, false); ) 402 DEF_BENCH( return new DashLineBench(p, SK_Scalar1, false); ) 403 DEF_BENCH( return new DashLineBench(p, 2 * SK_Scalar1, false); ) 404 DEF_BENCH( return new DashLineBench(p, 0, true); ) 405 DEF_BENCH( return new DashLineBench(p, SK_Scalar1, true); ) 406 DEF_BENCH( return new DashLineBench(p, 2 * SK_Scalar1, true); ) 407 408 DEF_BENCH( return new DrawPointsDashingBench(p, 1, 1, false); ) 409 DEF_BENCH( return new DrawPointsDashingBench(p, 1, 1, true); ) 410 DEF_BENCH( return new DrawPointsDashingBench(p, 3, 1, false); ) 411 DEF_BENCH( return new DrawPointsDashingBench(p, 3, 1, true); ) 412 DEF_BENCH( return new DrawPointsDashingBench(p, 5, 5, false); ) 413 DEF_BENCH( return new DrawPointsDashingBench(p, 5, 5, true); ) 414 415 DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kHori_LineType, 0); ) 416 DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kVert_LineType, 0); ) 417 DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kDiag_LineType, 0); ) 418 419 // pass 2 to explicitly avoid any 1-is-the-same-as-hairline special casing 420 421 // hori_2 is just too slow to enable at the moment 422 DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kHori_LineType, 2); ) 423 DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kVert_LineType, 2); ) 424 DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kDiag_LineType, 2); ) 425