bitmapTga.cpp
Engine/source/gfx/bitmap/loaders/bitmapTga.cpp
Classes:
class
Public Enumerations
enum
eImageType { TypeNoData = 0 TypeUncPaletted = 1 TypeUncTruecolor = 2 TypeUncGrayscale = 3 TypeRlePaletted = 9 TypeRleTruecolor = 10 TypeRleGrayscale = 11 }
enum
ePixelMap { MapLowerLeft = 0 MapLowerRight = 1 MapUpperLeft = 2 MapUpperRight = 3 }
Public Variables
struct _privateRegisterTGA
Public Functions
Detailed Description
Public Enumerations
eImageType
Enumerator
- TypeNoData = 0
- TypeUncPaletted = 1
- TypeUncTruecolor = 2
- TypeUncGrayscale = 3
- TypeRlePaletted = 9
- TypeRleTruecolor = 10
- TypeRleGrayscale = 11
ePixelMap
Enumerator
- MapLowerLeft = 0
- MapLowerRight = 1
- MapUpperLeft = 2
- MapUpperRight = 3
Public Variables
struct _privateRegisterTGA sStaticRegisterTGA
Public Functions
sReadTGA(Stream & stream, GBitmap * bitmap)
sWriteTGA(GBitmap * bitmap, Stream & stream, U32 compressionLevel)
tga_convert_color(U32 pixel, U32 bppIn, U8 alphabits, U32 bppOut)
tga_get_pixel(Stream & stream, U8 bppIn, U8 * colormap, U8 cmapBytesEntry)
tga_write_pixel_to_mem(U8 * dat, U8 img_spec, U32 number, U32 w, U32 h, U32 pixel, U32 bppOut)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "core/stream/stream.h" 25 26#include "gfx/bitmap/gBitmap.h" 27 28 29static bool sReadTGA(Stream &stream, GBitmap *bitmap); 30static bool sWriteTGA(GBitmap *bitmap, Stream &stream, U32 compressionLevel); 31 32static struct _privateRegisterTGA 33{ 34 _privateRegisterTGA() 35 { 36 GBitmap::Registration reg; 37 38 reg.extensions.push_back( "tga" ); 39 40 reg.readFunc = sReadTGA; 41 reg.writeFunc = sWriteTGA; 42 43 GBitmap::sRegisterFormat( reg ); 44 } 45} sStaticRegisterTGA; 46 47 48//------------------------------------------------------------------------------ 49//-------------------------------------- Supplementary I/O 50// 51 52enum eImageType 53{ 54 TypeNoData = 0, 55 TypeUncPaletted = 1, 56 TypeUncTruecolor = 2, 57 TypeUncGrayscale = 3, 58 TypeRlePaletted = 9, 59 TypeRleTruecolor = 10, 60 TypeRleGrayscale = 11 61}; 62 63enum ePixelMap 64{ 65 MapLowerLeft = 0, 66 MapLowerRight = 1, 67 MapUpperLeft = 2, 68 MapUpperRight = 3, 69}; 70 71static void tga_write_pixel_to_mem( U8 * dat, U8 img_spec, U32 number, 72 U32 w, U32 h, U32 pixel, U32 bppOut ) 73{ 74 // write the pixel to the data regarding how the 75 // header says the data is ordered. 76 77 U32 x, y; 78 79 switch( (img_spec & 0x30) >> 4 ) 80 { 81 case MapLowerRight: 82 x = w - 1 - (number % w); 83 y = h - 1 - (number / w); 84 break; 85 86 case MapUpperLeft: 87 x = number % w; 88 y = number / w; 89 break; 90 91 case MapUpperRight: 92 x = w - 1 - (number % w); 93 y = number / w; 94 break; 95 96 case MapLowerLeft: 97 default: 98 x = number % w; 99 y = h - 1 - (number / w); 100 break; 101 102 } 103 104 U32 addy = (y * w + x) * bppOut; 105 for ( U32 j = 0; j < bppOut; j++ ) 106 dat[addy + j] = (U8)((pixel >> (j * 8)) & 0xFF); 107} 108 109static U32 tga_get_pixel( Stream& stream, U8 bppIn, 110 U8 * colormap, U8 cmapBytesEntry ) 111{ 112 /* get the image data value out */ 113 114 U32 tmp_int32 = 0; 115 116 for ( U32 j = 0; j < bppIn; j++ ) 117 { 118 U8 tmp_byte; 119 if ( !stream.read( &tmp_byte ) ) 120 tmp_int32 = 0; 121 else 122 tmp_int32 += tmp_byte << (j * 8); 123 } 124 125 /* byte-order correct the thing */ 126 switch( bppIn ) 127 { 128 case 2: 129 tmp_int32 = convertLEndianToHost( (U16)tmp_int32 ); 130 break; 131 132 case 3: /* intentional fall-thru */ 133 case 4: 134 tmp_int32 = convertLEndianToHost( tmp_int32 ); 135 break; 136 } 137 138 U32 tmp_col; 139 140 if ( colormap ) 141 { 142 /* need to look up value to get real color */ 143 tmp_col = 0; 144 for ( U32 j = 0; j < cmapBytesEntry; j++ ) 145 tmp_col += colormap[cmapBytesEntry * tmp_int32 + j] << (8 * j); 146 } 147 else 148 { 149 tmp_col = tmp_int32; 150 } 151 152 return tmp_col; 153} 154 155static U32 tga_convert_color( U32 pixel, U32 bppIn, U8 alphabits, U32 bppOut ) 156{ 157 // this is not only responsible for converting from different depths 158 // to other depths, it also switches BGR to RGB. 159 160 // this thing will also premultiply alpha, on a pixel by pixel basis. 161 162 U8 r, g, b, a; 163 164 switch( bppIn ) 165 { 166 case 32: 167 if ( alphabits == 0 ) 168 goto is_24_bit_in_disguise; 169 // 32-bit to 32-bit -- nop. 170 break; 171 172 case 24: 173is_24_bit_in_disguise: 174 // 24-bit to 32-bit; (only force alpha to full) 175 pixel |= 0xFF000000; 176 break; 177 178 case 15: 179is_15_bit_in_disguise: 180 r = (U8)(((F32)((pixel & 0x7C00) >> 10)) * 8.2258f); 181 g = (U8)(((F32)((pixel & 0x03E0) >> 5 )) * 8.2258f); 182 b = (U8)(((F32)(pixel & 0x001F)) * 8.2258f); 183 // 15-bit to 32-bit; (force alpha to full) 184 pixel = 0xFF000000 + (r << 16) + (g << 8) + b; 185 break; 186 187 case 16: 188 if ( alphabits == 1 ) 189 goto is_15_bit_in_disguise; 190 191 // 16-bit to 32-bit; (force alpha to full) 192 r = (U8)(((F32)((pixel & 0xF800) >> 11)) * 8.2258f); 193 g = (U8)(((F32)((pixel & 0x07E0) >> 5 )) * 4.0476f); 194 b = (U8)(((F32)(pixel & 0x001F)) * 8.2258f); 195 pixel = 0xFF000000 + (r << 16) + (g << 8) + b; 196 break; 197 } 198 199 // convert the 32-bit pixel from BGR to RGB. 200 pixel = (pixel & 0xFF00FF00) + ((pixel & 0xFF) << 16) + ((pixel & 0xFF0000) >> 16); 201 202 r = pixel & 0x000000FF; 203 g = (pixel & 0x0000FF00) >> 8; 204 b = (pixel & 0x00FF0000) >> 16; 205 a = (pixel & 0xFF000000) >> 24; 206 207 // not premultiplied alpha -- multiply. 208 r = (U8)(((F32)r / 255.0f) * ((F32)a / 255.0f) * 255.0f); 209 g = (U8)(((F32)g / 255.0f) * ((F32)a / 255.0f) * 255.0f); 210 b = (U8)(((F32)b / 255.0f) * ((F32)a / 255.0f) * 255.0f); 211 212 pixel = r + (g << 8) + (b << 16) + (a << 24); 213 214 /* now convert from 32-bit to whatever they want. */ 215 switch( bppOut ) 216 { 217 case 4: 218 // 32 to 32 -- nop. 219 break; 220 221 case 3: 222 // 32 to 24 -- discard alpha. 223 pixel &= 0x00FFFFFF; 224 break; 225 } 226 227 return pixel; 228} 229 230static bool sReadTGA(Stream &stream, GBitmap *bitmap) 231{ 232 PROFILE_SCOPE(sReadTGA); 233 struct Header 234 { 235 U8 idLength; // length of the image_id string below. 236 U8 cmapType; // paletted image <=> cmapType 237 U8 imageType; // can be any of the IMG_TYPE constants above. 238 U16 cmapFirst; // 239 U16 cmapLength; // how long the colormap is 240 U8 cmapEntrySize; // how big a palette entry is. 241 U16 xOrigin; // the x origin of the image in the image data. 242 U16 yOrigin; // the y origin of the image in the image data. 243 U16 width; // the width of the image. 244 U16 height; // the height of the image. 245 U8 pixelDepth; // the depth of a pixel in the image. 246 U8 imageDesc; // the image descriptor. 247 }; 248 249 // Read header 250 Header header; 251 stream.read( &header.idLength ); 252 stream.read( &header.cmapType ); 253 stream.read( &header.imageType ); 254 stream.read( &header.cmapFirst ); 255 stream.read( &header.cmapLength ); 256 stream.read( &header.cmapEntrySize ); 257 stream.read( &header.xOrigin ); 258 stream.read( &header.yOrigin ); 259 stream.read( &header.width ); 260 stream.read( &header.height ); 261 stream.read( &header.pixelDepth ); 262 stream.read( &header.imageDesc ); 263 264 U32 numPixels = header.width * header.height; 265 if ( numPixels == 0 ) 266 { 267 //Con::errorf( "Texture has width and/or height set to 0" ); 268 return false; 269 } 270 271 U8 alphabits = header.imageDesc & 0x0F; 272 273 /* seek past the image id, if there is one */ 274 if ( header.idLength ) 275 { 276 if ( !stream.setPosition( stream.getPosition() + header.idLength ) ) 277 { 278 //Con::errorf( "Unexpected end of stream encountered" ); 279 return false; 280 } 281 } 282 283 /* if this is a 'nodata' image, just jump out. */ 284 if ( header.imageType == TypeNoData ) 285 { 286 //Con::errorf( "Texture contains no data" ); 287 return false; 288 } 289 290 /* deal with the colormap, if there is one. */ 291 U8* colormap = NULL; 292 U32 cmapBytes = 0; 293 U8 cmapBytesEntry = 0; 294 295 if ( header.cmapType ) 296 { 297 switch( header.imageType ) 298 { 299 case TypeUncPaletted: 300 case TypeRlePaletted: 301 break; 302 303 case TypeUncTruecolor: 304 case TypeRleTruecolor: 305 // this should really be an error, but some really old 306 // crusty targas might actually be like this (created by TrueVision, no less!) 307 // so, we'll hack our way through it. 308 break; 309 310 case TypeUncGrayscale: 311 case TypeRleGrayscale: 312 //Con::errorf( "Found colormap for a grayscale image" ); 313 return false; 314 } 315 316 /* ensure colormap entry size is something we support */ 317 if ( !(header.cmapEntrySize == 15 || 318 header.cmapEntrySize == 16 || 319 header.cmapEntrySize == 24 || 320 header.cmapEntrySize == 32) ) 321 { 322 //Con::errorf( "Unsupported colormap entry size" ); 323 return false; 324 } 325 326 /* allocate memory for a colormap */ 327 if ( header.cmapEntrySize & 0x07 ) 328 cmapBytesEntry = (((8 - (header.cmapEntrySize & 0x07)) + header.cmapEntrySize) >> 3); 329 else 330 cmapBytesEntry = (header.cmapEntrySize >> 3); 331 332 cmapBytes = cmapBytesEntry * header.cmapLength; 333 colormap = new U8[ cmapBytes ]; 334 335 for ( U32 i = 0; i < header.cmapLength; i++ ) 336 { 337 /* seek ahead to first entry used */ 338 if ( header.cmapFirst != 0 ) 339 stream.setPosition( stream.getPosition() + header.cmapFirst * cmapBytesEntry ); 340 341 U32 tmp_int32 = 0; 342 for ( U32 j = 0; j < cmapBytesEntry; j++ ) 343 { 344 U8 tmp_byte; 345 if ( !stream.read( &tmp_byte ) ) 346 { 347 delete [] colormap; 348 //Con::errorf( "Bad colormap" ); 349 return false; 350 } 351 tmp_int32 += tmp_byte << (j * 8); 352 } 353 354 // byte order correct. 355 tmp_int32 = convertLEndianToHost( tmp_int32 ); 356 357 for ( U32 j = 0; j < cmapBytesEntry; j++ ) 358 colormap[i * cmapBytesEntry + j] = (tmp_int32 >> (8 * j)) & 0xFF; 359 } 360 } 361 362 // compute number of bytes in an image data unit (either index or BGR triple) 363 U8 inBytesPerPixel = 0; 364 if ( header.pixelDepth & 0x07 ) 365 inBytesPerPixel = (((8 - (header.pixelDepth & 0x07)) + header.pixelDepth) >> 3); 366 else 367 inBytesPerPixel = (header.pixelDepth >> 3); 368 369 /* assume that there's one byte per pixel */ 370 if ( inBytesPerPixel == 0 ) 371 inBytesPerPixel = 1; 372 373 GFXFormat gfxFmt; 374 U32 outBytesPerPixel; 375 switch ( header.pixelDepth ) 376 { 377 case 32: 378 gfxFmt = GFXFormatR8G8B8A8; 379 outBytesPerPixel = 4; 380 break; 381 382 case 24: 383 default: 384 gfxFmt = GFXFormatR8G8B8; 385 outBytesPerPixel = 3; 386 break; 387 } 388 389 bitmap->allocateBitmap( header.width, header.height, false, gfxFmt ); 390 391 // compute the true number of bits per pixel 392 U8 trueBitsPerPixel = header.cmapType ? header.cmapEntrySize : header.pixelDepth; 393 394 // Override the number of alpha bits if necessary 395 // Some apps generate transparent TGAs with alphabits set to 0 in the image descriptor 396 if ( ( trueBitsPerPixel == 32 ) && ( alphabits == 0 ) ) 397 alphabits = 8; 398 399 switch( header.imageType ) 400 { 401 case TypeUncTruecolor: 402 case TypeUncGrayscale: 403 case TypeUncPaletted: 404 405 /* FIXME: support grayscale */ 406 407 for ( U32 i = 0; i < numPixels; i++ ) 408 { 409 // get the color value. 410 U32 tmp_col = tga_get_pixel( stream, inBytesPerPixel, colormap, cmapBytesEntry ); 411 tmp_col = tga_convert_color( tmp_col, trueBitsPerPixel, alphabits, outBytesPerPixel ); 412 413 // now write the data out. 414 tga_write_pixel_to_mem( bitmap->getAddress( 0, 0 ), header.imageDesc, 415 i, header.width, header.height, tmp_col, outBytesPerPixel ); 416 } 417 break; 418 419 case TypeRleTruecolor: 420 case TypeRleGrayscale: 421 case TypeRlePaletted: 422 423 // FIXME: handle grayscale.. 424 425 for ( U32 i = 0; i < numPixels; ) 426 { 427 /* a bit of work to do to read the data.. */ 428 U8 packet_header; 429 if ( !stream.read( 1, &packet_header ) ) 430 { 431 // well, just let them fill the rest with null pixels then... 432 packet_header = 1; 433 } 434 435 if ( packet_header & 0x80 ) 436 { 437 /* run length packet */ 438 U32 tmp_col = tga_get_pixel( stream, inBytesPerPixel, colormap, cmapBytesEntry ); 439 tmp_col = tga_convert_color( tmp_col, trueBitsPerPixel, alphabits, outBytesPerPixel ); 440 441 U8 repcount = (packet_header & 0x7F) + 1; 442 443 /* write all the data out */ 444 for ( U32 j = 0; j < repcount; j++ ) 445 { 446 tga_write_pixel_to_mem( bitmap->getAddress( 0, 0 ), header.imageDesc, 447 i + j, header.width, header.height, tmp_col, outBytesPerPixel ); 448 } 449 450 i += repcount; 451 452 } 453 else 454 { 455 /* raw packet */ 456 /* get pixel from file */ 457 U8 repcount = (packet_header & 0x7F) + 1; 458 459 for ( U32 j = 0; j < repcount; j++ ) 460 { 461 U32 tmp_col = tga_get_pixel( stream, inBytesPerPixel, colormap, cmapBytesEntry ); 462 tmp_col = tga_convert_color( tmp_col, trueBitsPerPixel, alphabits, outBytesPerPixel ); 463 464 tga_write_pixel_to_mem( bitmap->getAddress( 0, 0 ), header.imageDesc, 465 i + j, header.width, header.height, tmp_col, outBytesPerPixel ); 466 } 467 468 i += repcount; 469 } 470 } 471 break; 472 473 default: 474 //Con::errorf( "Unknown image type" ); 475 return false; 476 } 477 478 delete [] colormap; 479 480 // 32-bit tgas have an alpha channel 481 bitmap->setHasTransparency( header.pixelDepth == 32 ); 482 483 return true; 484} 485 486static bool sWriteTGA(GBitmap *bitmap, Stream &stream, U32 compressionLevel) 487{ 488 AssertISV(false, "GBitmap::writeTGA - doesn't support writing tga files!"); 489 490 return false; 491} 492