]> git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/image/tga.cpp
reformat code! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / plugins / image / tga.cpp
1 /*
2    Copyright (C) 2001-2006, William Joseph.
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 "tga.h"
23
24 #include "ifilesystem.h"
25 #include "iarchive.h"
26 #include "idatastream.h"
27
28 typedef unsigned char byte;
29
30 #include <stdlib.h>
31
32 #include "generic/bitfield.h"
33 #include "imagelib.h"
34 #include "bytestreamutils.h"
35
36 // represents x,y origin of tga image being decoded
37 class Flip00 {}; // no flip
38 class Flip01 {}; // vertical flip only
39 class Flip10 {}; // horizontal flip only
40 class Flip11 {}; // both
41
42 template<typename PixelDecoder>
43 void image_decode(PointerInputStream &istream, PixelDecoder &decode, RGBAImage &image, const Flip00 &)
44 {
45     RGBAPixel *end = image.pixels + (image.height * image.width);
46     for (RGBAPixel *row = end; row != image.pixels; row -= image.width) {
47         for (RGBAPixel *pixel = row - image.width; pixel != row; ++pixel) {
48             decode(istream, *pixel);
49         }
50     }
51 }
52
53 template<typename PixelDecoder>
54 void image_decode(PointerInputStream &istream, PixelDecoder &decode, RGBAImage &image, const Flip01 &)
55 {
56     RGBAPixel *end = image.pixels + (image.height * image.width);
57     for (RGBAPixel *row = image.pixels; row != end; row += image.width) {
58         for (RGBAPixel *pixel = row; pixel != row + image.width; ++pixel) {
59             decode(istream, *pixel);
60         }
61     }
62 }
63
64 template<typename PixelDecoder>
65 void image_decode(PointerInputStream &istream, PixelDecoder &decode, RGBAImage &image, const Flip10 &)
66 {
67     RGBAPixel *end = image.pixels + (image.height * image.width);
68     for (RGBAPixel *row = end; row != image.pixels; row -= image.width) {
69         for (RGBAPixel *pixel = row; pixel != row - image.width;) {
70             decode(istream, *--pixel);
71         }
72     }
73 }
74
75 template<typename PixelDecoder>
76 void image_decode(PointerInputStream &istream, PixelDecoder &decode, RGBAImage &image, const Flip11 &)
77 {
78     RGBAPixel *end = image.pixels + (image.height * image.width);
79     for (RGBAPixel *row = image.pixels; row != end; row += image.width) {
80         for (RGBAPixel *pixel = row + image.width; pixel != row;) {
81             decode(istream, *--pixel);
82         }
83     }
84 }
85
86 inline void istream_read_gray(PointerInputStream &istream, RGBAPixel &pixel)
87 {
88     istream.read(&pixel.blue, 1);
89     pixel.red = pixel.green = pixel.blue;
90     pixel.alpha = 0xff;
91 }
92
93 inline void istream_read_rgb(PointerInputStream &istream, RGBAPixel &pixel)
94 {
95     istream.read(&pixel.blue, 1);
96     istream.read(&pixel.green, 1);
97     istream.read(&pixel.red, 1);
98     pixel.alpha = 0xff;
99 }
100
101 inline void istream_read_rgba(PointerInputStream &istream, RGBAPixel &pixel)
102 {
103     istream.read(&pixel.blue, 1);
104     istream.read(&pixel.green, 1);
105     istream.read(&pixel.red, 1);
106     istream.read(&pixel.alpha, 1);
107 }
108
109 class TargaDecodeGrayPixel {
110 public:
111     void operator()(PointerInputStream &istream, RGBAPixel &pixel)
112     {
113         istream_read_gray(istream, pixel);
114     }
115 };
116
117 template<typename Flip>
118 void targa_decode_grayscale(PointerInputStream &istream, RGBAImage &image, const Flip &flip)
119 {
120     TargaDecodeGrayPixel decode;
121     image_decode(istream, decode, image, flip);
122 }
123
124 class TargaDecodeRGBPixel {
125 public:
126     void operator()(PointerInputStream &istream, RGBAPixel &pixel)
127     {
128         istream_read_rgb(istream, pixel);
129     }
130 };
131
132 template<typename Flip>
133 void targa_decode_rgb(PointerInputStream &istream, RGBAImage &image, const Flip &flip)
134 {
135     TargaDecodeRGBPixel decode;
136     image_decode(istream, decode, image, flip);
137 }
138
139 class TargaDecodeRGBAPixel {
140 public:
141     void operator()(PointerInputStream &istream, RGBAPixel &pixel)
142     {
143         istream_read_rgba(istream, pixel);
144     }
145 };
146
147 template<typename Flip>
148 void targa_decode_rgba(PointerInputStream &istream, RGBAImage &image, const Flip &flip)
149 {
150     TargaDecodeRGBAPixel decode;
151     image_decode(istream, decode, image, flip);
152 }
153
154 typedef byte TargaPacket;
155 typedef byte TargaPacketSize;
156
157 inline void targa_packet_read_istream(TargaPacket &packet, PointerInputStream &istream)
158 {
159     istream.read(&packet, 1);
160 }
161
162 inline bool targa_packet_is_rle(const TargaPacket &packet)
163 {
164     return (packet & 0x80) != 0;
165 }
166
167 inline TargaPacketSize targa_packet_size(const TargaPacket &packet)
168 {
169     return 1 + (packet & 0x7f);
170 }
171
172
173 class TargaDecodeGrayPixelRLE {
174     TargaPacketSize m_packetSize;
175     RGBAPixel m_pixel;
176     TargaPacket m_packet;
177 public:
178     TargaDecodeGrayPixelRLE() : m_packetSize(0)
179     {
180     }
181
182     void operator()(PointerInputStream &istream, RGBAPixel &pixel)
183     {
184         if (m_packetSize == 0) {
185             targa_packet_read_istream(m_packet, istream);
186             m_packetSize = targa_packet_size(m_packet);
187
188             if (targa_packet_is_rle(m_packet)) {
189                 istream_read_gray(istream, m_pixel);
190             }
191         }
192
193         if (targa_packet_is_rle(m_packet)) {
194             pixel = m_pixel;
195         } else {
196             istream_read_gray(istream, pixel);
197         }
198
199         --m_packetSize;
200     }
201 };
202
203 template<typename Flip>
204 void targa_decode_rle_grayscale(PointerInputStream &istream, RGBAImage &image, const Flip &flip)
205 {
206     TargaDecodeGrayPixelRLE decode;
207     image_decode(istream, decode, image, flip);
208 }
209
210 class TargaDecodeRGBPixelRLE {
211     TargaPacketSize m_packetSize;
212     RGBAPixel m_pixel;
213     TargaPacket m_packet;
214 public:
215     TargaDecodeRGBPixelRLE() : m_packetSize(0)
216     {
217     }
218
219     void operator()(PointerInputStream &istream, RGBAPixel &pixel)
220     {
221         if (m_packetSize == 0) {
222             targa_packet_read_istream(m_packet, istream);
223             m_packetSize = targa_packet_size(m_packet);
224
225             if (targa_packet_is_rle(m_packet)) {
226                 istream_read_rgb(istream, m_pixel);
227             }
228         }
229
230         if (targa_packet_is_rle(m_packet)) {
231             pixel = m_pixel;
232         } else {
233             istream_read_rgb(istream, pixel);
234         }
235
236         --m_packetSize;
237     }
238 };
239
240 template<typename Flip>
241 void targa_decode_rle_rgb(PointerInputStream &istream, RGBAImage &image, const Flip &flip)
242 {
243     TargaDecodeRGBPixelRLE decode;
244     image_decode(istream, decode, image, flip);
245 }
246
247 class TargaDecodeRGBAPixelRLE {
248     TargaPacketSize m_packetSize;
249     RGBAPixel m_pixel;
250     TargaPacket m_packet;
251 public:
252     TargaDecodeRGBAPixelRLE() : m_packetSize(0)
253     {
254     }
255
256     void operator()(PointerInputStream &istream, RGBAPixel &pixel)
257     {
258         if (m_packetSize == 0) {
259             targa_packet_read_istream(m_packet, istream);
260             m_packetSize = targa_packet_size(m_packet);
261
262             if (targa_packet_is_rle(m_packet)) {
263                 istream_read_rgba(istream, m_pixel);
264             }
265         }
266
267         if (targa_packet_is_rle(m_packet)) {
268             pixel = m_pixel;
269         } else {
270             istream_read_rgba(istream, pixel);
271         }
272
273         --m_packetSize;
274     }
275 };
276
277 template<typename Flip>
278 void targa_decode_rle_rgba(PointerInputStream &istream, RGBAImage &image, const Flip &flip)
279 {
280     TargaDecodeRGBAPixelRLE decode;
281     image_decode(istream, decode, image, flip);
282 }
283
284 struct TargaHeader {
285     unsigned char id_length, colormap_type, image_type;
286     unsigned short colormap_index, colormap_length;
287     unsigned char colormap_size;
288     unsigned short x_origin, y_origin, width, height;
289     unsigned char pixel_size, attributes;
290 };
291
292 inline void targa_header_read_istream(TargaHeader &targa_header, PointerInputStream &istream)
293 {
294     targa_header.id_length = istream_read_byte(istream);
295     targa_header.colormap_type = istream_read_byte(istream);
296     targa_header.image_type = istream_read_byte(istream);
297
298     targa_header.colormap_index = istream_read_int16_le(istream);
299     targa_header.colormap_length = istream_read_int16_le(istream);
300     targa_header.colormap_size = istream_read_byte(istream);
301     targa_header.x_origin = istream_read_int16_le(istream);
302     targa_header.y_origin = istream_read_int16_le(istream);
303     targa_header.width = istream_read_int16_le(istream);
304     targa_header.height = istream_read_int16_le(istream);
305     targa_header.pixel_size = istream_read_byte(istream);
306     targa_header.attributes = istream_read_byte(istream);
307
308     if (targa_header.id_length != 0) {
309         istream.seek(targa_header.id_length); // skip TARGA image comment
310     }
311 }
312
313 template<typename Type>
314 class ScopeDelete {
315     Type *m_value;
316
317     ScopeDelete(const ScopeDelete &);
318
319     ScopeDelete &operator=(const ScopeDelete &);
320
321 public:
322     ScopeDelete(Type *value) : m_value(value)
323     {
324     }
325
326     ~ScopeDelete()
327     {
328         delete m_value;
329     }
330
331     Type *get_pointer() const
332     {
333         return m_value;
334     }
335 };
336
337 template<typename Flip>
338 Image *Targa_decodeImageData(const TargaHeader &targa_header, PointerInputStream &istream, const Flip &flip)
339 {
340     RGBAImage *image = new RGBAImage(targa_header.width, targa_header.height);
341
342     if (targa_header.image_type == 2 || targa_header.image_type == 3) {
343         switch (targa_header.pixel_size) {
344             case 8:
345                 targa_decode_grayscale(istream, *image, flip);
346                 break;
347             case 24:
348                 targa_decode_rgb(istream, *image, flip);
349                 break;
350             case 32:
351                 targa_decode_rgba(istream, *image, flip);
352                 break;
353             default:
354                 globalErrorStream() << "LoadTGA: illegal pixel_size '" << targa_header.pixel_size << "'\n";
355                 image->release();
356                 return 0;
357         }
358     } else if (targa_header.image_type == 10 || targa_header.image_type == 11) {
359         switch (targa_header.pixel_size) {
360             case 8:
361                 targa_decode_rle_grayscale(istream, *image, flip);
362                 break;
363             case 24:
364                 targa_decode_rle_rgb(istream, *image, flip);
365                 break;
366             case 32:
367                 targa_decode_rle_rgba(istream, *image, flip);
368                 break;
369             default:
370                 globalErrorStream() << "LoadTGA: illegal pixel_size '" << targa_header.pixel_size << "'\n";
371                 image->release();
372                 return 0;
373         }
374     }
375
376     return image;
377 }
378
379 const unsigned int TGA_FLIP_HORIZONTAL = 0x10;
380 const unsigned int TGA_FLIP_VERTICAL = 0x20;
381
382 Image *LoadTGABuff(const byte *buffer)
383 {
384     PointerInputStream istream(buffer);
385     TargaHeader targa_header;
386
387     targa_header_read_istream(targa_header, istream);
388
389     if (targa_header.image_type != 2 && targa_header.image_type != 10 && targa_header.image_type != 3 &&
390         targa_header.image_type != 11) {
391         globalErrorStream() << "LoadTGA: TGA type " << targa_header.image_type << " not supported\n";
392         globalErrorStream() << "LoadTGA: Only type 2 (RGB), 3 (gray), 10 (RGB), and 11 (gray) TGA images supported\n";
393         return 0;
394     }
395
396     if (targa_header.colormap_type != 0) {
397         globalErrorStream() << "LoadTGA: colormaps not supported\n";
398         return 0;
399     }
400
401     if (((targa_header.image_type == 2 || targa_header.image_type == 10) && targa_header.pixel_size != 32 &&
402          targa_header.pixel_size != 24) ||
403         ((targa_header.image_type == 3 || targa_header.image_type == 11) && targa_header.pixel_size != 8)) {
404         globalErrorStream() << "LoadTGA: Only 32, 24 or 8 bit images supported\n";
405         return 0;
406     }
407
408     if (!bitfield_enabled(targa_header.attributes, TGA_FLIP_HORIZONTAL)
409         && !bitfield_enabled(targa_header.attributes, TGA_FLIP_VERTICAL)) {
410         return Targa_decodeImageData(targa_header, istream, Flip00());
411     }
412     if (!bitfield_enabled(targa_header.attributes, TGA_FLIP_HORIZONTAL)
413         && bitfield_enabled(targa_header.attributes, TGA_FLIP_VERTICAL)) {
414         return Targa_decodeImageData(targa_header, istream, Flip01());
415     }
416     if (bitfield_enabled(targa_header.attributes, TGA_FLIP_HORIZONTAL)
417         && !bitfield_enabled(targa_header.attributes, TGA_FLIP_VERTICAL)) {
418         return Targa_decodeImageData(targa_header, istream, Flip10());
419     }
420     if (bitfield_enabled(targa_header.attributes, TGA_FLIP_HORIZONTAL)
421         && bitfield_enabled(targa_header.attributes, TGA_FLIP_VERTICAL)) {
422         return Targa_decodeImageData(targa_header, istream, Flip11());
423     }
424
425     // unreachable
426     return 0;
427 }
428
429 Image *LoadTGA(ArchiveFile &file)
430 {
431     ScopedArchiveBuffer buffer(file);
432     return LoadTGABuff(buffer.buffer);
433 }