1 /*
2 * Copyright 2012 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.h"
9
10 #include "SkColorPriv.h"
11 #include "SkCanvas.h"
12 #include "SkGradientShader.h"
13 #include "SkGraphics.h"
14 #include "SkImage.h"
15 #include "SkShader.h"
16 #include "SkString.h"
17 #include "SkTDArray.h"
18
make_shader(SkBlendMode mode)19 static sk_sp<SkShader> make_shader(SkBlendMode mode) {
20 SkPoint pts[2];
21 SkColor colors[2];
22
23 pts[0].set(0, 0);
24 pts[1].set(SkIntToScalar(100), 0);
25 colors[0] = SK_ColorRED;
26 colors[1] = SK_ColorBLUE;
27 auto shaderA = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kClamp_TileMode);
28
29 pts[0].set(0, 0);
30 pts[1].set(0, SkIntToScalar(100));
31 colors[0] = SK_ColorBLACK;
32 colors[1] = SkColorSetARGB(0x80, 0, 0, 0);
33 auto shaderB = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kClamp_TileMode);
34
35 return SkShader::MakeComposeShader(std::move(shaderA), std::move(shaderB), mode);
36 }
37
38 class ComposeShaderGM : public skiagm::GM {
39 public:
ComposeShaderGM()40 ComposeShaderGM() {
41 fShader = make_shader(SkBlendMode::kDstIn);
42 }
43
44 protected:
onShortName()45 SkString onShortName() override {
46 return SkString("composeshader");
47 }
48
onISize()49 SkISize onISize() override {
50 return SkISize::Make(120, 120);
51 }
52
onDraw(SkCanvas * canvas)53 void onDraw(SkCanvas* canvas) override {
54 SkPaint paint;
55 paint.setColor(SK_ColorGREEN);
56 canvas->drawRect(SkRect::MakeWH(100, 100), paint);
57 paint.setShader(fShader);
58 canvas->drawRect(SkRect::MakeWH(100, 100), paint);
59 }
60
61 protected:
62 sk_sp<SkShader> fShader;
63
64 private:
65 typedef GM INHERITED ;
66 };
67 DEF_GM( return new ComposeShaderGM; )
68
69 class ComposeShaderAlphaGM : public skiagm::GM {
70 public:
ComposeShaderAlphaGM()71 ComposeShaderAlphaGM() {}
72
73 protected:
onShortName()74 SkString onShortName() override {
75 return SkString("composeshader_alpha");
76 }
77
onISize()78 SkISize onISize() override {
79 return SkISize::Make(750, 220);
80 }
81
onDraw(SkCanvas * canvas)82 void onDraw(SkCanvas* canvas) override {
83 sk_sp<SkShader> shaders[] = {
84 make_shader(SkBlendMode::kDstIn),
85 make_shader(SkBlendMode::kSrcOver),
86 };
87
88 SkPaint paint;
89 paint.setColor(SK_ColorGREEN);
90
91 const SkRect r = SkRect::MakeXYWH(5, 5, 100, 100);
92
93 for (size_t y = 0; y < SK_ARRAY_COUNT(shaders); ++y) {
94 canvas->save();
95 for (int alpha = 0xFF; alpha > 0; alpha -= 0x28) {
96 paint.setAlphaf(1.0f);
97 paint.setShader(nullptr);
98 canvas->drawRect(r, paint);
99
100 paint.setAlpha(alpha);
101 paint.setShader(shaders[y]);
102 canvas->drawRect(r, paint);
103
104 canvas->translate(r.width() + 5, 0);
105 }
106 canvas->restore();
107 canvas->translate(0, r.height() + 5);
108 }
109 }
110
111 private:
112 typedef GM INHERITED ;
113 };
DEF_GM(return new ComposeShaderAlphaGM;)114 DEF_GM( return new ComposeShaderAlphaGM; )
115
116 // creates a square bitmap with red background and a green circle in the center
117 static void draw_color_bm(SkBitmap* bm, int length) {
118 SkPaint paint;
119 paint.setColor(SK_ColorGREEN);
120
121 bm->allocN32Pixels(length, length);
122 bm->eraseColor(SK_ColorRED);
123
124 SkCanvas canvas(*bm);
125 canvas.drawCircle(SkIntToScalar(length/2), SkIntToScalar(length/2), SkIntToScalar(length/2),
126 paint);
127 }
128
129 // creates a square alpha8 bitmap with transparent background and an opaque circle in the center
draw_alpha8_bm(SkBitmap * bm,int length)130 static void draw_alpha8_bm(SkBitmap* bm, int length) {
131 SkPaint circlePaint;
132 circlePaint.setColor(SK_ColorBLACK);
133
134 bm->allocPixels(SkImageInfo::MakeA8(length, length));
135 bm->eraseColor(SK_ColorTRANSPARENT);
136
137 SkCanvas canvas(*bm);
138 canvas.drawCircle(SkIntToScalar(length/2), SkIntToScalar(length/2), SkIntToScalar(length/4),
139 circlePaint);
140 }
141
142 // creates a linear gradient shader
make_linear_gradient_shader(int length)143 static sk_sp<SkShader> make_linear_gradient_shader(int length) {
144 SkPoint pts[2];
145 SkColor colors[2];
146 pts[0].set(0, 0);
147 pts[1].set(SkIntToScalar(length), 0);
148 colors[0] = SK_ColorBLUE;
149 colors[1] = SkColorSetARGB(0, 0, 0, 0xFF);
150 return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kClamp_TileMode);
151 }
152
153
154 class ComposeShaderBitmapGM : public skiagm::GM {
155 public:
ComposeShaderBitmapGM()156 ComposeShaderBitmapGM() {}
157
158 protected:
onOnceBeforeDraw()159 void onOnceBeforeDraw() override {
160 draw_color_bm(&fColorBitmap, squareLength);
161 draw_alpha8_bm(&fAlpha8Bitmap, squareLength);
162 SkMatrix s;
163 s.reset();
164 fColorBitmapShader = SkShader::MakeBitmapShader(fColorBitmap, SkShader::kRepeat_TileMode,
165 SkShader::kRepeat_TileMode, &s);
166 fAlpha8BitmapShader = SkShader::MakeBitmapShader(fAlpha8Bitmap, SkShader::kRepeat_TileMode,
167 SkShader::kRepeat_TileMode, &s);
168 fLinearGradientShader = make_linear_gradient_shader(squareLength);
169 }
170
onShortName()171 SkString onShortName() override {
172 return SkString("composeshader_bitmap");
173 }
174
onISize()175 SkISize onISize() override {
176 return SkISize::Make(7 * (squareLength + 5), 2 * (squareLength + 5));
177 }
178
onDraw(SkCanvas * canvas)179 void onDraw(SkCanvas* canvas) override {
180 SkBlendMode mode = SkBlendMode::kDstOver;
181
182 sk_sp<SkShader> shaders[] = {
183 // gradient should appear over color bitmap
184 SkShader::MakeComposeShader(fLinearGradientShader, fColorBitmapShader, mode),
185 // gradient should appear over alpha8 bitmap colorized by the paint color
186 SkShader::MakeComposeShader(fLinearGradientShader, fAlpha8BitmapShader, mode),
187 };
188
189 SkPaint paint;
190 paint.setColor(SK_ColorYELLOW);
191
192 const SkRect r = SkRect::MakeXYWH(0, 0, SkIntToScalar(squareLength),
193 SkIntToScalar(squareLength));
194
195 for (size_t y = 0; y < SK_ARRAY_COUNT(shaders); ++y) {
196 canvas->save();
197 for (int alpha = 0xFF; alpha > 0; alpha -= 0x28) {
198 paint.setAlpha(alpha);
199 paint.setShader(shaders[y]);
200 canvas->drawRect(r, paint);
201
202 canvas->translate(r.width() + 5, 0);
203 }
204 canvas->restore();
205 canvas->translate(0, r.height() + 5);
206 }
207 }
208
209 private:
210 /** This determines the length and width of the bitmaps used in the ComposeShaders. Values
211 * above 20 may cause an SkASSERT to fail in SkSmallAllocator. However, larger values will
212 * work in a release build. You can change this parameter and then compile a release build
213 * to have this GM draw larger bitmaps for easier visual inspection.
214 */
215 static constexpr int squareLength = 20;
216
217 SkBitmap fColorBitmap;
218 SkBitmap fAlpha8Bitmap;
219 sk_sp<SkShader> fColorBitmapShader;
220 sk_sp<SkShader> fAlpha8BitmapShader;
221 sk_sp<SkShader> fLinearGradientShader;
222
223 typedef GM INHERITED;
224 };
225 DEF_GM( return new ComposeShaderBitmapGM; )
226
227 DEF_SIMPLE_GM(composeshader_bitmap2, canvas, 200, 200) {
228 int width = 255;
229 int height = 255;
230 SkTDArray<uint8_t> dst8Storage;
231 dst8Storage.setCount(width * height);
232 SkTDArray<uint32_t> dst32Storage;
233 dst32Storage.setCount(width * height * sizeof(int32_t));
234 for (int y = 0; y < height; ++y) {
235 for (int x = 0; x < width; ++x) {
236 dst8Storage[y * width + x] = (y + x) / 2;
237 dst32Storage[y * width + x] = SkPackARGB32(0xFF, x, y, 0);
238 }
239 }
240 SkPaint paint;
241 paint.setAntiAlias(true);
242 paint.setColor(SK_ColorBLUE);
243 SkRect r = {0, 0, SkIntToScalar(width), SkIntToScalar(height)};
244 canvas->drawRect(r, paint);
245 SkBitmap skBitmap, skMask;
246 SkImageInfo imageInfo = SkImageInfo::Make(width, height,
247 SkColorType::kN32_SkColorType, kPremul_SkAlphaType);
248 skBitmap.installPixels(imageInfo, dst32Storage.begin(), width * sizeof(int32_t),
249 nullptr, nullptr);
250 imageInfo = SkImageInfo::Make(width, height,
251 SkColorType::kAlpha_8_SkColorType, kPremul_SkAlphaType);
252 skMask.installPixels(imageInfo, dst8Storage.begin(), width, nullptr, nullptr);
253 sk_sp<SkImage> skSrc = SkImage::MakeFromBitmap(skBitmap);
254 sk_sp<SkImage> skMaskImage = SkImage::MakeFromBitmap(skMask);
255 paint.setShader(
256 SkShader::MakeComposeShader(skMaskImage->makeShader(), skSrc->makeShader(),
257 SkBlendMode::kSrcIn));
258 canvas->drawRect(r, paint);
259 }
260
261 ///////////////////////////////////////////////////////////////////////////////////////////////////
262
make_src_shader(SkScalar size)263 static sk_sp<SkShader> make_src_shader(SkScalar size) {
264 const SkPoint pts[] = { { 0, 0 }, { 0, size } };
265 const SkColor colors[] = { 0xFF0000FF, 0x000000FF };
266 return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kClamp_TileMode);
267 }
268
make_dst_shader(SkScalar size)269 static sk_sp<SkShader> make_dst_shader(SkScalar size) {
270 const SkPoint pts[] = { { 0, 0 }, { size, 0 } };
271 const SkColor colors[] = { SK_ColorRED, 0x00FF0000 };
272 return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kClamp_TileMode);
273 }
274
275 const SkScalar gCellSize = 100;
276
draw_cell(SkCanvas * canvas,sk_sp<SkShader> src,sk_sp<SkShader> dst,SkBlendMode mode,SkAlpha alpha)277 static void draw_cell(SkCanvas* canvas, sk_sp<SkShader> src, sk_sp<SkShader> dst,
278 SkBlendMode mode, SkAlpha alpha) {
279 const SkRect r = SkRect::MakeWH(gCellSize, gCellSize);
280 SkPaint p;
281 p.setAlpha(alpha);
282
283 SkAutoCanvasRestore acr(canvas, false);
284 canvas->saveLayer(&r, &p);
285 p.setAlpha(0xFF);
286
287 p.setShader(dst);
288 p.setBlendMode(SkBlendMode::kSrc);
289 canvas->drawRect(r, p);
290
291 p.setShader(src);
292 p.setBlendMode(mode);
293 canvas->drawRect(r, p);
294 }
295
draw_composed(SkCanvas * canvas,sk_sp<SkShader> src,sk_sp<SkShader> dst,SkBlendMode mode,SkAlpha alpha)296 static void draw_composed(SkCanvas* canvas, sk_sp<SkShader> src, sk_sp<SkShader> dst,
297 SkBlendMode mode, SkAlpha alpha) {
298 SkPaint p;
299 p.setAlpha(alpha);
300 p.setShader(SkShader::MakeCompose(dst, src, mode));
301 canvas->drawRect(SkRect::MakeWH(gCellSize, gCellSize), p);
302 }
303
draw_pair(SkCanvas * canvas,sk_sp<SkShader> src,sk_sp<SkShader> dst,SkBlendMode mode)304 static void draw_pair(SkCanvas* canvas, sk_sp<SkShader> src, sk_sp<SkShader> dst,
305 SkBlendMode mode) {
306 SkAutoCanvasRestore acr(canvas, true);
307
308 const SkScalar gap = 4;
309 SkRect r = SkRect::MakeWH(2 * gCellSize + gap, 2 * gCellSize + gap);
310 r.outset(gap + 1.5f, gap + 1.5f);
311 SkPaint p;
312 p.setStyle(SkPaint::kStroke_Style);
313 canvas->drawRect(r, p); // border
314
315 SkAlpha alpha = 0xFF;
316 for (int y = 0; y < 2; ++y) {
317 draw_cell(canvas, src, dst, mode, alpha);
318 canvas->save();
319 canvas->translate(gCellSize + gap, 0);
320 draw_composed(canvas, src, dst, mode, alpha);
321 canvas->restore();
322
323 canvas->translate(0, gCellSize + gap);
324 alpha = 0x80;
325 }
326 }
327
328 DEF_SIMPLE_GM(composeshader_grid, canvas, 882, 882) {
329 auto src = make_src_shader(gCellSize);
330 auto dst = make_dst_shader(gCellSize);
331
332 const SkScalar margin = 15;
333 const SkScalar dx = 2*gCellSize + margin;
334 const SkScalar dy = 2*gCellSize + margin;
335
336 canvas->translate(margin, margin);
337 canvas->save();
338 for (int m = 0; m < 16; ++m) {
339 SkBlendMode mode = static_cast<SkBlendMode>(m);
340 draw_pair(canvas, src, dst, mode);
341 if ((m % 4) == 3) {
342 canvas->restore();
343 canvas->translate(0, dy);
344 canvas->save();
345 } else {
346 canvas->translate(dx, 0);
347 }
348 }
349 canvas->restore();
350 }
351