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