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* MakeConical(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::CreateTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
83                                                   center0, (pts[1].fX - pts[0].fX) / 2,
84                                                   data.fColors, data.fPos, data.fCount, tm);
85}
86
87/// Ignores scale
88static SkShader* MakeConicalZeroRad(const SkPoint pts[2], const GradData& data,
89                                    SkShader::TileMode tm, float scale) {
90    SkPoint center0, center1;
91    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
92                SkScalarAve(pts[0].fY, pts[1].fY));
93    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
94                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
95    return SkGradientShader::CreateTwoPointConical(center1, 0.0,
96                                                   center0, (pts[1].fX - pts[0].fX) / 2,
97                                                   data.fColors, data.fPos, data.fCount, tm);
98}
99
100/// Ignores scale
101static SkShader* MakeConicalOutside(const SkPoint pts[2], const GradData& data,
102                                    SkShader::TileMode tm, float scale) {
103    SkPoint center0, center1;
104    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
105    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
106    center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
107    center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
108    return SkGradientShader::CreateTwoPointConical(center0, radius0,
109                                                   center1, radius1,
110                                                   data.fColors, data.fPos,
111                                                   data.fCount, tm);
112}
113
114/// Ignores scale
115static SkShader* MakeConicalOutsideZeroRad(const SkPoint pts[2], const GradData& data,
116                                           SkShader::TileMode tm, float scale) {
117    SkPoint center0, center1;
118    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
119    SkScalar radius1 = (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, 0.0,
123                                                   center1, radius1,
124                                                   data.fColors, data.fPos,
125                                                   data.fCount, tm);
126}
127
128typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
129                               SkShader::TileMode tm, float scale);
130
131static const struct {
132    GradMaker   fMaker;
133    const char* fName;
134} gGrads[] = {
135    { MakeLinear,                 "linear"  },
136    { MakeRadial,                 "radial1" },
137    { MakeSweep,                  "sweep"   },
138    { MakeConical,                "conical" },
139    { MakeConicalZeroRad,         "conicalZero" },
140    { MakeConicalOutside,         "conicalOut" },
141    { MakeConicalOutsideZeroRad,  "conicalOutZero" },
142};
143
144enum GradType { // these must match the order in gGrads
145    kLinear_GradType,
146    kRadial_GradType,
147    kSweep_GradType,
148    kConical_GradType,
149    kConicalZero_GradType,
150    kConicalOut_GradType,
151    kConicalOutZero_GradType
152};
153
154enum GeomType {
155    kRect_GeomType,
156    kOval_GeomType
157};
158
159static const char* tilemodename(SkShader::TileMode tm) {
160    switch (tm) {
161        case SkShader::kClamp_TileMode:
162            return "clamp";
163        case SkShader::kRepeat_TileMode:
164            return "repeat";
165        case SkShader::kMirror_TileMode:
166            return "mirror";
167        default:
168            SkDEBUGFAIL("unknown tilemode");
169            return "error";
170    }
171}
172
173static const char* geomtypename(GeomType gt) {
174    switch (gt) {
175        case kRect_GeomType:
176            return "rectangle";
177        case kOval_GeomType:
178            return "oval";
179        default:
180            SkDEBUGFAIL("unknown geometry type");
181            return "error";
182    }
183}
184
185///////////////////////////////////////////////////////////////////////////////
186
187class GradientBench : public Benchmark {
188    SkString fName;
189    SkShader* fShader;
190    bool fDither;
191    enum {
192        W   = 400,
193        H   = 400,
194    };
195public:
196    SkShader* makeShader(GradType gradType, GradData data, SkShader::TileMode tm, float scale) {
197        const SkPoint pts[2] = {
198            { 0, 0 },
199            { SkIntToScalar(W), SkIntToScalar(H) }
200        };
201
202        return gGrads[gradType].fMaker(pts, data, tm, scale);
203    }
204
205    GradientBench(GradType gradType,
206                  GradData data = gGradData[0],
207                  SkShader::TileMode tm = SkShader::kClamp_TileMode,
208                  GeomType geomType = kRect_GeomType,
209                  float scale = 1.0f) {
210        fName.printf("gradient_%s_%s", gGrads[gradType].fName,
211                     tilemodename(tm));
212        if (geomType != kRect_GeomType) {
213            fName.append("_");
214            fName.append(geomtypename(geomType));
215        }
216
217        if (scale != 1.f) {
218            fName.appendf("_scale_%g", scale);
219        }
220
221        fName.append(data.fName);
222
223        fDither = false;
224        fShader = this->makeShader(gradType, data, tm, scale);
225        fGeomType = geomType;
226    }
227
228    GradientBench(GradType gradType, GradData data, bool dither) {
229        const char *tmname = tilemodename(SkShader::kClamp_TileMode);
230        fName.printf("gradient_%s_%s", gGrads[gradType].fName, tmname);
231        fName.append(data.fName);
232
233        fDither = dither;
234        if (dither) {
235            fName.appendf("_dither");
236        }
237
238        fShader = this->makeShader(gradType, data, SkShader::kClamp_TileMode, 1.0f);
239        fGeomType = kRect_GeomType;
240    }
241
242    virtual ~GradientBench() {
243        fShader->unref();
244    }
245
246protected:
247    virtual const char* onGetName() {
248        return fName.c_str();
249    }
250
251    virtual void onDraw(const int loops, SkCanvas* canvas) {
252        SkPaint paint;
253        this->setupPaint(&paint);
254
255        paint.setShader(fShader);
256        if (fDither) {
257            paint.setDither(true);
258        }
259
260        SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
261        for (int i = 0; i < loops; i++) {
262            switch (fGeomType) {
263               case kRect_GeomType:
264                   canvas->drawRect(r, paint);
265                   break;
266               case kOval_GeomType:
267                   canvas->drawOval(r, paint);
268                   break;
269            }
270        }
271    }
272
273private:
274    typedef Benchmark INHERITED;
275
276    GeomType fGeomType;
277};
278
279DEF_BENCH( return new GradientBench(kLinear_GradType); )
280DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[1]); )
281DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[2]); )
282DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[0], SkShader::kMirror_TileMode); )
283
284DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0]); )
285DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[1]); )
286DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[2]); )
287// Draw a radial gradient of radius 1/2 on a rectangle; half the lines should
288// be completely pinned, the other half should pe partially pinned
289DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kClamp_TileMode, kRect_GeomType, 0.5f); )
290
291// Draw a radial gradient on a circle of equal size; all the lines should
292// hit the unpinned fast path (so long as GradientBench.W == H)
293DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kClamp_TileMode, kOval_GeomType); )
294
295DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kMirror_TileMode); )
296DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kRepeat_TileMode); )
297DEF_BENCH( return new GradientBench(kSweep_GradType); )
298DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[1]); )
299DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[2]); )
300DEF_BENCH( return new GradientBench(kConical_GradType); )
301DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[1]); )
302DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[2]); )
303DEF_BENCH( return new GradientBench(kConicalZero_GradType); )
304DEF_BENCH( return new GradientBench(kConicalZero_GradType, gGradData[1]); )
305DEF_BENCH( return new GradientBench(kConicalZero_GradType, gGradData[2]); )
306DEF_BENCH( return new GradientBench(kConicalOut_GradType); )
307DEF_BENCH( return new GradientBench(kConicalOut_GradType, gGradData[1]); )
308DEF_BENCH( return new GradientBench(kConicalOut_GradType, gGradData[2]); )
309DEF_BENCH( return new GradientBench(kConicalOutZero_GradType); )
310DEF_BENCH( return new GradientBench(kConicalOutZero_GradType, gGradData[1]); )
311DEF_BENCH( return new GradientBench(kConicalOutZero_GradType, gGradData[2]); )
312
313// Dithering
314DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[3], true); )
315DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[3], false); )
316DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[3], true); )
317DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[3], false); )
318DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[3], true); )
319DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[3], false); )
320DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[3], true); )
321DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[3], false); )
322
323///////////////////////////////////////////////////////////////////////////////
324
325class Gradient2Bench : public Benchmark {
326    SkString fName;
327    bool     fHasAlpha;
328
329public:
330    Gradient2Bench(bool hasAlpha)  {
331        fName.printf("gradient_create_%s", hasAlpha ? "alpha" : "opaque");
332        fHasAlpha = hasAlpha;
333    }
334
335protected:
336    virtual const char* onGetName() {
337        return fName.c_str();
338    }
339
340    virtual void onDraw(const int loops, SkCanvas* canvas) {
341        SkPaint paint;
342        this->setupPaint(&paint);
343
344        const SkRect r = { 0, 0, SkIntToScalar(4), SkIntToScalar(4) };
345        const SkPoint pts[] = {
346            { 0, 0 },
347            { SkIntToScalar(100), SkIntToScalar(100) },
348        };
349
350        for (int i = 0; i < loops; i++) {
351            const int gray = i % 256;
352            const int alpha = fHasAlpha ? gray : 0xFF;
353            SkColor colors[] = {
354                SK_ColorBLACK,
355                SkColorSetARGB(alpha, gray, gray, gray),
356                SK_ColorWHITE };
357            SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL,
358                                                         SK_ARRAY_COUNT(colors),
359                                                         SkShader::kClamp_TileMode);
360            paint.setShader(s)->unref();
361            canvas->drawRect(r, paint);
362        }
363    }
364
365private:
366    typedef Benchmark INHERITED;
367};
368
369DEF_BENCH( return new Gradient2Bench(false); )
370DEF_BENCH( return new Gradient2Bench(true); )
371