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 "SkColorPriv.h"
12#include "SkGradientShader.h"
13#include "SkPaint.h"
14#include "SkShader.h"
15#include "SkString.h"
16
17struct GradData {
18    int             fCount;
19    const SkColor*  fColors;
20    const SkScalar* fPos;
21    const char*     fName;
22};
23
24static const SkColor gColors[] = {
25    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK,
26    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK,
27    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK,
28    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK,
29    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK,
30    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK,
31    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK,
32    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK,
33    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK,
34    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK, // 10 lines, 50 colors
35};
36
37static const SkColor gShallowColors[] = { 0xFF555555, 0xFF444444 };
38
39// We have several special-cases depending on the number (and spacing) of colors, so
40// try to exercise those here.
41static const GradData gGradData[] = {
42    { 2, gColors, NULL, "" },
43    { 50, gColors, NULL, "_hicolor" }, // many color gradient
44    { 3, gColors, NULL, "_3color" },
45    { 2, gShallowColors, NULL, "_shallow" },
46};
47
48/// Ignores scale
49static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
50                            SkShader::TileMode tm, float scale) {
51    return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos, data.fCount, tm);
52}
53
54static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
55                            SkShader::TileMode tm, float scale) {
56    SkPoint center;
57    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
58               SkScalarAve(pts[0].fY, pts[1].fY));
59    return SkGradientShader::CreateRadial(center, center.fX * scale,
60                                          data.fColors,
61                                          data.fPos, data.fCount, tm);
62}
63
64/// Ignores scale
65static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
66                           SkShader::TileMode tm, float scale) {
67    SkPoint center;
68    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
69               SkScalarAve(pts[0].fY, pts[1].fY));
70    return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
71                                         data.fPos, data.fCount);
72}
73
74/// Ignores scale
75static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
76                             SkShader::TileMode tm, float scale) {
77    SkPoint center0, center1;
78    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
79                SkScalarAve(pts[0].fY, pts[1].fY));
80    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
81                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
82    return SkGradientShader::CreateTwoPointRadial(
83                                                  center1, (pts[1].fX - pts[0].fX) / 7,
84                                                  center0, (pts[1].fX - pts[0].fX) / 2,
85                                                  data.fColors, data.fPos, data.fCount, tm);
86}
87
88/// Ignores scale
89static SkShader* MakeConical(const SkPoint pts[2], const GradData& data,
90                             SkShader::TileMode tm, float scale) {
91    SkPoint center0, center1;
92    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
93                SkScalarAve(pts[0].fY, pts[1].fY));
94    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
95                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
96    return SkGradientShader::CreateTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
97                                                   center0, (pts[1].fX - pts[0].fX) / 2,
98                                                   data.fColors, data.fPos, data.fCount, tm);
99}
100
101/// Ignores scale
102static SkShader* MakeConicalZeroRad(const SkPoint pts[2], const GradData& data,
103                                    SkShader::TileMode tm, float scale) {
104    SkPoint center0, center1;
105    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
106                SkScalarAve(pts[0].fY, pts[1].fY));
107    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
108                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
109    return SkGradientShader::CreateTwoPointConical(center1, 0.0,
110                                                   center0, (pts[1].fX - pts[0].fX) / 2,
111                                                   data.fColors, data.fPos, data.fCount, tm);
112}
113
114/// Ignores scale
115static SkShader* MakeConicalOutside(const SkPoint pts[2], const GradData& data,
116                                    SkShader::TileMode tm, float scale) {
117    SkPoint center0, center1;
118    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
119    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
120    center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
121    center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
122    return SkGradientShader::CreateTwoPointConical(center0, radius0,
123                                                   center1, radius1,
124                                                   data.fColors, data.fPos,
125                                                   data.fCount, tm);
126}
127
128/// Ignores scale
129static SkShader* MakeConicalOutsideZeroRad(const SkPoint pts[2], const GradData& data,
130                                           SkShader::TileMode tm, float scale) {
131    SkPoint center0, center1;
132    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
133    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
134    center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
135    center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
136    return SkGradientShader::CreateTwoPointConical(center0, 0.0,
137                                                   center1, radius1,
138                                                   data.fColors, data.fPos,
139                                                   data.fCount, tm);
140}
141
142typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
143                               SkShader::TileMode tm, float scale);
144
145static const struct {
146    GradMaker   fMaker;
147    const char* fName;
148} gGrads[] = {
149    { MakeLinear,                 "linear"  },
150    { MakeRadial,                 "radial1" },
151    { MakeSweep,                  "sweep"   },
152    { Make2Radial,                "radial2" },
153    { MakeConical,                "conical" },
154    { MakeConicalZeroRad,         "conicalZero" },
155    { MakeConicalOutside,         "conicalOut" },
156    { MakeConicalOutsideZeroRad,  "conicalOutZero" },
157};
158
159enum GradType { // these must match the order in gGrads
160    kLinear_GradType,
161    kRadial_GradType,
162    kSweep_GradType,
163    kRadial2_GradType,
164    kConical_GradType,
165    kConicalZero_GradType,
166    kConicalOut_GradType,
167    kConicalOutZero_GradType
168};
169
170enum GeomType {
171    kRect_GeomType,
172    kOval_GeomType
173};
174
175static const char* tilemodename(SkShader::TileMode tm) {
176    switch (tm) {
177        case SkShader::kClamp_TileMode:
178            return "clamp";
179        case SkShader::kRepeat_TileMode:
180            return "repeat";
181        case SkShader::kMirror_TileMode:
182            return "mirror";
183        default:
184            SkDEBUGFAIL("unknown tilemode");
185            return "error";
186    }
187}
188
189static const char* geomtypename(GeomType gt) {
190    switch (gt) {
191        case kRect_GeomType:
192            return "rectangle";
193        case kOval_GeomType:
194            return "oval";
195        default:
196            SkDEBUGFAIL("unknown geometry type");
197            return "error";
198    }
199}
200
201///////////////////////////////////////////////////////////////////////////////
202
203class GradientBench : public Benchmark {
204    SkString fName;
205    SkShader* fShader;
206    bool fDither;
207    enum {
208        W   = 400,
209        H   = 400,
210    };
211public:
212    SkShader* makeShader(GradType gradType, GradData data, SkShader::TileMode tm, float scale) {
213        const SkPoint pts[2] = {
214            { 0, 0 },
215            { SkIntToScalar(W), SkIntToScalar(H) }
216        };
217
218        return gGrads[gradType].fMaker(pts, data, tm, scale);
219    }
220
221    GradientBench(GradType gradType,
222                  GradData data = gGradData[0],
223                  SkShader::TileMode tm = SkShader::kClamp_TileMode,
224                  GeomType geomType = kRect_GeomType,
225                  float scale = 1.0f) {
226        fName.printf("gradient_%s_%s", gGrads[gradType].fName,
227                     tilemodename(tm));
228        if (geomType != kRect_GeomType) {
229            fName.append("_");
230            fName.append(geomtypename(geomType));
231        }
232
233        if (scale != 1.f) {
234            fName.appendf("_scale_%g", scale);
235        }
236
237        fName.append(data.fName);
238
239        fDither = false;
240        fShader = this->makeShader(gradType, data, tm, scale);
241        fGeomType = geomType;
242    }
243
244    GradientBench(GradType gradType, GradData data, bool dither) {
245        const char *tmname = tilemodename(SkShader::kClamp_TileMode);
246        fName.printf("gradient_%s_%s", gGrads[gradType].fName, tmname);
247        fName.append(data.fName);
248
249        fDither = dither;
250        if (dither) {
251            fName.appendf("_dither");
252        }
253
254        fShader = this->makeShader(gradType, data, SkShader::kClamp_TileMode, 1.0f);
255        fGeomType = kRect_GeomType;
256    }
257
258    virtual ~GradientBench() {
259        fShader->unref();
260    }
261
262protected:
263    virtual const char* onGetName() {
264        return fName.c_str();
265    }
266
267    virtual void onDraw(const int loops, SkCanvas* canvas) {
268        SkPaint paint;
269        this->setupPaint(&paint);
270
271        paint.setShader(fShader);
272        if (fDither) {
273            paint.setDither(true);
274        }
275
276        SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
277        for (int i = 0; i < loops; i++) {
278            switch (fGeomType) {
279               case kRect_GeomType:
280                   canvas->drawRect(r, paint);
281                   break;
282               case kOval_GeomType:
283                   canvas->drawOval(r, paint);
284                   break;
285            }
286        }
287    }
288
289private:
290    typedef Benchmark INHERITED;
291
292    GeomType fGeomType;
293};
294
295DEF_BENCH( return new GradientBench(kLinear_GradType); )
296DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[1]); )
297DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[2]); )
298DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[0], SkShader::kMirror_TileMode); )
299
300DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0]); )
301DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[1]); )
302DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[2]); )
303// Draw a radial gradient of radius 1/2 on a rectangle; half the lines should
304// be completely pinned, the other half should pe partially pinned
305DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kClamp_TileMode, kRect_GeomType, 0.5f); )
306
307// Draw a radial gradient on a circle of equal size; all the lines should
308// hit the unpinned fast path (so long as GradientBench.W == H)
309DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kClamp_TileMode, kOval_GeomType); )
310
311DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kMirror_TileMode); )
312DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kRepeat_TileMode); )
313DEF_BENCH( return new GradientBench(kSweep_GradType); )
314DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[1]); )
315DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[2]); )
316DEF_BENCH( return new GradientBench(kRadial2_GradType); )
317DEF_BENCH( return new GradientBench(kRadial2_GradType, gGradData[1]); )
318DEF_BENCH( return new GradientBench(kRadial2_GradType, gGradData[0], SkShader::kMirror_TileMode); )
319DEF_BENCH( return new GradientBench(kConical_GradType); )
320DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[1]); )
321DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[2]); )
322DEF_BENCH( return new GradientBench(kConicalZero_GradType); )
323DEF_BENCH( return new GradientBench(kConicalZero_GradType, gGradData[1]); )
324DEF_BENCH( return new GradientBench(kConicalZero_GradType, gGradData[2]); )
325DEF_BENCH( return new GradientBench(kConicalOut_GradType); )
326DEF_BENCH( return new GradientBench(kConicalOut_GradType, gGradData[1]); )
327DEF_BENCH( return new GradientBench(kConicalOut_GradType, gGradData[2]); )
328DEF_BENCH( return new GradientBench(kConicalOutZero_GradType); )
329DEF_BENCH( return new GradientBench(kConicalOutZero_GradType, gGradData[1]); )
330DEF_BENCH( return new GradientBench(kConicalOutZero_GradType, gGradData[2]); )
331
332// Dithering
333DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[3], true); )
334DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[3], false); )
335DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[3], true); )
336DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[3], false); )
337DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[3], true); )
338DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[3], false); )
339DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[3], true); )
340DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[3], false); )
341
342///////////////////////////////////////////////////////////////////////////////
343
344class Gradient2Bench : public Benchmark {
345    SkString fName;
346    bool     fHasAlpha;
347
348public:
349    Gradient2Bench(bool hasAlpha)  {
350        fName.printf("gradient_create_%s", hasAlpha ? "alpha" : "opaque");
351        fHasAlpha = hasAlpha;
352    }
353
354protected:
355    virtual const char* onGetName() {
356        return fName.c_str();
357    }
358
359    virtual void onDraw(const int loops, SkCanvas* canvas) {
360        SkPaint paint;
361        this->setupPaint(&paint);
362
363        const SkRect r = { 0, 0, SkIntToScalar(4), SkIntToScalar(4) };
364        const SkPoint pts[] = {
365            { 0, 0 },
366            { SkIntToScalar(100), SkIntToScalar(100) },
367        };
368
369        for (int i = 0; i < loops; i++) {
370            const int gray = i % 256;
371            const int alpha = fHasAlpha ? gray : 0xFF;
372            SkColor colors[] = {
373                SK_ColorBLACK,
374                SkColorSetARGB(alpha, gray, gray, gray),
375                SK_ColorWHITE };
376            SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL,
377                                                         SK_ARRAY_COUNT(colors),
378                                                         SkShader::kClamp_TileMode);
379            paint.setShader(s)->unref();
380            canvas->drawRect(r, paint);
381        }
382    }
383
384private:
385    typedef Benchmark INHERITED;
386};
387
388DEF_BENCH( return new Gradient2Bench(false); )
389DEF_BENCH( return new Gradient2Bench(true); )
390