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