• 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 "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  */
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 SkBenchmark {
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     virtual const char* onGetName() SK_OVERRIDE {
61         return fName.c_str();
62     }
63 
onDraw(const int loops,SkCanvas * canvas)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(new SkDashPathEffect(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 SkBenchmark 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) 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 
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     SkMatrix matrix;
157     matrix.setScale(SkIntToScalar(100), SkIntToScalar(100));
158     path->transform(matrix);
159 }
160 
make_quad(SkPath * path)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 
make_cubic(SkPath * path)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 SkBenchmark {
179     SkString fName;
180     SkPath   fPath;
181     SkAutoTUnref<SkPathEffect> fPE;
182 
183 public:
MakeDashBench(void (* proc)(SkPath *),const char name[])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(new SkDashPathEffect(vals, 2, 0));
190     }
191 
192 protected:
onGetName()193     virtual const char* onGetName() SK_OVERRIDE {
194         return fName.c_str();
195     }
196 
onDraw(const int loops,SkCanvas *)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 SkBenchmark INHERITED;
209 };
210 
211 /*
212  *  We try to special case square dashes (intervals are equal to strokewidth).
213  */
214 class DashLineBench : public SkBenchmark {
215     SkString fName;
216     SkScalar fStrokeWidth;
217     bool     fIsRound;
218     SkAutoTUnref<SkPathEffect> fPE;
219 
220 public:
DashLineBench(SkScalar width,bool isRound)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(new SkDashPathEffect(vals, 2, 0));
228     }
229 
230 protected:
onGetName()231     virtual const char* onGetName() SK_OVERRIDE {
232         return fName.c_str();
233     }
234 
onDraw(const int loops,SkCanvas * canvas)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 SkBenchmark INHERITED;
249 };
250 
251 class DrawPointsDashingBench : public SkBenchmark {
252     SkString fName;
253     int      fStrokeWidth;
254     bool     fDoAA;
255 
256     SkAutoTUnref<SkPathEffect> fPathEffect;
257 
258 public:
DrawPointsDashingBench(int dashLength,int strokeWidth,bool doAA)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(new SkDashPathEffect(vals, 2, SK_Scalar1, false));
267     }
268 
269 protected:
onGetName()270     virtual const char* onGetName() SK_OVERRIDE {
271         return fName.c_str();
272     }
273 
onDraw(const int loops,SkCanvas * canvas)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 SkBenchmark INHERITED;
296 };
297 
298 // Want to test how we handle dashing when 99% of the dash is clipped out
299 class GiantDashBench : public SkBenchmark {
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 
LineTypeName(LineType lt)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 
GiantDashBench(LineType lt,SkScalar width)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[] = { 2, 1, 1, 1 };
326         fPathEffect.reset(new SkDashPathEffect(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:
onGetName()356     virtual const char* onGetName() SK_OVERRIDE {
357         return fName.c_str();
358     }
359 
onDraw(const int loops,SkCanvas * canvas)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 SkBenchmark INHERITED;
374 };
375 
376 
377 ///////////////////////////////////////////////////////////////////////////////
378 
379 static const SkScalar gDots[] = { SK_Scalar1, SK_Scalar1 };
380 
381 #define PARAM(array)    array, SK_ARRAY_COUNT(array)
382 
383 DEF_BENCH( return new DashBench(PARAM(gDots), 0); )
384 DEF_BENCH( return new DashBench(PARAM(gDots), 1); )
385 DEF_BENCH( return new DashBench(PARAM(gDots), 1, true); )
386 DEF_BENCH( return new DashBench(PARAM(gDots), 4); )
387 DEF_BENCH( return new MakeDashBench(make_poly, "poly"); )
388 DEF_BENCH( return new MakeDashBench(make_quad, "quad"); )
389 DEF_BENCH( return new MakeDashBench(make_cubic, "cubic"); )
390 DEF_BENCH( return new DashLineBench(0, false); )
391 DEF_BENCH( return new DashLineBench(SK_Scalar1, false); )
392 DEF_BENCH( return new DashLineBench(2 * SK_Scalar1, false); )
393 DEF_BENCH( return new DashLineBench(0, true); )
394 DEF_BENCH( return new DashLineBench(SK_Scalar1, true); )
395 DEF_BENCH( return new DashLineBench(2 * SK_Scalar1, true); )
396 
397 DEF_BENCH( return new DrawPointsDashingBench(1, 1, false); )
398 DEF_BENCH( return new DrawPointsDashingBench(1, 1, true); )
399 DEF_BENCH( return new DrawPointsDashingBench(3, 1, false); )
400 DEF_BENCH( return new DrawPointsDashingBench(3, 1, true); )
401 DEF_BENCH( return new DrawPointsDashingBench(5, 5, false); )
402 DEF_BENCH( return new DrawPointsDashingBench(5, 5, true); )
403 
404 /* Disable the GiantDashBench for Android devices until we can better control
405  * the memory usage. (https://code.google.com/p/skia/issues/detail?id=1430)
406  */
407 #ifndef SK_BUILD_FOR_ANDROID
408 DEF_BENCH( return new GiantDashBench(GiantDashBench::kHori_LineType, 0); )
409 DEF_BENCH( return new GiantDashBench(GiantDashBench::kVert_LineType, 0); )
410 DEF_BENCH( return new GiantDashBench(GiantDashBench::kDiag_LineType, 0); )
411 
412 // pass 2 to explicitly avoid any 1-is-the-same-as-hairline special casing
413 
414 // hori_2 is just too slow to enable at the moment
415 DEF_BENCH( return new GiantDashBench(GiantDashBench::kHori_LineType, 2); )
416 DEF_BENCH( return new GiantDashBench(GiantDashBench::kVert_LineType, 2); )
417 DEF_BENCH( return new GiantDashBench(GiantDashBench::kDiag_LineType, 2); )
418 #endif
419