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