1/*
2 * Copyright 2011 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 "SkBitmap.h"
11#include "SkShader.h"
12#include "SkPM4f.h"
13
14enum SrcType {
15    //! A WxH image with a rectangle in the lower right.
16    kRectangleImage_SrcType               = 0x01,
17    //! kRectangleImage_SrcType with an alpha of 34.5%.
18    kRectangleImageWithAlpha_SrcType      = 0x02,
19    //! kRectnagleImageWithAlpha_SrcType scaled down by half.
20    kSmallRectangleImageWithAlpha_SrcType = 0x04,
21    //! kRectangleImage_SrcType drawn directly instead in an image.
22    kRectangle_SrcType                    = 0x08,
23    //! Two rectangles, first on the right half, second on the bottom half.
24    kQuarterClear_SrcType                 = 0x10,
25    //! kQuarterClear_SrcType in a layer.
26    kQuarterClearInLayer_SrcType          = 0x20,
27    //! A W/2xH/2 transparent image.
28    kSmallTransparentImage_SrcType        = 0x40,
29    //! kRectangleImage_SrcType drawn directly with a mask.
30    kRectangleWithMask_SrcType            = 0x80,
31
32    kAll_SrcType                          = 0xFF, //!< All the source types.
33    kBasic_SrcType                        = 0x03, //!< Just basic source types.
34};
35
36const struct {
37    SkBlendMode fMode;
38    int         fSourceTypeMask;  // The source types to use this
39    // mode with. See draw_mode for
40    // an explanation of each type.
41    // PDF has to play some tricks
42    // to support the base modes,
43    // test those more extensively.
44} gModes[] = {
45    { SkBlendMode::kClear,        kAll_SrcType   },
46    { SkBlendMode::kSrc,          kAll_SrcType   },
47    { SkBlendMode::kDst,          kAll_SrcType   },
48    { SkBlendMode::kSrcOver,      kAll_SrcType   },
49    { SkBlendMode::kDstOver,      kAll_SrcType   },
50    { SkBlendMode::kSrcIn,        kAll_SrcType   },
51    { SkBlendMode::kDstIn,        kAll_SrcType   },
52    { SkBlendMode::kSrcOut,       kAll_SrcType   },
53    { SkBlendMode::kDstOut,       kAll_SrcType   },
54    { SkBlendMode::kSrcATop,      kAll_SrcType   },
55    { SkBlendMode::kDstATop,      kAll_SrcType   },
56
57    { SkBlendMode::kXor,          kBasic_SrcType },
58    { SkBlendMode::kPlus,         kBasic_SrcType },
59    { SkBlendMode::kModulate,     kAll_SrcType   },
60    { SkBlendMode::kScreen,       kBasic_SrcType },
61    { SkBlendMode::kOverlay,      kBasic_SrcType },
62    { SkBlendMode::kDarken,       kBasic_SrcType },
63    { SkBlendMode::kLighten,      kBasic_SrcType },
64    { SkBlendMode::kColorDodge,   kBasic_SrcType },
65    { SkBlendMode::kColorBurn,    kBasic_SrcType },
66    { SkBlendMode::kHardLight,    kBasic_SrcType },
67    { SkBlendMode::kSoftLight,    kBasic_SrcType },
68    { SkBlendMode::kDifference,   kBasic_SrcType },
69    { SkBlendMode::kExclusion,    kBasic_SrcType },
70    { SkBlendMode::kMultiply,     kAll_SrcType   },
71    { SkBlendMode::kHue,          kBasic_SrcType },
72    { SkBlendMode::kSaturation,   kBasic_SrcType },
73    { SkBlendMode::kColor,        kBasic_SrcType },
74    { SkBlendMode::kLuminosity,   kBasic_SrcType },
75};
76
77static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst,
78                         SkBitmap* transparent) {
79    src->allocN32Pixels(w, h);
80    src->eraseColor(SK_ColorTRANSPARENT);
81
82    SkPaint p;
83    p.setAntiAlias(true);
84
85    SkRect r;
86    SkScalar ww = SkIntToScalar(w);
87    SkScalar hh = SkIntToScalar(h);
88
89    {
90        SkCanvas c(*src);
91        p.setColor(sk_tool_utils::color_to_565(0xFFFFCC44));
92        r.set(0, 0, ww*3/4, hh*3/4);
93        c.drawOval(r, p);
94    }
95
96    dst->allocN32Pixels(w, h);
97    dst->eraseColor(SK_ColorTRANSPARENT);
98
99    {
100        SkCanvas c(*dst);
101        p.setColor(sk_tool_utils::color_to_565(0xFF66AAFF));
102        r.set(ww/3, hh/3, ww*19/20, hh*19/20);
103        c.drawRect(r, p);
104    }
105
106    transparent->allocN32Pixels(w, h);
107    transparent->eraseColor(SK_ColorTRANSPARENT);
108}
109
110static uint16_t gData[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
111
112class XfermodesGM : public skiagm::GM {
113    SkBitmap    fBG;
114    SkBitmap    fSrcB, fDstB, fTransparent;
115
116    /* The srcType argument indicates what to draw for the source part. Skia
117     * uses the implied shape of the drawing command and these modes
118     * demonstrate that.
119     */
120    void draw_mode(SkCanvas* canvas, SkBlendMode mode, SrcType srcType, SkScalar x, SkScalar y) {
121        SkPaint p;
122        SkMatrix m;
123        bool restoreNeeded = false;
124        m.setTranslate(x, y);
125
126        canvas->drawBitmap(fSrcB, x, y, &p);
127        p.setBlendMode(mode);
128        switch (srcType) {
129            case kSmallTransparentImage_SrcType: {
130                m.postScale(SK_ScalarHalf, SK_ScalarHalf, x, y);
131
132                SkAutoCanvasRestore acr(canvas, true);
133                canvas->concat(m);
134                canvas->drawBitmap(fTransparent, 0, 0, &p);
135                break;
136            }
137            case kQuarterClearInLayer_SrcType: {
138                SkRect bounds = SkRect::MakeXYWH(x, y, SkIntToScalar(W),
139                                                 SkIntToScalar(H));
140                canvas->saveLayer(&bounds, &p);
141                restoreNeeded = true;
142                p.setBlendMode(SkBlendMode::kSrcOver);
143                // Fall through.
144            }
145            case kQuarterClear_SrcType: {
146                SkScalar halfW = SkIntToScalar(W) / 2;
147                SkScalar halfH = SkIntToScalar(H) / 2;
148                p.setColor(sk_tool_utils::color_to_565(0xFF66AAFF));
149                SkRect r = SkRect::MakeXYWH(x + halfW, y, halfW,
150                                            SkIntToScalar(H));
151                canvas->drawRect(r, p);
152                p.setColor(sk_tool_utils::color_to_565(0xFFAA66FF));
153                r = SkRect::MakeXYWH(x, y + halfH, SkIntToScalar(W), halfH);
154                canvas->drawRect(r, p);
155                break;
156            }
157            case kRectangleWithMask_SrcType: {
158                canvas->save();
159                restoreNeeded = true;
160                SkScalar w = SkIntToScalar(W);
161                SkScalar h = SkIntToScalar(H);
162                SkRect r = SkRect::MakeXYWH(x, y + h / 4, w, h * 23 / 60);
163                canvas->clipRect(r);
164                // Fall through.
165            }
166            case kRectangle_SrcType: {
167                SkScalar w = SkIntToScalar(W);
168                SkScalar h = SkIntToScalar(H);
169                SkRect r = SkRect::MakeXYWH(x + w / 3, y + h / 3,
170                                            w * 37 / 60, h * 37 / 60);
171                p.setColor(sk_tool_utils::color_to_565(0xFF66AAFF));
172                canvas->drawRect(r, p);
173                break;
174            }
175            case kSmallRectangleImageWithAlpha_SrcType:
176                m.postScale(SK_ScalarHalf, SK_ScalarHalf, x, y);
177                // Fall through.
178            case kRectangleImageWithAlpha_SrcType:
179                p.setAlpha(0x88);
180                // Fall through.
181            case kRectangleImage_SrcType: {
182                SkAutoCanvasRestore acr(canvas, true);
183                canvas->concat(m);
184                canvas->drawBitmap(fDstB, 0, 0, &p);
185                break;
186            }
187            default:
188                break;
189        }
190
191        if (restoreNeeded) {
192            canvas->restore();
193        }
194    }
195
196    void onOnceBeforeDraw() override {
197        fBG.installPixels(SkImageInfo::Make(2, 2, kARGB_4444_SkColorType,
198                                            kOpaque_SkAlphaType),
199                          gData, 4);
200
201        make_bitmaps(W, H, &fSrcB, &fDstB, &fTransparent);
202    }
203
204public:
205    const static int W = 64;
206    const static int H = 64;
207    XfermodesGM() {}
208
209protected:
210    SkString onShortName() override {
211        return SkString("xfermodes");
212    }
213
214    SkISize onISize() override {
215        return SkISize::Make(1990, 570);
216    }
217
218    void onDraw(SkCanvas* canvas) override {
219        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
220
221        const SkScalar w = SkIntToScalar(W);
222        const SkScalar h = SkIntToScalar(H);
223        SkMatrix m;
224        m.setScale(SkIntToScalar(6), SkIntToScalar(6));
225        auto s = SkShader::MakeBitmapShader(fBG, SkShader::kRepeat_TileMode,
226                                            SkShader::kRepeat_TileMode, &m);
227
228        SkPaint labelP;
229        labelP.setAntiAlias(true);
230        sk_tool_utils::set_portable_typeface(&labelP);
231        labelP.setTextAlign(SkPaint::kCenter_Align);
232
233        const int W = 5;
234
235        SkScalar x0 = 0;
236        SkScalar y0 = 0;
237        for (int sourceType = 1; sourceType & kAll_SrcType; sourceType <<= 1) {
238            SkScalar x = x0, y = y0;
239            for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
240                if ((gModes[i].fSourceTypeMask & sourceType) == 0) {
241                    continue;
242                }
243                SkRect r{ x, y, x+w, y+h };
244
245                SkPaint p;
246                p.setStyle(SkPaint::kFill_Style);
247                p.setShader(s);
248                canvas->drawRect(r, p);
249
250                canvas->saveLayer(&r, nullptr);
251                draw_mode(canvas, gModes[i].fMode, static_cast<SrcType>(sourceType),
252                          r.fLeft, r.fTop);
253                canvas->restore();
254
255                r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
256                p.setStyle(SkPaint::kStroke_Style);
257                p.setShader(nullptr);
258                canvas->drawRect(r, p);
259
260#if 1
261                const char* label = SkBlendMode_Name(gModes[i].fMode);
262                canvas->drawString(label,
263                                 x + w/2, y - labelP.getTextSize()/2, labelP);
264#endif
265                x += w + SkIntToScalar(10);
266                if ((i % W) == W - 1) {
267                    x = x0;
268                    y += h + SkIntToScalar(30);
269                }
270            }
271            if (y < 320) {
272                if (x > x0) {
273                    y += h + SkIntToScalar(30);
274                }
275                y0 = y;
276            } else {
277                x0 += SkIntToScalar(400);
278                y0 = 0;
279            }
280        }
281    }
282
283private:
284    typedef GM INHERITED;
285};
286DEF_GM( return new XfermodesGM; )
287