Visualization Library 2.0.0-b3

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-2017, 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); // break;
183  case 32: swapBytes32_BGRA_RGBA(img->pixels(), img->requiredMemory()); break;
184  case 16: convertA1R5G5B5ToRGBA(img->pixels(), w*h, 0xFF); break;
185  }
186 
187  }
188  else
189  {
190  Log::error( Say("TGA ERROR: TGA_RGB_COMPRESSED %nbpp not supported.\n") << header.BitsPerPixel );
191  file->close();
192  return NULL;
193  }
194 
195  }
196  else
197  if (header.ImageType == TGA_RGB_UNCOMPRESSED)
198  {
199  int pixsize = 0;
200 
201  switch(header.BitsPerPixel)
202  {
203  case 32: pixsize = 4; break;
204  case 24: pixsize = 3; break;
205  case 16: pixsize = 2; break;
206  }
207 
208  if (pixsize)
209  {
210  file->seekSet(pixels_offset);
211  img->allocate2D(w, h, 4, IF_RGBA, IT_UNSIGNED_BYTE);
212  file->read(img->pixels(), w*h*pixsize);
213  switch(header.BitsPerPixel)
214  {
215  case 24: convertRGBToRGBA(img->pixels(), img->width(), img->height(), 0xFF); // break;
216  case 32: swapBytes32_BGRA_RGBA(img->pixels(), img->requiredMemory()); break;
217  case 16: convertA1R5G5B5ToRGBA(img->pixels(), w*h, 0xFF); break;
218  }
219  }
220  else
221  {
222  Log::error( Say("TGA ERROR: TGA_RGB_UNCOMPRESSED %nbpp not supported.\n") << header.BitsPerPixel );
223  file->close();
224  return NULL;
225  }
226 
227  }
228  else
229  if (header.ImageType == TGA_8BIT_UNCOMPRESSED || header.ImageType == TGA_8BIT_COMPRESSED)
230  {
231  if (header.BitsPerPixel == 8)
232  {
233 
234  unsigned int colmap_count = header.ColMapCount_lo + header.ColMapCount_hi*256;
235  VL_CHECK(colmap_count<=256);
236  if (header.ColMapEntrySize == 24)
237  {
238  TPalette3x256 palette;
239  file->seekSet(colmap_offset);
240  file->read(palette, colmap_count*3);
241 
242  file->seekSet(pixels_offset);
243 
244  img->allocate2D(w, h, 4, IF_RGBA, IT_UNSIGNED_BYTE);
245  if (header.ImageType == TGA_8BIT_UNCOMPRESSED)
246  {
247  file->read(img->pixels(), w*h*1);
248  }
249  else // TGA_8BIT_UNCOMPRESSED
250  {
251  int pixsize = 1;
252  int pixcount = w*h;
253  int pix = 0;
254  while(pix < pixcount)
255  {
256  unsigned char header_ch = 0;
257  file->read(&header_ch, 1);
258  if (header_ch >= 128)
259  {
260  int count = header_ch - 128 + 1;
261  unsigned char bgra[4];
262  file->read(bgra, pixsize);
263  while(count--)
264  {
265  memcpy((unsigned char*)img->pixels() + pix*pixsize, bgra, pixsize);
266  pix++;
267  }
268  }
269  else
270  {
271  int count = header_ch + 1;
272  file->read((unsigned char*)img->pixels() + pix*pixsize, pixsize*count);
273  pix += count;
274  }
275  }
276  }
277 
278  convert8ToRGBA(palette, img->pixels(), img->width(), img->height(), 0xFF);
280  }
281  else if (header.ColMapEntrySize == 32)
282  {
283  TPalette4x256 palette;
284  file->seekSet(colmap_offset);
285  file->read(palette, colmap_count*4);
286 
287  file->seekSet( pixels_offset );
288  img->allocate2D(w, h, 4, IF_RGBA, IT_UNSIGNED_BYTE);
289  if (header.ImageType == TGA_8BIT_UNCOMPRESSED)
290  {
291  file->read(img->pixels(), w*h*1);
292  }
293  else // TGA_8BIT_UNCOMPRESSED
294  {
295  int pixsize = 1;
296  int pixcount = w*h;
297  int pix = 0;
298  while(pix < pixcount)
299  {
300  unsigned char header_ch = 0;
301  file->read(&header_ch, 1);
302  if (header_ch >= 128)
303  {
304  int count = header_ch - 128 + 1;
305  unsigned char bgra[4];
306  file->read(bgra, pixsize);
307  while(count--)
308  {
309  memcpy((unsigned char*)img->pixels() + pix*pixsize, bgra, pixsize);
310  pix++;
311  }
312  }
313  else
314  {
315  int count = header_ch + 1;
316  file->read((unsigned char*)img->pixels() + pix*pixsize, pixsize*count);
317  pix += count;
318  }
319  }
320  }
321 
322  convert8ToRGBA(palette, img->pixels(), img->width(), img->height());
324  }
325  else
326  {
327  Log::error( Say("TGA ERROR: TGA_8BIT_UNCOMPRESSED entry size = %n not supported.\n") << header.ColMapEntrySize );
328  file->close();
329  return NULL;
330  }
331  }
332  else
333  {
334  Log::error( Say("TGA ERROR: TGA_8BIT_UNCOMPRESSED %nbpp bit not supported.\n") << header.BitsPerPixel );
335  file->close();
336  return NULL;
337  }
338  }
339  else
340  if (header.ImageType == TGA_GRAYSCALE_UNCOMPRESSED || header.ImageType == TGA_GRAYSCALE_COMPRESSED)
341  {
342  if (header.BitsPerPixel == 8)
343  {
344  file->seekSet(pixels_offset);
345  img->allocate2D(w, h, 1, IF_LUMINANCE, IT_UNSIGNED_BYTE);
346  if (header.ImageType == TGA_GRAYSCALE_UNCOMPRESSED)
347  {
348  file->read(img->pixels(), w*h);
349  }
350  else // TGA_GRAYSCALE_COMPRESSED
351  {
352  int pixsize = 1;
353  int pixcount = w*h;
354  int pix = 0;
355  while(pix < pixcount)
356  {
357  unsigned char header_ch = 0;
358  file->read(&header_ch, 1);
359  if (header_ch >= 128)
360  {
361  int count = header_ch - 128 + 1;
362  unsigned char bgra[4];
363  file->read(bgra, pixsize);
364  while(count--)
365  {
366  memcpy((unsigned char*)img->pixels() + pix*pixsize, bgra, pixsize);
367  pix++;
368  }
369  }
370  else
371  {
372  int count = header_ch + 1;
373  file->read((unsigned char*)img->pixels() + pix*pixsize, pixsize*count);
374  pix += count;
375  }
376  }
377  }
378  }
379  else
380  {
381  Log::error( Say("TGA ERROR: TGA_GRAYSCALE_UNCOMPRESSED %nbpp not supported.\n") << header.BitsPerPixel );
382  file->close();
383  return NULL;
384  }
385 
386  }
387  else
388  {
389  Log::error( Say("TGA ERROR: this type %n not supported.\n") << header.ImageType);
390  file->close();
391  return NULL;
392  }
393 
394  if ((header.ImageDescriptor & (1<<5)))
395  img->flipVertically();
396 
397  file->close();
398  return img;
399 }
400 //-----------------------------------------------------------------------------
401 bool vl::saveTGA(const Image* src, const String& path)
402 {
403  ref<DiskFile> file = new DiskFile(path);
404  return saveTGA(src, file.get());
405 }
406 //-----------------------------------------------------------------------------
407 bool vl::saveTGA(const Image* src, VirtualFile* fout)
408 {
409  //if (src->dimension() != ID_2D )
410  //{
411  // Log::error( Say("saveTGA('%s'): can save only 2D images.\n") << fout->path() );
412  // return false;
413  //}
414 
415  int w = src->width();
416  int h = src->height();
417  int d = src->depth();
418  if (h == 0) h=1;
419  if (d == 0) d=1;
420  if (src->isCubemap()) d=6;
421  h = h*d;
422 
423  // convert src to IT_UNSIGNED_BYTE / IF_RGBA
424  ref<Image> cimg;
425  if (src->type() != IT_UNSIGNED_BYTE)
426  {
427  cimg = src->convertType(IT_UNSIGNED_BYTE);
428  src = cimg.get();
429  if (!cimg)
430  {
431  Log::error( Say("saveTGA('%s'): could not convert image to IT_UNSIGNED_BYTE.\n") << fout->path() );
432  return false;
433  }
434  }
435  if (src->format() != IF_BGRA)
436  {
437  cimg = src->convertFormat(IF_BGRA);
438  src = cimg.get();
439  if (!cimg)
440  {
441  Log::error( Say("saveTGA('%s'): could not convert image to IF_BGRA.\n") << fout->path() );
442  return false;
443  }
444  }
445 
446  if(!fout->open(OM_WriteOnly))
447  {
448  Log::error( Say("TGA: could not write to '%s'.\n") << fout->path() );
449  return false;
450  }
451 
452  STGAHeader header;
453  memset(&header, 0, sizeof(STGAHeader));
454  // flip vertically
455  // header.ImageDescriptor |= 1<<5;
456  header.ImageType = TGA_RGB_UNCOMPRESSED;
457  header.Width_lo = (unsigned char)(w & 0x00FF);
458  header.Width_hi = (unsigned char)(w >> 8);
459  header.Height_lo = (unsigned char)(h & 0x00FF);
460  header.Height_hi = (unsigned char)(h >> 8);
461  header.BitsPerPixel = 32;
462  fout->write(&header, sizeof(header));
463 
464  fout->write(src->pixels(), src->requiredMemory());
465 
466  // TGA footer
467 
468  // extension area offset
469  fout->writeUInt32(0);
470  // developer directory offset
471  fout->writeUInt32(0);
472  // signature + "." + "0"
473  fout->write("TRUEVISION-XFILE.", 18);
474 
475  fout->close();
476  return true;
477 }
478 //-----------------------------------------------------------------------------
480 bool vl::isTGA( VirtualFile* file )
481 {
482  if (!file->open(OM_ReadOnly))
483  return false;
484 
485  STGAHeader header;
486  memset(&header, 0, sizeof(header));
487  file->read(&header, sizeof(STGAHeader) );
488 
489  char signature[17];
490  memset(signature, 0, 17);
491  file->seekEnd(-18);
492  file->read(signature, 16);
493  file->close();
494 
495  // unfortunately many TGA files are without this field
496 
497  if (strcmp("TRUEVISION-XFILE", signature) == 0)
498  return true;
499 
500  // do some heuristic checks
501 
502  switch( header.ImageType )
503  {
504  case 0:
505  case 1:
506  case 2:
507  case 3:
508  case 9:
509  case 10:
510  case 11:
511  break;
512  default:
513  return false;
514  }
515 
516  // unsigned int colmap_offset = 18 + header.IdFieldSize;
517  // unsigned int pixels_offset = colmap_offset + (header.ColMapCount_lo + header.ColMapCount_hi*256)*header.ColMapEntrySize/8;
518  unsigned int width = header.Width_lo + header.Width_hi*256;
519  unsigned int height = header.Height_lo + header.Height_hi*256;
520  unsigned int bpp = header.BitsPerPixel;
521 
522  if (width * height == 0)
523  return false;
524 
525  switch( bpp )
526  {
527  case 1:
528  case 4:
529  case 8:
530  case 16:
531  case 24:
532  case 32:
533  break;
534  default:
535  return false;
536  }
537 
538  // ends with .tga
539  if ( !file->path().toLowerCase().endsWith(".tga") )
540  return false;
541 
542  Log::warning( Say("isTGA: the file '%s' looks like a TGA but is missing the 'TRUEVISION-XFILE' signature.\n") << file->path() );
543  return true;
544 }
545 //-----------------------------------------------------------------------------
static void debug(const String &message)
Use this function to provide extra information useful to investigate and solve problems.
Definition: Log.cpp:144
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: init_core.cpp:153
VLCORE_EXPORT bool isTGA(VirtualFile *file)
A TGA file is accepted only if it has a &#39;TGA&#39; extension.
Definition: ioTGA.cpp:480
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:154
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:164
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:401
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