xref: /aosp_15_r20/external/skia/gm/xfermodeimagefilter.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2013 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/SkImage.h"
14 #include "include/core/SkImageFilter.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkScalar.h"
19 #include "include/core/SkSize.h"
20 #include "include/core/SkString.h"
21 #include "include/core/SkTypes.h"
22 #include "include/effects/SkImageFilters.h"
23 #include "tools/ToolUtils.h"
24 #include "tools/fonts/FontToolUtils.h"
25 
26 #include <utility>
27 
28 #define WIDTH 600
29 #define HEIGHT 700
30 #define MARGIN 12
31 
32 namespace skiagm {
33 
34 class XfermodeImageFilterGM : public GM {
35 public:
XfermodeImageFilterGM()36     XfermodeImageFilterGM(){
37         this->setBGColor(0xFF000000);
38     }
39 
40 protected:
getName() const41     SkString getName() const override { return SkString("xfermodeimagefilter"); }
42 
getISize()43     SkISize getISize() override { return SkISize::Make(WIDTH, HEIGHT); }
44 
onOnceBeforeDraw()45     void onOnceBeforeDraw() override {
46         fBitmap = ToolUtils::CreateStringBitmap(80, 80, 0xD000D000, 15, 65, 96, "e");
47 
48         fCheckerboard = ToolUtils::create_checkerboard_image(80, 80, 0xFFA0A0A0, 0xFF404040, 8);
49     }
50 
onDraw(SkCanvas * canvas)51     void onDraw(SkCanvas* canvas) override {
52         canvas->clear(SK_ColorBLACK);
53         SkPaint paint;
54 
55         const SkBlendMode gModes[] = {
56             SkBlendMode::kClear,
57             SkBlendMode::kSrc,
58             SkBlendMode::kDst,
59             SkBlendMode::kSrcOver,
60             SkBlendMode::kDstOver,
61             SkBlendMode::kSrcIn,
62             SkBlendMode::kDstIn,
63             SkBlendMode::kSrcOut,
64             SkBlendMode::kDstOut,
65             SkBlendMode::kSrcATop,
66             SkBlendMode::kDstATop,
67             SkBlendMode::kXor,
68 
69             SkBlendMode::kPlus,
70             SkBlendMode::kModulate,
71             SkBlendMode::kScreen,
72             SkBlendMode::kOverlay,
73             SkBlendMode::kDarken,
74             SkBlendMode::kLighten,
75             SkBlendMode::kColorDodge,
76             SkBlendMode::kColorBurn,
77             SkBlendMode::kHardLight,
78             SkBlendMode::kSoftLight,
79             SkBlendMode::kDifference,
80             SkBlendMode::kExclusion,
81             SkBlendMode::kMultiply,
82             SkBlendMode::kHue,
83             SkBlendMode::kSaturation,
84             SkBlendMode::kColor,
85             SkBlendMode::kLuminosity,
86         };
87 
88         int x = 0, y = 0;
89         sk_sp<SkImageFilter> background(SkImageFilters::Image(fCheckerboard,
90                                                               SkFilterMode::kNearest));
91         for (size_t i = 0; i < std::size(gModes); i++) {
92             paint.setImageFilter(SkImageFilters::Blend(gModes[i], background));
93             DrawClippedBitmap(canvas, fBitmap, paint, x, y);
94             x += fBitmap.width() + MARGIN;
95             if (x + fBitmap.width() > WIDTH) {
96                 x = 0;
97                 y += fBitmap.height() + MARGIN;
98             }
99         }
100         // Test arithmetic mode as image filter
101         paint.setImageFilter(SkImageFilters::Arithmetic(0, 1, 1, 0, true, background, nullptr));
102         DrawClippedBitmap(canvas, fBitmap, paint, x, y);
103         x += fBitmap.width() + MARGIN;
104         if (x + fBitmap.width() > WIDTH) {
105             x = 0;
106             y += fBitmap.height() + MARGIN;
107         }
108         // Test nullptr mode
109         paint.setImageFilter(SkImageFilters::Blend(SkBlendMode::kSrcOver, background));
110         DrawClippedBitmap(canvas, fBitmap, paint, x, y);
111         x += fBitmap.width() + MARGIN;
112         if (x + fBitmap.width() > WIDTH) {
113             x = 0;
114             y += fBitmap.height() + MARGIN;
115         }
116         SkRect clipRect = SkRect::MakeWH(SkIntToScalar(fBitmap.width() + 4),
117                                          SkIntToScalar(fBitmap.height() + 4));
118         // Test offsets on SrcMode (uses fixed-function blend)
119         sk_sp<SkImage> bitmapImage(fBitmap.asImage());
120         sk_sp<SkImageFilter> foreground(SkImageFilters::Image(std::move(bitmapImage),
121                                                               SkFilterMode::kNearest));
122         sk_sp<SkImageFilter> offsetForeground(SkImageFilters::Offset(4, -4, foreground));
123         sk_sp<SkImageFilter> offsetBackground(SkImageFilters::Offset(4, 4, background));
124         paint.setImageFilter(SkImageFilters::Blend(
125                 SkBlendMode::kSrcOver, offsetBackground, offsetForeground));
126         DrawClippedPaint(canvas, clipRect, paint, x, y);
127         x += fBitmap.width() + MARGIN;
128         if (x + fBitmap.width() > WIDTH) {
129             x = 0;
130             y += fBitmap.height() + MARGIN;
131         }
132         // Test offsets on Darken (uses shader blend)
133         paint.setImageFilter(SkImageFilters::Blend(
134                 SkBlendMode::kDarken, offsetBackground, offsetForeground));
135         DrawClippedPaint(canvas, clipRect, paint, x, y);
136         x += fBitmap.width() + MARGIN;
137         if (x + fBitmap.width() > WIDTH) {
138             x = 0;
139             y += fBitmap.height() + MARGIN;
140         }
141         // Test cropping
142         constexpr size_t nbSamples = 3;
143         const SkBlendMode sampledModes[nbSamples] = {
144             SkBlendMode::kOverlay, SkBlendMode::kSrcOver, SkBlendMode::kPlus
145         };
146         int offsets[nbSamples][4] = {{ 10,  10, -16, -16},
147                                      { 10,  10,  10,  10},
148                                      {-10, -10,  -6,  -6}};
149         for (size_t i = 0; i < nbSamples; ++i) {
150             SkIRect cropRect = SkIRect::MakeXYWH(offsets[i][0],
151                                                  offsets[i][1],
152                                                  fBitmap.width()  + offsets[i][2],
153                                                  fBitmap.height() + offsets[i][3]);
154             paint.setImageFilter(SkImageFilters::Blend(sampledModes[i], offsetBackground,
155                                                        offsetForeground, &cropRect));
156             DrawClippedPaint(canvas, clipRect, paint, x, y);
157             x += fBitmap.width() + MARGIN;
158             if (x + fBitmap.width() > WIDTH) {
159                 x = 0;
160                 y += fBitmap.height() + MARGIN;
161             }
162         }
163         // Test small bg, large fg with Screen (uses shader blend)
164         SkIRect cropRect = SkIRect::MakeXYWH(10, 10, 60, 60);
165         sk_sp<SkImageFilter> cropped(SkImageFilters::Offset(0, 0, foreground, &cropRect));
166         paint.setImageFilter(SkImageFilters::Blend(SkBlendMode::kScreen, cropped, background));
167         DrawClippedPaint(canvas, clipRect, paint, x, y);
168         x += fBitmap.width() + MARGIN;
169         if (x + fBitmap.width() > WIDTH) {
170             x = 0;
171             y += fBitmap.height() + MARGIN;
172         }
173         // Test small fg, large bg with Screen (uses shader blend)
174         paint.setImageFilter(SkImageFilters::Blend(SkBlendMode::kScreen, background, cropped));
175         DrawClippedPaint(canvas, clipRect, paint, x, y);
176         x += fBitmap.width() + MARGIN;
177         if (x + fBitmap.width() > WIDTH) {
178             x = 0;
179             y += fBitmap.height() + MARGIN;
180         }
181         // Test small fg, large bg with SrcIn with a crop that forces it to full size.
182         // This tests that SkXfermodeImageFilter correctly applies the compositing mode to
183         // the region outside the foreground.
184         SkIRect cropRectFull = SkIRect::MakeXYWH(0, 0, 80, 80);
185         paint.setImageFilter(SkImageFilters::Blend(SkBlendMode::kSrcIn, background, cropped,
186                                                    &cropRectFull));
187         DrawClippedPaint(canvas, clipRect, paint, x, y);
188         x += fBitmap.width() + MARGIN;
189         if (x + fBitmap.width() > WIDTH) {
190             x = 0;
191             y += fBitmap.height() + MARGIN;
192         }
193     }
194 
195 private:
DrawClippedBitmap(SkCanvas * canvas,const SkBitmap & bitmap,const SkPaint & paint,int x,int y)196     static void DrawClippedBitmap(SkCanvas* canvas, const SkBitmap& bitmap, const SkPaint& paint,
197                                   int x, int y) {
198         canvas->save();
199         canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
200         canvas->clipIRect(bitmap.bounds());
201         canvas->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &paint);
202         canvas->restore();
203     }
204 
DrawClippedPaint(SkCanvas * canvas,const SkRect & rect,const SkPaint & paint,int x,int y)205     static void DrawClippedPaint(SkCanvas* canvas, const SkRect& rect, const SkPaint& paint,
206                                  int x, int y) {
207         canvas->save();
208         canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
209         canvas->clipRect(rect);
210         canvas->drawPaint(paint);
211         canvas->restore();
212     }
213 
214     SkBitmap        fBitmap;
215     sk_sp<SkImage>  fCheckerboard;
216 
217     using INHERITED = GM;
218 };
219 
220 //////////////////////////////////////////////////////////////////////////////
221 
222 DEF_GM( return new XfermodeImageFilterGM; );
223 
224 }  // namespace skiagm
225