1/*
2 * Copyright 2016 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
11#include "Resources.h"
12#include "SkGradientShader.h"
13#include "SkTypeface.h"
14#include "SkStream.h"
15#include "SkPaint.h"
16#include "SkMipMap.h"
17#include "Resources.h"
18
19#define SHOW_MIP_COLOR  0xFF000000
20
21static SkBitmap make_bitmap(int w, int h) {
22    SkBitmap bm;
23    bm.allocN32Pixels(w, h);
24    SkCanvas canvas(bm);
25    canvas.clear(0xFFFFFFFF);
26    SkPaint paint;
27    paint.setStyle(SkPaint::kStroke_Style);
28    paint.setStrokeWidth(w / 16.0f);
29    paint.setColor(SHOW_MIP_COLOR);
30    canvas.drawCircle(w/2.0f, h/2.0f, w/3.0f, paint);
31    return bm;
32}
33
34static SkBitmap make_bitmap2(int w, int h) {
35    SkBitmap bm;
36    bm.allocN32Pixels(w, h);
37    SkCanvas canvas(bm);
38    canvas.clear(0xFFFFFFFF);
39    SkPaint paint;
40    paint.setColor(SHOW_MIP_COLOR);
41    paint.setStyle(SkPaint::kStroke_Style);
42
43    SkScalar inset = 2;
44    SkRect r = SkRect::MakeIWH(w, h).makeInset(0.5f, 0.5f);
45    while (r.width() > 4) {
46        canvas.drawRect(r, paint);
47        r.inset(inset, inset);
48        inset += 1;
49    }
50    return bm;
51}
52
53#include "SkNx.h"
54static SkBitmap make_bitmap3(int w, int h) {
55    SkBitmap bm;
56    bm.allocN32Pixels(w, h);
57    SkCanvas canvas(bm);
58    canvas.clear(0xFFFFFFFF);
59    SkPaint paint;
60    paint.setStyle(SkPaint::kStroke_Style);
61    paint.setStrokeWidth(2.1f);
62    paint.setColor(SHOW_MIP_COLOR);
63
64    SkScalar s = SkIntToScalar(w);
65    Sk4f p(s, -s, -s, s);
66    Sk4f d(5);
67    while (p[1] < s) {
68        canvas.drawLine(p[0],p[1], p[2], p[3], paint);
69        p = p + d;
70    }
71    return bm;
72}
73
74class ShowMipLevels : public skiagm::GM {
75    const int fN;
76    SkBitmap  fBM[4];
77
78public:
79    static unsigned gamma(unsigned n) {
80        float x = n / 255.0f;
81#if 0
82        x = sqrtf(x);
83#else
84        if (x > 0.0031308f) {
85            x = 1.055f * (powf(x, (1.0f / 2.4f))) - 0.055f;
86        } else {
87            x = 12.92f * x;
88        }
89#endif
90        return (int)(x * 255);
91    }
92
93    static void apply_gamma(const SkBitmap& bm) {
94        return; // below is our experiment for sRGB correction
95        for (int y = 0; y < bm.height(); ++y) {
96            for (int x = 0; x < bm.width(); ++x) {
97                SkPMColor c = *bm.getAddr32(x, y);
98                unsigned r = gamma(SkGetPackedR32(c));
99                unsigned g = gamma(SkGetPackedG32(c));
100                unsigned b = gamma(SkGetPackedB32(c));
101                *bm.getAddr32(x, y) = SkPackARGB32(0xFF, r, g, b);
102            }
103        }
104    }
105
106    ShowMipLevels(int N) : fN(N) { }
107
108protected:
109
110    SkString onShortName() override {
111        SkString str;
112        str.printf("showmiplevels_%d", fN);
113        return str;
114    }
115
116    SkISize onISize() override { return { 150, 862 }; }
117
118    static void DrawAndFrame(SkCanvas* canvas, const SkBitmap& orig, SkScalar x, SkScalar y) {
119        SkBitmap bm;
120        sk_tool_utils::copy_to(&bm, orig.colorType(), orig);
121        apply_gamma(bm);
122
123        canvas->drawBitmap(bm, x, y, nullptr);
124        SkPaint paint;
125        paint.setStyle(SkPaint::kStroke_Style);
126        paint.setColor(0xFFFFCCCC);
127        canvas->drawRect(SkRect::MakeIWH(bm.width(), bm.height()).makeOffset(x, y).makeOutset(0.5f, 0.5f), paint);
128    }
129
130    template <typename F> void drawLevels(SkCanvas* canvas, const SkBitmap& baseBM, F func) {
131        SkScalar x = 4;
132        SkScalar y = 4;
133
134        SkPixmap prevPM;
135        baseBM.peekPixels(&prevPM);
136
137        SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
138        sk_sp<SkMipMap> mm(SkMipMap::Build(baseBM, colorMode, nullptr));
139
140        int index = 0;
141        SkMipMap::Level level;
142        SkScalar scale = 0.5f;
143        while (mm->extractLevel(SkSize::Make(scale, scale), &level)) {
144            SkBitmap bm = func(prevPM, level.fPixmap);
145            DrawAndFrame(canvas, bm, x, y);
146
147            if (level.fPixmap.width() <= 2 || level.fPixmap.height() <= 2) {
148                break;
149            }
150            if (index & 1) {
151                x += level.fPixmap.width() + 4;
152            } else {
153                y += level.fPixmap.height() + 4;
154            }
155            scale /= 2;
156            prevPM = level.fPixmap;
157            index += 1;
158        }
159    }
160
161    void drawSet(SkCanvas* canvas, const SkBitmap& orig) {
162        SkAutoCanvasRestore acr(canvas, true);
163
164        drawLevels(canvas, orig, [](const SkPixmap& prev, const SkPixmap& curr) {
165            SkBitmap bm;
166            bm.installPixels(curr);
167            return bm;
168        });
169    }
170
171    void onOnceBeforeDraw() override {
172        fBM[0] = sk_tool_utils::create_checkerboard_bitmap(fN, fN, SK_ColorBLACK, SK_ColorWHITE, 2);
173        fBM[1] = make_bitmap(fN, fN);
174        fBM[2] = make_bitmap2(fN, fN);
175        fBM[3] = make_bitmap3(fN, fN);
176    }
177
178    void onDraw(SkCanvas* canvas) override {
179        canvas->translate(4, 4);
180        for (const auto& bm : fBM) {
181            this->drawSet(canvas, bm);
182            // round so we always produce an integral translate, so the GOLD tool won't show
183            // unimportant diffs if this is drawn on a GPU with different rounding rules
184            // since we draw the bitmaps using nearest-neighbor
185            canvas->translate(0, SkScalarRoundToScalar(bm.height() * 0.85f));
186        }
187    }
188
189private:
190    typedef skiagm::GM INHERITED;
191};
192DEF_GM( return new ShowMipLevels(255); )
193DEF_GM( return new ShowMipLevels(256); )
194
195///////////////////////////////////////////////////////////////////////////////////////////////////
196
197void copy_to(SkBitmap* dst, SkColorType dstColorType, const SkBitmap& src) {
198    if (kGray_8_SkColorType == dstColorType) {
199        return sk_tool_utils::copy_to_g8(dst, src);
200    }
201
202    const SkBitmap* srcPtr = &src;
203    SkBitmap tmp(src);
204    if (kRGB_565_SkColorType == dstColorType) {
205        tmp.setAlphaType(kOpaque_SkAlphaType);
206        srcPtr = &tmp;
207    }
208
209    sk_tool_utils::copy_to(dst, dstColorType, *srcPtr);
210}
211
212/**
213 *  Show mip levels that were built, for all supported colortypes
214 */
215class ShowMipLevels2 : public skiagm::GM {
216    const int fW, fH;
217    SkBitmap  fBM[4];
218
219public:
220    ShowMipLevels2(int w, int h) : fW(w), fH(h) { }
221
222protected:
223
224    SkString onShortName() override {
225        SkString str;
226        str.printf("showmiplevels2_%dx%d", fW, fH);
227        return str;
228    }
229
230    SkISize onISize() override {
231        return { 824, 862 };
232    }
233
234    static void DrawAndFrame(SkCanvas* canvas, const SkBitmap& bm, SkScalar x, SkScalar y) {
235        canvas->drawBitmap(bm, x, y, nullptr);
236        SkPaint paint;
237        paint.setStyle(SkPaint::kStroke_Style);
238        paint.setColor(0xFFFFCCCC);
239        canvas->drawRect(SkRect::MakeIWH(bm.width(), bm.height()).makeOffset(x, y).makeOutset(0.5f, 0.5f), paint);
240    }
241
242    void drawLevels(SkCanvas* canvas, const SkBitmap& baseBM) {
243        SkScalar x = 4;
244        SkScalar y = 4;
245
246        SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
247        sk_sp<SkMipMap> mm(SkMipMap::Build(baseBM, colorMode, nullptr));
248
249        int index = 0;
250        SkMipMap::Level level;
251        SkScalar scale = 0.5f;
252        while (mm->extractLevel(SkSize::Make(scale, scale), &level)) {
253            SkBitmap bm;
254            bm.installPixels(level.fPixmap);
255            DrawAndFrame(canvas, bm, x, y);
256
257            if (level.fPixmap.width() <= 2 || level.fPixmap.height() <= 2) {
258                break;
259            }
260            if (index & 1) {
261                x += level.fPixmap.width() + 4;
262            } else {
263                y += level.fPixmap.height() + 4;
264            }
265            scale /= 2;
266            index += 1;
267        }
268    }
269
270    void drawSet(SkCanvas* canvas, const SkBitmap& orig) {
271        const SkColorType ctypes[] = {
272            kN32_SkColorType, kRGB_565_SkColorType, kARGB_4444_SkColorType, kGray_8_SkColorType
273        };
274
275        SkAutoCanvasRestore acr(canvas, true);
276
277        for (auto ctype : ctypes) {
278            SkBitmap bm;
279            copy_to(&bm, ctype, orig);
280            drawLevels(canvas, bm);
281            canvas->translate(orig.width()/2 + 8.0f, 0);
282        }
283    }
284
285    void onOnceBeforeDraw() override {
286        fBM[0] = sk_tool_utils::create_checkerboard_bitmap(fW, fH,
287                                                           SHOW_MIP_COLOR, SK_ColorWHITE, 2);
288        fBM[1] = make_bitmap(fW, fH);
289        fBM[2] = make_bitmap2(fW, fH);
290        fBM[3] = make_bitmap3(fW, fH);
291    }
292
293    void onDraw(SkCanvas* canvas) override {
294        canvas->translate(4, 4);
295        for (const auto& bm : fBM) {
296            this->drawSet(canvas, bm);
297            // round so we always produce an integral translate, so the GOLD tool won't show
298            // unimportant diffs if this is drawn on a GPU with different rounding rules
299            // since we draw the bitmaps using nearest-neighbor
300            canvas->translate(0, SkScalarRoundToScalar(bm.height() * 0.85f));
301        }
302    }
303
304private:
305    typedef skiagm::GM INHERITED;
306};
307DEF_GM( return new ShowMipLevels2(255, 255); )
308DEF_GM( return new ShowMipLevels2(256, 255); )
309DEF_GM( return new ShowMipLevels2(255, 256); )
310DEF_GM( return new ShowMipLevels2(256, 256); )
311