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 "Benchmark.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 Benchmark { 33 protected: 34 SkString fName; 35 SkTDArray<SkScalar> fIntervals; 36 int fWidth; 37 SkPoint fPts[2]; 38 bool fDoClip; 39 40 public: 41 DashBench(const SkScalar intervals[], int count, int width, 42 bool doClip = false) { 43 fIntervals.append(count, intervals); 44 for (int i = 0; i < count; ++i) { 45 fIntervals[i] *= width; 46 } 47 fWidth = width; 48 fName.printf("dash_%d_%s", width, doClip ? "clipped" : "noclip"); 49 fDoClip = doClip; 50 51 fPts[0].set(SkIntToScalar(10), SkIntToScalar(10)); 52 fPts[1].set(SkIntToScalar(600), SkIntToScalar(10)); 53 } 54 55 virtual void makePath(SkPath* path) { 56 path_hline(path); 57 } 58 59 protected: 60 virtual const char* onGetName() SK_OVERRIDE { 61 return fName.c_str(); 62 } 63 64 virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE { 65 SkPaint paint; 66 this->setupPaint(&paint); 67 paint.setStyle(SkPaint::kStroke_Style); 68 paint.setStrokeWidth(SkIntToScalar(fWidth)); 69 paint.setAntiAlias(false); 70 71 SkPath path; 72 this->makePath(&path); 73 74 paint.setPathEffect(SkDashPathEffect::Create(fIntervals.begin(), 75 fIntervals.count(), 0))->unref(); 76 77 if (fDoClip) { 78 SkRect r = path.getBounds(); 79 r.inset(-SkIntToScalar(20), -SkIntToScalar(20)); 80 // now move it so we don't intersect 81 r.offset(0, r.height() * 3 / 2); 82 canvas->clipRect(r); 83 } 84 85 this->handlePath(canvas, path, paint, loops); 86 } 87 88 virtual void handlePath(SkCanvas* canvas, const SkPath& path, 89 const SkPaint& paint, int N) { 90 for (int i = 0; i < N; ++i) { 91 // canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, paint); 92 canvas->drawPath(path, paint); 93 } 94 } 95 96 private: 97 typedef Benchmark INHERITED; 98 }; 99 100 class RectDashBench : public DashBench { 101 public: 102 RectDashBench(const SkScalar intervals[], int count, int width) 103 : INHERITED(intervals, count, width) { 104 fName.append("_rect"); 105 } 106 107 protected: 108 virtual void handlePath(SkCanvas* canvas, const SkPath& path, 109 const SkPaint& paint, int N) SK_OVERRIDE { 110 SkPoint pts[2]; 111 if (!path.isLine(pts) || pts[0].fY != pts[1].fY) { 112 this->INHERITED::handlePath(canvas, path, paint, N); 113 } else { 114 SkRect rect; 115 rect.fLeft = pts[0].fX; 116 rect.fTop = pts[0].fY - paint.getStrokeWidth() / 2; 117 rect.fRight = rect.fLeft + SkIntToScalar(fWidth); 118 rect.fBottom = rect.fTop + paint.getStrokeWidth(); 119 120 SkPaint p(paint); 121 p.setStyle(SkPaint::kFill_Style); 122 p.setPathEffect(NULL); 123 124 int count = SkScalarRoundToInt((pts[1].fX - pts[0].fX) / (2*fWidth)); 125 SkScalar dx = SkIntToScalar(2 * fWidth); 126 127 for (int i = 0; i < N*10; ++i) { 128 SkRect r = rect; 129 for (int j = 0; j < count; ++j) { 130 canvas->drawRect(r, p); 131 r.offset(dx, 0); 132 } 133 } 134 } 135 } 136 137 private: 138 typedef DashBench INHERITED; 139 }; 140 141 static void make_unit_star(SkPath* path, int n) { 142 SkScalar rad = -SK_ScalarPI / 2; 143 const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n; 144 145 path->moveTo(0, -SK_Scalar1); 146 for (int i = 1; i < n; i++) { 147 rad += drad; 148 SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV); 149 path->lineTo(cosV, sinV); 150 } 151 path->close(); 152 } 153 154 static void make_poly(SkPath* path) { 155 make_unit_star(path, 9); 156 SkMatrix matrix; 157 matrix.setScale(SkIntToScalar(100), SkIntToScalar(100)); 158 path->transform(matrix); 159 } 160 161 static void make_quad(SkPath* path) { 162 SkScalar x0 = SkIntToScalar(10); 163 SkScalar y0 = SkIntToScalar(10); 164 path->moveTo(x0, y0); 165 path->quadTo(x0, y0 + 400 * SK_Scalar1, 166 x0 + 600 * SK_Scalar1, y0 + 400 * SK_Scalar1); 167 } 168 169 static void make_cubic(SkPath* path) { 170 SkScalar x0 = SkIntToScalar(10); 171 SkScalar y0 = SkIntToScalar(10); 172 path->moveTo(x0, y0); 173 path->cubicTo(x0, y0 + 400 * SK_Scalar1, 174 x0 + 600 * SK_Scalar1, y0 + 400 * SK_Scalar1, 175 x0 + 600 * SK_Scalar1, y0); 176 } 177 178 class MakeDashBench : public Benchmark { 179 SkString fName; 180 SkPath fPath; 181 SkAutoTUnref<SkPathEffect> fPE; 182 183 public: 184 MakeDashBench(void (*proc)(SkPath*), const char name[]) { 185 fName.printf("makedash_%s", name); 186 proc(&fPath); 187 188 SkScalar vals[] = { SkIntToScalar(4), SkIntToScalar(4) }; 189 fPE.reset(SkDashPathEffect::Create(vals, 2, 0)); 190 } 191 192 protected: 193 virtual const char* onGetName() SK_OVERRIDE { 194 return fName.c_str(); 195 } 196 197 virtual void onDraw(const int loops, SkCanvas*) SK_OVERRIDE { 198 SkPath dst; 199 for (int i = 0; i < loops; ++i) { 200 SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle); 201 202 fPE->filterPath(&dst, fPath, &rec, NULL); 203 dst.rewind(); 204 } 205 } 206 207 private: 208 typedef Benchmark INHERITED; 209 }; 210 211 /* 212 * We try to special case square dashes (intervals are equal to strokewidth). 213 */ 214 class DashLineBench : public Benchmark { 215 SkString fName; 216 SkScalar fStrokeWidth; 217 bool fIsRound; 218 SkAutoTUnref<SkPathEffect> fPE; 219 220 public: 221 DashLineBench(SkScalar width, bool isRound) { 222 fName.printf("dashline_%g_%s", SkScalarToFloat(width), isRound ? "circle" : "square"); 223 fStrokeWidth = width; 224 fIsRound = isRound; 225 226 SkScalar vals[] = { SK_Scalar1, SK_Scalar1 }; 227 fPE.reset(SkDashPathEffect::Create(vals, 2, 0)); 228 } 229 230 protected: 231 virtual const char* onGetName() SK_OVERRIDE { 232 return fName.c_str(); 233 } 234 235 virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE { 236 SkPaint paint; 237 this->setupPaint(&paint); 238 paint.setStrokeWidth(fStrokeWidth); 239 paint.setStrokeCap(fIsRound ? SkPaint::kRound_Cap : SkPaint::kSquare_Cap); 240 paint.setPathEffect(fPE); 241 for (int i = 0; i < loops; ++i) { 242 canvas->drawLine(10 * SK_Scalar1, 10 * SK_Scalar1, 243 640 * SK_Scalar1, 10 * SK_Scalar1, paint); 244 } 245 } 246 247 private: 248 typedef Benchmark INHERITED; 249 }; 250 251 class DrawPointsDashingBench : public Benchmark { 252 SkString fName; 253 int fStrokeWidth; 254 bool fDoAA; 255 256 SkAutoTUnref<SkPathEffect> fPathEffect; 257 258 public: 259 DrawPointsDashingBench(int dashLength, int strokeWidth, bool doAA) 260 { 261 fName.printf("drawpointsdash_%d_%d%s", dashLength, strokeWidth, doAA ? "_aa" : "_bw"); 262 fStrokeWidth = strokeWidth; 263 fDoAA = doAA; 264 265 SkScalar vals[] = { SkIntToScalar(dashLength), SkIntToScalar(dashLength) }; 266 fPathEffect.reset(SkDashPathEffect::Create(vals, 2, SK_Scalar1)); 267 } 268 269 protected: 270 virtual const char* onGetName() SK_OVERRIDE { 271 return fName.c_str(); 272 } 273 274 virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE { 275 SkPaint p; 276 this->setupPaint(&p); 277 p.setColor(SK_ColorBLACK); 278 p.setStyle(SkPaint::kStroke_Style); 279 p.setStrokeWidth(SkIntToScalar(fStrokeWidth)); 280 p.setPathEffect(fPathEffect); 281 p.setAntiAlias(fDoAA); 282 283 SkPoint pts[2] = { 284 { SkIntToScalar(10), 0 }, 285 { SkIntToScalar(640), 0 } 286 }; 287 288 for (int i = 0; i < loops; ++i) { 289 pts[0].fY = pts[1].fY = SkIntToScalar(i % 480); 290 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p); 291 } 292 } 293 294 private: 295 typedef Benchmark INHERITED; 296 }; 297 298 // Want to test how we handle dashing when 99% of the dash is clipped out 299 class GiantDashBench : public Benchmark { 300 SkString fName; 301 SkScalar fStrokeWidth; 302 SkPoint fPts[2]; 303 SkAutoTUnref<SkPathEffect> fPathEffect; 304 305 public: 306 enum LineType { 307 kHori_LineType, 308 kVert_LineType, 309 kDiag_LineType, 310 kLineTypeCount 311 }; 312 313 static const char* LineTypeName(LineType lt) { 314 static const char* gNames[] = { "hori", "vert", "diag" }; 315 SK_COMPILE_ASSERT(kLineTypeCount == SK_ARRAY_COUNT(gNames), names_wrong_size); 316 return gNames[lt]; 317 } 318 319 GiantDashBench(LineType lt, SkScalar width) { 320 fName.printf("giantdashline_%s_%g", LineTypeName(lt), width); 321 fStrokeWidth = width; 322 323 // deliberately pick intervals that won't be caught by asPoints(), so 324 // we can test the filterPath code-path. 325 const SkScalar intervals[] = { 20, 10, 10, 10 }; 326 fPathEffect.reset(SkDashPathEffect::Create(intervals, 327 SK_ARRAY_COUNT(intervals), 0)); 328 329 SkScalar cx = 640 / 2; // center X 330 SkScalar cy = 480 / 2; // center Y 331 SkMatrix matrix; 332 333 switch (lt) { 334 case kHori_LineType: 335 matrix.setIdentity(); 336 break; 337 case kVert_LineType: 338 matrix.setRotate(90, cx, cy); 339 break; 340 case kDiag_LineType: 341 matrix.setRotate(45, cx, cy); 342 break; 343 case kLineTypeCount: 344 // Not a real enum value. 345 break; 346 } 347 348 const SkScalar overshoot = 100*1000; 349 const SkPoint pts[2] = { 350 { -overshoot, cy }, { 640 + overshoot, cy } 351 }; 352 matrix.mapPoints(fPts, pts, 2); 353 } 354 355 protected: 356 virtual const char* onGetName() SK_OVERRIDE { 357 return fName.c_str(); 358 } 359 360 virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE { 361 SkPaint p; 362 this->setupPaint(&p); 363 p.setStyle(SkPaint::kStroke_Style); 364 p.setStrokeWidth(fStrokeWidth); 365 p.setPathEffect(fPathEffect); 366 367 for (int i = 0; i < loops; i++) { 368 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, p); 369 } 370 } 371 372 private: 373 typedef Benchmark INHERITED; 374 }; 375 376 // Want to test how we draw a dashed grid (like what is used in spreadsheets) of many 377 // small dashed lines switching back and forth between horizontal and vertical 378 class DashGridBench : public Benchmark { 379 SkString fName; 380 int fStrokeWidth; 381 bool fDoAA; 382 383 SkAutoTUnref<SkPathEffect> fPathEffect; 384 385 public: 386 DashGridBench(int dashLength, int strokeWidth, bool doAA) { 387 fName.printf("dashgrid_%d_%d%s", dashLength, strokeWidth, doAA ? "_aa" : "_bw"); 388 fStrokeWidth = strokeWidth; 389 fDoAA = doAA; 390 391 SkScalar vals[] = { SkIntToScalar(dashLength), SkIntToScalar(dashLength) }; 392 fPathEffect.reset(SkDashPathEffect::Create(vals, 2, SK_Scalar1)); 393 } 394 395 protected: 396 virtual const char* onGetName() SK_OVERRIDE { 397 return fName.c_str(); 398 } 399 400 virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE { 401 SkPaint p; 402 this->setupPaint(&p); 403 p.setColor(SK_ColorBLACK); 404 p.setStyle(SkPaint::kStroke_Style); 405 p.setStrokeWidth(SkIntToScalar(fStrokeWidth)); 406 p.setPathEffect(fPathEffect); 407 p.setAntiAlias(fDoAA); 408 409 SkPoint pts[4] = { 410 { SkIntToScalar(0), 20.5f }, 411 { SkIntToScalar(20), 20.5f }, 412 { 20.5f, SkIntToScalar(0) }, 413 { 20.5f, SkIntToScalar(20) } 414 }; 415 416 for (int i = 0; i < loops; ++i) { 417 for (int j = 0; j < 10; ++j) { 418 for (int k = 0; k < 10; ++k) { 419 // Horizontal line 420 SkPoint horPts[2]; 421 horPts[0].fX = pts[0].fX + k * 22.f; 422 horPts[0].fY = pts[0].fY + j * 22.f; 423 horPts[1].fX = pts[1].fX + k * 22.f; 424 horPts[1].fY = pts[1].fY + j * 22.f; 425 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, horPts, p); 426 427 // Vertical line 428 SkPoint vertPts[2]; 429 vertPts[0].fX = pts[2].fX + k * 22.f; 430 vertPts[0].fY = pts[2].fY + j * 22.f; 431 vertPts[1].fX = pts[3].fX + k * 22.f; 432 vertPts[1].fY = pts[3].fY + j * 22.f; 433 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, vertPts, p); 434 } 435 } 436 } 437 } 438 439 private: 440 typedef Benchmark INHERITED; 441 }; 442 443 /////////////////////////////////////////////////////////////////////////////// 444 445 static const SkScalar gDots[] = { SK_Scalar1, SK_Scalar1 }; 446 447 #define PARAM(array) array, SK_ARRAY_COUNT(array) 448 449 DEF_BENCH( return new DashBench(PARAM(gDots), 0); ) 450 DEF_BENCH( return new DashBench(PARAM(gDots), 1); ) 451 DEF_BENCH( return new DashBench(PARAM(gDots), 1, true); ) 452 DEF_BENCH( return new DashBench(PARAM(gDots), 4); ) 453 DEF_BENCH( return new MakeDashBench(make_poly, "poly"); ) 454 DEF_BENCH( return new MakeDashBench(make_quad, "quad"); ) 455 DEF_BENCH( return new MakeDashBench(make_cubic, "cubic"); ) 456 DEF_BENCH( return new DashLineBench(0, false); ) 457 DEF_BENCH( return new DashLineBench(SK_Scalar1, false); ) 458 DEF_BENCH( return new DashLineBench(2 * SK_Scalar1, false); ) 459 DEF_BENCH( return new DashLineBench(0, true); ) 460 DEF_BENCH( return new DashLineBench(SK_Scalar1, true); ) 461 DEF_BENCH( return new DashLineBench(2 * SK_Scalar1, true); ) 462 463 DEF_BENCH( return new DrawPointsDashingBench(1, 1, false); ) 464 DEF_BENCH( return new DrawPointsDashingBench(1, 1, true); ) 465 DEF_BENCH( return new DrawPointsDashingBench(3, 1, false); ) 466 DEF_BENCH( return new DrawPointsDashingBench(3, 1, true); ) 467 DEF_BENCH( return new DrawPointsDashingBench(5, 5, false); ) 468 DEF_BENCH( return new DrawPointsDashingBench(5, 5, true); ) 469 470 /* Disable the GiantDashBench for Android devices until we can better control 471 * the memory usage. (https://code.google.com/p/skia/issues/detail?id=1430) 472 */ 473 #ifndef SK_BUILD_FOR_ANDROID 474 DEF_BENCH( return new GiantDashBench(GiantDashBench::kHori_LineType, 0); ) 475 DEF_BENCH( return new GiantDashBench(GiantDashBench::kVert_LineType, 0); ) 476 DEF_BENCH( return new GiantDashBench(GiantDashBench::kDiag_LineType, 0); ) 477 478 // pass 2 to explicitly avoid any 1-is-the-same-as-hairline special casing 479 480 // hori_2 is just too slow to enable at the moment 481 DEF_BENCH( return new GiantDashBench(GiantDashBench::kHori_LineType, 2); ) 482 DEF_BENCH( return new GiantDashBench(GiantDashBench::kVert_LineType, 2); ) 483 DEF_BENCH( return new GiantDashBench(GiantDashBench::kDiag_LineType, 2); ) 484 485 DEF_BENCH( return new DashGridBench(1, 1, true); ) 486 DEF_BENCH( return new DashGridBench(1, 1, false); ) 487 DEF_BENCH( return new DashGridBench(3, 1, true); ) 488 DEF_BENCH( return new DashGridBench(3, 1, false); ) 489 #endif 490