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