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.h"
9#include "sk_tool_utils.h"
10#include "SkGradientShader.h"
11#include "SkSurface.h"
12#include "SkSurfaceProps.h"
13
14#define W 200
15#define H 100
16
17static sk_sp<SkShader> make_shader() {
18    int a = 0x99;
19    int b = 0xBB;
20    SkPoint pts[] = { { 0, 0 }, { W, H } };
21    SkColor colors[] = { SkColorSetRGB(a, a, a), SkColorSetRGB(b, b, b) };
22    return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kClamp_TileMode);
23}
24
25static sk_sp<SkSurface> make_surface(GrContext* ctx, const SkImageInfo& info, SkPixelGeometry geo) {
26    SkSurfaceProps props(0, geo);
27    if (ctx) {
28        return SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, 0, &props);
29    } else {
30        return SkSurface::MakeRaster(info, &props);
31    }
32}
33
34static void test_draw(SkCanvas* canvas, const char label[]) {
35    SkPaint paint;
36
37    paint.setAntiAlias(true);
38    paint.setLCDRenderText(true);
39    paint.setDither(true);
40
41    paint.setShader(make_shader());
42    canvas->drawRect(SkRect::MakeWH(W, H), paint);
43    paint.setShader(nullptr);
44
45    paint.setColor(SK_ColorWHITE);
46    paint.setTextSize(32);
47    paint.setTextAlign(SkPaint::kCenter_Align);
48    sk_tool_utils::set_portable_typeface(&paint);
49    canvas->drawString(label, W / 2, H * 3 / 4, paint);
50}
51
52class SurfacePropsGM : public skiagm::GM {
53public:
54    SurfacePropsGM() {}
55
56protected:
57    SkString onShortName() override {
58        return SkString("surfaceprops");
59    }
60
61    SkISize onISize() override {
62        return SkISize::Make(W, H * 5);
63    }
64
65    void onDraw(SkCanvas* canvas) override {
66        GrContext* ctx = canvas->getGrContext();
67
68        // must be opaque to have a hope of testing LCD text
69        const SkImageInfo info = SkImageInfo::MakeN32(W, H, kOpaque_SkAlphaType);
70
71        const struct {
72            SkPixelGeometry fGeo;
73            const char*     fLabel;
74        } recs[] = {
75            { kUnknown_SkPixelGeometry, "Unknown" },
76            { kRGB_H_SkPixelGeometry,   "RGB_H" },
77            { kBGR_H_SkPixelGeometry,   "BGR_H" },
78            { kRGB_V_SkPixelGeometry,   "RGB_V" },
79            { kBGR_V_SkPixelGeometry,   "BGR_V" },
80        };
81
82        SkScalar x = 0;
83        SkScalar y = 0;
84        for (const auto& rec : recs) {
85            auto surface(make_surface(ctx, info, rec.fGeo));
86            if (!surface) {
87                SkDebugf("failed to create surface! label: %s", rec.fLabel);
88                continue;
89            }
90            test_draw(surface->getCanvas(), rec.fLabel);
91            surface->draw(canvas, x, y, nullptr);
92            y += H;
93        }
94    }
95
96private:
97    typedef GM INHERITED;
98};
99DEF_GM( return new SurfacePropsGM )
100
101#ifdef SK_DEBUG
102static bool equal(const SkSurfaceProps& a, const SkSurfaceProps& b) {
103    return a.flags() == b.flags() && a.pixelGeometry() == b.pixelGeometry();
104}
105#endif
106
107class NewSurfaceGM : public skiagm::GM {
108public:
109    NewSurfaceGM() {}
110
111protected:
112    SkString onShortName() override {
113        return SkString("surfacenew");
114    }
115
116    SkISize onISize() override {
117        return SkISize::Make(300, 140);
118    }
119
120    static void drawInto(SkCanvas* canvas) {
121        canvas->drawColor(SK_ColorRED);
122    }
123
124    void onDraw(SkCanvas* canvas) override {
125        SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
126
127        auto surf(sk_tool_utils::makeSurface(canvas, info, nullptr));
128        drawInto(surf->getCanvas());
129
130        sk_sp<SkImage> image(surf->makeImageSnapshot());
131        canvas->drawImage(image, 10, 10, nullptr);
132
133        auto surf2(surf->makeSurface(info));
134        drawInto(surf2->getCanvas());
135
136        // Assert that the props were communicated transitively through the first image
137        SkASSERT(equal(surf->props(), surf2->props()));
138
139        sk_sp<SkImage> image2(surf2->makeImageSnapshot());
140        canvas->drawImage(image2.get(), 10 + SkIntToScalar(image->width()) + 10, 10, nullptr);
141    }
142
143private:
144    typedef GM INHERITED;
145};
146DEF_GM( return new NewSurfaceGM )
147
148///////////////////////////////////////////////////////////////////////////////////////////////////
149
150DEF_SIMPLE_GM(copy_on_write_retain, canvas, 256, 256) {
151    const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
152    sk_sp<SkSurface> surf = sk_tool_utils::makeSurface(canvas, info);
153
154    surf->getCanvas()->clear(SK_ColorRED);
155    // its important that image survives longer than the next draw, so the surface will see
156    // an outstanding image, and have to decide if it should retain or discard those pixels
157    sk_sp<SkImage> image = surf->makeImageSnapshot();
158
159    // normally a clear+opaque should trigger the discard optimization, but since we have a clip
160    // it should not (we need the previous red pixels).
161    surf->getCanvas()->clipRect(SkRect::MakeWH(128, 256));
162    surf->getCanvas()->clear(SK_ColorBLUE);
163
164    // expect to see two rects: blue | red
165    canvas->drawImage(surf->makeImageSnapshot(), 0, 0, nullptr);
166}
167
168DEF_SIMPLE_GM(copy_on_write_savelayer, canvas, 256, 256) {
169    const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
170    sk_sp<SkSurface> surf = sk_tool_utils::makeSurface(canvas, info);
171    surf->getCanvas()->clear(SK_ColorRED);
172    // its important that image survives longer than the next draw, so the surface will see
173    // an outstanding image, and have to decide if it should retain or discard those pixels
174    sk_sp<SkImage> image = surf->makeImageSnapshot();
175
176    // now draw into a full-screen layer. This should (a) trigger a copy-on-write, but it should
177    // not trigger discard, even tho its alpha (SK_ColorBLUE) is opaque, since it is in a layer
178    // with a non-opaque paint.
179    SkPaint paint;
180    paint.setAlpha(0x40);
181    surf->getCanvas()->saveLayer({0, 0, 256, 256}, &paint);
182    surf->getCanvas()->clear(SK_ColorBLUE);
183    surf->getCanvas()->restore();
184
185    // expect to see two rects: blue blended on red
186    canvas->drawImage(surf->makeImageSnapshot(), 0, 0, nullptr);
187}
188