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