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