]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/image/ktx.cpp
a12f8150dfce99e98e0a639ce8fb187481109665
[xonotic/netradiant.git] / plugins / image / ktx.cpp
1 /*
2    Copyright (C) 2015, SiPlus, Chasseur de bots.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
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.
11
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.
16
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
20  */
21
22 #include "ktx.h"
23
24 #include <string.h>
25
26 #include "bytestreamutils.h"
27 #include "etclib.h"
28 #include "ifilesystem.h"
29 #include "imagelib.h"
30
31
32 #define KTX_TYPE_UNSIGNED_BYTE                          0x1401
33 #define KTX_TYPE_UNSIGNED_SHORT_4_4_4_4         0x8033
34 #define KTX_TYPE_UNSIGNED_SHORT_5_5_5_1         0x8034
35 #define KTX_TYPE_UNSIGNED_SHORT_5_6_5           0x8363
36
37 #define KTX_FORMAT_ALPHA                                        0x1906
38 #define KTX_FORMAT_RGB                                          0x1907
39 #define KTX_FORMAT_RGBA                                         0x1908
40 #define KTX_FORMAT_LUMINANCE                            0x1909
41 #define KTX_FORMAT_LUMINANCE_ALPHA                      0x190A
42 #define KTX_FORMAT_BGR                                          0x80E0
43 #define KTX_FORMAT_BGRA                                         0x80E1
44
45 #define KTX_FORMAT_ETC1_RGB8                            0x8D64
46
47 class KTX_Decoder
48 {
49 public:
50         virtual void Decode( PointerInputStream& istream, byte* out ) = 0;
51         virtual unsigned int GetPixelSize() = 0;
52 protected:
53         bool m_bigEndian;
54 };
55
56 class KTX_Decoder_A8 : public KTX_Decoder
57 {
58 public:
59         virtual void Decode( PointerInputStream& istream, byte* out ){
60                 out[0] = out[1] = out[2] = 0;
61                 out[3] = istream_read_byte( istream );
62         }
63         virtual unsigned int GetPixelSize(){
64                 return 1;
65         }
66 };
67
68 class KTX_Decoder_RGB8 : public KTX_Decoder
69 {
70 public:
71         virtual void Decode( PointerInputStream& istream, byte* out ){
72                 istream.read( out, 3 );
73                 out[3] = 255;
74         }
75         virtual unsigned int GetPixelSize(){
76                 return 3;
77         }
78 };
79
80 class KTX_Decoder_RGBA8 : public KTX_Decoder
81 {
82 public:
83         virtual void Decode( PointerInputStream& istream, byte* out ){
84                 istream.read( out, 4 );
85         }
86         virtual unsigned int GetPixelSize(){
87                 return 4;
88         }
89 };
90
91 class KTX_Decoder_L8 : public KTX_Decoder
92 {
93 public:
94         virtual void Decode( PointerInputStream& istream, byte* out ){
95                 byte l = istream_read_byte( istream );
96                 out[0] = out[1] = out[2] = l;
97                 out[3] = 255;
98         }
99         virtual unsigned int GetPixelSize(){
100                 return 1;
101         }
102 };
103
104 class KTX_Decoder_LA8 : public KTX_Decoder
105 {
106 public:
107         virtual void Decode( PointerInputStream& istream, byte* out ){
108                 byte la[2];
109                 istream.read( la, 2 );
110                 out[0] = out[1] = out[2] = la[0];
111                 out[3] = la[1];
112         }
113         virtual unsigned int GetPixelSize(){
114                 return 2;
115         }
116 };
117
118 class KTX_Decoder_BGR8 : public KTX_Decoder
119 {
120 public:
121         virtual void Decode( PointerInputStream& istream, byte* out ){
122                 byte bgr[3];
123                 istream.read( bgr, 3 );
124                 out[0] = bgr[2];
125                 out[1] = bgr[1];
126                 out[2] = bgr[0];
127                 out[3] = 255;
128         }
129         virtual unsigned int GetPixelSize(){
130                 return 3;
131         }
132 };
133
134 class KTX_Decoder_BGRA8 : public KTX_Decoder
135 {
136 public:
137         virtual void Decode( PointerInputStream& istream, byte* out ){
138                 byte bgra[4];
139                 istream.read( bgra, 4 );
140                 out[0] = bgra[2];
141                 out[1] = bgra[1];
142                 out[2] = bgra[0];
143                 out[3] = bgra[3];
144         }
145         virtual unsigned int GetPixelSize(){
146                 return 4;
147         }
148 };
149
150 class KTX_Decoder_RGBA4 : public KTX_Decoder
151 {
152 protected:
153         bool m_bigEndian;
154 public:
155         KTX_Decoder_RGBA4( bool bigEndian ) : m_bigEndian( bigEndian ){}
156         virtual void Decode( PointerInputStream& istream, byte* out ){
157                 uint16_t rgba;
158                 if ( m_bigEndian ) {
159                         rgba = istream_read_uint16_be( istream );
160                 }
161                 else {
162                         rgba = istream_read_uint16_le( istream );
163                 }
164                 int r = ( rgba >> 12 ) & 0xf;
165                 int g = ( rgba >> 8 ) & 0xf;
166                 int b = ( rgba >> 4 ) & 0xf;
167                 int a = rgba & 0xf;
168                 out[0] = ( r << 4 ) | r;
169                 out[1] = ( g << 4 ) | g;
170                 out[2] = ( b << 4 ) | b;
171                 out[3] = ( a << 4 ) | a;
172         }
173         virtual unsigned int GetPixelSize(){
174                 return 2;
175         }
176 };
177
178 class KTX_Decoder_RGBA5 : public KTX_Decoder
179 {
180 protected:
181         bool m_bigEndian;
182 public:
183         KTX_Decoder_RGBA5( bool bigEndian ) : m_bigEndian( bigEndian ){}
184         virtual void Decode( PointerInputStream& istream, byte* out ){
185                 uint16_t rgba;
186                 if ( m_bigEndian ) {
187                         rgba = istream_read_uint16_be( istream );
188                 }
189                 else {
190                         rgba = istream_read_uint16_le( istream );
191                 }
192                 int r = ( rgba >> 11 ) & 0x1f;
193                 int g = ( rgba >> 6 ) & 0x1f;
194                 int b = ( rgba >> 1 ) & 0x1f;
195                 out[0] = ( r << 3 ) | ( r >> 2 );
196                 out[1] = ( g << 3 ) | ( g >> 2 );
197                 out[2] = ( b << 3 ) | ( b >> 2 );
198                 out[3] = ( rgba & 1 ) * 255;
199         }
200         virtual unsigned int GetPixelSize(){
201                 return 2;
202         }
203 };
204
205 class KTX_Decoder_RGB5 : public KTX_Decoder
206 {
207 protected:
208         bool m_bigEndian;
209 public:
210         KTX_Decoder_RGB5( bool bigEndian ) : m_bigEndian( bigEndian ){}
211         virtual void Decode( PointerInputStream& istream, byte* out ){
212                 uint16_t rgb;
213                 if ( m_bigEndian ) {
214                         rgb = istream_read_uint16_be( istream );
215                 }
216                 else {
217                         rgb = istream_read_uint16_le( istream );
218                 }
219                 int r = ( rgb >> 11 ) & 0x1f;
220                 int g = ( rgb >> 5 ) & 0x3f;
221                 int b = rgb & 0x1f;
222                 out[0] = ( r << 3 ) | ( r >> 2 );
223                 out[1] = ( g << 2 ) | ( g >> 4 );
224                 out[2] = ( b << 3 ) | ( b >> 2 );
225                 out[3] = 255;
226         }
227         virtual unsigned int GetPixelSize(){
228                 return 2;
229         }
230 };
231
232 static void KTX_DecodeETC1( PointerInputStream& istream, Image& image ){
233         unsigned int width = image.getWidth(), height = image.getHeight();
234         unsigned int stride = width * 4;
235         byte* pixbuf = image.getRGBAPixels();
236         byte etc[8], rgba[64];
237
238         for ( unsigned int y = 0; y < height; y += 4, pixbuf += stride * 4 )
239         {
240                 unsigned int blockrows = height - y;
241                 if ( blockrows > 4 ) {
242                         blockrows = 4;
243                 }
244
245                 byte* p = pixbuf;
246                 for ( unsigned int x = 0; x < width; x += 4, p += 16 )
247                 {
248                         istream.read( etc, 8 );
249                         ETC_DecodeETC1Block( etc, rgba, qtrue );
250
251                         unsigned int blockrowsize = width - x;
252                         if ( blockrowsize > 4 ) {
253                                 blockrowsize = 4;
254                         }
255                         blockrowsize *= 4;
256                         for ( unsigned int blockrow = 0; blockrow < blockrows; blockrow++ )
257                         {
258                                 memcpy( p + blockrow * stride, rgba + blockrow * 16, blockrowsize );
259                         }
260                 }
261         }
262 }
263
264 Image* LoadKTXBuff( PointerInputStream& istream ){
265         byte identifier[12];
266         istream.read( identifier, 12 );
267         if ( memcmp( identifier, "\xABKTX 11\xBB\r\n\x1A\n", 12 ) ) {
268                 globalErrorStream() << "LoadKTX: Image has the wrong identifier\n";
269                 return 0;
270         }
271
272         bool bigEndian = ( istream_read_uint32_le( istream ) == 0x01020304 );
273
274         unsigned int type;
275         if ( bigEndian ) {
276                 type = istream_read_uint32_be( istream );
277         }
278         else {
279                 type = istream_read_uint32_le( istream );
280         }
281
282         // For compressed textures, the format is in glInternalFormat.
283         // For uncompressed textures, it's in glBaseInternalFormat.
284         istream.seek( ( type ? 3 : 2 ) * sizeof( uint32_t ) );
285         unsigned int format;
286         if ( bigEndian ) {
287                 format = istream_read_uint32_be( istream );
288         }
289         else {
290                 format = istream_read_uint32_le( istream );
291         }
292         if ( !type ) {
293                 istream.seek( sizeof( uint32_t ) );
294         }
295
296         unsigned int width, height;
297         if ( bigEndian ) {
298                 width = istream_read_uint32_be( istream );
299                 height = istream_read_uint32_be( istream );
300         }
301         else {
302                 width = istream_read_uint32_le( istream );
303                 height = istream_read_uint32_le( istream );
304         }
305         if ( !width ) {
306                 globalErrorStream() << "LoadKTX: Image has zero width\n";
307                 return 0;
308         }
309         if ( !height ) {
310                 height = 1;
311         }
312
313         // Skip the key/values and load the first 2D image in the texture.
314         // Since KTXorientation is only a hint and has no effect on the texture data and coordinates, it must be ignored.
315         istream.seek( 4 * sizeof( uint32_t ) );
316         unsigned int bytesOfKeyValueData;
317         if ( bigEndian ) {
318                 bytesOfKeyValueData = istream_read_uint32_be( istream );
319         }
320         else {
321                 bytesOfKeyValueData = istream_read_uint32_le( istream );
322         }
323         istream.seek( bytesOfKeyValueData + sizeof( uint32_t ) );
324
325         RGBAImage* image = new RGBAImage( width, height );
326
327         if ( type ) {
328                 KTX_Decoder* decoder = NULL;
329                 switch ( type )
330                 {
331                 case KTX_TYPE_UNSIGNED_BYTE:
332                         switch ( format )
333                         {
334                         case KTX_FORMAT_ALPHA:
335                                 decoder = new KTX_Decoder_A8();
336                                 break;
337                         case KTX_FORMAT_RGB:
338                                 decoder = new KTX_Decoder_RGB8();
339                                 break;
340                         case KTX_FORMAT_RGBA:
341                                 decoder = new KTX_Decoder_RGBA8();
342                                 break;
343                         case KTX_FORMAT_LUMINANCE:
344                                 decoder = new KTX_Decoder_L8();
345                                 break;
346                         case KTX_FORMAT_LUMINANCE_ALPHA:
347                                 decoder = new KTX_Decoder_LA8();
348                                 break;
349                         case KTX_FORMAT_BGR:
350                                 decoder = new KTX_Decoder_BGR8();
351                                 break;
352                         case KTX_FORMAT_BGRA:
353                                 decoder = new KTX_Decoder_BGRA8();
354                                 break;
355                         }
356                         break;
357                 case KTX_TYPE_UNSIGNED_SHORT_4_4_4_4:
358                         if ( format == KTX_FORMAT_RGBA ) {
359                                 decoder = new KTX_Decoder_RGBA4( bigEndian );
360                         }
361                         break;
362                 case KTX_TYPE_UNSIGNED_SHORT_5_5_5_1:
363                         if ( format == KTX_FORMAT_RGBA ) {
364                                 decoder = new KTX_Decoder_RGBA5( bigEndian );
365                         }
366                         break;
367                 case KTX_TYPE_UNSIGNED_SHORT_5_6_5:
368                         if ( format == KTX_FORMAT_RGB ) {
369                                 decoder = new KTX_Decoder_RGB5( bigEndian );
370                         }
371                         break;
372                 }
373
374                 if ( !decoder ) {
375                         globalErrorStream() << "LoadKTX: Image has an unsupported pixel type " << type << " or format " << format << "\n";
376                         image->release();
377                         return 0;
378                 }
379
380                 unsigned int inRowLength = width * decoder->GetPixelSize();
381                 unsigned int inPadding = ( ( inRowLength + 3 ) & ~3 ) - inRowLength;
382                 byte* out = image->getRGBAPixels();
383                 for ( unsigned int y = 0; y < height; y++ )
384                 {
385                         for ( unsigned int x = 0; x < width; x++, out += 4 )
386                         {
387                                 decoder->Decode( istream, out );
388                         }
389
390                         if ( inPadding ) {
391                                 istream.seek( inPadding );
392                         }
393                 }
394
395                 delete decoder;
396         }
397         else {
398                 switch ( format )
399                 {
400                 case KTX_FORMAT_ETC1_RGB8:
401                         KTX_DecodeETC1( istream, *image );
402                         break;
403                 default:
404                         globalErrorStream() << "LoadKTX: Image has an unsupported compressed format " << format << "\n";
405                         image->release();
406                         return 0;
407                 }
408         }
409
410         return image;
411 }
412
413 Image* LoadKTX( ArchiveFile& file ){
414         ScopedArchiveBuffer buffer( file );
415         PointerInputStream istream( buffer.buffer );
416         return LoadKTXBuff( istream );
417 }