1 /*
2  * Copyright 2014 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/SkBlendMode.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkFont.h"
14 #include "include/core/SkImage.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkPaint.h"
17 #include "include/core/SkPoint.h"
18 #include "include/core/SkRect.h"
19 #include "include/core/SkRefCnt.h"
20 #include "include/core/SkScalar.h"
21 #include "include/core/SkShader.h"
22 #include "include/core/SkSize.h"
23 #include "include/core/SkString.h"
24 #include "include/core/SkSurface.h"
25 #include "include/core/SkSurfaceProps.h"
26 #include "include/core/SkTileMode.h"
27 #include "include/core/SkTypeface.h"
28 #include "include/core/SkTypes.h"
29 #include "include/effects/SkGradientShader.h"
30 #include "include/utils/SkTextUtils.h"
31 #include "tools/ToolUtils.h"
32 
33 #define W 200
34 #define H 100
35 
make_shader()36 static sk_sp<SkShader> make_shader() {
37     int a = 0x99;
38     int b = 0xBB;
39     SkPoint pts[] = { { 0, 0 }, { W, H } };
40     SkColor colors[] = { SkColorSetRGB(a, a, a), SkColorSetRGB(b, b, b) };
41     return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);
42 }
43 
make_surface(GrRecordingContext * ctx,const SkImageInfo & info,SkPixelGeometry geo)44 static sk_sp<SkSurface> make_surface(GrRecordingContext* ctx,
45                                      const SkImageInfo& info,
46                                      SkPixelGeometry geo) {
47     SkSurfaceProps props(0, geo);
48     if (ctx) {
49         return SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, 0, &props);
50     } else {
51         return SkSurface::MakeRaster(info, &props);
52     }
53 }
54 
test_draw(SkCanvas * canvas,const char label[])55 static void test_draw(SkCanvas* canvas, const char label[]) {
56     SkPaint paint;
57 
58     paint.setAntiAlias(true);
59     paint.setDither(true);
60 
61     paint.setShader(make_shader());
62     canvas->drawRect(SkRect::MakeWH(W, H), paint);
63     paint.setShader(nullptr);
64 
65     paint.setColor(SK_ColorWHITE);
66     SkFont font(ToolUtils::create_portable_typeface(), 32);
67     font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
68     SkTextUtils::DrawString(canvas, label, W / 2, H * 3 / 4, font, paint,
69                             SkTextUtils::kCenter_Align);
70 }
71 
72 class SurfacePropsGM : public skiagm::GM {
73 public:
SurfacePropsGM()74     SurfacePropsGM() {}
75 
76 protected:
onShortName()77     SkString onShortName() override {
78         return SkString("surfaceprops");
79     }
80 
onISize()81     SkISize onISize() override {
82         return SkISize::Make(W, H * 5);
83     }
84 
onDraw(SkCanvas * canvas)85     void onDraw(SkCanvas* canvas) override {
86         auto ctx = canvas->recordingContext();
87 
88         // must be opaque to have a hope of testing LCD text
89         const SkImageInfo info = SkImageInfo::MakeN32(W, H, kOpaque_SkAlphaType);
90 
91         const struct {
92             SkPixelGeometry fGeo;
93             const char*     fLabel;
94         } recs[] = {
95             { kUnknown_SkPixelGeometry, "Unknown" },
96             { kRGB_H_SkPixelGeometry,   "RGB_H" },
97             { kBGR_H_SkPixelGeometry,   "BGR_H" },
98             { kRGB_V_SkPixelGeometry,   "RGB_V" },
99             { kBGR_V_SkPixelGeometry,   "BGR_V" },
100         };
101 
102         SkScalar x = 0;
103         SkScalar y = 0;
104         for (const auto& rec : recs) {
105             auto surface(make_surface(ctx, info, rec.fGeo));
106             if (!surface) {
107                 SkDebugf("failed to create surface! label: %s", rec.fLabel);
108                 continue;
109             }
110             test_draw(surface->getCanvas(), rec.fLabel);
111             surface->draw(canvas, x, y, nullptr);
112             y += H;
113         }
114     }
115 
116 private:
117     using INHERITED = GM;
118 };
DEF_GM(return new SurfacePropsGM)119 DEF_GM( return new SurfacePropsGM )
120 
121 #ifdef SK_DEBUG
122 static bool equal(const SkSurfaceProps& a, const SkSurfaceProps& b) {
123     return a.flags() == b.flags() && a.pixelGeometry() == b.pixelGeometry();
124 }
125 #endif
126 
127 class NewSurfaceGM : public skiagm::GM {
128 public:
NewSurfaceGM()129     NewSurfaceGM() {}
130 
131 protected:
onShortName()132     SkString onShortName() override {
133         return SkString("surfacenew");
134     }
135 
onISize()136     SkISize onISize() override {
137         return SkISize::Make(300, 140);
138     }
139 
drawInto(SkCanvas * canvas)140     static void drawInto(SkCanvas* canvas) {
141         canvas->drawColor(SK_ColorRED);
142     }
143 
onDraw(SkCanvas * canvas)144     void onDraw(SkCanvas* canvas) override {
145         SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
146 
147         auto surf(ToolUtils::makeSurface(canvas, info, nullptr));
148         drawInto(surf->getCanvas());
149 
150         sk_sp<SkImage> image(surf->makeImageSnapshot());
151         canvas->drawImage(image, 10, 10, nullptr);
152 
153         auto surf2(surf->makeSurface(info));
154         drawInto(surf2->getCanvas());
155 
156         // Assert that the props were communicated transitively through the first image
157         SkASSERT(equal(surf->props(), surf2->props()));
158 
159         sk_sp<SkImage> image2(surf2->makeImageSnapshot());
160         canvas->drawImage(image2.get(), 10 + SkIntToScalar(image->width()) + 10, 10, nullptr);
161     }
162 
163 private:
164     using INHERITED = GM;
165 };
166 DEF_GM( return new NewSurfaceGM )
167 
168 ///////////////////////////////////////////////////////////////////////////////////////////////////
169 
170 DEF_SIMPLE_GM(copy_on_write_retain, canvas, 256, 256) {
171     const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
172     sk_sp<SkSurface>  surf = ToolUtils::makeSurface(canvas, info);
173 
174     surf->getCanvas()->clear(SK_ColorRED);
175     // its important that image survives longer than the next draw, so the surface will see
176     // an outstanding image, and have to decide if it should retain or discard those pixels
177     sk_sp<SkImage> image = surf->makeImageSnapshot();
178 
179     // normally a clear+opaque should trigger the discard optimization, but since we have a clip
180     // it should not (we need the previous red pixels).
181     surf->getCanvas()->clipRect(SkRect::MakeWH(128, 256));
182     surf->getCanvas()->clear(SK_ColorBLUE);
183 
184     // expect to see two rects: blue | red
185     canvas->drawImage(surf->makeImageSnapshot(), 0, 0, nullptr);
186 }
187 
188 DEF_SIMPLE_GM(copy_on_write_savelayer, canvas, 256, 256) {
189     const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
190     sk_sp<SkSurface>  surf = ToolUtils::makeSurface(canvas, info);
191     surf->getCanvas()->clear(SK_ColorRED);
192     // its important that image survives longer than the next draw, so the surface will see
193     // an outstanding image, and have to decide if it should retain or discard those pixels
194     sk_sp<SkImage> image = surf->makeImageSnapshot();
195 
196     // now draw into a full-screen layer. This should (a) trigger a copy-on-write, but it should
197     // not trigger discard, even tho its alpha (SK_ColorBLUE) is opaque, since it is in a layer
198     // with a non-opaque paint.
199     SkPaint paint;
200     paint.setAlphaf(0.25f);
201     surf->getCanvas()->saveLayer({0, 0, 256, 256}, &paint);
202     surf->getCanvas()->clear(SK_ColorBLUE);
203     surf->getCanvas()->restore();
204 
205     // expect to see two rects: blue blended on red
206     canvas->drawImage(surf->makeImageSnapshot(), 0, 0, nullptr);
207 }
208 
209 DEF_SIMPLE_GM(surface_underdraw, canvas, 256, 256) {
210     SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256, nullptr);
211     auto        surf = ToolUtils::makeSurface(canvas, info);
212 
213     const SkIRect subset = SkIRect::MakeLTRB(180, 0, 256, 256);
214 
215     // noisy background
216     {
217         SkPoint pts[] = {{0, 0}, {40, 50}};
218         SkColor colors[] = {SK_ColorRED, SK_ColorBLUE};
219         auto sh = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kRepeat);
220         SkPaint paint;
221         paint.setShader(sh);
222         surf->getCanvas()->drawPaint(paint);
223     }
224 
225     // save away the right-hand strip, then clear it
226     sk_sp<SkImage> saveImg = surf->makeImageSnapshot(subset);
227     {
228         SkPaint paint;
229         paint.setBlendMode(SkBlendMode::kClear);
230         surf->getCanvas()->drawRect(SkRect::Make(subset), paint);
231     }
232 
233     // draw the "foreground"
234     {
235         SkPaint paint;
236         paint.setColor(SK_ColorGREEN);
237         SkRect r = { 0, 10, 256, 35 };
238         while (r.fBottom < 256) {
239             surf->getCanvas()->drawRect(r, paint);
240             r.offset(0, r.height() * 2);
241         }
242     }
243 
244     // apply the "fade"
245     {
246         SkPoint pts[] = {{SkIntToScalar(subset.left()), 0}, {SkIntToScalar(subset.right()), 0}};
247         SkColor colors[] = {0xFF000000, 0};
248         auto sh = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);
249         SkPaint paint;
250         paint.setShader(sh);
251         paint.setBlendMode(SkBlendMode::kDstIn);
252         surf->getCanvas()->drawRect(SkRect::Make(subset), paint);
253     }
254 
255     // restore the original strip, drawing it "under" the current foreground
256     {
257         SkPaint paint;
258         paint.setBlendMode(SkBlendMode::kDstOver);
259         surf->getCanvas()->drawImage(saveImg,
260                                      SkIntToScalar(subset.left()), SkIntToScalar(subset.top()),
261                                      &paint);
262     }
263 
264     // show it on screen
265    surf->draw(canvas, 0, 0, nullptr);
266 }
267