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