/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 | | } |