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