Coverage Report

Created: 2026-08-31 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreETCCodec.cpp
Line
Count
Source
1
/*
2
-----------------------------------------------------------------------------
3
This source file is part of OGRE
4
(Object-oriented Graphics Rendering Engine)
5
For the latest info, see http://www.ogre3d.org/
6
7
Copyright (c) 2000-2014 Torus Knot Software Ltd
8
9
Permission is hereby granted, free of charge, to any person obtaining a copy
10
of this software and associated documentation files (the "Software"), to deal
11
in the Software without restriction, including without limitation the rights
12
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
copies of the Software, and to permit persons to whom the Software is
14
furnished to do so, subject to the following conditions:
15
16
The above copyright notice and this permission notice shall be included in
17
all copies or substantial portions of the Software.
18
19
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
THE SOFTWARE.
26
-----------------------------------------------------------------------------
27
*/
28
29
#include "OgreStableHeaders.h"
30
31
#include "OgreETCCodec.h"
32
#include "OgreImage.h"
33
34
#define KTX_ENDIAN_REF      (0x04030201)
35
0
#define KTX_ENDIAN_REF_REV  (0x01020304)
36
37
// In a PKM-file, the codecs are stored using the following identifiers
38
//
39
// identifier                         value               codec
40
// --------------------------------------------------------------------
41
// ETC1_RGB_NO_MIPMAPS                  0                 GL_ETC1_RGB8_OES
42
// ETC2PACKAGE_RGB_NO_MIPMAPS           1                 GL_COMPRESSED_RGB8_ETC2
43
// ETC2PACKAGE_RGBA_NO_MIPMAPS_OLD      2, not used       -
44
// ETC2PACKAGE_RGBA_NO_MIPMAPS          3                 GL_COMPRESSED_RGBA8_ETC2_EAC
45
// ETC2PACKAGE_RGBA1_NO_MIPMAPS         4                 GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
46
// ETC2PACKAGE_R_NO_MIPMAPS             5                 GL_COMPRESSED_R11_EAC
47
// ETC2PACKAGE_RG_NO_MIPMAPS            6                 GL_COMPRESSED_RG11_EAC
48
// ETC2PACKAGE_R_SIGNED_NO_MIPMAPS      7                 GL_COMPRESSED_SIGNED_R11_EAC
49
// ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS     8                 GL_COMPRESSED_SIGNED_RG11_EAC
50
51
namespace Ogre {
52
53
    const uint32 PKM_MAGIC = FOURCC('P', 'K', 'M', ' ');
54
    const uint32 KTX_MAGIC = FOURCC(0xAB, 0x4B, 0x54, 0x58);
55
56
    typedef struct {
57
        uint8  name[4];
58
        uint8  version[2];
59
        uint8  iTextureTypeMSB;
60
        uint8  iTextureTypeLSB;
61
        uint8  iPaddedWidthMSB;
62
        uint8  iPaddedWidthLSB;
63
        uint8  iPaddedHeightMSB;
64
        uint8  iPaddedHeightLSB;
65
        uint8  iWidthMSB;
66
        uint8  iWidthLSB;
67
        uint8  iHeightMSB;
68
        uint8  iHeightLSB;
69
    } PKMHeader;
70
71
    typedef struct {
72
        uint8     identifier[12];
73
        uint32    endianness;
74
        uint32    glType;
75
        uint32    glTypeSize;
76
        uint32    glFormat;
77
        uint32    glInternalFormat;
78
        uint32    glBaseInternalFormat;
79
        uint32    pixelWidth;
80
        uint32    pixelHeight;
81
        uint32    pixelDepth;
82
        uint32    numberOfArrayElements;
83
        uint32    numberOfFaces;
84
        uint32    numberOfMipmapLevels;
85
        uint32    bytesOfKeyValueData;
86
    } KTXHeader;
87
88
    //---------------------------------------------------------------------
89
    ETCCodec* ETCCodec::msPKMInstance = 0;
90
    ETCCodec* ETCCodec::msKTXInstance = 0;
91
    //---------------------------------------------------------------------
92
    void ETCCodec::startup(void)
93
2
    {
94
2
        if (!msPKMInstance)
95
2
        {
96
2
            msPKMInstance = OGRE_NEW ETCCodec("pkm");
97
2
            Codec::registerCodec(msPKMInstance);
98
2
        }
99
100
2
        if (!msKTXInstance)
101
2
        {
102
2
            msKTXInstance = OGRE_NEW ETCCodec("ktx");
103
2
            Codec::registerCodec(msKTXInstance);
104
2
        }
105
106
2
        LogManager::getSingleton().logMessage(LML_NORMAL,
107
2
                                              "ETC codec registering");
108
2
    }
109
    //---------------------------------------------------------------------
110
    void ETCCodec::shutdown(void)
111
1
    {
112
1
        if(msPKMInstance)
113
1
        {
114
1
            Codec::unregisterCodec(msPKMInstance);
115
1
            OGRE_DELETE msPKMInstance;
116
1
            msPKMInstance = 0;
117
1
        }
118
119
1
        if(msKTXInstance)
120
1
        {
121
1
            Codec::unregisterCodec(msKTXInstance);
122
1
            OGRE_DELETE msKTXInstance;
123
1
            msKTXInstance = 0;
124
1
        }
125
1
    }
126
    //---------------------------------------------------------------------
127
    ETCCodec::ETCCodec(const String &type):
128
4
        mType(type)
129
4
    {
130
4
    }
131
    //---------------------------------------------------------------------
132
    void ETCCodec::decode(const DataStreamPtr& stream, const Any& output) const
133
0
    {
134
0
        Image* image = any_cast<Image*>(output);
135
136
0
        mType == "pkm" ? decodePKM(stream, image) : decodeKTX(stream, image);
137
0
    }
138
    //---------------------------------------------------------------------
139
    String ETCCodec::getType() const
140
6
    {
141
6
        return mType;
142
6
    }
143
    //---------------------------------------------------------------------
144
    String ETCCodec::magicNumberToFileExt(const char *magicNumberPtr, size_t maxbytes) const
145
0
    {
146
0
        if (maxbytes >= sizeof(uint32))
147
0
        {
148
0
            uint32 fileType;
149
0
            memcpy(&fileType, magicNumberPtr, sizeof(uint32));
150
0
            flipEndian(&fileType, sizeof(uint32));
151
152
0
            if (PKM_MAGIC == fileType)
153
0
                return String("pkm");
154
155
0
            if (KTX_MAGIC == fileType)
156
0
                return String("ktx");
157
0
        }
158
159
0
        return BLANKSTRING;
160
0
    }
161
    //---------------------------------------------------------------------
162
    void ETCCodec::decodePKM(const DataStreamPtr& stream, Image* image)
163
0
    {
164
0
        PKMHeader header;
165
166
        // Read the ETC header
167
0
        stream->read(&header, sizeof(PKMHeader));
168
169
0
        if (PKM_MAGIC != FOURCC(header.name[0], header.name[1], header.name[2], header.name[3]) ) // "PKM 10"
170
0
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "No PKM header found");
171
172
0
        uint16 width = (header.iWidthMSB << 8) | header.iWidthLSB;
173
0
        uint16 height = (header.iHeightMSB << 8) | header.iHeightLSB;
174
        //uint16 paddedWidth = (header.iPaddedWidthMSB << 8) | header.iPaddedWidthLSB;
175
        //uint16 paddedHeight = (header.iPaddedHeightMSB << 8) | header.iPaddedHeightLSB;
176
0
        uint16 type = (header.iTextureTypeMSB << 8) | header.iTextureTypeLSB;
177
178
0
        PixelFormat format = PF_UNKNOWN;
179
180
        // File version 2.0 supports ETC2 in addition to ETC1
181
0
        if(header.version[0] == '2' && header.version[1] == '0')
182
0
        {
183
0
            switch (type) {
184
0
                case 0:
185
0
                    format = PF_ETC1_RGB8;
186
0
                    break;
187
188
                    // GL_COMPRESSED_RGB8_ETC2
189
0
                case 1:
190
0
                    format = PF_ETC2_RGB8;
191
0
                    break;
192
193
                    // GL_COMPRESSED_RGBA8_ETC2_EAC
194
0
                case 3:
195
0
                    format = PF_ETC2_RGBA8;
196
0
                    break;
197
198
                    // GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
199
0
                case 4:
200
0
                    format = PF_ETC2_RGB8A1;
201
0
                    break;
202
203
                    // Default case is ETC1
204
0
                default:
205
0
                    format = PF_ETC1_RGB8;
206
0
                    break;
207
0
            }
208
0
        }
209
0
        else
210
0
            format = PF_ETC1_RGB8;
211
212
        // ETC has no support for mipmaps - malideveloper.com has a example
213
        // where the load mipmap levels from different external files
214
0
        image->create(format, width, height);
215
0
        stream->read(image->getData(), image->getSize());
216
0
    }
