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