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 "SkArithmeticMode.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.reset(SkImage::NewFromBitmap(
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 struct {
52            SkXfermode::Mode  fMode;
53            const char*         fLabel;
54        } gModes[] = {
55            { SkXfermode::kClear_Mode,    "Clear"     },
56            { SkXfermode::kSrc_Mode,      "Src"       },
57            { SkXfermode::kDst_Mode,      "Dst"       },
58            { SkXfermode::kSrcOver_Mode,  "SrcOver"   },
59            { SkXfermode::kDstOver_Mode,  "DstOver"   },
60            { SkXfermode::kSrcIn_Mode,    "SrcIn"     },
61            { SkXfermode::kDstIn_Mode,    "DstIn"     },
62            { SkXfermode::kSrcOut_Mode,   "SrcOut"    },
63            { SkXfermode::kDstOut_Mode,   "DstOut"    },
64            { SkXfermode::kSrcATop_Mode,  "SrcATop"   },
65            { SkXfermode::kDstATop_Mode,  "DstATop"   },
66            { SkXfermode::kXor_Mode,      "Xor"       },
67
68            { SkXfermode::kPlus_Mode,         "Plus"          },
69            { SkXfermode::kModulate_Mode,     "Modulate"      },
70            { SkXfermode::kScreen_Mode,       "Screen"        },
71            { SkXfermode::kOverlay_Mode,      "Overlay"       },
72            { SkXfermode::kDarken_Mode,       "Darken"        },
73            { SkXfermode::kLighten_Mode,      "Lighten"       },
74            { SkXfermode::kColorDodge_Mode,   "ColorDodge"    },
75            { SkXfermode::kColorBurn_Mode,    "ColorBurn"     },
76            { SkXfermode::kHardLight_Mode,    "HardLight"     },
77            { SkXfermode::kSoftLight_Mode,    "SoftLight"     },
78            { SkXfermode::kDifference_Mode,   "Difference"    },
79            { SkXfermode::kExclusion_Mode,    "Exclusion"     },
80            { SkXfermode::kMultiply_Mode,     "Multiply"      },
81            { SkXfermode::kHue_Mode,          "Hue"           },
82            { SkXfermode::kSaturation_Mode,   "Saturation"    },
83            { SkXfermode::kColor_Mode,        "Color"         },
84            { SkXfermode::kLuminosity_Mode,   "Luminosity"    },
85        };
86
87        int x = 0, y = 0;
88        SkAutoTUnref<SkImageFilter> background(SkImageSource::Create(fCheckerboard));
89        for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
90            SkAutoTUnref<SkXfermode> mode(SkXfermode::Create(gModes[i].fMode));
91            SkAutoTUnref<SkImageFilter> filter(SkXfermodeImageFilter::Create(mode, background));
92            paint.setImageFilter(filter);
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        SkAutoTUnref<SkXfermode> mode(SkArithmeticMode::Create(0, SK_Scalar1, SK_Scalar1, 0));
102        SkAutoTUnref<SkImageFilter> filter(SkXfermodeImageFilter::Create(mode, background));
103        paint.setImageFilter(filter);
104        DrawClippedBitmap(canvas, fBitmap, paint, x, y);
105        x += fBitmap.width() + MARGIN;
106        if (x + fBitmap.width() > WIDTH) {
107            x = 0;
108            y += fBitmap.height() + MARGIN;
109        }
110        // Test nullptr mode
111        filter.reset(SkXfermodeImageFilter::Create(nullptr, background));
112        paint.setImageFilter(filter);
113        DrawClippedBitmap(canvas, fBitmap, paint, x, y);
114        x += fBitmap.width() + MARGIN;
115        if (x + fBitmap.width() > WIDTH) {
116            x = 0;
117            y += fBitmap.height() + MARGIN;
118        }
119        SkRect clipRect = SkRect::MakeWH(SkIntToScalar(fBitmap.width() + 4),
120                                         SkIntToScalar(fBitmap.height() + 4));
121        // Test offsets on SrcMode (uses fixed-function blend)
122        SkAutoTUnref<SkImage> bitmapImage(SkImage::NewFromBitmap(fBitmap));
123        SkAutoTUnref<SkImageFilter> foreground(SkImageSource::Create(bitmapImage));
124        SkAutoTUnref<SkImageFilter> offsetForeground(SkOffsetImageFilter::Create(
125            SkIntToScalar(4), SkIntToScalar(-4), foreground));
126        SkAutoTUnref<SkImageFilter> offsetBackground(SkOffsetImageFilter::Create(
127            SkIntToScalar(4), SkIntToScalar(4), background));
128        mode.reset(SkXfermode::Create(SkXfermode::kSrcOver_Mode));
129        filter.reset(SkXfermodeImageFilter::Create(mode, offsetBackground, offsetForeground));
130        paint.setImageFilter(filter);
131        DrawClippedPaint(canvas, clipRect, paint, x, y);
132        x += fBitmap.width() + MARGIN;
133        if (x + fBitmap.width() > WIDTH) {
134            x = 0;
135            y += fBitmap.height() + MARGIN;
136        }
137        // Test offsets on Darken (uses shader blend)
138        mode.reset(SkXfermode::Create(SkXfermode::kDarken_Mode));
139        filter.reset(SkXfermodeImageFilter::Create(mode, offsetBackground, offsetForeground));
140        paint.setImageFilter(filter);
141        DrawClippedPaint(canvas, clipRect, paint, x, y);
142        x += fBitmap.width() + MARGIN;
143        if (x + fBitmap.width() > WIDTH) {
144            x = 0;
145            y += fBitmap.height() + MARGIN;
146        }
147        // Test cropping
148        static const size_t nbSamples = 3;
149        SkXfermode::Mode sampledModes[nbSamples] = {SkXfermode::kOverlay_Mode,
150                                                    SkXfermode::kSrcOver_Mode,
151                                                    SkXfermode::kPlus_Mode};
152        int offsets[nbSamples][4] = {{ 10,  10, -16, -16},
153                                     { 10,  10,  10,  10},
154                                     {-10, -10,  -6,  -6}};
155        for (size_t i = 0; i < nbSamples; ++i) {
156            SkIRect cropRect = SkIRect::MakeXYWH(offsets[i][0],
157                                                 offsets[i][1],
158                                                 fBitmap.width()  + offsets[i][2],
159                                                 fBitmap.height() + offsets[i][3]);
160            SkImageFilter::CropRect rect(SkRect::Make(cropRect));
161            mode.reset(SkXfermode::Create(sampledModes[i]));
162            filter.reset(SkXfermodeImageFilter::Create(
163                                    mode, offsetBackground, offsetForeground, &rect));
164            paint.setImageFilter(filter);
165            DrawClippedPaint(canvas, clipRect, paint, x, y);
166            x += fBitmap.width() + MARGIN;
167            if (x + fBitmap.width() > WIDTH) {
168                x = 0;
169                y += fBitmap.height() + MARGIN;
170            }
171        }
172        // Test small bg, large fg with Screen (uses shader blend)
173        mode.reset(SkXfermode::Create(SkXfermode::kScreen_Mode));
174        SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(10, 10, 60, 60));
175        SkAutoTUnref<SkImageFilter> cropped(
176            SkOffsetImageFilter::Create(0, 0, foreground, &cropRect));
177        filter.reset(SkXfermodeImageFilter::Create(mode, cropped, background));
178        paint.setImageFilter(filter);
179        DrawClippedPaint(canvas, clipRect, paint, x, y);
180        x += fBitmap.width() + MARGIN;
181        if (x + fBitmap.width() > WIDTH) {
182            x = 0;
183            y += fBitmap.height() + MARGIN;
184        }
185        // Test small fg, large bg with Screen (uses shader blend)
186        filter.reset(SkXfermodeImageFilter::Create(mode, background, cropped));
187        paint.setImageFilter(filter);
188        DrawClippedPaint(canvas, clipRect, paint, x, y);
189        x += fBitmap.width() + MARGIN;
190        if (x + fBitmap.width() > WIDTH) {
191            x = 0;
192            y += fBitmap.height() + MARGIN;
193        }
194    }
195
196private:
197    static void DrawClippedBitmap(SkCanvas* canvas, const SkBitmap& bitmap, const SkPaint& paint,
198                           int x, int y) {
199        canvas->save();
200        canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
201        canvas->clipRect(SkRect::MakeWH(
202            SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())));
203        canvas->drawBitmap(bitmap, 0, 0, &paint);
204        canvas->restore();
205    }
206
207    static void DrawClippedPaint(SkCanvas* canvas, const SkRect& rect, const SkPaint& paint,
208                          int x, int y) {
209        canvas->save();
210        canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
211        canvas->clipRect(rect);
212        canvas->drawPaint(paint);
213        canvas->restore();
214    }
215
216    SkBitmap              fBitmap;
217    SkAutoTUnref<SkImage> fCheckerboard;
218
219    typedef GM INHERITED;
220};
221
222//////////////////////////////////////////////////////////////////////////////
223
224DEF_GM( return new XfermodeImageFilterGM; );
225
226}
227