Visualization Library v1.0.3A lightweight C++ OpenGL middleware for 2D/3D graphics |
[Download] [Tutorials] [All Classes] [Grouped Classes] |
00001 /**************************************************************************************/ 00002 /* */ 00003 /* Visualization Library */ 00004 /* http://visualizationlibrary.org */ 00005 /* */ 00006 /* Copyright (c) 2005-2010, Michele Bosi */ 00007 /* All rights reserved. */ 00008 /* */ 00009 /* Redistribution and use in source and binary forms, with or without modification, */ 00010 /* are permitted provided that the following conditions are met: */ 00011 /* */ 00012 /* - Redistributions of source code must retain the above copyright notice, this */ 00013 /* list of conditions and the following disclaimer. */ 00014 /* */ 00015 /* - Redistributions in binary form must reproduce the above copyright notice, this */ 00016 /* list of conditions and the following disclaimer in the documentation and/or */ 00017 /* other materials provided with the distribution. */ 00018 /* */ 00019 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */ 00020 /* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */ 00021 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */ 00022 /* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */ 00023 /* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */ 00024 /* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */ 00025 /* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */ 00026 /* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */ 00027 /* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */ 00028 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 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; /* at offset 18 it starts, usually 0 */ 00058 unsigned char HasColMap; /* 1 for indexed images, 0 otherwise */ 00059 unsigned char ImageType; /* see defines above */ 00060 unsigned char ColMapOrigin[2]; 00061 unsigned char ColMapCount_lo; 00062 unsigned char ColMapCount_hi; 00063 unsigned char ColMapEntrySize; /* 16, 24, 32 */ 00064 unsigned char ImageOrigins[4]; /* lo/hi bytes for x/y origins */ 00065 unsigned char Width_lo; 00066 unsigned char Width_hi; 00067 unsigned char Height_lo; 00068 unsigned char Height_hi; 00069 unsigned char BitsPerPixel; /* 8/16(?), 16, 24, 32 */ 00070 unsigned char ImageDescriptor; /* origin | alpha channel bits */ 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); // break; 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); // break; 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 // TGA_8BIT_UNCOMPRESSED 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 // TGA_8BIT_UNCOMPRESSED 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 // TGA_GRAYSCALE_COMPRESSED 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 //if (src->dimension() != ID_2D ) 00410 //{ 00411 // Log::error( Say("saveTGA('%s'): can save only 2D images.\n") << fout->path() ); 00412 // return false; 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 // convert src to IT_UNSIGNED_BYTE / IF_RGBA 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 // flip vertically 00455 // header.ImageDescriptor |= 1<<5; 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 // TGA footer 00467 00468 // extension area offset 00469 fout->writeUInt32(0); 00470 // developer directory offset 00471 fout->writeUInt32(0); 00472 // signature + "." + "0" 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 // unfortunately many TGA files are without this field 00496 00497 if (strcmp("TRUEVISION-XFILE", signature) == 0) 00498 return true; 00499 00500 // do some heuristic checks 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 // unsigned int colmap_offset = 18 + header.IdFieldSize; 00517 // unsigned int pixels_offset = colmap_offset + (header.ColMapCount_lo + header.ColMapCount_hi*256)*header.ColMapEntrySize/8; 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 // ends with .tga 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 //-----------------------------------------------------------------------------