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