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 "SkBitmap.h"
11 #include "SkGradientShader.h"
12 #include "SkSurface.h"
13 #include "SkBlendModePriv.h"
14 #include "SkColorPriv.h"
15 #include "SkTextUtils.h"
16 #include "GrContext.h"
17 
18 namespace skiagm {
19 
20 /**
21  * This tests drawing device-covering rects with solid colors and bitmap shaders over a
22  * checkerboard background using different xfermodes.
23  */
24 class Xfermodes3GM : public GM {
25 public:
Xfermodes3GM()26     Xfermodes3GM() { this->setBGColor(sk_tool_utils::color_to_565(0xFF70D0E0)); }
27 
28 protected:
onShortName()29     SkString onShortName() override {
30         return SkString("xfermodes3");
31     }
32 
onISize()33     SkISize onISize() override {
34         return SkISize::Make(630, 1215);
35     }
36 
onDraw(SkCanvas * canvas)37     void onDraw(SkCanvas* canvas) override {
38         canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
39 
40         SkFont font(sk_tool_utils::create_portable_typeface());
41         SkPaint labelP;
42 
43         constexpr SkColor kSolidColors[] = {
44             SK_ColorTRANSPARENT,
45             SK_ColorBLUE,
46             0x80808000
47         };
48 
49         constexpr SkColor kBmpAlphas[] = {
50             0xff,
51             0x80,
52         };
53 
54         auto tempSurface(this->makeTempSurface(canvas, kSize, kSize));
55 
56         int test = 0;
57         int x = 0, y = 0;
58         constexpr struct { SkPaint::Style fStyle; SkScalar fWidth; } kStrokes[] = {
59             {SkPaint::kFill_Style, 0},
60             {SkPaint::kStroke_Style, SkIntToScalar(kSize) / 2},
61         };
62         for (size_t s = 0; s < SK_ARRAY_COUNT(kStrokes); ++s) {
63             for (size_t m = 0; m <= (size_t)SkBlendMode::kLastMode; ++m) {
64                 SkBlendMode mode = static_cast<SkBlendMode>(m);
65                 canvas->drawString(SkBlendMode_Name(mode),
66                                    SkIntToScalar(x),
67                                    SkIntToScalar(y + kSize + 3) + font.getSize(),
68                                    font, labelP);
69                 for (size_t c = 0; c < SK_ARRAY_COUNT(kSolidColors); ++c) {
70                     SkPaint modePaint;
71                     modePaint.setBlendMode(mode);
72                     modePaint.setColor(kSolidColors[c]);
73                     modePaint.setStyle(kStrokes[s].fStyle);
74                     modePaint.setStrokeWidth(kStrokes[s].fWidth);
75 
76                     this->drawMode(canvas, x, y, kSize, kSize, modePaint, tempSurface.get());
77 
78                     ++test;
79                     x += kSize + 10;
80                     if (!(test % kTestsPerRow)) {
81                         x = 0;
82                         y += kSize + 30;
83                     }
84                 }
85                 for (size_t a = 0; a < SK_ARRAY_COUNT(kBmpAlphas); ++a) {
86                     SkPaint modePaint;
87                     modePaint.setBlendMode(mode);
88                     modePaint.setAlpha(kBmpAlphas[a]);
89                     modePaint.setShader(fBmpShader);
90                     modePaint.setStyle(kStrokes[s].fStyle);
91                     modePaint.setStrokeWidth(kStrokes[s].fWidth);
92 
93                     this->drawMode(canvas, x, y, kSize, kSize, modePaint, tempSurface.get());
94 
95                     ++test;
96                     x += kSize + 10;
97                     if (!(test % kTestsPerRow)) {
98                         x = 0;
99                         y += kSize + 30;
100                     }
101                 }
102             }
103         }
104     }
105 
106 private:
107     /**
108      * GrContext has optimizations around full rendertarget draws that can be replaced with clears.
109      * We are trying to test those. We could use saveLayer() to create small SkGpuDevices but
110      * saveLayer() uses the texture cache. This means that the actual render target may be larger
111      * than the layer. Because the clip will contain the layer's bounds, no draws will be full-RT.
112      * So explicitly create a temporary canvas with dimensions exactly the layer size.
113      */
makeTempSurface(SkCanvas * baseCanvas,int w,int h)114     sk_sp<SkSurface> makeTempSurface(SkCanvas* baseCanvas, int w, int h) {
115         SkImageInfo baseInfo = baseCanvas->imageInfo();
116         SkImageInfo info = SkImageInfo::Make(w, h, baseInfo.colorType(), baseInfo.alphaType(),
117                                              baseInfo.refColorSpace());
118         return baseCanvas->makeSurface(info);
119     }
120 
drawMode(SkCanvas * canvas,int x,int y,int w,int h,const SkPaint & modePaint,SkSurface * surface)121     void drawMode(SkCanvas* canvas,
122                   int x, int y, int w, int h,
123                   const SkPaint& modePaint, SkSurface* surface) {
124         canvas->save();
125         canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
126 
127         SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h));
128 
129         SkCanvas* modeCanvas;
130         if (nullptr == surface) {
131             canvas->saveLayer(&r, nullptr);
132             canvas->clipRect(r);
133             modeCanvas = canvas;
134         } else {
135             modeCanvas = surface->getCanvas();
136         }
137 
138         SkPaint bgPaint;
139         bgPaint.setAntiAlias(false);
140         bgPaint.setShader(fBGShader);
141         modeCanvas->drawRect(r, bgPaint);
142         modeCanvas->drawRect(r, modePaint);
143         modeCanvas = nullptr;
144 
145         if (nullptr == surface) {
146             canvas->restore();
147         } else {
148             surface->draw(canvas, 0, 0, nullptr);
149         }
150 
151         r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
152         SkPaint borderPaint;
153         borderPaint.setStyle(SkPaint::kStroke_Style);
154         canvas->drawRect(r, borderPaint);
155 
156         canvas->restore();
157     }
158 
onOnceBeforeDraw()159     void onOnceBeforeDraw() override {
160         const uint32_t kCheckData[] = {
161             SkPackARGB32(0xFF, 0x42, 0x41, 0x42),
162             SkPackARGB32(0xFF, 0xD6, 0xD3, 0xD6),
163             SkPackARGB32(0xFF, 0xD6, 0xD3, 0xD6),
164             SkPackARGB32(0xFF, 0x42, 0x41, 0x42)
165         };
166         SkBitmap bg;
167         bg.allocN32Pixels(2, 2, true);
168         memcpy(bg.getPixels(), kCheckData, sizeof(kCheckData));
169 
170         SkMatrix lm;
171         lm.setScale(SkIntToScalar(kCheckSize), SkIntToScalar(kCheckSize));
172         fBGShader = SkShader::MakeBitmapShader(bg, SkShader::kRepeat_TileMode,
173                                                SkShader::kRepeat_TileMode, &lm);
174 
175         SkPaint bmpPaint;
176         const SkPoint kCenter = { SkIntToScalar(kSize) / 2, SkIntToScalar(kSize) / 2 };
177         const SkColor kColors[] = {
178             SK_ColorTRANSPARENT, 0x80800000, 0xF020F060, SK_ColorWHITE
179         };
180         bmpPaint.setShader(SkGradientShader::MakeRadial(kCenter, 3 * SkIntToScalar(kSize) / 4,
181                                                         kColors, nullptr, SK_ARRAY_COUNT(kColors),
182                                                         SkShader::kRepeat_TileMode));
183 
184         SkBitmap bmp;
185         bmp.allocN32Pixels(kSize, kSize);
186         SkCanvas bmpCanvas(bmp);
187 
188         bmpCanvas.clear(SK_ColorTRANSPARENT);
189         SkRect rect = { SkIntToScalar(kSize) / 8, SkIntToScalar(kSize) / 8,
190                         7 * SkIntToScalar(kSize) / 8, 7 * SkIntToScalar(kSize) / 8};
191         bmpCanvas.drawRect(rect, bmpPaint);
192 
193         fBmpShader = SkShader::MakeBitmapShader(bmp, SkShader::kClamp_TileMode,
194                                                 SkShader::kClamp_TileMode);
195     }
196 
197     enum {
198         kCheckSize = 8,
199         kSize = 30,
200         kTestsPerRow = 15,
201     };
202 
203     sk_sp<SkShader> fBGShader;
204     sk_sp<SkShader> fBmpShader;
205 
206     typedef GM INHERITED;
207 };
208 
209 //////////////////////////////////////////////////////////////////////////////
210 
211 DEF_GM(return new Xfermodes3GM;)
212 
213 }
214