aboutsummaryrefslogtreecommitdiffhomepage
path: root/gm/etc1bitmap.cpp
blob: 9d479991516fbab933a1713c7fea5612280d7fcc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm.h"

#include "Resources.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkImage.h"
#include "SkImageGenerator.h"
#include "SkOSFile.h"
#include "SkTemplates.h"

#ifndef SK_IGNORE_ETC1_SUPPORT

#include "etc1.h"

/**
 *  Remove the last row and column of ETC1 blocks, effectively
 *  making a texture that started as power of two into a texture
 *  that is no longer power of two...
 */
bool slice_etc1_data(void *data, int* width, int* height) {

    // First, parse the data and get to it...
    etc1_byte *origData = reinterpret_cast<etc1_byte *>(data);
    if (!etc1_pkm_is_valid(origData)) {
        return false;
    }

    int origW = etc1_pkm_get_width(origData);
    int origH = etc1_pkm_get_height(origData);

    int blockWidth = (origW + 3) >> 2;
    int blockHeight = (origH + 3) >> 2;

    // Make sure that we have blocks to trim off..
    if (blockWidth < 2 || blockHeight < 2) {
        return false;
    }

    int newWidth = (blockWidth - 1) << 2;
    int newHeight = (blockHeight - 1) << 2;

    size_t newDataSz = etc1_get_encoded_data_size(newWidth, newHeight) + ETC_PKM_HEADER_SIZE;
    SkAutoTMalloc<etc1_byte> am(newDataSz);

    etc1_byte* newData = am.get();

    etc1_pkm_format_header(newData, newWidth, newHeight);
    newData += ETC_PKM_HEADER_SIZE;
    origData += ETC_PKM_HEADER_SIZE;

    for (int j = 0; j < blockHeight - 1; ++j) {
        memcpy(newData, origData, (blockWidth - 1)*ETC1_ENCODED_BLOCK_SIZE);
        origData += blockWidth*ETC1_ENCODED_BLOCK_SIZE;
        newData += (blockWidth - 1)*ETC1_ENCODED_BLOCK_SIZE;
    }

    // Stick the data back whence it came
    memcpy(data, am.get(), newDataSz);
    *width = newWidth;
    *height = newHeight;

    return true;
}
#endif  // SK_IGNORE_ETC1_SUPPORT

namespace skiagm {

/**
 *  Test decoding an image from a PKM or KTX file and then
 *  from compressed ETC1 data.
 */
class ETC1BitmapGM : public GM {
public:
    ETC1BitmapGM() { }
    virtual ~ETC1BitmapGM() { }

protected:
    SkString onShortName() override {
        SkString str = SkString("etc1bitmap_");
        str.append(this->fileExtension());
        return str;
    }

    SkISize onISize() override {
        return SkISize::Make(128, 128);
    }

    virtual SkString fileExtension() const = 0;

    void onDraw(SkCanvas* canvas) override {
        SkBitmap bm;
        SkString filename = GetResourcePath("mandrill_128.");
        filename.append(this->fileExtension());
        sk_sp<SkData> fileData(SkData::MakeFromFileName(filename.c_str()));
        if (nullptr == fileData) {
            SkDebugf("Could not open the file. Did you forget to set the resourcePath?\n");
            return;
        }

        sk_sp<SkImage> image(SkImage::MakeFromEncoded(std::move(fileData)));
        if (nullptr == image) {
            SkDebugf("Could not decode the ETC file. ETC may not be included in this platform.\n");
            return;
        }
        canvas->drawImage(image, 0, 0);
    }

private:
    typedef GM INHERITED;
};

// This class specializes ETC1BitmapGM to load the mandrill_128.pkm file.
class ETC1Bitmap_PKM_GM : public ETC1BitmapGM {
public:
    ETC1Bitmap_PKM_GM() : ETC1BitmapGM() { }
    virtual ~ETC1Bitmap_PKM_GM() { }

protected:

    SkString fileExtension() const override { return SkString("pkm"); }

private:
    typedef ETC1BitmapGM INHERITED;
};

// This class specializes ETC1BitmapGM to load the mandrill_128.ktx file.
class ETC1Bitmap_KTX_GM : public ETC1BitmapGM {
public:
    ETC1Bitmap_KTX_GM() : ETC1BitmapGM() { }
    virtual ~ETC1Bitmap_KTX_GM() { }

protected:

    SkString fileExtension() const override { return SkString("ktx"); }

private:
    typedef ETC1BitmapGM INHERITED;
};

// This class specializes ETC1BitmapGM to load the mandrill_128.r11.ktx file.
class ETC1Bitmap_R11_KTX_GM : public ETC1BitmapGM {
public:
    ETC1Bitmap_R11_KTX_GM() : ETC1BitmapGM() { }
    virtual ~ETC1Bitmap_R11_KTX_GM() { }

protected:

    SkString fileExtension() const override { return SkString("r11.ktx"); }

private:
    typedef ETC1BitmapGM INHERITED;
};

#ifndef SK_IGNORE_ETC1_SUPPORT
/**
 *  Test decoding an image from a PKM file and then
 *  from non-power-of-two compressed ETC1 data. First slice
 *  off a row and column of blocks in order to make it non-power
 *  of two.
 */
class ETC1Bitmap_NPOT_GM : public GM {
public:
    ETC1Bitmap_NPOT_GM() { }
    virtual ~ETC1Bitmap_NPOT_GM() { }

protected:
    SkString onShortName() override {
        return SkString("etc1bitmap_npot");
    }

    SkISize onISize() override {
        return SkISize::Make(124, 124);
    }

    void onDraw(SkCanvas* canvas) override {
        SkBitmap bm;
        SkString pkmFilename = GetResourcePath("mandrill_128.pkm");
        SkAutoDataUnref fileData(SkData::NewFromFileName(pkmFilename.c_str()));
        if (nullptr == fileData) {
            SkDebugf("Could not open the file. Did you forget to set the resourcePath?\n");
            return;
        }

        SkAutoMalloc am(fileData->size());
        memcpy(am.get(), fileData->data(), fileData->size());

        int width, height;
        if (!slice_etc1_data(am.get(), &width, &height)) {
            SkDebugf("ETC1 Data is poorly formatted.\n");
            return;
        }

        SkASSERT(124 == width);
        SkASSERT(124 == height);

        size_t dataSz = etc1_get_encoded_data_size(width, height) + ETC_PKM_HEADER_SIZE;
        sk_sp<SkData> nonPOTData(SkData::MakeWithCopy(am.get(), dataSz));
        canvas->drawImage(SkImage::MakeFromEncoded(std::move(nonPOTData)).get(), 0, 0);
    }

private:
    typedef GM INHERITED;
};
#endif  // SK_IGNORE_ETC1_SUPPORT

}  // namespace skiagm

//////////////////////////////////////////////////////////////////////////////

DEF_GM(return new skiagm::ETC1Bitmap_PKM_GM;)
DEF_GM(return new skiagm::ETC1Bitmap_KTX_GM;)
DEF_GM(return new skiagm::ETC1Bitmap_R11_KTX_GM;)

#ifndef SK_IGNORE_ETC1_SUPPORT
DEF_GM(return new skiagm::ETC1Bitmap_NPOT_GM;)
#endif  // SK_IGNORE_ETC1_SUPPORT