xref: /aosp_15_r20/external/skia/gm/composeshader.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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