1
2/*
3 * Copyright 2013 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "gm.h"
9#include "SkBitmap.h"
10#include "SkGradientShader.h"
11#include "SkXfermode.h"
12#include "SkColorPriv.h"
13
14#if SK_SUPPORT_GPU
15#include "GrContext.h"
16#include "SkGpuDevice.h"
17#endif
18
19namespace skiagm {
20
21/**
22 * This tests drawing device-covering rects with solid colors and bitmap shaders over a
23 * checkerboard background using different xfermodes.
24 */
25class Xfermodes3GM : public GM {
26public:
27    Xfermodes3GM() {}
28
29protected:
30    virtual SkString onShortName() SK_OVERRIDE {
31        return SkString("xfermodes3");
32    }
33
34    virtual SkISize onISize() SK_OVERRIDE {
35        return SkISize::Make(630, 1215);
36    }
37
38    virtual void onDrawBackground(SkCanvas* canvas) SK_OVERRIDE {
39        SkPaint bgPaint;
40        bgPaint.setColor(0xFF70D0E0);
41        canvas->drawPaint(bgPaint);
42    }
43
44    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
45        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
46
47        SkPaint labelP;
48        labelP.setAntiAlias(true);
49        sk_tool_utils::set_portable_typeface(&labelP);
50
51        static const SkColor kSolidColors[] = {
52            SK_ColorTRANSPARENT,
53            SK_ColorBLUE,
54            0x80808000
55        };
56
57        static const SkColor kBmpAlphas[] = {
58            0xff,
59            0x80,
60        };
61
62        SkAutoTUnref<SkCanvas> tempCanvas(this->possiblyCreateTempCanvas(canvas, kSize, kSize));
63
64        int test = 0;
65        int x = 0, y = 0;
66        static const struct { SkPaint::Style fStyle; SkScalar fWidth; } kStrokes[] = {
67            {SkPaint::kFill_Style, 0},
68            {SkPaint::kStroke_Style, SkIntToScalar(kSize) / 2},
69        };
70        for (size_t s = 0; s < SK_ARRAY_COUNT(kStrokes); ++s) {
71            for (size_t m = 0; m <= SkXfermode::kLastMode; ++m) {
72                SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(m);
73                canvas->drawText(SkXfermode::ModeName(mode),
74                                 strlen(SkXfermode::ModeName(mode)),
75                                 SkIntToScalar(x),
76                                 SkIntToScalar(y + kSize + 3) + labelP.getTextSize(),
77                                 labelP);
78                for (size_t c = 0; c < SK_ARRAY_COUNT(kSolidColors); ++c) {
79                    SkPaint modePaint;
80                    modePaint.setXfermodeMode(mode);
81                    modePaint.setColor(kSolidColors[c]);
82                    modePaint.setStyle(kStrokes[s].fStyle);
83                    modePaint.setStrokeWidth(kStrokes[s].fWidth);
84
85                    this->drawMode(canvas, x, y, kSize, kSize, modePaint, tempCanvas.get());
86
87                    ++test;
88                    x += kSize + 10;
89                    if (!(test % kTestsPerRow)) {
90                        x = 0;
91                        y += kSize + 30;
92                    }
93                }
94                for (size_t a = 0; a < SK_ARRAY_COUNT(kBmpAlphas); ++a) {
95                    SkPaint modePaint;
96                    modePaint.setXfermodeMode(mode);
97                    modePaint.setAlpha(kBmpAlphas[a]);
98                    modePaint.setShader(fBmpShader);
99                    modePaint.setStyle(kStrokes[s].fStyle);
100                    modePaint.setStrokeWidth(kStrokes[s].fWidth);
101
102                    this->drawMode(canvas, x, y, kSize, kSize, modePaint, tempCanvas.get());
103
104                    ++test;
105                    x += kSize + 10;
106                    if (!(test % kTestsPerRow)) {
107                        x = 0;
108                        y += kSize + 30;
109                    }
110                }
111            }
112        }
113    }
114
115private:
116    /**
117     * GrContext has optimizations around full rendertarget draws that can be replaced with clears.
118     * We are trying to test those. We could use saveLayer() to create small SkGpuDevices but
119     * saveLayer() uses the texture cache. This means that the actual render target may be larger
120     * than the layer. Because the clip will contain the layer's bounds, no draws will be full-RT.
121     * So when running on a GPU canvas we explicitly create a temporary canvas using a texture with
122     * dimensions exactly matching the layer size.
123     */
124    SkCanvas* possiblyCreateTempCanvas(SkCanvas* baseCanvas, int w, int h) {
125        SkCanvas* tempCanvas = NULL;
126#if SK_SUPPORT_GPU
127        GrContext* context = baseCanvas->getGrContext();
128        if (context) {
129            GrTextureDesc desc;
130            desc.fWidth = w;
131            desc.fHeight = h;
132            desc.fConfig = SkImageInfo2GrPixelConfig(baseCanvas->imageInfo());
133            desc.fFlags = kRenderTarget_GrTextureFlagBit;
134            SkAutoTUnref<GrSurface> surface(context->createUncachedTexture(desc, NULL, 0));
135            SkAutoTUnref<SkBaseDevice> device(SkGpuDevice::Create(surface.get(),
136                                          SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)));
137            if (device.get()) {
138                tempCanvas = SkNEW_ARGS(SkCanvas, (device.get()));
139            }
140        }
141#endif
142        return tempCanvas;
143    }
144
145    void drawMode(SkCanvas* canvas,
146                  int x, int y, int w, int h,
147                  const SkPaint& modePaint, SkCanvas* layerCanvas) {
148        canvas->save();
149
150        canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
151
152        SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h));
153
154        SkCanvas* modeCanvas;
155        if (NULL == layerCanvas) {
156            canvas->saveLayer(&r, NULL);
157            modeCanvas = canvas;
158        } else {
159            modeCanvas = layerCanvas;
160        }
161
162        SkPaint bgPaint;
163        bgPaint.setAntiAlias(false);
164        bgPaint.setShader(fBGShader);
165        modeCanvas->drawRect(r, bgPaint);
166        modeCanvas->drawRect(r, modePaint);
167        modeCanvas = NULL;
168
169        if (NULL == layerCanvas) {
170            canvas->restore();
171        } else {
172            SkAutoROCanvasPixels ropixels(layerCanvas);
173            SkBitmap bitmap;
174            if (ropixels.asROBitmap(&bitmap)) {
175                canvas->drawBitmap(bitmap, 0, 0);
176            }
177        }
178
179        r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
180        SkPaint borderPaint;
181        borderPaint.setStyle(SkPaint::kStroke_Style);
182        canvas->drawRect(r, borderPaint);
183
184        canvas->restore();
185    }
186
187    virtual void onOnceBeforeDraw() SK_OVERRIDE {
188        static const uint32_t kCheckData[] = {
189            SkPackARGB32(0xFF, 0x40, 0x40, 0x40),
190            SkPackARGB32(0xFF, 0xD0, 0xD0, 0xD0),
191            SkPackARGB32(0xFF, 0xD0, 0xD0, 0xD0),
192            SkPackARGB32(0xFF, 0x40, 0x40, 0x40)
193        };
194        SkBitmap bg;
195        bg.allocN32Pixels(2, 2, true);
196        SkAutoLockPixels bgAlp(bg);
197        memcpy(bg.getPixels(), kCheckData, sizeof(kCheckData));
198
199        SkMatrix lm;
200        lm.setScale(SkIntToScalar(kCheckSize), SkIntToScalar(kCheckSize));
201        fBGShader.reset(SkShader::CreateBitmapShader(bg,
202                                                     SkShader::kRepeat_TileMode,
203                                                     SkShader::kRepeat_TileMode,
204                                                     &lm));
205
206        SkPaint bmpPaint;
207        static const SkPoint kCenter = { SkIntToScalar(kSize) / 2, SkIntToScalar(kSize) / 2 };
208        static const SkColor kColors[] = { SK_ColorTRANSPARENT, 0x80800000,
209                                          0xF020F060, SK_ColorWHITE };
210        bmpPaint.setShader(SkGradientShader::CreateRadial(kCenter,
211                                                          3 * SkIntToScalar(kSize) / 4,
212                                                          kColors,
213                                                          NULL,
214                                                          SK_ARRAY_COUNT(kColors),
215                                                          SkShader::kRepeat_TileMode))->unref();
216
217        SkBitmap bmp;
218        bmp.allocN32Pixels(kSize, kSize);
219        SkCanvas bmpCanvas(bmp);
220
221        bmpCanvas.clear(SK_ColorTRANSPARENT);
222        SkRect rect = { SkIntToScalar(kSize) / 8, SkIntToScalar(kSize) / 8,
223                        7 * SkIntToScalar(kSize) / 8, 7 * SkIntToScalar(kSize) / 8};
224        bmpCanvas.drawRect(rect, bmpPaint);
225
226        fBmpShader.reset(SkShader::CreateBitmapShader(bmp,
227                                                      SkShader::kClamp_TileMode,
228                                                      SkShader::kClamp_TileMode));
229    }
230
231    enum {
232        kCheckSize = 8,
233        kSize = 30,
234        kTestsPerRow = 15,
235    };
236
237    SkAutoTUnref<SkShader> fBGShader;
238    SkAutoTUnref<SkShader> fBmpShader;
239
240    typedef GM INHERITED;
241};
242
243//////////////////////////////////////////////////////////////////////////////
244
245DEF_GM(return new Xfermodes3GM;)
246
247}
248