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
20namespace skiagm {
21
22class XfermodeImageFilterGM : public GM {
23public:
24    XfermodeImageFilterGM(){
25        this->setBGColor(0xFF000000);
26    }
27
28protected:
29    SkString onShortName() override {
30        return SkString("xfermodeimagefilter");
31    }
32
33    SkISize onISize() override {
34        return SkISize::Make(WIDTH, HEIGHT);
35    }
36
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
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                                                           nullptr, nullptr));
98        DrawClippedBitmap(canvas, fBitmap, paint, x, y);
99        x += fBitmap.width() + MARGIN;
100        if (x + fBitmap.width() > WIDTH) {
101            x = 0;
102            y += fBitmap.height() + MARGIN;
103        }
104        // Test nullptr mode
105        paint.setImageFilter(SkXfermodeImageFilter::Make(SkBlendMode::kSrcOver, background));
106        DrawClippedBitmap(canvas, fBitmap, paint, x, y);
107        x += fBitmap.width() + MARGIN;
108        if (x + fBitmap.width() > WIDTH) {
109            x = 0;
110            y += fBitmap.height() + MARGIN;
111        }
112        SkRect clipRect = SkRect::MakeWH(SkIntToScalar(fBitmap.width() + 4),
113                                         SkIntToScalar(fBitmap.height() + 4));
114        // Test offsets on SrcMode (uses fixed-function blend)
115        sk_sp<SkImage> bitmapImage(SkImage::MakeFromBitmap(fBitmap));
116        sk_sp<SkImageFilter> foreground(SkImageSource::Make(std::move(bitmapImage)));
117        sk_sp<SkImageFilter> offsetForeground(SkOffsetImageFilter::Make(SkIntToScalar(4),
118                                                                        SkIntToScalar(-4),
119                                                                        foreground));
120        sk_sp<SkImageFilter> offsetBackground(SkOffsetImageFilter::Make(SkIntToScalar(4),
121                                                                        SkIntToScalar(4),
122                                                                        background));
123        paint.setImageFilter(SkXfermodeImageFilter::Make(SkBlendMode::kSrcOver,
124                                                         offsetBackground,
125                                                         offsetForeground,
126                                                         nullptr));
127        DrawClippedPaint(canvas, clipRect, paint, x, y);
128        x += fBitmap.width() + MARGIN;
129        if (x + fBitmap.width() > WIDTH) {
130            x = 0;
131            y += fBitmap.height() + MARGIN;
132        }
133        // Test offsets on Darken (uses shader blend)
134        paint.setImageFilter(SkXfermodeImageFilter::Make(SkBlendMode::kDarken,
135                                                         offsetBackground,
136                                                         offsetForeground,
137                                                         nullptr));
138        DrawClippedPaint(canvas, clipRect, paint, x, y);
139        x += fBitmap.width() + MARGIN;
140        if (x + fBitmap.width() > WIDTH) {
141            x = 0;
142            y += fBitmap.height() + MARGIN;
143        }
144        // Test cropping
145        constexpr size_t nbSamples = 3;
146        const SkBlendMode sampledModes[nbSamples] = {
147            SkBlendMode::kOverlay, SkBlendMode::kSrcOver, SkBlendMode::kPlus
148        };
149        int offsets[nbSamples][4] = {{ 10,  10, -16, -16},
150                                     { 10,  10,  10,  10},
151                                     {-10, -10,  -6,  -6}};
152        for (size_t i = 0; i < nbSamples; ++i) {
153            SkIRect cropRect = SkIRect::MakeXYWH(offsets[i][0],
154                                                 offsets[i][1],
155                                                 fBitmap.width()  + offsets[i][2],
156                                                 fBitmap.height() + offsets[i][3]);
157            SkImageFilter::CropRect rect(SkRect::Make(cropRect));
158            paint.setImageFilter(SkXfermodeImageFilter::Make(sampledModes[i],
159                                                             offsetBackground,
160                                                             offsetForeground,
161                                                             &rect));
162            DrawClippedPaint(canvas, clipRect, paint, x, y);
163            x += fBitmap.width() + MARGIN;
164            if (x + fBitmap.width() > WIDTH) {
165                x = 0;
166                y += fBitmap.height() + MARGIN;
167            }
168        }
169        // Test small bg, large fg with Screen (uses shader blend)
170        SkBlendMode mode = SkBlendMode::kScreen;
171        SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(10, 10, 60, 60));
172        sk_sp<SkImageFilter> cropped(SkOffsetImageFilter::Make(0, 0, foreground, &cropRect));
173        paint.setImageFilter(SkXfermodeImageFilter::Make(mode, cropped, background, nullptr));
174        DrawClippedPaint(canvas, clipRect, paint, x, y);
175        x += fBitmap.width() + MARGIN;
176        if (x + fBitmap.width() > WIDTH) {
177            x = 0;
178            y += fBitmap.height() + MARGIN;
179        }
180        // Test small fg, large bg with Screen (uses shader blend)
181        paint.setImageFilter(SkXfermodeImageFilter::Make(mode, background, cropped, nullptr));
182        DrawClippedPaint(canvas, clipRect, paint, x, y);
183        x += fBitmap.width() + MARGIN;
184        if (x + fBitmap.width() > WIDTH) {
185            x = 0;
186            y += fBitmap.height() + MARGIN;
187        }
188        // Test small fg, large bg with SrcIn with a crop that forces it to full size.
189        // This tests that SkXfermodeImageFilter correctly applies the compositing mode to
190        // the region outside the foreground.
191        mode = SkBlendMode::kSrcIn;
192        SkImageFilter::CropRect cropRectFull(SkRect::MakeXYWH(0, 0, 80, 80));
193        paint.setImageFilter(SkXfermodeImageFilter::Make(mode, background,
194                                                         cropped, &cropRectFull));
195        DrawClippedPaint(canvas, clipRect, paint, x, y);
196        x += fBitmap.width() + MARGIN;
197        if (x + fBitmap.width() > WIDTH) {
198            x = 0;
199            y += fBitmap.height() + MARGIN;
200        }
201    }
202
203private:
204    static void DrawClippedBitmap(SkCanvas* canvas, const SkBitmap& bitmap, const SkPaint& paint,
205                                  int x, int y) {
206        canvas->save();
207        canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
208        canvas->clipRect(SkRect::MakeIWH(bitmap.width(), bitmap.height()));
209        canvas->drawBitmap(bitmap, 0, 0, &paint);
210        canvas->restore();
211    }
212
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
230DEF_GM( return new XfermodeImageFilterGM; );
231
232}
233