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