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
17 struct GradData {
18 int fCount;
19 const SkColor* fColors;
20 const SkScalar* fPos;
21 const char* fName;
22 };
23
24 static 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
37 static 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.
41 static 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
MakeLinear(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,float scale)49 static 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
MakeRadial(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,float scale)54 static 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
MakeSweep(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,float scale)65 static 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
MakeConical(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,float scale)75 static 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
MakeConicalZeroRad(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,float scale)88 static 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
MakeConicalOutside(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,float scale)101 static 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
MakeConicalOutsideZeroRad(const SkPoint pts[2],const GradData & data,SkShader::TileMode tm,float scale)115 static 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
128 typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
129 SkShader::TileMode tm, float scale);
130
131 static 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
144 enum 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
154 enum GeomType {
155 kRect_GeomType,
156 kOval_GeomType
157 };
158
tilemodename(SkShader::TileMode tm)159 static 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
geomtypename(GeomType gt)173 static 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
187 class GradientBench : public Benchmark {
188 SkString fName;
189 SkShader* fShader;
190 bool fDither;
191 enum {
192 W = 400,
193 H = 400,
194 };
195 public:
makeShader(GradType gradType,GradData data,SkShader::TileMode tm,float scale)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
GradientBench(GradType gradType,GradData data=gGradData[0],SkShader::TileMode tm=SkShader::kClamp_TileMode,GeomType geomType=kRect_GeomType,float scale=1.0f)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
GradientBench(GradType gradType,GradData data,bool dither)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
~GradientBench()242 virtual ~GradientBench() {
243 fShader->unref();
244 }
245
246 protected:
onGetName()247 virtual const char* onGetName() {
248 return fName.c_str();
249 }
250
onDraw(const int loops,SkCanvas * canvas)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
273 private:
274 typedef Benchmark INHERITED;
275
276 GeomType fGeomType;
277 };
278
279 DEF_BENCH( return new GradientBench(kLinear_GradType); )
280 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[1]); )
281 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[2]); )
282 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[0], SkShader::kMirror_TileMode); )
283
284 DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0]); )
285 DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[1]); )
286 DEF_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
289 DEF_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)
293 DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kClamp_TileMode, kOval_GeomType); )
294
295 DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kMirror_TileMode); )
296 DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[0], SkShader::kRepeat_TileMode); )
297 DEF_BENCH( return new GradientBench(kSweep_GradType); )
298 DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[1]); )
299 DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[2]); )
300 DEF_BENCH( return new GradientBench(kConical_GradType); )
301 DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[1]); )
302 DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[2]); )
303 DEF_BENCH( return new GradientBench(kConicalZero_GradType); )
304 DEF_BENCH( return new GradientBench(kConicalZero_GradType, gGradData[1]); )
305 DEF_BENCH( return new GradientBench(kConicalZero_GradType, gGradData[2]); )
306 DEF_BENCH( return new GradientBench(kConicalOut_GradType); )
307 DEF_BENCH( return new GradientBench(kConicalOut_GradType, gGradData[1]); )
308 DEF_BENCH( return new GradientBench(kConicalOut_GradType, gGradData[2]); )
309 DEF_BENCH( return new GradientBench(kConicalOutZero_GradType); )
310 DEF_BENCH( return new GradientBench(kConicalOutZero_GradType, gGradData[1]); )
311 DEF_BENCH( return new GradientBench(kConicalOutZero_GradType, gGradData[2]); )
312
313 // Dithering
314 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[3], true); )
315 DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[3], false); )
316 DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[3], true); )
317 DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[3], false); )
318 DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[3], true); )
319 DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[3], false); )
320 DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[3], true); )
321 DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[3], false); )
322
323 ///////////////////////////////////////////////////////////////////////////////
324
325 class Gradient2Bench : public Benchmark {
326 SkString fName;
327 bool fHasAlpha;
328
329 public:
Gradient2Bench(bool hasAlpha)330 Gradient2Bench(bool hasAlpha) {
331 fName.printf("gradient_create_%s", hasAlpha ? "alpha" : "opaque");
332 fHasAlpha = hasAlpha;
333 }
334
335 protected:
onGetName()336 virtual const char* onGetName() {
337 return fName.c_str();
338 }
339
onDraw(const int loops,SkCanvas * canvas)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
365 private:
366 typedef Benchmark INHERITED;
367 };
368
369 DEF_BENCH( return new Gradient2Bench(false); )
370 DEF_BENCH( return new Gradient2Bench(true); )
371