bitmapTga.cpp

Engine/source/gfx/bitmap/loaders/bitmapTga.cpp

More...

Classes:

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

bool
sReadTGA(Stream & stream, GBitmap * bitmap)
bool
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)

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