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