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
8 #include "gm/gm.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkFont.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPoint.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkRefCnt.h"
19 #include "include/core/SkScalar.h"
20 #include "include/core/SkShader.h"
21 #include "include/core/SkSize.h"
22 #include "include/core/SkString.h"
23 #include "include/core/SkTileMode.h"
24 #include "include/core/SkTypeface.h"
25 #include "include/core/SkTypes.h"
26 #include "include/effects/SkGradientShader.h"
27 #include "include/utils/SkTextUtils.h"
28 #include "tools/Resources.h"
29 #include "tools/ToolUtils.h"
30
31 #include <functional>
32
makebm(SkBitmap * bm,SkColorType ct,int w,int h)33 static void makebm(SkBitmap* bm, SkColorType ct, int w, int h) {
34 bm->allocPixels(SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType));
35 bm->eraseColor(SK_ColorTRANSPARENT);
36
37 SkCanvas canvas(*bm);
38 SkPoint pts[] = { { 0, 0 }, { SkIntToScalar(w), SkIntToScalar(h)} };
39 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
40 SkScalar pos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
41 SkPaint paint;
42
43 paint.setDither(true);
44 paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, SK_ARRAY_COUNT(colors),
45 SkTileMode::kClamp));
46 canvas.drawPaint(paint);
47 }
48
setup(SkPaint * paint,const SkBitmap & bm,SkFilterMode fm,SkTileMode tmx,SkTileMode tmy)49 static void setup(SkPaint* paint, const SkBitmap& bm, SkFilterMode fm,
50 SkTileMode tmx, SkTileMode tmy) {
51 paint->setShader(bm.makeShader(tmx, tmy, SkSamplingOptions(fm)));
52 }
53
54 constexpr SkColorType gColorTypes[] = {
55 kN32_SkColorType,
56 kRGB_565_SkColorType,
57 };
58
59 class TilingGM : public skiagm::GM {
60 public:
TilingGM(bool powerOfTwoSize)61 TilingGM(bool powerOfTwoSize)
62 : fPowerOfTwoSize(powerOfTwoSize) {
63 }
64
65 SkBitmap fTexture[SK_ARRAY_COUNT(gColorTypes)];
66
67 protected:
68
69 enum {
70 kPOTSize = 32,
71 kNPOTSize = 21,
72 };
73
onShortName()74 SkString onShortName() override {
75 SkString name("tilemodes");
76 if (!fPowerOfTwoSize) {
77 name.append("_npot");
78 }
79 return name;
80 }
81
onISize()82 SkISize onISize() override { return SkISize::Make(880, 560); }
83
onOnceBeforeDraw()84 void onOnceBeforeDraw() override {
85 int size = fPowerOfTwoSize ? kPOTSize : kNPOTSize;
86 for (size_t i = 0; i < SK_ARRAY_COUNT(gColorTypes); i++) {
87 makebm(&fTexture[i], gColorTypes[i], size, size);
88 }
89 }
90
onDraw(SkCanvas * canvas)91 void onDraw(SkCanvas* canvas) override {
92 SkPaint textPaint;
93 SkFont font(ToolUtils::create_portable_typeface());
94
95 int size = fPowerOfTwoSize ? kPOTSize : kNPOTSize;
96
97 SkRect r = { 0, 0, SkIntToScalar(size*2), SkIntToScalar(size*2) };
98
99 const char* gConfigNames[] = { "8888", "565", "4444" };
100
101 constexpr SkFilterMode gFilters[] = { SkFilterMode::kNearest, SkFilterMode::kLinear };
102 static const char* gFilterNames[] = { "point", "bilinear" };
103
104 constexpr SkTileMode gModes[] = {
105 SkTileMode::kClamp, SkTileMode::kRepeat, SkTileMode::kMirror };
106 static const char* gModeNames[] = { "C", "R", "M" };
107
108 SkScalar y = SkIntToScalar(24);
109 SkScalar x = SkIntToScalar(10);
110
111 for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
112 for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
113 SkPaint p;
114 p.setDither(true);
115 SkString str;
116 str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]);
117
118 SkTextUtils::DrawString(canvas, str.c_str(), x + r.width()/2, y, font, p,
119 SkTextUtils::kCenter_Align);
120
121 x += r.width() * 4 / 3;
122 }
123 }
124
125 y += SkIntToScalar(16);
126
127 for (size_t i = 0; i < SK_ARRAY_COUNT(gColorTypes); i++) {
128 for (size_t j = 0; j < SK_ARRAY_COUNT(gFilters); j++) {
129 x = SkIntToScalar(10);
130 for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
131 for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
132 SkPaint paint;
133 #if 1 // Temporary change to regen bitmap before each draw. This may help tracking down an issue
134 // on SGX where resizing NPOT textures to POT textures exhibits a driver bug.
135 if (!fPowerOfTwoSize) {
136 makebm(&fTexture[i], gColorTypes[i], size, size);
137 }
138 #endif
139 setup(&paint, fTexture[i], gFilters[j], gModes[kx], gModes[ky]);
140 paint.setDither(true);
141
142 canvas->save();
143 canvas->translate(x, y);
144 canvas->drawRect(r, paint);
145 canvas->restore();
146
147 x += r.width() * 4 / 3;
148 }
149 }
150 canvas->drawString(SkStringPrintf("%s, %s", gConfigNames[i], gFilterNames[j]),
151 x, y + r.height() * 2 / 3, font, textPaint);
152
153 y += r.height() * 4 / 3;
154 }
155 }
156 }
157
158 private:
159 bool fPowerOfTwoSize;
160 using INHERITED = skiagm::GM;
161 };
162 DEF_GM( return new TilingGM(true); )
163 DEF_GM( return new TilingGM(false); )
164
165 constexpr int gWidth = 32;
166 constexpr int gHeight = 32;
167
make_bm(SkTileMode tx,SkTileMode ty)168 static sk_sp<SkShader> make_bm(SkTileMode tx, SkTileMode ty) {
169 SkBitmap bm;
170 makebm(&bm, kN32_SkColorType, gWidth, gHeight);
171 return bm.makeShader(tx, ty, SkSamplingOptions());
172 }
173
make_grad(SkTileMode tx,SkTileMode ty)174 static sk_sp<SkShader> make_grad(SkTileMode tx, SkTileMode ty) {
175 SkPoint pts[] = { { 0, 0 }, { SkIntToScalar(gWidth), SkIntToScalar(gHeight)} };
176 SkPoint center = { SkIntToScalar(gWidth)/2, SkIntToScalar(gHeight)/2 };
177 SkScalar rad = SkIntToScalar(gWidth)/2;
178 SkColor colors[] = {0xFFFF0000, ToolUtils::color_to_565(0xFF0044FF)};
179
180 int index = (int)ty;
181 switch (index % 3) {
182 case 0:
183 return SkGradientShader::MakeLinear(pts, colors, nullptr, SK_ARRAY_COUNT(colors), tx);
184 case 1:
185 return SkGradientShader::MakeRadial(center, rad, colors, nullptr, SK_ARRAY_COUNT(colors), tx);
186 case 2:
187 return SkGradientShader::MakeSweep(center.fX, center.fY, colors, nullptr,
188 SK_ARRAY_COUNT(colors), tx, 135, 225, 0, nullptr);
189 }
190 return nullptr;
191 }
192
193 typedef sk_sp<SkShader> (*ShaderProc)(SkTileMode, SkTileMode);
194
195 class Tiling2GM : public skiagm::GM {
196 ShaderProc fProc;
197 const char* fName;
198
199 public:
Tiling2GM(ShaderProc proc,const char name[])200 Tiling2GM(ShaderProc proc, const char name[]) : fProc(proc), fName(name) {}
201
202 private:
onShortName()203 SkString onShortName() override { return SkString(fName); }
204
onISize()205 SkISize onISize() override { return SkISize::Make(650, 610); }
206
onDraw(SkCanvas * canvas)207 void onDraw(SkCanvas* canvas) override {
208 canvas->scale(SkIntToScalar(3)/2, SkIntToScalar(3)/2);
209
210 const SkScalar w = SkIntToScalar(gWidth);
211 const SkScalar h = SkIntToScalar(gHeight);
212 SkRect r = { -w, -h, w*2, h*2 };
213
214 constexpr SkTileMode gModes[] = {
215 SkTileMode::kClamp, SkTileMode::kRepeat, SkTileMode::kMirror
216 };
217 const char* gModeNames[] = {
218 "Clamp", "Repeat", "Mirror"
219 };
220
221 SkScalar y = SkIntToScalar(24);
222 SkScalar x = SkIntToScalar(66);
223
224 SkFont font(ToolUtils::create_portable_typeface());
225
226 for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
227 SkString str(gModeNames[kx]);
228 SkTextUtils::DrawString(canvas, str.c_str(), x + r.width()/2, y, font, SkPaint(),
229 SkTextUtils::kCenter_Align);
230 x += r.width() * 4 / 3;
231 }
232
233 y += SkIntToScalar(16) + h;
234
235 for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
236 x = SkIntToScalar(16) + w;
237
238 SkString str(gModeNames[ky]);
239 SkTextUtils::DrawString(canvas, str.c_str(), x, y + h/2, font, SkPaint(),
240 SkTextUtils::kRight_Align);
241
242 x += SkIntToScalar(50);
243 for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
244 SkPaint paint;
245 paint.setShader(fProc(gModes[kx], gModes[ky]));
246
247 canvas->save();
248 canvas->translate(x, y);
249 canvas->drawRect(r, paint);
250 canvas->restore();
251
252 x += r.width() * 4 / 3;
253 }
254 y += r.height() * 4 / 3;
255 }
256 }
257 };
258
259 DEF_GM( return new Tiling2GM(make_bm, "tilemode_bitmap"); )
260 DEF_GM( return new Tiling2GM(make_grad, "tilemode_gradient"); )
261
262 ////////////////////
263
264 DEF_SIMPLE_GM(tilemode_decal, canvas, 720, 1100) {
265 auto img = GetResourceAsImage("images/mandrill_128.png");
266 SkPaint bgpaint;
267 bgpaint.setColor(SK_ColorYELLOW);
268
269 SkRect r = { -20, -20, img->width() + 20.0f, img->height() + 20.0f };
270 canvas->translate(45, 45);
271
272 std::function<void(SkPaint*, SkTileMode, SkTileMode)> shader_procs[] = {
__anona7b52a950202() 273 [img](SkPaint* paint, SkTileMode tx, SkTileMode ty) {
274 // Test no filtering with decal mode
275 paint->setShader(img->makeShader(tx, ty, SkSamplingOptions(SkFilterMode::kNearest)));
276 },
__anona7b52a950302() 277 [img](SkPaint* paint, SkTileMode tx, SkTileMode ty) {
278 // Test bilerp approximation for decal mode (or clamp to border HW)
279 paint->setShader(img->makeShader(tx, ty, SkSamplingOptions(SkFilterMode::kLinear)));
280 },
__anona7b52a950402() 281 [img](SkPaint* paint, SkTileMode tx, SkTileMode ty) {
282 // Test bicubic filter with decal mode
283 paint->setShader(img->makeShader(tx, ty, SkSamplingOptions(SkCubicResampler::Mitchell())));
284 },
__anona7b52a950502() 285 [img](SkPaint* paint, SkTileMode tx, SkTileMode ty) {
286 SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
287 const SkPoint pts[] = {{ 0, 0 }, {img->width()*1.0f, img->height()*1.0f }};
288 const SkScalar* pos = nullptr;
289 const int count = SK_ARRAY_COUNT(colors);
290 paint->setShader(SkGradientShader::MakeLinear(pts, colors, pos, count, tx));
291 },
__anona7b52a950602() 292 [img](SkPaint* paint, SkTileMode tx, SkTileMode ty) {
293 SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
294 const SkScalar* pos = nullptr;
295 const int count = SK_ARRAY_COUNT(colors);
296 paint->setShader(SkGradientShader::MakeRadial({ img->width()*0.5f, img->width()*0.5f },
297 img->width()*0.5f, colors, pos, count, tx));
298 },
299 };
300
301 const struct XY {
302 SkTileMode fX;
303 SkTileMode fY;
304 } pairs[] = {
305 { SkTileMode::kClamp, SkTileMode::kClamp },
306 { SkTileMode::kClamp, SkTileMode::kDecal },
307 { SkTileMode::kDecal, SkTileMode::kClamp },
308 { SkTileMode::kDecal, SkTileMode::kDecal },
309 };
310 for (const auto& p : pairs) {
311 SkPaint paint;
312 canvas->save();
313 for (const auto& proc : shader_procs) {
314 canvas->save();
315 // Apply a slight rotation to highlight the differences between filtered and unfiltered
316 // decal edges
317 canvas->rotate(4);
318 canvas->drawRect(r, bgpaint);
319 proc(&paint, p.fX, p.fY);
320 canvas->drawRect(r, paint);
321 canvas->restore();
322 canvas->translate(0, r.height() + 20);
323 }
324 canvas->restore();
325 canvas->translate(r.width() + 10, 0);
326 }
327 }
328
329