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