00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "ioTGA.hpp"
00033 #include <vlCore/LoadWriterManager.hpp>
00034 #include <vlCore/VisualizationLibrary.hpp>
00035 #include <vlCore/VisualizationLibrary.hpp>
00036 #include <vlCore/FileSystem.hpp>
00037 #include <vlCore/DiskFile.hpp>
00038 #include <vlCore/Image.hpp>
00039
00040 using namespace vl;
00041
00042 #include <vlCore/ImageTools.hpp>
00043
00044
00045 namespace
00046 {
00047 const unsigned long TGA_NO_IMAGE_DATA = 0;
00048 const unsigned long TGA_8BIT_UNCOMPRESSED = 1;
00049 const unsigned long TGA_RGB_UNCOMPRESSED = 2;
00050 const unsigned long TGA_GRAYSCALE_UNCOMPRESSED = 3;
00051 const unsigned long TGA_8BIT_COMPRESSED = 9;
00052 const unsigned long TGA_RGB_COMPRESSED = 10;
00053 const unsigned long TGA_GRAYSCALE_COMPRESSED = 11;
00054
00055 typedef struct
00056 {
00057 unsigned char IdFieldSize;
00058 unsigned char HasColMap;
00059 unsigned char ImageType;
00060 unsigned char ColMapOrigin[2];
00061 unsigned char ColMapCount_lo;
00062 unsigned char ColMapCount_hi;
00063 unsigned char ColMapEntrySize;
00064 unsigned char ImageOrigins[4];
00065 unsigned char Width_lo;
00066 unsigned char Width_hi;
00067 unsigned char Height_lo;
00068 unsigned char Height_hi;
00069 unsigned char BitsPerPixel;
00070 unsigned char ImageDescriptor;
00071 } STGAHeader;
00072 }
00073
00088 ref<Image> vl::loadTGA( const String& path )
00089 {
00090 ref<VirtualFile> file = defFileSystem()->locateFile(path);
00091 if ( !file )
00092 {
00093 Log::error( Say("File '%s' not found.\n") << path );
00094 return NULL;
00095 }
00096
00097 return loadTGA( file.get() );
00098 }
00099
00100 ref<Image> vl::loadTGA( VirtualFile* file )
00101 {
00102 if ( !file->open(OM_ReadOnly) )
00103 {
00104 Log::error( Say("loadTGA: cannot load TGA file '%s'\n") << file->path() );
00105 return NULL;
00106 }
00107
00108 ref<Image> img = new Image;
00109 img->setObjectName(file->path().toStdString().c_str());
00110
00111 STGAHeader header;
00112 memset(&header, 0, sizeof(header));
00113 file->read( &header, sizeof(STGAHeader) );
00114
00115 unsigned int colmap_offset = 18 + header.IdFieldSize;
00116 unsigned int pixels_offset = colmap_offset +
00117 (header.ColMapCount_lo + header.ColMapCount_hi*256)*header.ColMapEntrySize/8;
00118 unsigned int w = header.Width_lo + header.Width_hi*256;
00119 unsigned int h = header.Height_lo + header.Height_hi*256;
00120
00121 #ifndef NDEBUG
00122 unsigned int bpp = header.BitsPerPixel;
00123 const char *type = "";
00124 switch(header.ImageType)
00125 {
00126 case TGA_NO_IMAGE_DATA: type = "TGA_NO_IMAGE_DATA"; break;
00127 case TGA_8BIT_UNCOMPRESSED: type = "TGA_8BIT_UNCOMPRESSED"; break;
00128 case TGA_RGB_UNCOMPRESSED: type = "TGA_RGB_UNCOMPRESSED"; break;
00129 case TGA_GRAYSCALE_UNCOMPRESSED: type = "TGA_GRAYSCALE_UNCOMPRESSED"; break;
00130 case TGA_8BIT_COMPRESSED: type = "TGA_8BIT_COMPRESSED"; break;
00131 case TGA_RGB_COMPRESSED: type = "TGA_RGB_COMPRESSED"; break;
00132 case TGA_GRAYSCALE_COMPRESSED: type = "TGA_GRAYSCALE_COMPRESSED"; break;
00133 }
00134 Log::debug( Say("TGA %s: w=%n, h=%n, bpp=%n/%n %s\n") << file->path() << w << h << bpp << header.ColMapEntrySize << type);
00135 #endif
00136
00137 if (header.ImageType == TGA_NO_IMAGE_DATA)
00138 return NULL;
00139
00140 if (header.ImageType == TGA_RGB_COMPRESSED)
00141 {
00142 int pixsize = 0;
00143 switch(header.BitsPerPixel)
00144 {
00145 case 32: pixsize = 4; break;
00146 case 24: pixsize = 3; break;
00147 case 16: pixsize = 2; break;
00148 }
00149
00150 if (pixsize)
00151 {
00152 img->allocate2D(w, h, 4, IF_RGBA, IT_UNSIGNED_BYTE);
00153
00154 file->seekSet(pixels_offset);
00155 int pixcount = w*h;
00156 int pix = 0;
00157 while(pix < pixcount)
00158 {
00159 unsigned char header_ch = 0;
00160 file->read(&header_ch, 1);
00161 if (header_ch >= 128)
00162 {
00163 int count = header_ch - 128 + 1;
00164 unsigned char bgra[4];
00165 file->read(bgra, pixsize);
00166 while(count--)
00167 {
00168 memcpy((unsigned char*)img->pixels() + pix*pixsize, bgra, pixsize);
00169 pix++;
00170 }
00171 }
00172 else
00173 {
00174 int count = header_ch + 1;
00175 file->read((unsigned char*)img->pixels() + pix*pixsize, pixsize*count);
00176 pix += count;
00177 }
00178 }
00179
00180 switch(header.BitsPerPixel)
00181 {
00182 case 24: convertRGBToRGBA(img->pixels(), img->width(), img->height(), 0xFF);
00183 case 32: swapBytes32_BGRA_RGBA(img->pixels(), img->requiredMemory()); break;
00184 case 16: convertA1R5G5B5ToRGBA(img->pixels(), w*h, 0xFF); break;
00185 }
00186
00187 }
00188 else
00189 {
00190 Log::error( Say("TGA ERROR: TGA_RGB_COMPRESSED %nbpp not supported.\n") << header.BitsPerPixel );
00191 file->close();
00192 return NULL;
00193 }
00194
00195 }
00196 else
00197 if (header.ImageType == TGA_RGB_UNCOMPRESSED)
00198 {
00199 int pixsize = 0;
00200
00201 switch(header.BitsPerPixel)
00202 {
00203 case 32: pixsize = 4; break;
00204 case 24: pixsize = 3; break;
00205 case 16: pixsize = 2; break;
00206 }
00207
00208 if (pixsize)
00209 {
00210 file->seekSet(pixels_offset);
00211 img->allocate2D(w, h, 4, IF_RGBA, IT_UNSIGNED_BYTE);
00212 file->read(img->pixels(), w*h*pixsize);
00213 switch(header.BitsPerPixel)
00214 {
00215 case 24: convertRGBToRGBA(img->pixels(), img->width(), img->height(), 0xFF);
00216 case 32: swapBytes32_BGRA_RGBA(img->pixels(), img->requiredMemory()); break;
00217 case 16: convertA1R5G5B5ToRGBA(img->pixels(), w*h, 0xFF); break;
00218 }
00219 }
00220 else
00221 {
00222 Log::error( Say("TGA ERROR: TGA_RGB_UNCOMPRESSED %nbpp not supported.\n") << header.BitsPerPixel );
00223 file->close();
00224 return NULL;
00225 }
00226
00227 }
00228 else
00229 if (header.ImageType == TGA_8BIT_UNCOMPRESSED || header.ImageType == TGA_8BIT_COMPRESSED)
00230 {
00231 if (header.BitsPerPixel == 8)
00232 {
00233
00234 unsigned int colmap_count = header.ColMapCount_lo + header.ColMapCount_hi*256;
00235 VL_CHECK(colmap_count<=256);
00236 if (header.ColMapEntrySize == 24)
00237 {
00238 TPalette3x256 palette;
00239 file->seekSet(colmap_offset);
00240 file->read(palette, colmap_count*3);
00241
00242 file->seekSet(pixels_offset);
00243
00244 img->allocate2D(w, h, 4, IF_RGBA, IT_UNSIGNED_BYTE);
00245 if (header.ImageType == TGA_8BIT_UNCOMPRESSED)
00246 {
00247 file->read(img->pixels(), w*h*1);
00248 }
00249 else
00250 {
00251 int pixsize = 1;
00252 int pixcount = w*h;
00253 int pix = 0;
00254 while(pix < pixcount)
00255 {
00256 unsigned char header_ch = 0;
00257 file->read(&header_ch, 1);
00258 if (header_ch >= 128)
00259 {
00260 int count = header_ch - 128 + 1;
00261 unsigned char bgra[4];
00262 file->read(bgra, pixsize);
00263 while(count--)
00264 {
00265 memcpy((unsigned char*)img->pixels() + pix*pixsize, bgra, pixsize);
00266 pix++;
00267 }
00268 }
00269 else
00270 {
00271 int count = header_ch + 1;
00272 file->read((unsigned char*)img->pixels() + pix*pixsize, pixsize*count);
00273 pix += count;
00274 }
00275 }
00276 }
00277
00278 convert8ToRGBA(palette, img->pixels(), img->width(), img->height(), 0xFF);
00279 swapBytes32_BGRA_RGBA(img->pixels(), img->requiredMemory());
00280 }
00281 else if (header.ColMapEntrySize == 32)
00282 {
00283 TPalette4x256 palette;
00284 file->seekSet(colmap_offset);
00285 file->read(palette, colmap_count*4);
00286
00287 file->seekSet( pixels_offset );
00288 img->allocate2D(w, h, 4, IF_RGBA, IT_UNSIGNED_BYTE);
00289 if (header.ImageType == TGA_8BIT_UNCOMPRESSED)
00290 {
00291 file->read(img->pixels(), w*h*1);
00292 }
00293 else
00294 {
00295 int pixsize = 1;
00296 int pixcount = w*h;
00297 int pix = 0;
00298 while(pix < pixcount)
00299 {
00300 unsigned char header_ch = 0;
00301 file->read(&header_ch, 1);
00302 if (header_ch >= 128)
00303 {
00304 int count = header_ch - 128 + 1;
00305 unsigned char bgra[4];
00306 file->read(bgra, pixsize);
00307 while(count--)
00308 {
00309 memcpy((unsigned char*)img->pixels() + pix*pixsize, bgra, pixsize);
00310 pix++;
00311 }
00312 }
00313 else
00314 {
00315 int count = header_ch + 1;
00316 file->read((unsigned char*)img->pixels() + pix*pixsize, pixsize*count);
00317 pix += count;
00318 }
00319 }
00320 }
00321
00322 convert8ToRGBA(palette, img->pixels(), img->width(), img->height());
00323 swapBytes32_BGRA_RGBA(img->pixels(), img->requiredMemory());
00324 }
00325 else
00326 {
00327 Log::error( Say("TGA ERROR: TGA_8BIT_UNCOMPRESSED entry size = %n not supported.\n") << header.ColMapEntrySize );
00328 file->close();
00329 return NULL;
00330 }
00331 }
00332 else
00333 {
00334 Log::error( Say("TGA ERROR: TGA_8BIT_UNCOMPRESSED %nbpp bit not supported.\n") << header.BitsPerPixel );
00335 file->close();
00336 return NULL;
00337 }
00338 }
00339 else
00340 if (header.ImageType == TGA_GRAYSCALE_UNCOMPRESSED || header.ImageType == TGA_GRAYSCALE_COMPRESSED)
00341 {
00342 if (header.BitsPerPixel == 8)
00343 {
00344 file->seekSet(pixels_offset);
00345 img->allocate2D(w, h, 1, IF_LUMINANCE, IT_UNSIGNED_BYTE);
00346 if (header.ImageType == TGA_GRAYSCALE_UNCOMPRESSED)
00347 {
00348 file->read(img->pixels(), w*h);
00349 }
00350 else
00351 {
00352 int pixsize = 1;
00353 int pixcount = w*h;
00354 int pix = 0;
00355 while(pix < pixcount)
00356 {
00357 unsigned char header_ch = 0;
00358 file->read(&header_ch, 1);
00359 if (header_ch >= 128)
00360 {
00361 int count = header_ch - 128 + 1;
00362 unsigned char bgra[4];
00363 file->read(bgra, pixsize);
00364 while(count--)
00365 {
00366 memcpy((unsigned char*)img->pixels() + pix*pixsize, bgra, pixsize);
00367 pix++;
00368 }
00369 }
00370 else
00371 {
00372 int count = header_ch + 1;
00373 file->read((unsigned char*)img->pixels() + pix*pixsize, pixsize*count);
00374 pix += count;
00375 }
00376 }
00377 }
00378 }
00379 else
00380 {
00381 Log::error( Say("TGA ERROR: TGA_GRAYSCALE_UNCOMPRESSED %nbpp not supported.\n") << header.BitsPerPixel );
00382 file->close();
00383 return NULL;
00384 }
00385
00386 }
00387 else
00388 {
00389 Log::error( Say("TGA ERROR: this type %n not supported.\n") << header.ImageType);
00390 file->close();
00391 return NULL;
00392 }
00393
00394 if ((header.ImageDescriptor & (1<<5)))
00395 img->flipVertically();
00396
00397 file->close();
00398 return img;
00399 }
00400
00401 bool vl::saveTGA(const Image* src, const String& path)
00402 {
00403 ref<DiskFile> file = new DiskFile(path);
00404 return saveTGA(src, file.get());
00405 }
00406
00407 bool vl::saveTGA(const Image* src, VirtualFile* fout)
00408 {
00409
00410
00411
00412
00413
00414
00415 int w = src->width();
00416 int h = src->height();
00417 int d = src->depth();
00418 if (h == 0) h=1;
00419 if (d == 0) d=1;
00420 if (src->isCubemap()) d=6;
00421 h = h*d;
00422
00423
00424 ref<Image> cimg;
00425 if (src->type() != IT_UNSIGNED_BYTE)
00426 {
00427 cimg = src->convertType(IT_UNSIGNED_BYTE);
00428 src = cimg.get();
00429 if (!cimg)
00430 {
00431 Log::error( Say("saveTGA('%s'): could not convert image to IT_UNSIGNED_BYTE.\n") << fout->path() );
00432 return false;
00433 }
00434 }
00435 if (src->format() != IF_BGRA)
00436 {
00437 cimg = src->convertFormat(IF_BGRA);
00438 src = cimg.get();
00439 if (!cimg)
00440 {
00441 Log::error( Say("saveTGA('%s'): could not convert image to IF_BGRA.\n") << fout->path() );
00442 return false;
00443 }
00444 }
00445
00446 if(!fout->open(OM_WriteOnly))
00447 {
00448 Log::error( Say("TGA: could not write to '%s'.\n") << fout->path() );
00449 return false;
00450 }
00451
00452 STGAHeader header;
00453 memset(&header, 0, sizeof(STGAHeader));
00454
00455
00456 header.ImageType = TGA_RGB_UNCOMPRESSED;
00457 header.Width_lo = (unsigned char)(w & 0x00FF);
00458 header.Width_hi = (unsigned char)(w >> 8);
00459 header.Height_lo = (unsigned char)(h & 0x00FF);
00460 header.Height_hi = (unsigned char)(h >> 8);
00461 header.BitsPerPixel = 32;
00462 fout->write(&header, sizeof(header));
00463
00464 fout->write(src->pixels(), src->requiredMemory());
00465
00466
00467
00468
00469 fout->writeUInt32(0);
00470
00471 fout->writeUInt32(0);
00472
00473 fout->write("TRUEVISION-XFILE.", 18);
00474
00475 fout->close();
00476 return true;
00477 }
00478
00480 bool vl::isTGA( VirtualFile* file )
00481 {
00482 if (!file->open(OM_ReadOnly))
00483 return false;
00484
00485 STGAHeader header;
00486 memset(&header, 0, sizeof(header));
00487 file->read(&header, sizeof(STGAHeader) );
00488
00489 char signature[17];
00490 memset(signature, 0, 17);
00491 file->seekEnd(-18);
00492 file->read(signature, 16);
00493 file->close();
00494
00495
00496
00497 if (strcmp("TRUEVISION-XFILE", signature) == 0)
00498 return true;
00499
00500
00501
00502 switch( header.ImageType )
00503 {
00504 case 0:
00505 case 1:
00506 case 2:
00507 case 3:
00508 case 9:
00509 case 10:
00510 case 11:
00511 break;
00512 default:
00513 return false;
00514 }
00515
00516
00517
00518 unsigned int width = header.Width_lo + header.Width_hi*256;
00519 unsigned int height = header.Height_lo + header.Height_hi*256;
00520 unsigned int bpp = header.BitsPerPixel;
00521
00522 if (width * height == 0)
00523 return false;
00524
00525 switch( bpp )
00526 {
00527 case 1:
00528 case 4:
00529 case 8:
00530 case 16:
00531 case 24:
00532 case 32:
00533 break;
00534 default:
00535 return false;
00536 }
00537
00538
00539 if ( !file->path().toLowerCase().endsWith(".tga") )
00540 return false;
00541
00542 Log::warning( Say("isTGA: the file '%s' looks like a TGA but is missing the 'TRUEVISION-XFILE' signature.\n") << file->path() );
00543 return true;
00544 }
00545