Coverage Report

Created: 2026-02-26 06:40

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