| |
| /* |
| * Copyright 2011 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| #include "Benchmark.h" |
| #include "SkBitmap.h" |
| #include "SkCanvas.h" |
| #include "SkDashPathEffect.h" |
| #include "SkPaint.h" |
| #include "SkPath.h" |
| #include "SkRandom.h" |
| #include "SkString.h" |
| #include "SkTDArray.h" |
| |
| |
| /* |
| * Cases to consider: |
| * |
| * 1. antialiasing on/off (esp. width <= 1) |
| * 2. strokewidth == 0, 1, 2 |
| * 3. hline, vline, diagonal, rect, oval |
| * 4. dots [1,1] ([N,N] where N=strokeWidth?) or arbitrary (e.g. [2,1] or [1,2,3,2]) |
| */ |
| static void path_hline(SkPath* path) { |
| path->moveTo(SkIntToScalar(10), SkIntToScalar(10)); |
| path->lineTo(SkIntToScalar(600), SkIntToScalar(10)); |
| } |
| |
| class DashBench : public Benchmark { |
| protected: |
| SkString fName; |
| SkTDArray<SkScalar> fIntervals; |
| int fWidth; |
| SkPoint fPts[2]; |
| bool fDoClip; |
| |
| public: |
| DashBench(const SkScalar intervals[], int count, int width, |
| bool doClip = false) { |
| fIntervals.append(count, intervals); |
| for (int i = 0; i < count; ++i) { |
| fIntervals[i] *= width; |
| } |
| fWidth = width; |
| fName.printf("dash_%d_%s", width, doClip ? "clipped" : "noclip"); |
| fDoClip = doClip; |
| |
| fPts[0].set(SkIntToScalar(10), SkIntToScalar(10)); |
| fPts[1].set(SkIntToScalar(600), SkIntToScalar(10)); |
| } |
| |
| virtual void makePath(SkPath* path) { |
| path_hline(path); |
| } |
| |
| protected: |
| const char* onGetName() override { |
| return fName.c_str(); |
| } |
| |
| void onDraw(const int loops, SkCanvas* canvas) override { |
| SkPaint paint; |
| this->setupPaint(&paint); |
| paint.setStyle(SkPaint::kStroke_Style); |
| paint.setStrokeWidth(SkIntToScalar(fWidth)); |
| paint.setAntiAlias(false); |
| |
| SkPath path; |
| this->makePath(&path); |
| |
| paint.setPathEffect(SkDashPathEffect::Create(fIntervals.begin(), |
| fIntervals.count(), 0))->unref(); |
| |
| if (fDoClip) { |
| SkRect r = path.getBounds(); |
| r.inset(-SkIntToScalar(20), -SkIntToScalar(20)); |
| // now move it so we don't intersect |
| r.offset(0, r.height() * 3 / 2); |
| canvas->clipRect(r); |
| } |
| |
| this->handlePath(canvas, path, paint, loops); |
| } |
| |
| virtual void handlePath(SkCanvas* canvas, const SkPath& path, |
| const SkPaint& paint, int N) { |
| for (int i = 0; i < N; ++i) { |
| // canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, paint); |
| canvas->drawPath(path, paint); |
| } |
| } |
| |
| private: |
| typedef Benchmark INHERITED; |
| }; |
| |
| class RectDashBench : public DashBench { |
| public: |
| RectDashBench(const SkScalar intervals[], int count, int width) |
| : INHERITED(intervals, count, width) { |
| fName.append("_rect"); |
| } |
| |
| protected: |
| virtual void handlePath(SkCanvas* canvas, const SkPath& path, |
| const SkPaint& paint, int N) override { |
| SkPoint pts[2]; |
| if (!path.isLine(pts) || pts[0].fY != pts[1].fY) { |
| this->INHERITED::handlePath(canvas, path, paint, N); |
| } else { |
| SkRect rect; |
| rect.fLeft = pts[0].fX; |
| rect.fTop = pts[0].fY - paint.getStrokeWidth() / 2; |
| rect.fRight = rect.fLeft + SkIntToScalar(fWidth); |
| rect.fBottom = rect.fTop + paint.getStrokeWidth(); |
| |
| SkPaint p(paint); |
| p.setStyle(SkPaint::kFill_Style); |
| p.setPathEffect(NULL); |
| |
| int count = SkScalarRoundToInt((pts[1].fX - pts[0].fX) / (2*fWidth)); |
| SkScalar dx = SkIntToScalar(2 * fWidth); |
| |
| for (int i = 0; i < N*10; ++i) { |
| SkRect r = rect; |
| for (int j = 0; j < count; ++j) { |
| canvas->drawRect(r, p); |
| r.offset(dx, 0); |
| } |
| } |
| } |
| } |
| |
| private: |
| typedef DashBench INHERITED; |
| }; |
| |
| static void make_unit_star(SkPath* path, int n) { |
| SkScalar rad = -SK_ScalarPI / 2; |
| const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n; |
| |
| path->moveTo(0, -SK_Scalar1); |
| for (int i = 1; i < n; i++) { |
| rad += drad; |
| SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV); |
| path->lineTo(cosV, sinV); |
| } |
| path->close(); |
| } |
| |
| static void make_poly(SkPath* path) { |
| make_unit_star(path, 9); |
| const SkMatrix matrix = SkMatrix::MakeScale(SkIntToScalar(100), SkIntToScalar(100)); |
| path->transform(matrix); |
| } |
| |
| static void make_quad(SkPath* path) { |
| SkScalar x0 = SkIntToScalar(10); |
| SkScalar y0 = SkIntToScalar(10); |
| path->moveTo(x0, y0); |
| path->quadTo(x0, y0 + 400 * SK_Scalar1, |
| x0 + 600 * SK_Scalar1, y0 + 400 * SK_Scalar1); |
| } |
| |
| static void make_cubic(SkPath* path) { |
| SkScalar x0 = SkIntToScalar(10); |
| SkScalar y0 = SkIntToScalar(10); |
| path->moveTo(x0, y0); |
| path->cubicTo(x0, y0 + 400 * SK_Scalar1, |
| x0 + 600 * SK_Scalar1, y0 + 400 * SK_Scalar1, |
| x0 + 600 * SK_Scalar1, y0); |
| } |
| |
| class MakeDashBench : public Benchmark { |
| SkString fName; |
| SkPath fPath; |
| SkAutoTUnref<SkPathEffect> fPE; |
| |
| public: |
| MakeDashBench(void (*proc)(SkPath*), const char name[]) { |
| fName.printf("makedash_%s", name); |
| proc(&fPath); |
| |
| SkScalar vals[] = { SkIntToScalar(4), SkIntToScalar(4) }; |
| fPE.reset(SkDashPathEffect::Create(vals, 2, 0)); |
| } |
| |
| protected: |
| const char* onGetName() override { |
| return fName.c_str(); |
| } |
| |
| void onDraw(const int loops, SkCanvas*) override { |
| SkPath dst; |
| for (int i = 0; i < loops; ++i) { |
| SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle); |
| |
| fPE->filterPath(&dst, fPath, &rec, NULL); |
| dst.rewind(); |
| } |
| } |
| |
| private: |
| typedef Benchmark INHERITED; |
| }; |
| |
| /* |
| * We try to special case square dashes (intervals are equal to strokewidth). |
| */ |
| class DashLineBench : public Benchmark { |
| SkString fName; |
| SkScalar fStrokeWidth; |
| bool fIsRound; |
| SkAutoTUnref<SkPathEffect> fPE; |
| |
| public: |
| DashLineBench(SkScalar width, bool isRound) { |
| fName.printf("dashline_%g_%s", SkScalarToFloat(width), isRound ? "circle" : "square"); |
| fStrokeWidth = width; |
| fIsRound = isRound; |
| |
| SkScalar vals[] = { SK_Scalar1, SK_Scalar1 }; |
| fPE.reset(SkDashPathEffect::Create(vals, 2, 0)); |
| } |
| |
| protected: |
| const char* onGetName() override { |
| return fName.c_str(); |
| } |
| |
| void onDraw(const int loops, SkCanvas* canvas) override { |
| SkPaint paint; |
| this->setupPaint(&paint); |
| paint.setStrokeWidth(fStrokeWidth); |
| paint.setStrokeCap(fIsRound ? SkPaint::kRound_Cap : SkPaint::kSquare_Cap); |
| paint.setPathEffect(fPE); |
| for (int i = 0; i < loops; ++i) { |
| canvas->drawLine(10 * SK_Scalar1, 10 * SK_Scalar1, |
| 640 * SK_Scalar1, 10 * SK_Scalar1, paint); |
| } |
| } |
| |
| private: |
| typedef Benchmark INHERITED; |
| }; |
| |
| class DrawPointsDashingBench : public Benchmark { |
| SkString fName; |
| int fStrokeWidth; |
| bool fDoAA; |
| |
| SkAutoTUnref<SkPathEffect> fPathEffect; |
| |
| public: |
| DrawPointsDashingBench(int dashLength, int strokeWidth, bool doAA) |
| { |
| fName.printf("drawpointsdash_%d_%d%s", dashLength, strokeWidth, doAA ? "_aa" : "_bw"); |
| fStrokeWidth = strokeWidth; |
| fDoAA = doAA; |
| |
| SkScalar vals[] = { SkIntToScalar(dashLength), SkIntToScalar(dashLength) }; |
| fPathEffect.reset(SkDashPathEffect::Create(vals, 2, SK_Scalar1)); |
| } |
| |
| protected: |
| const char* onGetName() override { |
| return fName.c_str(); |
| } |
| |
| void onDraw(const int loops, SkCanvas* canvas) override { |
| SkPaint p; |
| this->setupPaint(&p); |
| p.setColor(SK_ColorBLACK); |
| p.setStyle(SkPaint::kStroke_Style); |
| p.setStrokeWidth(SkIntToScalar(fStrokeWidth)); |
| p.setPathEffect(fPathEffect); |
| p.setAntiAlias(fDoAA); |
| |
| SkPoint pts[2] = { |
| { SkIntToScalar(10), 0 }, |
| { SkIntToScalar(640), 0 } |
| }; |
| |
| for (int i = 0; i < loops; ++i) { |
| pts[0].fY = pts[1].fY = SkIntToScalar(i % 480); |
| canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p); |
| } |
| } |
| |
| private: |
| typedef Benchmark INHERITED; |
| }; |
| |
| // Want to test how we handle dashing when 99% of the dash is clipped out |
| class GiantDashBench : public Benchmark { |
| SkString fName; |
| SkScalar fStrokeWidth; |
| SkPoint fPts[2]; |
| SkAutoTUnref<SkPathEffect> fPathEffect; |
| |
| public: |
| enum LineType { |
| kHori_LineType, |
| kVert_LineType, |
| kDiag_LineType, |
| kLineTypeCount |
| }; |
| |
| static const char* LineTypeName(LineType lt) { |
| static const char* gNames[] = { "hori", "vert", "diag" }; |
| static_assert(kLineTypeCount == SK_ARRAY_COUNT(gNames), "names_wrong_size"); |
| return gNames[lt]; |
| } |
| |
| GiantDashBench(LineType lt, SkScalar width) { |
| fName.printf("giantdashline_%s_%g", LineTypeName(lt), width); |
| fStrokeWidth = width; |
| |
| // deliberately pick intervals that won't be caught by asPoints(), so |
| // we can test the filterPath code-path. |
| const SkScalar intervals[] = { 20, 10, 10, 10 }; |
| fPathEffect.reset(SkDashPathEffect::Create(intervals, |
| SK_ARRAY_COUNT(intervals), 0)); |
| |
| SkScalar cx = 640 / 2; // center X |
| SkScalar cy = 480 / 2; // center Y |
| SkMatrix matrix; |
| |
| switch (lt) { |
| case kHori_LineType: |
| matrix.setIdentity(); |
| break; |
| case kVert_LineType: |
| matrix.setRotate(90, cx, cy); |
| break; |
| case kDiag_LineType: |
| matrix.setRotate(45, cx, cy); |
| break; |
| case kLineTypeCount: |
| // Not a real enum value. |
| break; |
| } |
| |
| const SkScalar overshoot = 100*1000; |
| const SkPoint pts[2] = { |
| { -overshoot, cy }, { 640 + overshoot, cy } |
| }; |
| matrix.mapPoints(fPts, pts, 2); |
| } |
| |
| protected: |
| const char* onGetName() override { |
| return fName.c_str(); |
| } |
| |
| void onDraw(const int loops, SkCanvas* canvas) override { |
| SkPaint p; |
| this->setupPaint(&p); |
| p.setStyle(SkPaint::kStroke_Style); |
| p.setStrokeWidth(fStrokeWidth); |
| p.setPathEffect(fPathEffect); |
| |
| for (int i = 0; i < loops; i++) { |
| canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, p); |
| } |
| } |
| |
| private: |
| typedef Benchmark INHERITED; |
| }; |
| |
| // Want to test how we draw a dashed grid (like what is used in spreadsheets) of many |
| // small dashed lines switching back and forth between horizontal and vertical |
| class DashGridBench : public Benchmark { |
| SkString fName; |
| int fStrokeWidth; |
| bool fDoAA; |
| |
| SkAutoTUnref<SkPathEffect> fPathEffect; |
| |
| public: |
| DashGridBench(int dashLength, int strokeWidth, bool doAA) { |
| fName.printf("dashgrid_%d_%d%s", dashLength, strokeWidth, doAA ? "_aa" : "_bw"); |
| fStrokeWidth = strokeWidth; |
| fDoAA = doAA; |
| |
| SkScalar vals[] = { SkIntToScalar(dashLength), SkIntToScalar(dashLength) }; |
| fPathEffect.reset(SkDashPathEffect::Create(vals, 2, SK_Scalar1)); |
| } |
| |
| protected: |
| const char* onGetName() override { |
| return fName.c_str(); |
| } |
| |
| void onDraw(const int loops, SkCanvas* canvas) override { |
| SkPaint p; |
| this->setupPaint(&p); |
| p.setColor(SK_ColorBLACK); |
| p.setStyle(SkPaint::kStroke_Style); |
| p.setStrokeWidth(SkIntToScalar(fStrokeWidth)); |
| p.setPathEffect(fPathEffect); |
| p.setAntiAlias(fDoAA); |
| |
| SkPoint pts[4] = { |
| { SkIntToScalar(0), 20.5f }, |
| { SkIntToScalar(20), 20.5f }, |
| { 20.5f, SkIntToScalar(0) }, |
| { 20.5f, SkIntToScalar(20) } |
| }; |
| |
| for (int i = 0; i < loops; ++i) { |
| for (int j = 0; j < 10; ++j) { |
| for (int k = 0; k < 10; ++k) { |
| // Horizontal line |
| SkPoint horPts[2]; |
| horPts[0].fX = pts[0].fX + k * 22.f; |
| horPts[0].fY = pts[0].fY + j * 22.f; |
| horPts[1].fX = pts[1].fX + k * 22.f; |
| horPts[1].fY = pts[1].fY + j * 22.f; |
| canvas->drawPoints(SkCanvas::kLines_PointMode, 2, horPts, p); |
| |
| // Vertical line |
| SkPoint vertPts[2]; |
| vertPts[0].fX = pts[2].fX + k * 22.f; |
| vertPts[0].fY = pts[2].fY + j * 22.f; |
| vertPts[1].fX = pts[3].fX + k * 22.f; |
| vertPts[1].fY = pts[3].fY + j * 22.f; |
| canvas->drawPoints(SkCanvas::kLines_PointMode, 2, vertPts, p); |
| } |
| } |
| } |
| } |
| |
| private: |
| typedef Benchmark INHERITED; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| static const SkScalar gDots[] = { SK_Scalar1, SK_Scalar1 }; |
| |
| #define PARAM(array) array, SK_ARRAY_COUNT(array) |
| |
| DEF_BENCH( return new DashBench(PARAM(gDots), 0); ) |
| DEF_BENCH( return new DashBench(PARAM(gDots), 1); ) |
| DEF_BENCH( return new DashBench(PARAM(gDots), 1, true); ) |
| DEF_BENCH( return new DashBench(PARAM(gDots), 4); ) |
| DEF_BENCH( return new MakeDashBench(make_poly, "poly"); ) |
| DEF_BENCH( return new MakeDashBench(make_quad, "quad"); ) |
| DEF_BENCH( return new MakeDashBench(make_cubic, "cubic"); ) |
| DEF_BENCH( return new DashLineBench(0, false); ) |
| DEF_BENCH( return new DashLineBench(SK_Scalar1, false); ) |
| DEF_BENCH( return new DashLineBench(2 * SK_Scalar1, false); ) |
| DEF_BENCH( return new DashLineBench(0, true); ) |
| DEF_BENCH( return new DashLineBench(SK_Scalar1, true); ) |
| DEF_BENCH( return new DashLineBench(2 * SK_Scalar1, true); ) |
| |
| DEF_BENCH( return new DrawPointsDashingBench(1, 1, false); ) |
| DEF_BENCH( return new DrawPointsDashingBench(1, 1, true); ) |
| DEF_BENCH( return new DrawPointsDashingBench(3, 1, false); ) |
| DEF_BENCH( return new DrawPointsDashingBench(3, 1, true); ) |
| DEF_BENCH( return new DrawPointsDashingBench(5, 5, false); ) |
| DEF_BENCH( return new DrawPointsDashingBench(5, 5, true); ) |
| |
| /* Disable the GiantDashBench for Android devices until we can better control |
| * the memory usage. (https://code.google.com/p/skia/issues/detail?id=1430) |
| */ |
| #ifndef SK_BUILD_FOR_ANDROID |
| DEF_BENCH( return new GiantDashBench(GiantDashBench::kHori_LineType, 0); ) |
| DEF_BENCH( return new GiantDashBench(GiantDashBench::kVert_LineType, 0); ) |
| DEF_BENCH( return new GiantDashBench(GiantDashBench::kDiag_LineType, 0); ) |
| |
| // pass 2 to explicitly avoid any 1-is-the-same-as-hairline special casing |
| |
| // hori_2 is just too slow to enable at the moment |
| DEF_BENCH( return new GiantDashBench(GiantDashBench::kHori_LineType, 2); ) |
| DEF_BENCH( return new GiantDashBench(GiantDashBench::kVert_LineType, 2); ) |
| DEF_BENCH( return new GiantDashBench(GiantDashBench::kDiag_LineType, 2); ) |
| |
| DEF_BENCH( return new DashGridBench(1, 1, true); ) |
| DEF_BENCH( return new DashGridBench(1, 1, false); ) |
| DEF_BENCH( return new DashGridBench(3, 1, true); ) |
| DEF_BENCH( return new DashGridBench(3, 1, false); ) |
| #endif |