CMake: Make dll bundling optional
[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 TargaDecodeGrayPixelRLE
169 {
170 TargaPacketSize m_packetSize;
171 RGBAPixel m_pixel;
172 TargaPacket m_packet;
173 public:
174 TargaDecodeGrayPixelRLE() : 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_gray( 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_gray( istream, pixel );
192         }
193
194         --m_packetSize;
195 }
196 };
197
198 template<typename Flip>
199 void targa_decode_rle_grayscale( PointerInputStream& istream, RGBAImage& image, const Flip& flip ){
200         TargaDecodeGrayPixelRLE decode;
201         image_decode( istream, decode, image, flip );
202 }
203
204 class TargaDecodeRGBPixelRLE
205 {
206 TargaPacketSize m_packetSize;
207 RGBAPixel m_pixel;
208 TargaPacket m_packet;
209 public:
210 TargaDecodeRGBPixelRLE() : 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_rgb( 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_rgb( istream, pixel );
228         }
229
230         --m_packetSize;
231 }
232 };
233
234 template<typename Flip>
235 void targa_decode_rle_rgb( PointerInputStream& istream, RGBAImage& image, const Flip& flip ){
236         TargaDecodeRGBPixelRLE decode;
237         image_decode( istream, decode, image, flip );
238 }
239
240 class TargaDecodeRGBAPixelRLE
241 {
242 TargaPacketSize m_packetSize;
243 RGBAPixel m_pixel;
244 TargaPacket m_packet;
245 public:
246 TargaDecodeRGBAPixelRLE() : m_packetSize( 0 ){
247 }
248 void operator()( PointerInputStream& istream, RGBAPixel& pixel ){
249         if ( m_packetSize == 0 ) {
250                 targa_packet_read_istream( m_packet, istream );
251                 m_packetSize = targa_packet_size( m_packet );
252
253                 if ( targa_packet_is_rle( m_packet ) ) {
254                         istream_read_rgba( istream, m_pixel );
255                 }
256         }
257
258         if ( targa_packet_is_rle( m_packet ) ) {
259                 pixel = m_pixel;
260         }
261         else
262         {
263                 istream_read_rgba( istream, pixel );
264         }
265
266         --m_packetSize;
267 }
268 };
269
270 template<typename Flip>
271 void targa_decode_rle_rgba( PointerInputStream& istream, RGBAImage& image, const Flip& flip ){
272         TargaDecodeRGBAPixelRLE decode;
273         image_decode( istream, decode, image, flip );
274 }
275
276 struct TargaHeader
277 {
278         unsigned char id_length, colormap_type, image_type;
279         unsigned short colormap_index, colormap_length;
280         unsigned char colormap_size;
281         unsigned short x_origin, y_origin, width, height;
282         unsigned char pixel_size, attributes;
283 };
284
285 inline void targa_header_read_istream( TargaHeader& targa_header, PointerInputStream& istream ){
286         targa_header.id_length = istream_read_byte( istream );
287         targa_header.colormap_type = istream_read_byte( istream );
288         targa_header.image_type = istream_read_byte( istream );
289
290         targa_header.colormap_index = istream_read_int16_le( istream );
291         targa_header.colormap_length = istream_read_int16_le( istream );
292         targa_header.colormap_size = istream_read_byte( istream );
293         targa_header.x_origin = istream_read_int16_le( istream );
294         targa_header.y_origin = istream_read_int16_le( istream );
295         targa_header.width = istream_read_int16_le( istream );
296         targa_header.height = istream_read_int16_le( istream );
297         targa_header.pixel_size = istream_read_byte( istream );
298         targa_header.attributes = istream_read_byte( istream );
299
300         if ( targa_header.id_length != 0 ) {
301                 istream.seek( targa_header.id_length ); // skip TARGA image comment
302         }
303 }
304
305 template<typename Type>
306 class ScopeDelete
307 {
308 Type* m_value;
309 ScopeDelete( const ScopeDelete& );
310 ScopeDelete& operator=( const ScopeDelete& );
311 public:
312 ScopeDelete( Type* value ) : m_value( value ){
313 }
314 ~ScopeDelete(){
315         delete m_value;
316 }
317 Type* get_pointer() const {
318         return m_value;
319 }
320 };
321
322 template<typename Flip>
323 Image* Targa_decodeImageData( const TargaHeader& targa_header, PointerInputStream& istream, const Flip& flip ){
324         RGBAImage* image = new RGBAImage( targa_header.width, targa_header.height );
325
326         if ( targa_header.image_type == 2 || targa_header.image_type == 3 ) {
327                 switch ( targa_header.pixel_size )
328                 {
329                 case 8:
330                         targa_decode_grayscale( istream, *image, flip );
331                         break;
332                 case 24:
333                         targa_decode_rgb( istream, *image, flip );
334                         break;
335                 case 32:
336                         targa_decode_rgba( istream, *image, flip );
337                         break;
338                 default:
339                         globalErrorStream() << "LoadTGA: illegal pixel_size '" << targa_header.pixel_size << "'\n";
340                         image->release();
341                         return 0;
342                 }
343         }
344         else if ( targa_header.image_type == 10 || targa_header.image_type == 11 ) {
345                 switch ( targa_header.pixel_size )
346                 {
347                 case 8:
348                         targa_decode_rle_grayscale( istream, *image, flip );
349                         break;
350                 case 24:
351                         targa_decode_rle_rgb( istream, *image, flip );
352                         break;
353                 case 32:
354                         targa_decode_rle_rgba( istream, *image, flip );
355                         break;
356                 default:
357                         globalErrorStream() << "LoadTGA: illegal pixel_size '" << targa_header.pixel_size << "'\n";
358                         image->release();
359                         return 0;
360                 }
361         }
362
363         return image;
364 }
365
366 const unsigned int TGA_FLIP_HORIZONTAL = 0x10;
367 const unsigned int TGA_FLIP_VERTICAL = 0x20;
368
369 Image* LoadTGABuff( const byte* buffer ){
370         PointerInputStream istream( buffer );
371         TargaHeader targa_header;
372
373         targa_header_read_istream( targa_header, istream );
374
375         if ( targa_header.image_type != 2 && targa_header.image_type != 10 && targa_header.image_type != 3 && targa_header.image_type != 11 ) {
376                 globalErrorStream() << "LoadTGA: TGA type " << targa_header.image_type << " not supported\n";
377                 globalErrorStream() << "LoadTGA: Only type 2 (RGB), 3 (gray), 10 (RGB), and 11 (gray) TGA images supported\n";
378                 return 0;
379         }
380
381         if ( targa_header.colormap_type != 0 ) {
382                 globalErrorStream() << "LoadTGA: colormaps not supported\n";
383                 return 0;
384         }
385
386         if ( ( ( targa_header.image_type == 2 || targa_header.image_type == 10 ) && targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) ||
387              ( ( targa_header.image_type == 3 || targa_header.image_type == 11 ) && targa_header.pixel_size != 8 ) ) {
388                 globalErrorStream() << "LoadTGA: Only 32, 24 or 8 bit images supported\n";
389                 return 0;
390         }
391
392         if ( !bitfield_enabled( targa_header.attributes, TGA_FLIP_HORIZONTAL )
393                  && !bitfield_enabled( targa_header.attributes, TGA_FLIP_VERTICAL ) ) {
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                 return Targa_decodeImageData( targa_header, istream, Flip01() );
399         }
400         if ( bitfield_enabled( targa_header.attributes, TGA_FLIP_HORIZONTAL )
401                  && !bitfield_enabled( targa_header.attributes, TGA_FLIP_VERTICAL ) ) {
402                 return Targa_decodeImageData( targa_header, istream, Flip10() );
403         }
404         if ( bitfield_enabled( targa_header.attributes, TGA_FLIP_HORIZONTAL )
405                  && bitfield_enabled( targa_header.attributes, TGA_FLIP_VERTICAL ) ) {
406                 return Targa_decodeImageData( targa_header, istream, Flip11() );
407         }
408
409         // unreachable
410         return 0;
411 }
412
413 Image* LoadTGA( ArchiveFile& file ){
414         ScopedArchiveBuffer buffer( file );
415         return LoadTGABuff( buffer.buffer );
416 }