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