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        kRepeat = 15,
211    };
212public:
213    SkShader* makeShader(GradType gradType, GradData data, SkShader::TileMode tm, float scale) {
214        const SkPoint pts[2] = {
215            { 0, 0 },
216            { SkIntToScalar(W), SkIntToScalar(H) }
217        };
218
219        return gGrads[gradType].fMaker(pts, data, tm, scale);
220    }
221
222    GradientBench(GradType gradType,
223                  GradData data = gGradData[0],
224                  SkShader::TileMode tm = SkShader::kClamp_TileMode,
225                  GeomType geomType = kRect_GeomType,
226                  float scale = 1.0f) {
227        fName.printf("gradient_%s_%s", gGrads[gradType].fName,
228                     tilemodename(tm));
229        if (geomType != kRect_GeomType) {
230            fName.append("_");
231            fName.append(geomtypename(geomType));
232        }
233
234        if (scale != 1.f) {
235            fName.appendf("_scale_%g", scale);
236        }
237
238        fName.append(data.fName);
239
240        fDither = false;
241        fShader = this->makeShader(gradType, data, tm, scale);
242        fGeomType = geomType;
243    }
244
245    GradientBench(GradType gradType, GradData data, bool dither) {
246        const char *tmname = tilemodename(SkShader::kClamp_TileMode);
247        fName.printf("gradient_%s_%s", gGrads[gradType].fName, tmname);
248        fName.append(data.fName);
249
250        fDither = dither;
251        if (dither) {
252            fName.appendf("_dither");
253        }
254
255        fShader = this->makeShader(gradType, data, SkShader::kClamp_TileMode, 1.0f);
256        fGeomType = kRect_GeomType;
257    }
258
259    virtual ~GradientBench() {
260        fShader->unref();
261    }
262
263protected:
264    virtual const char* onGetName() {
265        return fName.c_str();
266    }
267
268    virtual void onDraw(const int loops, SkCanvas* canvas) {
269        SkPaint paint;
270        this->setupPaint(&paint);
271
272        paint.setShader(fShader);
273        if (fDither) {
274            paint.setDither(true);
275        }
276
277        SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
278        for (int i = 0; i < loops * kRepeat; i++) {
279            switch (fGeomType) {
280               case kRect_GeomType:
281                   canvas->drawRect(r, paint);
282                   break;
283               case kOval_GeomType:
284                   canvas->drawOval(r, paint);
285                   break;
286            }
287        }
288    }
289
290private:
291    typedef Benchmark INHERITED;
292
293    GeomType fGeomType;
294};
295
296DEF_BENCH( return new GradientBench(kLinear_GradType); )
297DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[1]); )
298DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[2]); )
299DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[0], SkShader::kMirror_TileMode); )
300
301DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0]); )
302DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[1]); )
303DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[2]); )
304// Draw a radial gradient of radius 1/2 on a rectangle; half the lines should
305// be completely pinned, the other half should pe partially pinned
306DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kClamp_TileMode, kRect_GeomType, 0.5f); )
307
308// Draw a radial gradient on a circle of equal size; all the lines should
309// hit the unpinned fast path (so long as GradientBench.W == H)
310DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kClamp_TileMode, kOval_GeomType); )
311
312DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kMirror_TileMode); )
313DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kRepeat_TileMode); )
314DEF_BENCH( return new GradientBench(kSweep_GradType); )
315DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[1]); )
316DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[2]); )
317DEF_BENCH( return new GradientBench(kRadial2_GradType); )
318DEF_BENCH( return new GradientBench(kRadial2_GradType, gGradData[1]); )
319DEF_BENCH( return new GradientBench(kRadial2_GradType, gGradData[0], SkShader::kMirror_TileMode); )
320DEF_BENCH( return new GradientBench(kConical_GradType); )
321DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[1]); )
322DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[2]); )
323DEF_BENCH( return new GradientBench(kConicalZero_GradType); )
324DEF_BENCH( return new GradientBench(kConicalZero_GradType, gGradData[1]); )
325DEF_BENCH( return new GradientBench(kConicalZero_GradType, gGradData[2]); )
326DEF_BENCH( return new GradientBench(kConicalOut_GradType); )
327DEF_BENCH( return new GradientBench(kConicalOut_GradType, gGradData[1]); )
328DEF_BENCH( return new GradientBench(kConicalOut_GradType, gGradData[2]); )
329DEF_BENCH( return new GradientBench(kConicalOutZero_GradType); )
330DEF_BENCH( return new GradientBench(kConicalOutZero_GradType, gGradData[1]); )
331DEF_BENCH( return new GradientBench(kConicalOutZero_GradType, gGradData[2]); )
332
333// Dithering
334DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[3], true); )
335DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[3], false); )
336DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[3], true); )
337DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[3], false); )
338DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[3], true); )
339DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[3], false); )
340DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[3], true); )
341DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[3], false); )
342
343///////////////////////////////////////////////////////////////////////////////
344
345class Gradient2Bench : public Benchmark {
346    SkString fName;
347    bool     fHasAlpha;
348
349public:
350    Gradient2Bench(bool hasAlpha)  {
351        fName.printf("gradient_create_%s", hasAlpha ? "alpha" : "opaque");
352        fHasAlpha = hasAlpha;
353    }
354
355protected:
356    virtual const char* onGetName() {
357        return fName.c_str();
358    }
359
360    virtual void onDraw(const int loops, SkCanvas* canvas) {
361        SkPaint paint;
362        this->setupPaint(&paint);
363
364        const SkRect r = { 0, 0, SkIntToScalar(4), SkIntToScalar(4) };
365        const SkPoint pts[] = {
366            { 0, 0 },
367            { SkIntToScalar(100), SkIntToScalar(100) },
368        };
369
370        for (int i = 0; i < loops; i++) {
371            const int gray = i % 256;
372            const int alpha = fHasAlpha ? gray : 0xFF;
373            SkColor colors[] = {
374                SK_ColorBLACK,
375                SkColorSetARGB(alpha, gray, gray, gray),
376                SK_ColorWHITE };
377            SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL,
378                                                         SK_ARRAY_COUNT(colors),
379                                                         SkShader::kClamp_TileMode);
380            paint.setShader(s)->unref();
381            canvas->drawRect(r, paint);
382        }
383    }
384
385private:
386    typedef Benchmark INHERITED;
387};
388
389DEF_BENCH( return new Gradient2Bench(false); )
390DEF_BENCH( return new Gradient2Bench(true); )
391