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