2 Copyright (C) 2015, SiPlus, Chasseur de bots.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 #include "bytestreamutils.h"
28 #include "ifilesystem.h"
32 const int KTX_TYPE_UNSIGNED_BYTE = 0x1401;
33 const int KTX_TYPE_UNSIGNED_SHORT_4_4_4_4 = 0x8033;
34 const int KTX_TYPE_UNSIGNED_SHORT_5_5_5_1 = 0x8034;
35 const int KTX_TYPE_UNSIGNED_SHORT_5_6_5 = 0x8363;
37 const int KTX_FORMAT_ALPHA = 0x1906;
38 const int KTX_FORMAT_RGB = 0x1907;
39 const int KTX_FORMAT_RGBA = 0x1908;
40 const int KTX_FORMAT_LUMINANCE = 0x1909;
41 const int KTX_FORMAT_LUMINANCE_ALPHA = 0x190A;
42 const int KTX_FORMAT_BGR = 0x80E0;
43 const int KTX_FORMAT_BGRA = 0x80E1;
45 const int KTX_FORMAT_ETC1_RGB8 = 0x8D64;
50 virtual ~KTX_Decoder() = default;
51 virtual void Decode( PointerInputStream& istream, byte* out ) = 0;
52 virtual unsigned int GetPixelSize() = 0;
55 class KTX_Decoder_A8 : public KTX_Decoder
58 virtual void Decode( PointerInputStream& istream, byte* out ){
59 out[0] = out[1] = out[2] = 0;
60 out[3] = istream_read_byte( istream );
62 virtual unsigned int GetPixelSize(){
67 class KTX_Decoder_RGB8 : public KTX_Decoder
70 virtual void Decode( PointerInputStream& istream, byte* out ){
71 istream.read( out, 3 );
74 virtual unsigned int GetPixelSize(){
79 class KTX_Decoder_RGBA8 : public KTX_Decoder
82 virtual void Decode( PointerInputStream& istream, byte* out ){
83 istream.read( out, 4 );
85 virtual unsigned int GetPixelSize(){
90 class KTX_Decoder_L8 : public KTX_Decoder
93 virtual void Decode( PointerInputStream& istream, byte* out ){
94 byte l = istream_read_byte( istream );
95 out[0] = out[1] = out[2] = l;
98 virtual unsigned int GetPixelSize(){
103 class KTX_Decoder_LA8 : public KTX_Decoder
106 virtual void Decode( PointerInputStream& istream, byte* out ){
108 istream.read( la, 2 );
109 out[0] = out[1] = out[2] = la[0];
112 virtual unsigned int GetPixelSize(){
117 class KTX_Decoder_BGR8 : public KTX_Decoder
120 virtual void Decode( PointerInputStream& istream, byte* out ){
122 istream.read( bgr, 3 );
128 virtual unsigned int GetPixelSize(){
133 class KTX_Decoder_BGRA8 : public KTX_Decoder
136 virtual void Decode( PointerInputStream& istream, byte* out ){
138 istream.read( bgra, 4 );
144 virtual unsigned int GetPixelSize(){
149 class KTX_Decoder_RGBA4 : public KTX_Decoder
154 KTX_Decoder_RGBA4( bool bigEndian ) : m_bigEndian( bigEndian ){}
155 virtual void Decode( PointerInputStream& istream, byte* out ){
158 rgba = istream_read_uint16_be( istream );
161 rgba = istream_read_uint16_le( istream );
163 int r = ( rgba >> 12 ) & 0xf;
164 int g = ( rgba >> 8 ) & 0xf;
165 int b = ( rgba >> 4 ) & 0xf;
167 out[0] = ( r << 4 ) | r;
168 out[1] = ( g << 4 ) | g;
169 out[2] = ( b << 4 ) | b;
170 out[3] = ( a << 4 ) | a;
172 virtual unsigned int GetPixelSize(){
177 class KTX_Decoder_RGBA5 : public KTX_Decoder
182 KTX_Decoder_RGBA5( bool bigEndian ) : m_bigEndian( bigEndian ){}
183 virtual void Decode( PointerInputStream& istream, byte* out ){
186 rgba = istream_read_uint16_be( istream );
189 rgba = istream_read_uint16_le( istream );
191 int r = ( rgba >> 11 ) & 0x1f;
192 int g = ( rgba >> 6 ) & 0x1f;
193 int b = ( rgba >> 1 ) & 0x1f;
194 out[0] = ( r << 3 ) | ( r >> 2 );
195 out[1] = ( g << 3 ) | ( g >> 2 );
196 out[2] = ( b << 3 ) | ( b >> 2 );
197 out[3] = ( rgba & 1 ) * 255;
199 virtual unsigned int GetPixelSize(){
204 class KTX_Decoder_RGB5 : public KTX_Decoder
209 KTX_Decoder_RGB5( bool bigEndian ) : m_bigEndian( bigEndian ){}
210 virtual void Decode( PointerInputStream& istream, byte* out ){
213 rgb = istream_read_uint16_be( istream );
216 rgb = istream_read_uint16_le( istream );
218 int r = ( rgb >> 11 ) & 0x1f;
219 int g = ( rgb >> 5 ) & 0x3f;
221 out[0] = ( r << 3 ) | ( r >> 2 );
222 out[1] = ( g << 2 ) | ( g >> 4 );
223 out[2] = ( b << 3 ) | ( b >> 2 );
226 virtual unsigned int GetPixelSize(){
231 static void KTX_DecodeETC1( PointerInputStream& istream, Image& image ){
232 unsigned int width = image.getWidth(), height = image.getHeight();
233 unsigned int stride = width * 4;
234 byte* pixbuf = image.getRGBAPixels();
235 byte etc[8], rgba[64];
237 for ( unsigned int y = 0; y < height; y += 4, pixbuf += stride * 4 )
239 unsigned int blockrows = height - y;
240 if ( blockrows > 4 ) {
245 for ( unsigned int x = 0; x < width; x += 4, p += 16 )
247 istream.read( etc, 8 );
248 ETC_DecodeETC1Block( etc, rgba, qtrue );
250 unsigned int blockrowsize = width - x;
251 if ( blockrowsize > 4 ) {
255 for ( unsigned int blockrow = 0; blockrow < blockrows; blockrow++ )
257 memcpy( p + blockrow * stride, rgba + blockrow * 16, blockrowsize );
263 Image* LoadKTXBuff( PointerInputStream& istream ){
265 istream.read( identifier, 12 );
266 if ( memcmp( identifier, "\xABKTX 11\xBB\r\n\x1A\n", 12 ) ) {
267 globalErrorStream() << "LoadKTX: Image has the wrong identifier\n";
271 bool bigEndian = ( istream_read_uint32_le( istream ) == 0x01020304 );
275 type = istream_read_uint32_be( istream );
278 type = istream_read_uint32_le( istream );
281 // For compressed textures, the format is in glInternalFormat.
282 // For uncompressed textures, it's in glBaseInternalFormat.
283 istream.seek( ( type ? 3 : 2 ) * sizeof( uint32_t ) );
286 format = istream_read_uint32_be( istream );
289 format = istream_read_uint32_le( istream );
292 istream.seek( sizeof( uint32_t ) );
295 unsigned int width, height;
297 width = istream_read_uint32_be( istream );
298 height = istream_read_uint32_be( istream );
301 width = istream_read_uint32_le( istream );
302 height = istream_read_uint32_le( istream );
305 globalErrorStream() << "LoadKTX: Image has zero width\n";
312 // Skip the key/values and load the first 2D image in the texture.
313 // Since KTXorientation is only a hint and has no effect on the texture data and coordinates, it must be ignored.
314 istream.seek( 4 * sizeof( uint32_t ) );
315 unsigned int bytesOfKeyValueData;
317 bytesOfKeyValueData = istream_read_uint32_be( istream );
320 bytesOfKeyValueData = istream_read_uint32_le( istream );
322 istream.seek( bytesOfKeyValueData + sizeof( uint32_t ) );
324 RGBAImage* image = new RGBAImage( width, height );
327 KTX_Decoder* decoder = NULL;
330 case KTX_TYPE_UNSIGNED_BYTE:
333 case KTX_FORMAT_ALPHA:
334 decoder = new KTX_Decoder_A8();
337 decoder = new KTX_Decoder_RGB8();
339 case KTX_FORMAT_RGBA:
340 decoder = new KTX_Decoder_RGBA8();
342 case KTX_FORMAT_LUMINANCE:
343 decoder = new KTX_Decoder_L8();
345 case KTX_FORMAT_LUMINANCE_ALPHA:
346 decoder = new KTX_Decoder_LA8();
349 decoder = new KTX_Decoder_BGR8();
351 case KTX_FORMAT_BGRA:
352 decoder = new KTX_Decoder_BGRA8();
356 case KTX_TYPE_UNSIGNED_SHORT_4_4_4_4:
357 if ( format == KTX_FORMAT_RGBA ) {
358 decoder = new KTX_Decoder_RGBA4( bigEndian );
361 case KTX_TYPE_UNSIGNED_SHORT_5_5_5_1:
362 if ( format == KTX_FORMAT_RGBA ) {
363 decoder = new KTX_Decoder_RGBA5( bigEndian );
366 case KTX_TYPE_UNSIGNED_SHORT_5_6_5:
367 if ( format == KTX_FORMAT_RGB ) {
368 decoder = new KTX_Decoder_RGB5( bigEndian );
374 globalErrorStream() << "LoadKTX: Image has an unsupported pixel type " << type << " or format " << format << "\n";
379 unsigned int inRowLength = width * decoder->GetPixelSize();
380 unsigned int inPadding = ( ( inRowLength + 3 ) & ~3 ) - inRowLength;
381 byte* out = image->getRGBAPixels();
382 for ( unsigned int y = 0; y < height; y++ )
384 for ( unsigned int x = 0; x < width; x++, out += 4 )
386 decoder->Decode( istream, out );
390 istream.seek( inPadding );
399 case KTX_FORMAT_ETC1_RGB8:
400 KTX_DecodeETC1( istream, *image );
403 globalErrorStream() << "LoadKTX: Image has an unsupported compressed format " << format << "\n";
412 Image* LoadKTX( ArchiveFile& file ){
413 ScopedArchiveBuffer buffer( file );
414 PointerInputStream istream( buffer.buffer );
415 return LoadKTXBuff( istream );