217
    //---------------------------------------------------------------------
218
    void ETCCodec::decodeKTX(const DataStreamPtr& stream, Image* image)
219
0
    {
220
0
        KTXHeader header;
221
        // Read the KTX header
222
0
        stream->read(&header, sizeof(KTXHeader));
223
224
0
        const uint8 KTXFileIdentifier[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
225
0
        if (memcmp(KTXFileIdentifier, &header.identifier, sizeof(KTXFileIdentifier)) != 0)
226
0
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "No KTX header found");
227
228
0
        if (header.endianness == KTX_ENDIAN_REF_REV)
229
0
            flipEndian(&header.glType, sizeof(uint32));
230
0
        if (header.numberOfFaces == 0)
231
0
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "KTX file has zero faces");
232
233
0
        if (header.numberOfMipmapLevels == 0)
234
0
            header.numberOfMipmapLevels = 1; // per KTX spec, 0 means generate mipmaps; treat as 1
235
236
0
        PixelFormat format = PF_UNKNOWN;
237
238
0
        switch(header.glInternalFormat)
239
0
        {
240
0
        case 37492: // GL_COMPRESSED_RGB8_ETC2
241
0
            format = PF_ETC2_RGB8;
242
0
            break;
243
0
        case 37496:// GL_COMPRESSED_RGBA8_ETC2_EAC
244
0
            format = PF_ETC2_RGBA8;
245
0
            break;
246
0
        case 37494: // GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
247
0
            format = PF_ETC2_RGB8A1;
248
0
            break;
249
0
        case 35986: // ATC_RGB
250
0
            format = PF_ATC_RGB;
251
0
            break;
252
0
        case 35987: // ATC_RGB_Explicit
253
0
            format = PF_ATC_RGBA_EXPLICIT_ALPHA;
254
0
            break;
255
0
        case 34798: // ATC_RGB_Interpolated
256
0
            format = PF_ATC_RGBA_INTERPOLATED_ALPHA;
257
0
            break;
258
0
        case 33777: // DXT 1
259
0
            format = PF_DXT1;
260
0
            break;
261
0
        case 33778: // DXT 3
262
0
            format = PF_DXT3;
263
0
            break;
264
0
        case 33779: // DXT 5
265
0
            format = PF_DXT5;
266
0
            break;
267
0
         case 0x8c00: // COMPRESSED_RGB_PVRTC_4BPPV1_IMG
268
0
            format = PF_PVRTC_RGB4;
269
0
            break;
270
0
        case 0x8c01: // COMPRESSED_RGB_PVRTC_2BPPV1_IMG
271
0
            format = PF_PVRTC_RGB2;
272
0
            break;
273
0
        case 0x8c02: // COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
274
0
            format = PF_PVRTC_RGBA4;
275
0
            break;
276
0
        case 0x8c03: // COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
277
0
            format = PF_PVRTC_RGBA2;
278
0
            break;
279
0
        case 0x93B0: // COMPRESSED_RGBA_ASTC_4x4_KHR
280
0
            format = PF_ASTC_RGBA_4X4_LDR;
281
0
            break;
282
0
        case 0x93B1: // COMPRESSED_RGBA_ASTC_5x4_KHR
283
0
            format = PF_ASTC_RGBA_5X4_LDR;
284
0
            break;
285
0
        case 0x93B2: // COMPRESSED_RGBA_ASTC_5x5_KHR
286
0
            format = PF_ASTC_RGBA_5X5_LDR;
287
0
            break;
288
0
        case 0x93B3: // COMPRESSED_RGBA_ASTC_6x5_KHR
289
0
            format = PF_ASTC_RGBA_6X5_LDR;
290
0
            break;
291
0
        case 0x93B4: // COMPRESSED_RGBA_ASTC_6x6_KHR
292
0
            format = PF_ASTC_RGBA_6X6_LDR;
293
0
            break;
294
0
        case 0x93B5: // COMPRESSED_RGBA_ASTC_8x5_KHR
295
0
            format = PF_ASTC_RGBA_8X5_LDR;
296
0
            break;
297
0
        case 0x93B6: // COMPRESSED_RGBA_ASTC_8x6_KHR
298
0
            format = PF_ASTC_RGBA_8X6_LDR;
299
0
            break;
300
0
        case 0x93B7: // COMPRESSED_RGBA_ASTC_8x8_KHR
301
0
            format = PF_ASTC_RGBA_8X8_LDR;
302
0
            break;
303
0
        case 0x93B8: // COMPRESSED_RGBA_ASTC_10x5_KHR
304
0
            format = PF_ASTC_RGBA_10X5_LDR;
305
0
            break;
306
0
        case 0x93B9: // COMPRESSED_RGBA_ASTC_10x6_KHR
307
0
            format = PF_ASTC_RGBA_10X6_LDR;
308
0
            break;
309
0
        case 0x93BA: // COMPRESSED_RGBA_ASTC_10x8_KHR
310
0
            format = PF_ASTC_RGBA_10X8_LDR;
311
0
            break;
312
0
        case 0x93BB: // COMPRESSED_RGBA_ASTC_10x10_KHR
313
0
            format = PF_ASTC_RGBA_10X10_LDR;
314
0
            break;
315
0
        case 0x93BC: // COMPRESSED_RGBA_ASTC_12x10_KHR
316
0
            format = PF_ASTC_RGBA_12X10_LDR;
317
0
            break;
318
0
        case 0x93BD: // COMPRESSED_RGBA_ASTC_12x12_KHR
319
0
            format = PF_ASTC_RGBA_12X12_LDR;
320
0
            break;
321
0
        case 0x8D64: // GL_ETC1_RGB8_OES
322
0
            format = PF_ETC1_RGB8;
323
0
            break;
324
0
        case 0x8C3A: // GL_R11F_G11F_B10F
325
0
            format = PF_R11G11B10_FLOAT;
326
0
            break;
327
0
        default:
328
0
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported glInternalFormat");
329
0
        }
330
331
0
        image->create(format, header.pixelWidth, header.pixelHeight, 1, header.numberOfFaces,
332
0
                      header.numberOfMipmapLevels - 1);
333
334
0
        stream->skip(header.bytesOfKeyValueData);
335
336
        // Now deal with the data
337
0
        uchar* destPtr = image->getData();
338
0
        size_t mipOffset = 0;
339
0
        uint32 numFaces = header.numberOfFaces;
340
0
        size_t faceSize = image->getSize() / numFaces;
341
0
        for (uint32 level = 0; level < header.numberOfMipmapLevels; ++level)
342
0
        {
343
0
            uint32 imageSize = 0;
344
0
            stream->read(&imageSize, sizeof(uint32));
345
346
0
            if (mipOffset + imageSize > faceSize)
347
0
                OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "KTX file contains more data than expected");
348
349
0
            for(uint32 face = 0; face < numFaces; ++face)
350
0
            {
351
0
                uchar* placePtr = destPtr + faceSize * face + mipOffset; // shuffle mip and face
352
0
                stream->read(placePtr, imageSize);
353
0
            }
354
0
            mipOffset += imageSize;
355
0
        }
356
0
    }
357
}