Visualization Library 2.0.0

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

[Download] [Tutorials] [All Classes] [Grouped Classes]
ioTGA.cpp
Go to the documentation of this file.
1 /**************************************************************************************/
2 /* */
3 /* Visualization Library */
4 /* http://visualizationlibrary.org */
5 /* */
6 /* Copyright (c) 2005-2020, Michele Bosi */
7 /* All rights reserved. */
8 /* */
9 /* Redistribution and use in source and binary forms, with or without modification, */
10 /* are permitted provided that the following conditions are met: */
11 /* */
12 /* - Redistributions of source code must retain the above copyright notice, this */
13 /* list of conditions and the following disclaimer. */
14 /* */
15 /* - Redistributions in binary form must reproduce the above copyright notice, this */
16 /* list of conditions and the following disclaimer in the documentation and/or */
17 /* other materials provided with the distribution. */
18 /* */
19 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */
20 /* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
21 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
22 /* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */
23 /* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
24 /* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
25 /* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
26 /* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
27 /* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
28 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 /* */
30 /**************************************************************************************/
31 
32 #include "ioTGA.hpp"
36 #include <vlCore/FileSystem.hpp>
37 #include <vlCore/DiskFile.hpp>
38 #include <vlCore/Image.hpp>
39 
40 using namespace vl;
41 
42 #include <vlCore/ImageTools.hpp>
43 
44 //-----------------------------------------------------------------------------
45 namespace
46 {
47  const unsigned long TGA_NO_IMAGE_DATA = 0;
48  const unsigned long TGA_8BIT_UNCOMPRESSED = 1;
49  const unsigned long TGA_RGB_UNCOMPRESSED = 2;
50  const unsigned long TGA_GRAYSCALE_UNCOMPRESSED = 3;
51  const unsigned long TGA_8BIT_COMPRESSED = 9;
52  const unsigned long TGA_RGB_COMPRESSED = 10;
53  const unsigned long TGA_GRAYSCALE_COMPRESSED = 11;
54 
55  typedef struct
56  {
57  unsigned char IdFieldSize; /* at offset 18 it starts, usually 0 */
58  unsigned char HasColMap; /* 1 for indexed images, 0 otherwise */
59  unsigned char ImageType; /* see defines above */
60  unsigned char ColMapOrigin[2];
61  unsigned char ColMapCount_lo;
62  unsigned char ColMapCount_hi;
63  unsigned char ColMapEntrySize; /* 16, 24, 32 */
64  unsigned char ImageOrigins[4]; /* lo/hi bytes for x/y origins */
65  unsigned char Width_lo;
66  unsigned char Width_hi;
67  unsigned char Height_lo;
68  unsigned char Height_hi;
69  unsigned char BitsPerPixel; /* 8/16(?), 16, 24, 32 */
70  unsigned char ImageDescriptor; /* origin | alpha channel bits */
71  } STGAHeader;
72 }
73 //-----------------------------------------------------------------------------
89 {
91  if ( !file )
92  {
93  Log::error( Say("File '%s' not found.\n") << path );
94  return NULL;
95  }
96 
97  return loadTGA( file.get() );
98 }
99 //-----------------------------------------------------------------------------
101 {
102  if ( !file->open(OM_ReadOnly) )
103  {
104  Log::error( Say("loadTGA: cannot load TGA file '%s'\n") << file->path() );
105  return NULL;
106  }
107 
108  ref<Image> img = new Image;
109  img->setObjectName(file->path().toStdString().c_str());
110 
111  STGAHeader header;
112  memset(&header, 0, sizeof(header));
113  file->read( &header, sizeof(STGAHeader) );
114 
115  unsigned int colmap_offset = 18 + header.IdFieldSize;
116  unsigned int pixels_offset = colmap_offset +
117  (header.ColMapCount_lo + header.ColMapCount_hi*256)*header.ColMapEntrySize/8;
118  unsigned int w = header.Width_lo + header.Width_hi*256;
119  unsigned int h = header.Height_lo + header.Height_hi*256;
120 
121  #ifndef NDEBUG
122  unsigned int bpp = header.BitsPerPixel;
123  const char *type = "";
124  switch(header.ImageType)
125  {
126  case TGA_NO_IMAGE_DATA: type = "TGA_NO_IMAGE_DATA"; break;
127  case TGA_8BIT_UNCOMPRESSED: type = "TGA_8BIT_UNCOMPRESSED"; break;
128  case TGA_RGB_UNCOMPRESSED: type = "TGA_RGB_UNCOMPRESSED"; break;
129  case TGA_GRAYSCALE_UNCOMPRESSED: type = "TGA_GRAYSCALE_UNCOMPRESSED"; break;
130  case TGA_8BIT_COMPRESSED: type = "TGA_8BIT_COMPRESSED"; break;
131  case TGA_RGB_COMPRESSED: type = "TGA_RGB_COMPRESSED"; break;
132  case TGA_GRAYSCALE_COMPRESSED: type = "TGA_GRAYSCALE_COMPRESSED"; break;
133  }
134  Log::debug( Say("TGA %s: w=%n, h=%n, bpp=%n/%n %s\n") << file->path() << w << h << bpp << header.ColMapEntrySize << type);
135  #endif
136 
137  if (header.ImageType == TGA_NO_IMAGE_DATA)
138  return NULL;
139 
140  if (header.ImageType == TGA_RGB_COMPRESSED)
141  {
142  int pixsize = 0;
143  switch(header.BitsPerPixel)
144  {
145  case 32: pixsize = 4; break;
146  case 24: pixsize = 3; break;
147  case 16: pixsize = 2; break;
148  }
149 
150  if (pixsize)
151  {
152  img->allocate2D(w, h, 4, IF_RGBA, IT_UNSIGNED_BYTE);
153 
154  file->seekSet(pixels_offset);
155  int pixcount = w*h;
156  int pix = 0;
157  while(pix < pixcount)
158  {
159  unsigned char header_ch = 0;
160  file->read(&header_ch, 1);
161  if (header_ch >= 128)
162  {
163  int count = header_ch - 128 + 1;
164  unsigned char bgra[4];
165  file->read(bgra, pixsize);
166  while(count--)
167  {
168  memcpy((unsigned char*)img->pixels() + pix*pixsize, bgra, pixsize);
169  pix++;
170  }
171  }
172  else
173  {
174  int count = header_ch + 1;
175  file->read((unsigned char*)img->pixels() + pix*pixsize, pixsize*count);
176  pix += count;
177  }
178  }
179 
180  switch(header.BitsPerPixel)
181  {
182  case 24: convertRGBToRGBA(img->pixels(), img->width(), img->height(), 0xFF);
183  // break; -- ok to fallthrough
184  case 32: swapBytes32_BGRA_RGBA(img->pixels(), img->requiredMemory());
185  break;
186  case 16: convertA1R5G5B5ToRGBA(img->pixels(), w*h, 0xFF);
187  break;
188  }
189 
190  }
191  else
192  {
193  Log::error( Say("TGA ERROR: TGA_RGB_COMPRESSED %nbpp not supported.\n") << header.BitsPerPixel );
194  file->close();
195  return NULL;
196  }
197 
198  }
199  else
200  if (header.ImageType == TGA_RGB_UNCOMPRESSED)
201  {
202  int pixsize = 0;
203 
204  switch(header.BitsPerPixel)
205  {
206  case 32: pixsize = 4; break;
207  case 24: pixsize = 3; break;
208  case 16: pixsize = 2; break;
209  }
210 
211  if (pixsize)
212  {
213  file->seekSet(pixels_offset);
214  img->allocate2D(w, h, 4, IF_RGBA, IT_UNSIGNED_BYTE);
215  file->read(img->pixels(), w*h*pixsize);
216  switch(header.BitsPerPixel)
217  {
218  case 24: convertRGBToRGBA(img->pixels(), img->width(), img->height(), 0xFF);
219  // break; -- ok to fallthrough
220  case 32: swapBytes32_BGRA_RGBA(img->pixels(), img->requiredMemory());
221  break;
222  case 16: convertA1R5G5B5ToRGBA(img->pixels(), w*h, 0xFF);
223  break;
224  }
225  }
226  else
227  {
228  Log::error( Say("TGA ERROR: TGA_RGB_UNCOMPRESSED %nbpp not supported.\n") << header.BitsPerPixel );
229  file->close();
230  return NULL;
231  }
232 
233  }
234  else
235  if (header.ImageType == TGA_8BIT_UNCOMPRESSED || header.ImageType == TGA_8BIT_COMPRESSED)
236  {
237  if (header.BitsPerPixel == 8)
238  {
239 
240  unsigned int colmap_count = header.ColMapCount_lo + header.ColMapCount_hi*256;
241  VL_CHECK(colmap_count<=256);
242  if (header.ColMapEntrySize == 24)
243  {
244  TPalette3x256 palette;
245  file->seekSet(colmap_offset);
246  file->read(palette, colmap_count*3);
247 
248  file->seekSet(pixels_offset);
249 
250  img->allocate2D(w, h, 4, IF_RGBA, IT_UNSIGNED_BYTE);
251  if (header.ImageType == TGA_8BIT_UNCOMPRESSED)
252  {
253  file->read(img->pixels(), w*h*1);
254  }
255  else // TGA_8BIT_UNCOMPRESSED
256  {
257  int pixsize = 1;
258  int pixcount = w*h;
259  int pix = 0;
260  while(pix < pixcount)
261  {
262  unsigned char header_ch = 0;
263  file->read(&header_ch, 1);
264  if (header_ch >= 128)
265  {
266  int count = header_ch - 128 + 1;
267  unsigned char bgra[4];
268  file->read(bgra, pixsize);
269  while(count--)
270  {
271  memcpy((unsigned char*)img->pixels() + pix*pixsize, bgra, pixsize);
272  pix++;
273  }
274  }
275  else
276  {
277  int count = header_ch + 1;
278  file->read((unsigned char*)img->pixels() + pix*pixsize, pixsize*count);
279  pix += count;
280  }
281  }
282  }
283 
284  convert8ToRGBA(palette, img->pixels(), img->width(), img->height(), 0xFF);
286  }
287  else if (header.ColMapEntrySize == 32)
288  {
289  TPalette4x256 palette;
290  file->seekSet(colmap_offset);
291  file->read(palette, colmap_count*4);
292 
293  file->seekSet( pixels_offset );
294  img->allocate2D(w, h, 4, IF_RGBA, IT_UNSIGNED_BYTE);
295  if (header.ImageType == TGA_8BIT_UNCOMPRESSED)
296  {
297  file->read(img->pixels(), w*h*1);
298  }
299  else // TGA_8BIT_UNCOMPRESSED
300  {
301  int pixsize = 1;
302  int pixcount = w*h;
303  int pix = 0;
304  while(pix < pixcount)
305  {
306  unsigned char header_ch = 0;
307  file->read(&header_ch, 1);
308  if (header_ch >= 128)
309  {
310  int count = header_ch - 128 + 1;
311  unsigned char bgra[4];
312  file->read(bgra, pixsize);
313  while(count--)
314  {
315  memcpy((unsigned char*)img->pixels() + pix*pixsize, bgra, pixsize);
316  pix++;
317  }
318  }
319  else
320  {
321  int count = header_ch + 1;
322  file->read((unsigned char*)img->pixels() + pix*pixsize, pixsize*count);
323  pix += count;
324  }
325  }
326  }
327 
328  convert8ToRGBA(palette, img->pixels(), img->width(), img->height());
330  }
331  else
332  {
333  Log::error( Say("TGA ERROR: TGA_8BIT_UNCOMPRESSED entry size = %n not supported.\n") << header.ColMapEntrySize );
334  file->close();
335  return NULL;
336  }
337  }
338  else
339  {
340  Log::error( Say("TGA ERROR: TGA_8BIT_UNCOMPRESSED %nbpp bit not supported.\n") << header.BitsPerPixel );
341  file->close();
342  return NULL;
343  }
344  }
345  else
346  if (header.ImageType == TGA_GRAYSCALE_UNCOMPRESSED || header.ImageType == TGA_GRAYSCALE_COMPRESSED)
347  {
348  if (header.BitsPerPixel == 8)
349  {
350  file->seekSet(pixels_offset);
351  img->allocate2D(w, h, 1, IF_LUMINANCE, IT_UNSIGNED_BYTE);
352  if (header.ImageType == TGA_GRAYSCALE_UNCOMPRESSED)
353  {
354  file->read(img->pixels(), w*h);
355  }
356  else // TGA_GRAYSCALE_COMPRESSED
357  {
358  int pixsize = 1;
359  int pixcount = w*h;
360  int pix = 0;
361  while(pix < pixcount)
362  {
363  unsigned char header_ch = 0;
364  file->read(&header_ch, 1);
365  if (header_ch >= 128)
366  {
367  int count = header_ch - 128 + 1;
368  unsigned char bgra[4];
369  file->read(bgra, pixsize);
370  while(count--)
371  {
372  memcpy((unsigned char*)img->pixels() + pix*pixsize, bgra, pixsize);
373  pix++;
374  }
375  }
376  else
377  {
378  int count = header_ch + 1;
379  file->read((unsigned char*)img->pixels() + pix*pixsize, pixsize*count);
380  pix += count;
381  }
382  }
383  }
384  }
385  else
386  {
387  Log::error( Say("TGA ERROR: TGA_GRAYSCALE_UNCOMPRESSED %nbpp not supported.\n") << header.BitsPerPixel );
388  file->close();
389  return NULL;
390  }
391 
392  }
393  else
394  {
395  Log::error( Say("TGA ERROR: this type %n not supported.\n") << header.ImageType);
396  file->close();
397  return NULL;
398  }
399 
400  if ((header.ImageDescriptor & (1<<5)))
401  img->flipVertically();
402 
403  file->close();
404  return img;
405 }
406 //-----------------------------------------------------------------------------
407 bool vl::saveTGA(const Image* src, const String& path)
408 {
409  ref<DiskFile> file = new DiskFile(path);
410  return saveTGA(src, file.get());
411 }
412 //-----------------------------------------------------------------------------
413 bool vl::saveTGA(const Image* src, VirtualFile* fout)
414 {
415  //if (src->dimension() != ID_2D )
416  //{
417  // Log::error( Say("saveTGA('%s'): can save only 2D images.\n") << fout->path() );
418  // return false;
419  //}
420 
421  int w = src->width();
422  int h = src->height();
423  int d = src->depth();
424  if (h == 0) h=1;
425  if (d == 0) d=1;
426  if (src->isCubemap()) d=6;
427  h = h*d;
428 
429  // convert src to IT_UNSIGNED_BYTE / IF_RGBA
430  ref<Image> cimg;
431  if (src->type() != IT_UNSIGNED_BYTE)
432  {
433  cimg = src->convertType(IT_UNSIGNED_BYTE);
434  src = cimg.get();
435  if (!cimg)
436  {
437  Log::error( Say("saveTGA('%s'): could not convert image to IT_UNSIGNED_BYTE.\n") << fout->path() );
438  return false;
439  }
440  }
441  if (src->format() != IF_BGRA)
442  {
443  cimg = src->convertFormat(IF_BGRA);
444  src = cimg.get();
445  if (!cimg)
446  {
447  Log::error( Say("saveTGA('%s'): could not convert image to IF_BGRA.\n") << fout->path() );
448  return false;
449  }
450  }
451 
452  if(!fout->open(OM_WriteOnly))
453  {
454  Log::error( Say("TGA: could not write to '%s'.\n") << fout->path() );
455  return false;
456  }
457 
458  STGAHeader header;
459  memset(&header, 0, sizeof(STGAHeader));
460  // flip vertically
461  // header.ImageDescriptor |= 1<<5;
462  header.ImageType = TGA_RGB_UNCOMPRESSED;
463  header.Width_lo = (unsigned char)(w & 0x00FF);
464  header.Width_hi = (unsigned char)(w >> 8);
465  header.Height_lo = (unsigned char)(h & 0x00FF);
466  header.Height_hi = (unsigned char)(h >> 8);
467  header.BitsPerPixel = 32;
468  fout->write(&header, sizeof(header));
469 
470  fout->write(src->pixels(), src->requiredMemory());
471 
472  // TGA footer
473 
474  // extension area offset
475  fout->writeUInt32(0);
476  // developer directory offset
477  fout->writeUInt32(0);
478  // signature + "." + "0"
479  fout->write("TRUEVISION-XFILE.", 18);
480 
481  fout->close();
482  return true;
483 }
484 //-----------------------------------------------------------------------------
486 bool vl::isTGA( VirtualFile* file )
487 {
488  if (!file->open(OM_ReadOnly))
489  return false;
490 
491  STGAHeader header;
492  memset(&header, 0, sizeof(header));
493  file->read(&header, sizeof(STGAHeader) );
494 
495  char signature[17];
496  memset(signature, 0, 17);
497  file->seekEnd(-18);
498  file->read(signature, 16);
499  file->close();
500 
501  // unfortunately many TGA files are without this field
502 
503  if (strcmp("TRUEVISION-XFILE", signature) == 0)
504  return true;
505 
506  // do some heuristic checks
507 
508  switch( header.ImageType )
509  {
510  case 0:
511  case 1:
512  case 2:
513  case 3:
514  case 9:
515  case 10:
516  case 11:
517  break;
518  default:
519  return false;
520  }
521 
522  // unsigned int colmap_offset = 18 + header.IdFieldSize;
523  // unsigned int pixels_offset = colmap_offset + (header.ColMapCount_lo + header.ColMapCount_hi*256)*header.ColMapEntrySize/8;
524  unsigned int width = header.Width_lo + header.Width_hi*256;
525  unsigned int height = header.Height_lo + header.Height_hi*256;
526  unsigned int bpp = header.BitsPerPixel;
527 
528  if (width * height == 0)
529  return false;
530 
531  switch( bpp )
532  {
533  case 1:
534  case 4:
535  case 8:
536  case 16:
537  case 24:
538  case 32:
539  break;
540  default:
541  return false;
542  }
543 
544  // ends with .tga
545  if ( !file->path().toLowerCase().endsWith(".tga") )
546  return false;
547 
548  Log::warning( Say("isTGA: the file '%s' looks like a TGA but is missing the 'TRUEVISION-XFILE' signature.\n") << file->path() );
549  return true;
550 }
551 //-----------------------------------------------------------------------------
static void debug(const String &message)
Use this function to provide extra information useful to investigate and solve problems.
Definition: Log.cpp:145
String toLowerCase() const
Returns the lower-case version of a String.
Definition: String.cpp:755
long long read(void *buffer, long long byte_count)
Reads byte_count bytes from a file. Returns the number of bytes actually read.
Definition: VirtualFile.cpp:82
VLCORE_EXPORT FileSystem * defFileSystem()
Returns the default FileSystem used by VisualizationLibrary.
Definition: pimpl.cpp:97
VLCORE_EXPORT bool isTGA(VirtualFile *file)
A TGA file is accepted only if it has a &#39;TGA&#39; extension.
Definition: ioTGA.cpp:486
int depth() const
Definition: Image.hpp:211
const T * get() const
Definition: Object.hpp:128
An abstract class representing a file.
Definition: VirtualFile.hpp:60
void convert8ToRGBA(const TPalette3x256 &palette, void *buf, int w, int h, unsigned char alpha, int bytealign=1)
Definition: ImageTools.hpp:117
A simple String formatting class.
Definition: Say.hpp:124
const unsigned char * pixels() const
Raw pointer to pixels.
Definition: Image.hpp:170
void setObjectName(const char *name)
The name of the object, by default set to the object&#39;s class name in debug builds.
Definition: Object.hpp:220
static void warning(const String &message)
Use this function to provide information about situations that might lead to errors or loss of data...
Definition: Log.cpp:155
void swapBytes32_BGRA_RGBA(void *buf, int bytecount)
Definition: ImageTools.hpp:211
The String class implements an advanced UTF16 (Unicode BMP) string manipulation engine.
Definition: String.hpp:62
ref< Image > convertType(EImageType new_type) const
Converts the type() of an image.
Definition: Image.cpp:1303
static void error(const String &message)
Use this function to provide information about run-time errors: file not found, out of memory...
Definition: Log.cpp:165
virtual ref< VirtualFile > locateFile(const String &full_path, const String &alternate_path=String()) const
Looks for a VirtualFile on the disk and in the currently active FileSystem.
Definition: FileSystem.cpp:61
VLCORE_EXPORT ref< Image > loadTGA(VirtualFile *file)
Definition: ioTGA.cpp:100
int requiredMemory() const
Returns the number of bytes requested to store the image.
Definition: Image.cpp:532
virtual void close()=0
Closes the file.
long long writeUInt32(unsigned int data, bool little_endian_data=true)
Writes a single entry. Returns the number of bytes written.
const String & path() const
Returns the path of the file.
Definition: VirtualFile.hpp:98
void convertA1R5G5B5ToRGBA(void *buf, int size, unsigned char alpha)
Definition: ImageTools.hpp:97
Visualization Library main namespace.
void convertRGBToRGBA(void *buf, int w, int h, unsigned char alpha, int bytealign=1)
Definition: ImageTools.hpp:43
int height() const
Definition: Image.hpp:209
int width() const
Definition: Image.hpp:207
long long write(const void *buffer, long long byte_count)
Writes byte_count bytes to a file. Returns the number of bytes actually written.
Definition: VirtualFile.cpp:90
virtual bool open(EOpenMode mode)=0
Opens the file in the specified mode.
unsigned char TPalette3x256[256 *3]
Definition: ImageTools.hpp:40
#define NULL
Definition: OpenGLDefs.hpp:81
bool seekSet(long long offset)
Changes the current read/write position of a file.
ref< Image > convertFormat(EImageFormat new_format) const
Converts the format() of an image.
Definition: Image.cpp:1719
A VirtualFile that operates on regular disk files.
Definition: DiskFile.hpp:64
EImageType type() const
Definition: Image.hpp:217
Implements a generic 1d, 2d, 3d and cubemap image that can have mipmaps.
Definition: Image.hpp:54
The ref<> class is used to reference-count an Object.
Definition: Object.hpp:55
void allocate2D(int x, int y, int bytealign, EImageFormat format, EImageType type)
Definition: Image.cpp:608
bool seekEnd(long long offset)
Changes the current read/write position of a file.
std::string toStdString() const
Returns a UTF8 encoded std::string.
Definition: String.cpp:1156
bool endsWith(const String &str) const
Returns true if a String ends with the specified String str.
Definition: String.cpp:705
#define VL_CHECK(expr)
Definition: checks.hpp:73
VLCORE_EXPORT bool saveTGA(const Image *src, const String &path)
Definition: ioTGA.cpp:407
void flipVertically()
Definition: Image.cpp:942
unsigned char TPalette4x256[256 *4]
Definition: ImageTools.hpp:41
EImageFormat format() const
Definition: Image.hpp:215
bool isCubemap() const
Definition: Image.hpp:78