1 // ==========================================================
2 // TARGA Loader and Writer
3 //
4 // Design and implementation by
5 // - Floris van den Berg (flvdberg@wxs.nl)
6 // - Jani Kajala (janik@remedy.fi)
7 // - Martin Weber (martweb@gmx.net)
8 // - Machiel ten Brinke (brinkem@uni-one.nl)
9 // - Peter Lemmens (peter.lemmens@planetinternet.be)
10 // - Hervé Drolon (drolon@infonie.fr)
11 // - Mihail Naydenov (mnaydenov@users.sourceforge.net)
12 //
13 // This file is part of FreeImage 3
14 //
15 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
16 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
17 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
18 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
19 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
20 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
21 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
22 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
23 // THIS DISCLAIMER.
24 //
25 // Use at your own risk!
26 // ==========================================================
27 
28 #include "FreeImage.h"
29 #include "Utilities.h"
30 
31 // ----------------------------------------------------------
32 //   Constants + headers
33 // ----------------------------------------------------------
34 
35 #ifdef _WIN32
36 #pragma pack(push, 1)
37 #else
38 #pragma pack(1)
39 #endif
40 
41 typedef struct tagTGAHEADER {
42 	BYTE id_length;				//! length of the image ID field
43 	BYTE color_map_type;		//! whether a color map is included
44 	BYTE image_type;			//! compression and color types
45 
46 	WORD cm_first_entry;		//! first entry index (offset into the color map table)
47 	WORD cm_length;				//! color map length (number of entries)
48 	BYTE cm_size;				//! color map entry size, in bits (number of bits per pixel)
49 
50 	WORD is_xorigin;			//! X-origin of image (absolute coordinate of lower-left corner for displays where origin is at the lower left)
51 	WORD is_yorigin;			//! Y-origin of image (as for X-origin)
52 	WORD is_width;				//! image width
53 	WORD is_height;				//! image height
54 	BYTE is_pixel_depth;		//! bits per pixel
55 	BYTE is_image_descriptor;	//! image descriptor, bits 3-0 give the alpha channel depth, bits 5-4 give direction
56 } TGAHEADER;
57 
58 typedef struct tagTGAEXTENSIONAREA {
59 	WORD extension_size;		// Size in bytes of the extension area, always 495
60 	char author_name[41];		// Name of the author. If not used, bytes should be set to NULL (\0) or spaces
61 	char author_comments[324];	// A comment, organized as four lines, each consisting of 80 characters plus a NULL
62 	WORD datetime_stamp[6];		// Date and time at which the image was created
63 	char job_name[41];			// Job ID
64 	WORD job_time[3];			// Hours, minutes and seconds spent creating the file (for billing, etc.)
65 	char software_id[41];		// The application that created the file
66 	BYTE software_version[3];
67 	DWORD key_color;
68 	WORD pixel_aspect_ratio[2];
69 	WORD gamma_value[2];
70 	DWORD color_correction_offset;	// Number of bytes from the beginning of the file to the color correction table if present
71 	DWORD postage_stamp_offset;		// Number of bytes from the beginning of the file to the postage stamp image if present
72 	DWORD scan_line_offset;			// Number of bytes from the beginning of the file to the scan lines table if present
73 	BYTE attributes_type;			// Specifies the alpha channel
74 } TGAEXTENSIONAREA;
75 
76 typedef struct tagTGAFOOTER {
77 	DWORD extension_offset;	// extension area offset : offset in bytes from the beginning of the file
78 	DWORD developer_offset;	// developer directory offset : offset in bytes from the beginning of the file
79 	char signature[18];		// signature string : contains "TRUEVISION-XFILE.\0"
80 } TGAFOOTER;
81 
82 #ifdef _WIN32
83 #pragma pack(pop)
84 #else
85 #pragma pack()
86 #endif
87 
88 static const char *FI_MSG_ERROR_CORRUPTED = "Image data corrupted";
89 
90 // ----------------------------------------------------------
91 // Image type
92 //
93 #define TGA_NULL		0	// no image data included
94 #define TGA_CMAP		1	// uncompressed, color-mapped image
95 #define TGA_RGB			2	// uncompressed, true-color image
96 #define TGA_MONO		3	// uncompressed, black-and-white image
97 #define TGA_RLECMAP		9	// run-length encoded, color-mapped image
98 #define TGA_RLERGB		10	// run-length encoded, true-color image
99 #define TGA_RLEMONO		11	// run-length encoded, black-and-white image
100 #define TGA_CMPCMAP		32	// compressed (Huffman/Delta/RLE) color-mapped image (e.g., VDA/D) - Obsolete
101 #define TGA_CMPCMAP4	33	// compressed (Huffman/Delta/RLE) color-mapped four pass image (e.g., VDA/D) - Obsolete
102 
103 // ==========================================================
104 // Thumbnail functions
105 // ==========================================================
106 
107 class TargaThumbnail
108 {
109 public:
TargaThumbnail()110 	TargaThumbnail() : _w(0), _h(0), _depth(0), _data(NULL) {
111 	}
~TargaThumbnail()112 	~TargaThumbnail() {
113 		if(_data) {
114 			free(_data);
115 		}
116 	}
117 
isNull() const118 	BOOL isNull() const {
119 		return (_data == NULL);
120 	}
121 
read(FreeImageIO * io,fi_handle handle,size_t size)122 	BOOL read(FreeImageIO *io, fi_handle handle, size_t size) {
123 		io->read_proc(&_w, 1, 1, handle);
124 		io->read_proc(&_h, 1, 1, handle);
125 
126 		const size_t sizeofData = size - 2;
127 		_data = (BYTE*)malloc(sizeofData);
128 		if(_data) {
129 			return (io->read_proc(_data, 1, (unsigned)sizeofData, handle) == sizeofData);
130 		}
131 		return FALSE;
132 	}
133 
setDepth(BYTE dp)134 	void setDepth(BYTE dp) {
135 		_depth = dp;
136 	}
137 
138 	FIBITMAP* toFIBITMAP();
139 
140 private:
141 	BYTE _w;
142 	BYTE _h;
143 	BYTE _depth;
144 	BYTE* _data;
145 };
146 
147 #ifdef FREEIMAGE_BIGENDIAN
148 static void
swapShortPixels(FIBITMAP * dib)149 swapShortPixels(FIBITMAP* dib) {
150 	if(FreeImage_GetImageType(dib) != FIT_BITMAP) {
151 		return;
152 	}
153 
154 	const unsigned Bpp = FreeImage_GetBPP(dib)/8;
155 	if(Bpp != 2) {
156 		return;
157 	}
158 
159 	BYTE* bits = FreeImage_GetBits(dib);
160 	const unsigned height = FreeImage_GetHeight(dib);
161 	const unsigned pitch = FreeImage_GetPitch(dib);
162 
163 	BYTE* line = bits;
164 	for(unsigned y = 0; y < height; y++, line += pitch) {
165 		for(BYTE* pixel = line; pixel < line + pitch ; pixel += Bpp) {
166 			SwapShort((WORD*)pixel);
167 		}
168 	}
169 }
170 #endif // FREEIMAGE_BIGENDIAN
171 
toFIBITMAP()172 FIBITMAP* TargaThumbnail::toFIBITMAP() {
173 	if(isNull() || _depth == 0) {
174 		return NULL;
175 	}
176 
177 	const unsigned line_size = _depth * _w / 8;
178 	FIBITMAP* dib = FreeImage_Allocate(_w, _h, _depth);
179 	if(!dib) {
180 		return NULL;
181 	}
182 
183 	const BYTE* line = _data;
184 	const BYTE height = _h;
185 	for (BYTE h = 0; h < height; ++h, line += line_size) {
186 		BYTE* dst_line = FreeImage_GetScanLine(dib, height - 1 - h);
187 		memcpy(dst_line, line, line_size);
188 	}
189 
190 #ifdef FREEIMAGE_BIGENDIAN
191 	swapShortPixels(dib);
192 #endif
193 
194 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
195 	SwapRedBlue32(dib);
196 #endif
197 
198 	return dib;
199 }
200 // ==========================================================
201 // Internal functions
202 // ==========================================================
203 
204 /** This class is used when loading RLE compressed images, it implements io cache of fixed size.
205 	In general RLE compressed images *should* be compressed line by line with line sizes stored in Scan Line Table section.
206 	In reality, however there are images not obeying the specification, compressing image data continuously across lines,
207 	making it impossible to load the file cached at every line.
208 */
209 class IOCache
210 {
211 public:
IOCache(FreeImageIO * io,fi_handle handle,size_t size)212 	IOCache(FreeImageIO *io, fi_handle handle, size_t size) :
213 		_ptr(NULL), _begin(NULL), _end(NULL), _size(size), _io(io), _handle(handle)	{
214 			_begin = (BYTE*)malloc(size);
215 			if (_begin) {
216 			_end = _begin + _size;
217 			_ptr = _end;	// will force refill on first access
218 		}
219 	}
220 
~IOCache()221 	~IOCache() {
222 		if (_begin != NULL) {
223 			free(_begin);
224 		}
225 	}
226 
isNull()227 	BOOL isNull() { return _begin == NULL;}
228 
229 	inline
getByte()230 	BYTE getByte() {
231 		if (_ptr >= _end) {
232 			// need refill
233 			_ptr = _begin;
234 			_io->read_proc(_ptr, sizeof(BYTE), (unsigned)_size, _handle);	//### EOF - no problem?
235 		}
236 
237 		BYTE result = *_ptr;
238 
239 		_ptr++;
240 
241 		return result;
242 	}
243 
244 	inline
getBytes(size_t count)245 	BYTE* getBytes(size_t count /*must be < _size!*/) {
246 		if (_ptr + count >= _end) {
247 
248 			// need refill
249 
250 			// 'count' bytes might span two cache bounds,
251 			// SEEK back to add the remains of the current cache again into the new one
252 
253 			long read = long(_ptr - _begin);
254 			long remaining = long(_size - read);
255 
256 			_io->seek_proc(_handle, -remaining, SEEK_CUR);
257 
258 			_ptr = _begin;
259 			_io->read_proc(_ptr, sizeof(BYTE), (unsigned)_size, _handle);	//### EOF - no problem?
260 		}
261 
262 		BYTE *result = _ptr;
263 
264 		_ptr += count;
265 
266 		return result;
267 	}
268 
269 private:
270 	IOCache& operator=(const IOCache& src); // deleted
271 	IOCache(const IOCache& other); // deleted
272 
273 private:
274 	BYTE *_ptr;
275 	BYTE *_begin;
276 	BYTE *_end;
277 	const size_t _size;
278 	const FreeImageIO *_io;
279 	const fi_handle _handle;
280 };
281 
282 #ifdef FREEIMAGE_BIGENDIAN
283 static void
SwapHeader(TGAHEADER * header)284 SwapHeader(TGAHEADER *header) {
285 	SwapShort(&header->cm_first_entry);
286 	SwapShort(&header->cm_length);
287 	SwapShort(&header->is_xorigin);
288 	SwapShort(&header->is_yorigin);
289 	SwapShort(&header->is_width);
290 	SwapShort(&header->is_height);
291 }
292 
293 static void
SwapExtensionArea(TGAEXTENSIONAREA * ex)294 SwapExtensionArea(TGAEXTENSIONAREA *ex) {
295 	SwapShort(&ex->extension_size);
296 	SwapShort(&ex->datetime_stamp[0]);
297 	SwapShort(&ex->datetime_stamp[1]);
298 	SwapShort(&ex->datetime_stamp[2]);
299 	SwapShort(&ex->datetime_stamp[3]);
300 	SwapShort(&ex->datetime_stamp[4]);
301 	SwapShort(&ex->datetime_stamp[5]);
302 	SwapShort(&ex->job_time[0]);
303 	SwapShort(&ex->job_time[1]);
304 	SwapShort(&ex->job_time[2]);
305 	SwapLong (&ex->key_color);
306 	SwapShort(&ex->pixel_aspect_ratio[0]);
307 	SwapShort(&ex->pixel_aspect_ratio[1]);
308 	SwapShort(&ex->gamma_value[0]);
309 	SwapShort(&ex->gamma_value[1]);
310 	SwapLong (&ex->color_correction_offset);
311 	SwapLong (&ex->postage_stamp_offset);
312 	SwapLong (&ex->scan_line_offset);
313 }
314 
315 static void
SwapFooter(TGAFOOTER * footer)316 SwapFooter(TGAFOOTER *footer) {
317 	SwapLong(&footer->extension_offset);
318 	SwapLong(&footer->developer_offset);
319 }
320 
321 #endif // FREEIMAGE_BIGENDIAN
322 
323 // ==========================================================
324 // Plugin Interface
325 // ==========================================================
326 
327 static int s_format_id;
328 
329 // ==========================================================
330 // Plugin Implementation
331 // ==========================================================
332 
333 static const char * DLL_CALLCONV
Format()334 Format() {
335 	return "TARGA";
336 }
337 
338 static const char * DLL_CALLCONV
Description()339 Description() {
340 	return "Truevision Targa";
341 }
342 
343 static const char * DLL_CALLCONV
Extension()344 Extension() {
345 	return "tga,targa";
346 }
347 
348 static const char * DLL_CALLCONV
RegExpr()349 RegExpr() {
350 	return NULL;
351 }
352 
353 static const char * DLL_CALLCONV
MimeType()354 MimeType() {
355 	return "image/x-tga";
356 }
357 
358 static BOOL
isTARGA20(FreeImageIO * io,fi_handle handle)359 isTARGA20(FreeImageIO *io, fi_handle handle) {
360 	const unsigned sizeofSig = 18;
361 	BYTE signature[sizeofSig] = { 0 };
362 	// tga_signature = "TRUEVISION-XFILE." (TGA 2.0 only)
363 	BYTE tga_signature[sizeofSig] = { 84, 82, 85, 69, 86, 73, 83, 73, 79, 78, 45, 88, 70, 73, 76, 69, 46, 0 };
364 	// get the start offset
365 	const long start_offset = io->tell_proc(handle);
366 	// get the end-of-file
367 	io->seek_proc(handle, 0, SEEK_END);
368 	const long eof = io->tell_proc(handle);
369 	// read the signature
370 	const long start_of_signature = start_offset + eof - sizeofSig;
371 	if (start_of_signature > 0) {
372 		io->seek_proc(handle, start_of_signature, SEEK_SET);
373 		io->read_proc(&signature, 1, sizeofSig, handle);
374 	}
375 	// rewind
376 	io->seek_proc(handle, start_offset, SEEK_SET);
377 
378 	return (memcmp(tga_signature, signature, sizeofSig) == 0);
379 }
380 
381 static BOOL DLL_CALLCONV
Validate(FreeImageIO * io,fi_handle handle)382 Validate(FreeImageIO *io, fi_handle handle) {
383 	if(isTARGA20(io, handle)) {
384 		return TRUE;
385 	}
386 
387 	// not a 2.0 image, try testing if it's a valid TGA anyway (not robust)
388 	{
389 		const long start_offset = io->tell_proc(handle);
390 
391 		// get the header
392 		TGAHEADER header;
393 		if (io->read_proc(&header, sizeof(tagTGAHEADER), 1, handle) < 1) {
394 			return FALSE;
395 		}
396 #ifdef FREEIMAGE_BIGENDIAN
397 		SwapHeader(&header);
398 #endif
399 		// rewind
400 		io->seek_proc(handle, start_offset, SEEK_SET);
401 
402 		// the color map type should be a 0 or a 1...
403 		if(header.color_map_type != 0 && header.color_map_type != 1) {
404 			return FALSE;
405 		}
406 		// if the color map type is 1 then we validate the map entry information...
407 		if(header.color_map_type == 1) {
408 			// it doesn't make any sense if the first entry is larger than the color map table
409 			if(header.cm_first_entry >= header.cm_length) {
410 				return FALSE;
411 			}
412 			// check header.cm_size, don't allow 0 or anything bigger than 32
413 			if(header.cm_size == 0 || header.cm_size > 32) {
414 				return FALSE;
415 			}
416 		}
417 		// the width/height shouldn't be 0, right ?
418 		if(header.is_width == 0 || header.is_height == 0) {
419 			return FALSE;
420 		}
421 		// let's now verify all the types that are supported by FreeImage (this is our final verification)
422 		switch(header.image_type) {
423 			case TGA_CMAP:
424 			case TGA_RGB:
425 			case TGA_MONO:
426 			case TGA_RLECMAP:
427 			case TGA_RLERGB:
428 			case TGA_RLEMONO:
429 				switch(header.is_pixel_depth) {
430 					case 8	:
431 					case 16:
432 					case 24:
433 					case 32:
434 						return TRUE;
435 					default:
436 						return FALSE;
437 				}
438 				break;
439 			default:
440 				return FALSE;
441 		}
442 	}
443 }
444 
445 static BOOL DLL_CALLCONV
SupportsExportDepth(int depth)446 SupportsExportDepth(int depth) {
447 	return (
448 		(depth == 8) ||
449 		(depth == 16) ||
450 		(depth == 24) ||
451 		(depth == 32)
452 		);
453 }
454 
455 static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type)456 SupportsExportType(FREE_IMAGE_TYPE type) {
457 	return (type == FIT_BITMAP) ? TRUE : FALSE;
458 }
459 
460 static BOOL DLL_CALLCONV
SupportsNoPixels()461 SupportsNoPixels() {
462 	return TRUE;
463 }
464 
465 // ----------------------------------------------------------
466 
467 /**
468 Used for all 32 and 24 bit loading of uncompressed images
469 */
470 static void
loadTrueColor(FIBITMAP * dib,int width,int height,int file_pixel_size,FreeImageIO * io,fi_handle handle,BOOL as24bit)471 loadTrueColor(FIBITMAP* dib, int width, int height, int file_pixel_size, FreeImageIO* io, fi_handle handle, BOOL as24bit) {
472 	const int pixel_size = as24bit ? 3 : file_pixel_size;
473 
474 	// input line cache
475 	BYTE* file_line = (BYTE*)malloc( width * file_pixel_size);
476 
477 	if (!file_line) {
478 		throw FI_MSG_ERROR_MEMORY;
479 	}
480 
481 	for (int y = 0; y < height; y++) {
482 		BYTE *bits = FreeImage_GetScanLine(dib, y);
483 		io->read_proc(file_line, file_pixel_size, width, handle);
484 		BYTE *bgra = file_line;
485 
486 		for (int x = 0; x < width; x++) {
487 
488 			bits[FI_RGBA_BLUE]	= bgra[0];
489 			bits[FI_RGBA_GREEN] = bgra[1];
490 			bits[FI_RGBA_RED]	= bgra[2];
491 
492 			if (!as24bit) {
493 				bits[FI_RGBA_ALPHA] = bgra[3];
494 			}
495 
496 			bgra += file_pixel_size;
497 
498 			bits += pixel_size;
499 		}
500 	}
501 
502 	free(file_line);
503 }
504 
505 /**
506 For the generic RLE loader we need to abstract away the pixel format.
507 We use a specific overload based on bits-per-pixel for each type of pixel
508 */
509 
510 template <int nBITS>
511 inline static void
_assignPixel(BYTE * bits,BYTE * val,BOOL as24bit=FALSE)512 _assignPixel(BYTE* bits, BYTE* val, BOOL as24bit = FALSE) {
513 	// static assert should go here
514 	assert(FALSE);
515 }
516 
517 template <>
518 inline void
_assignPixel(BYTE * bits,BYTE * val,BOOL as24bit)519 _assignPixel<8>(BYTE* bits, BYTE* val, BOOL as24bit) {
520 	*bits = *val;
521 }
522 
523 template <>
524 inline void
_assignPixel(BYTE * bits,BYTE * val,BOOL as24bit)525 _assignPixel<16>(BYTE* bits, BYTE* val, BOOL as24bit) {
526 	WORD value(*reinterpret_cast<WORD*>(val));
527 
528 #ifdef FREEIMAGE_BIGENDIAN
529 	SwapShort(&value);
530 #endif
531 
532 	if (as24bit) {
533 		bits[FI_RGBA_BLUE]  = (BYTE)((((value & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
534 		bits[FI_RGBA_GREEN] = (BYTE)((((value & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
535 		bits[FI_RGBA_RED]   = (BYTE)((((value & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
536 
537 	} else {
538 		*reinterpret_cast<WORD *>(bits) = 0x7FFF & value;
539 	}
540 }
541 
542 template <>
543 inline void
_assignPixel(BYTE * bits,BYTE * val,BOOL as24bit)544 _assignPixel<24>(BYTE* bits, BYTE* val, BOOL as24bit) {
545 	bits[FI_RGBA_BLUE]	= val[0];
546 	bits[FI_RGBA_GREEN] = val[1];
547 	bits[FI_RGBA_RED]	= val[2];
548 }
549 
550 template <>
551 inline void
_assignPixel(BYTE * bits,BYTE * val,BOOL as24bit)552 _assignPixel<32>(BYTE* bits, BYTE* val, BOOL as24bit) {
553 	if (as24bit) {
554 		_assignPixel<24>(bits, val, TRUE);
555 
556 	} else {
557 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
558 		*(reinterpret_cast<unsigned*>(bits)) = *(reinterpret_cast<unsigned*> (val));
559 #else // NOTE This is faster then doing reinterpret_cast to int + INPLACESWAP !
560 		bits[FI_RGBA_BLUE]	= val[0];
561 		bits[FI_RGBA_GREEN] = val[1];
562 		bits[FI_RGBA_RED]	= val[2];
563 		bits[FI_RGBA_ALPHA]	= val[3];
564 #endif
565 	}
566 }
567 
568 /**
569 Generic RLE loader
570 */
571 template<int bPP>
572 static void
loadRLE(FIBITMAP * dib,int width,int height,FreeImageIO * io,fi_handle handle,long eof,BOOL as24bit)573 loadRLE(FIBITMAP* dib, int width, int height, FreeImageIO* io, fi_handle handle, long eof, BOOL as24bit) {
574 	const int file_pixel_size = bPP/8;
575 	const int pixel_size = as24bit ? 3 : file_pixel_size;
576 
577 	const BYTE bpp = as24bit ? 24 : bPP;
578 	const int line_size = CalculateLine(width, bpp);
579 
580 	// Note, many of the params can be computed inside the function.
581 	// However, because this is a template function, it will lead to redundant code duplication.
582 
583 	BYTE rle;
584 	BYTE *line_bits;
585 
586 	// this is used to guard against writing beyond the end of the image (on corrupted rle block)
587 	const BYTE* dib_end = FreeImage_GetScanLine(dib, height);//< one-past-end row
588 
589 	// Compute the rough size of a line...
590 	long pixels_offset = io->tell_proc(handle);
591 	long sz = ((eof - pixels_offset) / height);
592 
593 	// ...and allocate cache of this size (yields good results)
594 	IOCache cache(io, handle, sz);
595 	if(cache.isNull()) {
596 		FreeImage_Unload(dib);
597 		dib = NULL;
598 		return;
599 	}
600 
601 	int x = 0, y = 0;
602 
603 	line_bits = FreeImage_GetScanLine(dib, y);
604 
605 	while (y < height) {
606 
607 		rle = cache.getByte();
608 
609 		BOOL has_rle = rle & 0x80;
610 		rle &= ~0x80; // remove type-bit
611 
612 		BYTE packet_count = rle + 1;
613 
614 		//packet_count might be corrupt, test if we are not about to write beyond the last image bit
615 
616 		if ((line_bits+x) + packet_count*pixel_size > dib_end) {
617 			FreeImage_OutputMessageProc(s_format_id, FI_MSG_ERROR_CORRUPTED);
618 			// return what is left from the bitmap
619 			return;
620 		}
621 
622 		if (has_rle) {
623 
624 			// read a pixel value from file...
625 			BYTE *val = cache.getBytes(file_pixel_size);
626 
627 			//...and fill packet_count pixels with it
628 
629 			for (int ix = 0; ix < packet_count; ix++) {
630 				_assignPixel<bPP>((line_bits+x), val, as24bit);
631 				x += pixel_size;
632 
633 				if (x >= line_size) {
634 					x = 0;
635 					y++;
636 					line_bits = FreeImage_GetScanLine(dib, y);
637 				}
638 			}
639 
640 		} else {
641 			// no rle commpresion
642 
643 			// copy packet_count pixels from file to dib
644 			for (int ix = 0; ix < packet_count; ix++) {
645 				BYTE *val = cache.getBytes(file_pixel_size);
646 				_assignPixel<bPP>((line_bits+x), val, as24bit);
647 				x += pixel_size;
648 
649 				if (x >= line_size) {
650 					x = 0;
651 					y++;
652 					line_bits = FreeImage_GetScanLine(dib, y);
653 				}
654 			} //< packet_count
655 		} //< has_rle
656 
657 	} //< while height
658 
659 }
660 
661 // --------------------------------------------------------------------------
662 
663 static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO * io,fi_handle handle,int page,int flags,void * data)664 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
665 	FIBITMAP *dib = NULL;
666 
667 	if (!handle) {
668 		return NULL;
669 	}
670 
671 	try {
672 
673 		const BOOL header_only =  (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
674 
675 		// remember the start offset
676 		long start_offset = io->tell_proc(handle);
677 
678 		// remember end-of-file (used for RLE cache)
679 		io->seek_proc(handle, 0, SEEK_END);
680 		long eof = io->tell_proc(handle);
681 		io->seek_proc(handle, start_offset, SEEK_SET);
682 
683 		// read and process the bitmap's footer
684 
685 		TargaThumbnail thumbnail;
686 		if(isTARGA20(io, handle)) {
687 			TGAFOOTER footer;
688 			const long footer_offset = start_offset + eof - sizeof(footer);
689 
690 			io->seek_proc(handle, footer_offset, SEEK_SET);
691 			io->read_proc(&footer, sizeof(tagTGAFOOTER), 1, handle);
692 
693 #ifdef FREEIMAGE_BIGENDIAN
694 			SwapFooter(&footer);
695 #endif
696 			BOOL hasExtensionArea = footer.extension_offset > 0;
697 			if(hasExtensionArea) {
698 				TGAEXTENSIONAREA extensionarea;
699 				io->seek_proc(handle, footer.extension_offset, SEEK_SET);
700 				io->read_proc(&extensionarea, sizeof(extensionarea), 1, handle);
701 
702 #ifdef FREEIMAGE_BIGENDIAN
703 				SwapExtensionArea(&extensionarea);
704 #endif
705 
706 				DWORD postage_stamp_offset = extensionarea.postage_stamp_offset;
707 				BOOL hasThumbnail = (postage_stamp_offset > 0) && (postage_stamp_offset < (DWORD)footer_offset);
708 				if(hasThumbnail) {
709 					io->seek_proc(handle, postage_stamp_offset, SEEK_SET);
710 					thumbnail.read(io, handle, footer_offset - postage_stamp_offset);
711 				}
712 			}
713 		}
714 
715 		// read and process the bitmap's header
716 
717 		TGAHEADER header;
718 
719 		io->seek_proc(handle, start_offset, SEEK_SET);
720 		io->read_proc(&header, sizeof(tagTGAHEADER), 1, handle);
721 
722 #ifdef FREEIMAGE_BIGENDIAN
723 		SwapHeader(&header);
724 #endif
725 
726 		thumbnail.setDepth(header.is_pixel_depth);
727 
728 		const int line = CalculateLine(header.is_width, header.is_pixel_depth);
729 		const int pixel_bits = header.is_pixel_depth;
730 		const int pixel_size = pixel_bits/8;
731 
732 		int fliphoriz = (header.is_image_descriptor & 0x10) ? 1 : 0;
733 		int flipvert = (header.is_image_descriptor & 0x20) ? 1 : 0;
734 
735 		// skip comment
736 		io->seek_proc(handle, header.id_length, SEEK_CUR);
737 
738 		switch (header.is_pixel_depth) {
739 			case 8 : {
740 				dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, 8);
741 
742 				if (dib == NULL) {
743 					throw FI_MSG_ERROR_DIB_MEMORY;
744 				}
745 
746 				// read the palette (even if header only)
747 
748 				RGBQUAD *palette = FreeImage_GetPalette(dib);
749 
750 				if (header.color_map_type > 0) {
751 					unsigned count, csize;
752 
753 					// calculate the color map size
754 					csize = header.cm_length * header.cm_size / 8;
755 
756 					// read the color map
757 					BYTE *cmap = (BYTE*)malloc(csize * sizeof(BYTE));
758 					if (cmap == NULL) {
759 						throw FI_MSG_ERROR_DIB_MEMORY;
760 					}
761 					io->read_proc(cmap, sizeof(BYTE), csize, handle);
762 
763 					// build the palette
764 
765 					switch (header.cm_size) {
766 						case 16: {
767 							WORD *rgb555 = (WORD*)&cmap[0];
768 							unsigned start = (unsigned)header.cm_first_entry;
769 							unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length);
770 
771 							for (count = start; count < stop; count++) {
772 								palette[count].rgbRed   = (BYTE)((((*rgb555 & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
773 								palette[count].rgbGreen = (BYTE)((((*rgb555 & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
774 								palette[count].rgbBlue  = (BYTE)((((*rgb555 & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
775 								rgb555++;
776 							}
777 						}
778 						break;
779 
780 						case 24: {
781 							FILE_BGR *bgr = (FILE_BGR*)&cmap[0];
782 							unsigned start = (unsigned)header.cm_first_entry;
783 							unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length);
784 
785 							for (count = start; count < stop; count++) {
786 								palette[count].rgbBlue  = bgr->b;
787 								palette[count].rgbGreen = bgr->g;
788 								palette[count].rgbRed   = bgr->r;
789 								bgr++;
790 							}
791 						}
792 						break;
793 
794 						case 32: {
795 							BYTE trns[256];
796 
797 							// clear the transparency table
798 							memset(trns, 0xFF, 256);
799 
800 							FILE_BGRA *bgra = (FILE_BGRA*)&cmap[0];
801 							unsigned start = (unsigned)header.cm_first_entry;
802 							unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length);
803 
804 							for (count = start; count < stop; count++) {
805 								palette[count].rgbBlue  = bgra->b;
806 								palette[count].rgbGreen = bgra->g;
807 								palette[count].rgbRed   = bgra->r;
808 								// alpha
809 								trns[count] = bgra->a;
810 								bgra++;
811 							}
812 
813 							// set the tranparency table
814 							FreeImage_SetTransparencyTable(dib, trns, 256);
815 						}
816 						break;
817 
818 					} // switch(header.cm_size)
819 
820 					free(cmap);
821 				}
822 
823 				// handle thumbnail
824 
825 				FIBITMAP* th = thumbnail.toFIBITMAP();
826 				if(th) {
827 					RGBQUAD* pal = FreeImage_GetPalette(dib);
828 					RGBQUAD* dst_pal = FreeImage_GetPalette(th);
829 					if(dst_pal && pal) {
830 						for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) {
831 							dst_pal[i] = pal[i];
832 						}
833 					}
834 
835 					FreeImage_SetTransparencyTable(th, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
836 
837 					FreeImage_SetThumbnail(dib, th);
838 					FreeImage_Unload(th);
839 				}
840 
841 				if(header_only) {
842 					return dib;
843 				}
844 
845 				// read in the bitmap bits
846 
847 				switch (header.image_type) {
848 					case TGA_CMAP:
849 					case TGA_MONO: {
850 						BYTE *bits = NULL;
851 
852 						for (unsigned count = 0; count < header.is_height; count++) {
853 							bits = FreeImage_GetScanLine(dib, count);
854 							io->read_proc(bits, sizeof(BYTE), line, handle);
855 						}
856 					}
857 					break;
858 
859 					case TGA_RLECMAP:
860 					case TGA_RLEMONO: { //(8 bit)
861 						loadRLE<8>(dib, header.is_width, header.is_height, io, handle, eof, FALSE);
862 					}
863 					break;
864 
865 					default :
866 						FreeImage_Unload(dib);
867 						return NULL;
868 				}
869 			}
870 			break; // header.is_pixel_depth == 8
871 
872 			case 15 :
873 
874 			case 16 : {
875 				int pixel_bits = 16;
876 
877 				// allocate the dib
878 
879 				if (TARGA_LOAD_RGB888 & flags) {
880 					pixel_bits = 24;
881 					dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
882 
883 				} else {
884 					dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
885 				}
886 
887 				if (dib == NULL) {
888 					throw FI_MSG_ERROR_DIB_MEMORY;
889 				}
890 
891 				// handle thumbnail
892 
893 				FIBITMAP* th = thumbnail.toFIBITMAP();
894 				if(th) {
895 					if(TARGA_LOAD_RGB888 & flags) {
896 						FIBITMAP* t = FreeImage_ConvertTo24Bits(th);
897 						FreeImage_Unload(th);
898 						th = t;
899 					}
900 
901 					FreeImage_SetThumbnail(dib, th);
902 					FreeImage_Unload(th);
903 				}
904 
905 				if(header_only) {
906 					return dib;
907 				}
908 
909 				int line = CalculateLine(header.is_width, pixel_bits);
910 
911 				const unsigned pixel_size = unsigned(pixel_bits) / 8;
912 				const unsigned src_pixel_size = sizeof(WORD);
913 
914 				// note header.cm_size is a misleading name, it should be seen as header.cm_bits
915 				// ignore current position in file and set filepointer explicitly from the beginning of the file
916 
917 				int garblen = 0;
918 
919 				if (header.color_map_type != 0) {
920 					garblen = (int)((header.cm_size + 7) / 8) * header.cm_length; /* should byte align */
921 
922 				} else {
923 					garblen = 0;
924 				}
925 
926 				io->seek_proc(handle, start_offset, SEEK_SET);
927 				io->seek_proc(handle, sizeof(tagTGAHEADER) + header.id_length + garblen, SEEK_SET);
928 
929 				// read in the bitmap bits
930 
931 				switch (header.image_type) {
932 					case TGA_RGB: { //(16 bit)
933 						// input line cache
934 						BYTE *in_line = (BYTE*)malloc(header.is_width * sizeof(WORD));
935 
936 						if (!in_line)
937 							throw FI_MSG_ERROR_MEMORY;
938 
939 						const int h = header.is_height;
940 
941 						for (int y = 0; y < h; y++) {
942 
943 							BYTE *bits = FreeImage_GetScanLine(dib, y);
944 							io->read_proc(in_line, src_pixel_size, header.is_width, handle);
945 
946 							BYTE *val = in_line;
947 							for (int x = 0; x < line; x += pixel_size) {
948 
949 								_assignPixel<16>(bits+x, val, TARGA_LOAD_RGB888 & flags);
950 
951 								val += src_pixel_size;
952 							}
953 						}
954 
955 						free(in_line);
956 					}
957 					break;
958 
959 					case TGA_RLERGB: { //(16 bit)
960 						loadRLE<16>(dib, header.is_width, header.is_height, io, handle, eof, TARGA_LOAD_RGB888 & flags);
961 					}
962 					break;
963 
964 					default :
965 						FreeImage_Unload(dib);
966 						return NULL;
967 				}
968 			}
969 			break; // header.is_pixel_depth == 15 or 16
970 
971 			case 24 : {
972 
973 				dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
974 
975 				if (dib == NULL) {
976 					throw FI_MSG_ERROR_DIB_MEMORY;
977 				}
978 
979 				FIBITMAP* th = thumbnail.toFIBITMAP();
980 				if(th) {
981 					FreeImage_SetThumbnail(dib, th);
982 					FreeImage_Unload(th);
983 				}
984 
985 				if(header_only) {
986 					return dib;
987 				}
988 
989 				// read in the bitmap bits
990 
991 				switch (header.image_type) {
992 					case TGA_RGB: { //(24 bit)
993 						//uncompressed
994 						loadTrueColor(dib, header.is_width, header.is_height, pixel_size,io, handle, TRUE);
995 					}
996 					break;
997 
998 					case TGA_RLERGB: { //(24 bit)
999 						loadRLE<24>(dib, header.is_width, header.is_height, io, handle, eof, TRUE);
1000 					}
1001 					break;
1002 
1003 					default :
1004 						FreeImage_Unload(dib);
1005 						return NULL;
1006 				}
1007 			}
1008 			break;	// header.is_pixel_depth == 24
1009 
1010 			case 32 : {
1011 				int pixel_bits = 32;
1012 
1013 				if (TARGA_LOAD_RGB888 & flags) {
1014 					pixel_bits = 24;
1015 				}
1016 
1017 				dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
1018 
1019 				if (dib == NULL) {
1020 					throw FI_MSG_ERROR_DIB_MEMORY;
1021 				}
1022 
1023 				// handle thumbnail
1024 
1025 				FIBITMAP* th = thumbnail.toFIBITMAP();
1026 				if(th) {
1027 					if(TARGA_LOAD_RGB888 & flags) {
1028 						FIBITMAP* t = FreeImage_ConvertTo24Bits(th);
1029 						FreeImage_Unload(th);
1030 						th = t;
1031 					}
1032 					FreeImage_SetThumbnail(dib, th);
1033 					FreeImage_Unload(th);
1034 				}
1035 
1036 				if(header_only) {
1037 					return dib;
1038 				}
1039 
1040 				// read in the bitmap bits
1041 
1042 				switch (header.image_type) {
1043 					case TGA_RGB: { //(32 bit)
1044 						// uncompressed
1045 						loadTrueColor(dib, header.is_width, header.is_height, 4 /*file_pixel_size*/, io, handle, TARGA_LOAD_RGB888 & flags);
1046 					}
1047 					break;
1048 
1049 					case TGA_RLERGB: { //(32 bit)
1050 						loadRLE<32>(dib, header.is_width, header.is_height, io, handle, eof, TARGA_LOAD_RGB888 & flags);
1051 					}
1052 					break;
1053 
1054 					default :
1055 						FreeImage_Unload(dib);
1056 						return NULL;
1057 				}
1058 			}
1059 			break; // header.is_pixel_depth == 32
1060 
1061 		} // switch(header.is_pixel_depth)
1062 
1063 		if (flipvert) {
1064 			FreeImage_FlipVertical(dib);
1065 		}
1066 
1067 		if (fliphoriz) {
1068 			FreeImage_FlipHorizontal(dib);
1069 		}
1070 
1071 		return dib;
1072 
1073 	} catch (const char *message) {
1074 		if (dib) {
1075 			FreeImage_Unload(dib);
1076 		}
1077 
1078 		FreeImage_OutputMessageProc(s_format_id, message);
1079 
1080 		return NULL;
1081 	}
1082 }
1083 
1084 // --------------------------------------------------------------------------
1085 
1086 static BOOL
hasValidThumbnail(FIBITMAP * dib)1087 hasValidThumbnail(FIBITMAP* dib) {
1088 	FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib);
1089 
1090 	return thumbnail
1091 		&& SupportsExportType(FreeImage_GetImageType(thumbnail))
1092 		&& SupportsExportDepth(FreeImage_GetBPP(thumbnail))
1093 		// Requirements according to the specification:
1094 		&& FreeImage_GetBPP(thumbnail) == FreeImage_GetBPP(dib)
1095 		&& FreeImage_GetImageType(thumbnail) == FreeImage_GetImageType(dib)
1096 		&& FreeImage_GetWidth(thumbnail) <= 255
1097 		&& FreeImage_GetHeight(thumbnail) <= 255;
1098 }
1099 
1100 /**
1101 Writes the ready RLE packet to buffer
1102 */
1103 static inline void
flushPacket(BYTE * & dest,unsigned pixel_size,BYTE * packet_begin,BYTE * & packet,BYTE & packet_count,BOOL & has_rle)1104 flushPacket(BYTE*& dest, unsigned pixel_size, BYTE* packet_begin, BYTE*& packet, BYTE& packet_count, BOOL& has_rle) {
1105 	if (packet_count) {
1106 		const BYTE type_bit = has_rle ? 0x80 : 0x0;
1107 		const BYTE write_count = has_rle ? 1 : packet_count;
1108 
1109 		// build packet header: zero-based count + type bit
1110 		assert(packet_count >= 1);
1111 		BYTE rle = packet_count - 1;
1112 		rle |= type_bit;
1113 
1114 		// write packet header
1115 		*dest = rle;
1116 		++dest;
1117 
1118 		// write packet data
1119 		memcpy(dest, packet_begin, write_count * pixel_size);
1120 		dest += write_count * pixel_size;
1121 
1122 		// reset state
1123 		packet_count = 0;
1124 		packet = packet_begin;
1125 		has_rle = FALSE;
1126 	}
1127 }
1128 
1129 
1130 static inline void
writeToPacket(BYTE * packet,BYTE * pixel,unsigned pixel_size)1131 writeToPacket(BYTE* packet, BYTE* pixel, unsigned pixel_size) {
1132 	// Take care of channel and byte order here, because packet will be flushed straight to the file
1133 	switch (pixel_size) {
1134 		case 1:
1135 			*packet = *pixel;
1136 			break;
1137 
1138 		case 2: {
1139 			WORD val(*(WORD*)pixel);
1140 #ifdef FREEIMAGE_BIGENDIAN
1141 			SwapShort(&val);
1142 #endif
1143 			*(WORD*)packet = val;
1144 		}
1145 		break;
1146 
1147 		case 3: {
1148 			packet[0] = pixel[FI_RGBA_BLUE];
1149 			packet[1] = pixel[FI_RGBA_GREEN];
1150 			packet[2] = pixel[FI_RGBA_RED];
1151 		}
1152 		break;
1153 
1154 		case 4: {
1155 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
1156 			*(reinterpret_cast<unsigned*>(packet)) = *(reinterpret_cast<unsigned*> (pixel));
1157 #else
1158 			packet[0] = pixel[FI_RGBA_BLUE];
1159 			packet[1] = pixel[FI_RGBA_GREEN];
1160 			packet[2] = pixel[FI_RGBA_RED];
1161 			packet[3] = pixel[FI_RGBA_ALPHA];
1162 #endif
1163 		}
1164 		break;
1165 
1166 		default:
1167 			assert(FALSE);
1168 	}
1169 }
1170 
1171 static inline BOOL
isEqualPixel(BYTE * lhs,BYTE * rhs,unsigned pixel_size)1172 isEqualPixel(BYTE* lhs, BYTE* rhs, unsigned pixel_size) {
1173 	switch (pixel_size) {
1174 		case 1:
1175 			return *lhs == *rhs;
1176 
1177 		case 2:
1178 			return *(WORD*)lhs == *(WORD*)rhs;
1179 
1180 		case 3:
1181 			return *(WORD*)lhs == *(WORD*)rhs && lhs[2] == rhs[2];
1182 
1183 		case 4:
1184 			return *(unsigned*)lhs == *(unsigned*)rhs;
1185 
1186 		default:
1187 			assert(FALSE);
1188 			return FALSE;
1189 	}
1190 }
1191 
1192 static void
saveRLE(FIBITMAP * dib,FreeImageIO * io,fi_handle handle)1193 saveRLE(FIBITMAP* dib, FreeImageIO* io, fi_handle handle) {
1194 	// Image is compressed line by line, packets don't span multiple lines (TGA2.0 recommendation)
1195 
1196 	const unsigned width = FreeImage_GetWidth(dib);
1197 	const unsigned height = FreeImage_GetHeight(dib);
1198 	const unsigned pixel_size = FreeImage_GetBPP(dib)/8;
1199 	const unsigned line_size = FreeImage_GetLine(dib);
1200 
1201 	const BYTE max_packet_size = 128;
1202 	BYTE packet_count = 0;
1203 	BOOL has_rle = FALSE;
1204 
1205 	// packet (compressed or not) to be written to line
1206 
1207 	BYTE* const packet_begin = (BYTE*)malloc(max_packet_size * pixel_size);
1208 	BYTE* packet = packet_begin;
1209 
1210 	// line to be written to disk
1211 	// Note: we need some extra bytes for anti-commpressed lines. The worst case is:
1212 	// 8 bit images were every 3th pixel is different.
1213 	// Rle packet becomes two pixels, but nothing is compressed: two byte pixels are transformed into byte header and byte pixel value
1214 	// After every rle packet there is a non-rle packet of one pixel: an extra byte for the header will be added for it
1215 	// In the end we gain no bytes from compression, but also must insert a byte at every 3th pixel
1216 
1217 	// add extra space for anti-commpressed lines
1218 	size_t extra_space = (size_t)ceil(width / 3.0);
1219 	BYTE* const line_begin = (BYTE*)malloc(width * pixel_size + extra_space);
1220 	BYTE* line = line_begin;
1221 
1222 	BYTE *current = (BYTE*)malloc(pixel_size);
1223 	BYTE *next    = (BYTE*)malloc(pixel_size);
1224 
1225 	for(unsigned y = 0; y < height; y++) {
1226 		BYTE *bits = FreeImage_GetScanLine(dib, y);
1227 
1228 		// rewind line pointer
1229 		line = line_begin;
1230 
1231 		for(unsigned x = 0; x < line_size; x += pixel_size) {
1232 
1233 			AssignPixel(current, (bits + x), pixel_size);
1234 
1235 			// read next pixel from dib
1236 
1237 			if( x + 1*pixel_size < line_size) {
1238 				AssignPixel(next, (bits + x + 1*pixel_size), pixel_size);
1239 
1240 			} else {
1241 				// last pixel in line
1242 
1243 				// include current pixel and flush
1244 				if(!has_rle) {
1245 
1246 					writeToPacket(packet, current, pixel_size);
1247 					packet += pixel_size;
1248 
1249 				}
1250 
1251 				assert(packet_count < max_packet_size);
1252 
1253 				++packet_count;
1254 
1255 				flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
1256 
1257 				// start anew on next line
1258 				break;
1259 			}
1260 
1261 			if(isEqualPixel(current, next, pixel_size)) {
1262 
1263 				// has rle
1264 
1265 				if(!has_rle) {
1266 					// flush non rle packet
1267 
1268 					flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
1269 
1270 					// start a rle packet
1271 
1272 					has_rle = TRUE;
1273 
1274 					writeToPacket(packet, current, pixel_size);
1275 					packet += pixel_size;
1276 				}
1277 
1278 				// otherwise do nothing. We will just increase the count at the end
1279 
1280 			} else {
1281 
1282 				// no rle
1283 
1284 				if(has_rle) {
1285 					// flush rle packet
1286 
1287 					// include current pixel first
1288 					assert(packet_count < max_packet_size);
1289 					++packet_count;
1290 
1291 					flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
1292 
1293 					// start anew on the next pixel
1294 					continue;
1295 
1296 				} else {
1297 
1298 					writeToPacket(packet, current, pixel_size);
1299 					packet += pixel_size;
1300 				}
1301 
1302 			}
1303 
1304 			// increase counter on every pixel
1305 
1306 			++packet_count;
1307 
1308 			if(packet_count == max_packet_size) {
1309 				flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
1310 			}
1311 
1312 		}//for width
1313 
1314 		// write line to disk
1315 		io->write_proc(line_begin, 1, (unsigned)(line - line_begin), handle);
1316 
1317 	}//for height
1318 
1319 	free(line_begin);
1320 	free(packet_begin);
1321 	free(current);
1322 	free(next);
1323 }
1324 
1325 static BOOL DLL_CALLCONV
Save(FreeImageIO * io,FIBITMAP * dib,fi_handle handle,int page,int flags,void * data)1326 Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
1327 	if ((dib == NULL) || (handle == NULL)) {
1328 		return FALSE;
1329 	}
1330 
1331 	RGBQUAD *palette = FreeImage_GetPalette(dib);
1332 	const unsigned bpp = FreeImage_GetBPP(dib);
1333 
1334 	// write the file header
1335 
1336 	TGAHEADER header;
1337 
1338 	header.id_length = 0;
1339 	header.cm_first_entry = 0;
1340 	header.is_xorigin = 0;
1341 	header.is_yorigin = 0;
1342 	header.is_width = (WORD)FreeImage_GetWidth(dib);
1343 	header.is_height = (WORD)FreeImage_GetHeight(dib);
1344 	header.is_pixel_depth = (BYTE)bpp;
1345 	header.is_image_descriptor = (bpp == 32 ? 8 : 0);
1346 
1347 	if (palette) {
1348 		header.color_map_type = 1;
1349 		header.image_type = (TARGA_SAVE_RLE & flags) ? TGA_RLECMAP : TGA_CMAP;
1350 		header.cm_length = (WORD)(1 << bpp);
1351 
1352 		if (FreeImage_IsTransparent(dib)) {
1353 			header.cm_size = 32;
1354 		} else {
1355 			header.cm_size = 24;
1356 		}
1357 
1358 	} else {
1359 		header.color_map_type = 0;
1360 		header.image_type = (TARGA_SAVE_RLE & flags) ? TGA_RLERGB : TGA_RGB;
1361 		header.cm_length = 0;
1362 		header.cm_size = 0;
1363 	}
1364 
1365 	// write the header
1366 
1367 #ifdef FREEIMAGE_BIGENDIAN
1368 	SwapHeader(&header);
1369 #endif
1370 
1371 	io->write_proc(&header, sizeof(header), 1, handle);
1372 
1373 #ifdef FREEIMAGE_BIGENDIAN
1374 	SwapHeader(&header);
1375 #endif
1376 
1377 	// write the palette
1378 
1379 	if (palette) {
1380 		if (FreeImage_IsTransparent(dib)) {
1381 			FILE_BGRA *bgra_pal = (FILE_BGRA*)malloc(header.cm_length * sizeof(FILE_BGRA));
1382 
1383 			// get the transparency table
1384 			BYTE *trns = FreeImage_GetTransparencyTable(dib);
1385 
1386 			for (unsigned i = 0; i < header.cm_length; i++) {
1387 				bgra_pal[i].b = palette[i].rgbBlue;
1388 				bgra_pal[i].g = palette[i].rgbGreen;
1389 				bgra_pal[i].r = palette[i].rgbRed;
1390 				bgra_pal[i].a = trns[i];
1391 			}
1392 
1393 			io->write_proc(bgra_pal, sizeof(FILE_BGRA), header.cm_length, handle);
1394 
1395 			free(bgra_pal);
1396 
1397 		} else {
1398 			FILE_BGR *bgr_pal = (FILE_BGR*)malloc(header.cm_length * sizeof(FILE_BGR));
1399 
1400 			for (unsigned i = 0; i < header.cm_length; i++) {
1401 				bgr_pal[i].b = palette[i].rgbBlue;
1402 				bgr_pal[i].g = palette[i].rgbGreen;
1403 				bgr_pal[i].r = palette[i].rgbRed;
1404 			}
1405 
1406 			io->write_proc(bgr_pal, sizeof(FILE_BGR), header.cm_length, handle);
1407 
1408 			free(bgr_pal);
1409 		}
1410 	}
1411 
1412 	// write the data bits
1413 
1414 
1415 	if (TARGA_SAVE_RLE & flags) {
1416 
1417 		saveRLE(dib, io, handle);
1418 
1419 	} else {
1420 
1421 		// -- no rle compression --
1422 
1423 		const unsigned width = header.is_width;
1424 		const unsigned height = header.is_height;
1425 		const unsigned pixel_size = bpp/8;
1426 
1427 		BYTE *line, *const line_begin = (BYTE*)malloc(width * pixel_size);
1428 		BYTE *line_source = line_begin;
1429 
1430 		for (unsigned y = 0; y < height; y++) {
1431 			BYTE *scanline = FreeImage_GetScanLine(dib, y);
1432 
1433 			// rewind the line pointer
1434 			line = line_begin;
1435 
1436 			switch (bpp) {
1437 				case 8: {
1438 					// don't copy line, read straight from dib
1439 					line_source = scanline;
1440 				}
1441 				break;
1442 
1443 				case 16: {
1444 					for (unsigned x = 0; x < width; x++) {
1445 						WORD pixel = *(((WORD *)scanline) + x);
1446 
1447 #ifdef FREEIMAGE_BIGENDIAN
1448 						SwapShort(&pixel);
1449 #endif
1450 						*(WORD*)line = pixel;
1451 
1452 						line += pixel_size;
1453 					}
1454 				}
1455 				break;
1456 
1457 				case 24: {
1458 
1459 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
1460 						line_source = scanline;
1461 #else
1462 					for (unsigned x = 0; x < width; ++x) {
1463 						RGBTRIPLE* trip = ((RGBTRIPLE *)scanline) + x;
1464 						line[0] = trip->rgbtBlue;
1465 						line[1] = trip->rgbtGreen;
1466 						line[2] = trip->rgbtRed;
1467 
1468 						line += pixel_size;
1469 					}
1470 #endif
1471 				}
1472 				break;
1473 
1474 				case 32: {
1475 
1476 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
1477 					line_source = scanline;
1478 #else
1479 					for (unsigned x = 0; x < width; ++x) {
1480 						RGBQUAD* quad = ((RGBQUAD *)scanline) + x;
1481 						line[0] = quad->rgbBlue;
1482 						line[1] = quad->rgbGreen;
1483 						line[2] = quad->rgbRed;
1484 						line[3] = quad->rgbReserved;
1485 
1486 						line += pixel_size;
1487 					}
1488 #endif
1489 				}
1490 				break;
1491 
1492 			}//switch(bpp)
1493 
1494 			// write line to disk
1495 
1496 			io->write_proc(line_source, pixel_size, width, handle);
1497 
1498 		}//for height
1499 
1500 		free(line_begin);
1501 	}
1502 
1503 
1504 	long extension_offset = 0 ;
1505 	if(hasValidThumbnail(dib)) {
1506 		// write extension area
1507 
1508 		extension_offset = io->tell_proc(handle);
1509 
1510 		TGAEXTENSIONAREA ex;
1511 		memset(&ex, 0, sizeof(ex));
1512 
1513 		assert(sizeof(ex) == 495);
1514 		ex.extension_size = sizeof(ex);
1515 		ex.postage_stamp_offset = extension_offset + ex.extension_size + 0 /*< no Scan Line Table*/;
1516 		ex.attributes_type = FreeImage_GetBPP(dib) == 32 ? 3 /*< useful Alpha channel data*/ : 0 /*< no Alpha data*/;
1517 
1518 #ifdef FREEIMAGE_BIGENDIAN
1519 		SwapExtensionArea(&ex);
1520 #endif
1521 
1522 		io->write_proc(&ex, sizeof(ex), 1, handle);
1523 
1524 		// (no Scan Line Table)
1525 
1526 		// write thumbnail
1527 
1528 		io->seek_proc(handle, ex.postage_stamp_offset, SEEK_SET);
1529 
1530 		FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib);
1531 		BYTE width = (BYTE)FreeImage_GetWidth(thumbnail);
1532 		BYTE height = (BYTE)FreeImage_GetHeight(thumbnail);
1533 
1534 		io->write_proc(&width, 1, 1, handle);
1535 		io->write_proc(&height, 1, 1, handle);
1536 
1537 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
1538 		SwapRedBlue32(dib);
1539 #endif
1540 
1541 #ifdef FREEIMAGE_BIGENDIAN
1542 		swapShortPixels(dib);
1543 #endif
1544 
1545 		const unsigned line_size = FreeImage_GetLine(thumbnail);
1546 
1547 		for (BYTE h = 0; h < height; ++h) {
1548 			BYTE* src_line = FreeImage_GetScanLine(thumbnail, height - 1 - h);
1549 			io->write_proc(src_line, 1, line_size, handle);
1550 		}
1551 	}
1552 
1553 	// (no Color Correction Table)
1554 
1555 	// write the footer
1556 
1557 	TGAFOOTER footer;
1558 	footer.extension_offset = extension_offset;
1559 	footer.developer_offset = 0;
1560 	strcpy(footer.signature, "TRUEVISION-XFILE.");
1561 
1562 
1563 #ifdef FREEIMAGE_BIGENDIAN
1564 	SwapFooter(&footer);
1565 #endif
1566 
1567 	io->write_proc(&footer, sizeof(footer), 1, handle);
1568 
1569 	return TRUE;
1570 }
1571 
1572 // ==========================================================
1573 //   Init
1574 // ==========================================================
1575 
1576 void DLL_CALLCONV
InitTARGA(Plugin * plugin,int format_id)1577 InitTARGA(Plugin *plugin, int format_id) {
1578 	s_format_id = format_id;
1579 
1580 	plugin->format_proc = Format;
1581 	plugin->description_proc = Description;
1582 	plugin->extension_proc = Extension;
1583 	plugin->regexpr_proc = RegExpr;
1584 	plugin->open_proc = NULL;
1585 	plugin->close_proc = NULL;
1586 	plugin->pagecount_proc = NULL;
1587 	plugin->pagecapability_proc = NULL;
1588 	plugin->load_proc = Load;
1589 	plugin->save_proc = Save;
1590 	plugin->validate_proc = Validate;
1591 	plugin->mime_proc = MimeType;
1592 	plugin->supports_export_bpp_proc = SupportsExportDepth;
1593 	plugin->supports_export_type_proc = SupportsExportType;
1594 	plugin->supports_icc_profiles_proc = NULL;
1595 	plugin->supports_no_pixels_proc = SupportsNoPixels;
1596 }
1597