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