/*============================================================================= TgaImage.cpp : TGA image file format implementation. Copyright (c) 2001 Crytek Studios. All Rights Reserved. Revision history: * Created by Khonich Andrey =============================================================================*/ #include "RenderPCH.h" #include "CImage.h" #include "TgaImage.h" #define MAXCOLORS 16384 static int mapped, rlencoded; static SRGBPixel ColorMap[MAXCOLORS]; static int RLE_count = 0, RLE_flag = 0; static void readtga ( byte*& ptr, struct SImageHeader* tgaP ); static void get_map_entry ( byte*& ptr, SRGBPixel* Value, int Size ); static void get_pixel ( byte*& ptr, SRGBPixel* dest, int Size ); static byte getbyte ( byte*& ptr ); CImageTgaFile::~CImageTgaFile () { } CImageTgaFile::CImageTgaFile (byte* ptr, long filesize) : CImageFile () { (void)filesize; struct SImageHeader tga_head; int i; unsigned int temp1, temp2; int rows, cols, row, col, realrow, truerow, baserow; int maxval; SRGBPixel *pixels; /* @@@ to do: Add TGA format detection */ /* Read the Targa file header. */ readtga (ptr, &tga_head); rows = ( (int) tga_head.Height_lo ) + ( (int) tga_head.Height_hi ) * 256; cols = ( (int) tga_head.Width_lo ) + ( (int) tga_head.Width_hi ) * 256; m_eFormat = eIF_Tga; switch ( tga_head.ImgType ) { case TGA_Map: case TGA_RGB: case TGA_Mono: case TGA_RLEMap: case TGA_RLERGB: case TGA_RLEMono: break; default: mfSet_error (eIFE_BadFormat, "Unknown Targa image type"); return; } if ( tga_head.ImgType == TGA_Map || tga_head.ImgType == TGA_RLEMap || tga_head.ImgType == TGA_CompMap || tga_head.ImgType == TGA_CompMap4 ) { /* Color-mapped image */ if ( tga_head.CoMapType != 1 ) { mfSet_error (eIFE_BadFormat, "Mapped image with bad color map type"); return; } mapped = 1; /* Figure maxval from CoSize. */ switch ( tga_head.CoSize ) { case 8: case 24: case 32: maxval = 255; break; case 15: case 16: maxval = 31; break; default: mfSet_error (eIFE_BadFormat, "Unknown colormap pixel size"); return; } } else { /* Not colormap, so figure maxval from PixelSize. */ mapped = 0; switch ( tga_head.PixelSize ) { case 8: case 24: case 32: maxval = 255; break; case 15: case 16: maxval = 31; break; default: mfSet_error (eIFE_BadFormat, "Unknown pixel size"); return; } } mfSet_bps(tga_head.PixelSize); /* If required, read the color map information. */ if ( tga_head.CoMapType != 0 ) { temp1 = tga_head.Index_lo + tga_head.Index_hi * 256; temp2 = tga_head.Length_lo + tga_head.Length_hi * 256; if ( ( temp1 + temp2 + 1 ) >= MAXCOLORS ) { mfSet_error (eIFE_BadFormat, "Too many colors in colormap"); return; } for ( i = temp1; i < (int)( temp1 + temp2 ); ++i ) get_map_entry( ptr, &ColorMap[i], (int) tga_head.CoSize ); } /* Check run-length encoding. */ if ( tga_head.ImgType == TGA_RLEMap || tga_head.ImgType == TGA_RLERGB || tga_head.ImgType == TGA_RLEMono ) rlencoded = 1; else rlencoded = 0; /* Read the Targa file body and convert to portable format. */ mfSet_dimensions (cols, rows); mfSet_ImageSize(cols * rows * 4); pixels = (SRGBPixel *)mfGet_image(); truerow = 0; baserow = 0; for ( row = 0; row < rows; ++row ) { realrow = truerow; if ( tga_head.OrgBit == 0 ) realrow = rows - realrow - 1; for ( col = 0; col < cols; ++col ) get_pixel( ptr, &(pixels[realrow*cols+col]), (int) tga_head.PixelSize ); if ( tga_head.IntrLve == TGA_IL_Four ) truerow += 4; else if ( tga_head.IntrLve == TGA_IL_Two ) truerow += 2; else ++truerow; if ( truerow >= rows ) truerow = ++baserow; } } static void readtga (byte*& ptr, SImageHeader* tgaP) { byte flags; tgaP->IDLength = getbyte( ptr ); tgaP->CoMapType = getbyte( ptr ); tgaP->ImgType = getbyte( ptr ); tgaP->Index_lo = getbyte( ptr ); tgaP->Index_hi = getbyte( ptr ); tgaP->Length_lo = getbyte( ptr ); tgaP->Length_hi = getbyte( ptr ); tgaP->CoSize = getbyte( ptr ); tgaP->X_org_lo = getbyte( ptr ); tgaP->X_org_hi = getbyte( ptr ); tgaP->Y_org_lo = getbyte( ptr ); tgaP->Y_org_hi = getbyte( ptr ); tgaP->Width_lo = getbyte( ptr ); tgaP->Width_hi = getbyte( ptr ); tgaP->Height_lo = getbyte( ptr ); tgaP->Height_hi = getbyte( ptr ); tgaP->PixelSize = getbyte( ptr ); flags = getbyte( ptr ); tgaP->AttBits = flags & 0xf; tgaP->Rsrvd = ( flags & 0x10 ) >> 4; tgaP->OrgBit = ( flags & 0x20 ) >> 5; tgaP->IntrLve = ( flags & 0xc0 ) >> 6; if ( tgaP->IDLength != 0 ) ptr += tgaP->IDLength; } static void get_map_entry (byte*& ptr, SRGBPixel* Value, int Size) { byte j, k, r, g, b; r=g=b=0; /* get rid of 'might be used uninited' warning */ /* Read appropriate number of bytes, break into rgb & put in map. */ switch ( Size ) { case 8: /* Grey scale, read and triplicate. */ r = g = b = getbyte( ptr ); break; case 16: /* 5 bits each of red green and blue. */ case 15: /* Watch for byte order. */ j = getbyte( ptr ); k = getbyte( ptr ); r = ( k & 0x7C ) >> 2; g = ( ( k & 0x03 ) << 3 ) + ( ( j & 0xE0 ) >> 5 ); b = j & 0x1F; break; case 32: case 24: /* 8 bits each of blue green and red. */ b = getbyte( ptr ); g = getbyte( ptr ); r = getbyte( ptr ); if ( Size == 32 ) (void) getbyte( ptr ); /* Read alpha byte & throw away. */ break; default: //mfSet_error (eIFE_BadFormat, "Unknown colormap pixel size"); return; } Value->red=r; Value->green=g; Value->blue=b; } static void get_pixel (byte*& ptr, SRGBPixel* dest, int Size) { static int Red, Grn, Blu, Alpha; byte j, k; static unsigned int l; if (Size != 32) Alpha = 255; /* Check if run length encoded. */ if ( rlencoded ) { if ( RLE_count == 0 ) { /* Have to restart run. */ byte i; i = getbyte( ptr ); RLE_flag = ( i & 0x80 ); if ( RLE_flag == 0 ) /* Stream of unencoded pixels. */ RLE_count = i + 1; else /* Single pixel replicated. */ RLE_count = i - 127; /* Decrement count & get pixel. */ --RLE_count; } else { /* Have already read count & (at least) first pixel. */ --RLE_count; if ( RLE_flag != 0 ) /* Replicated pixels. */ goto PixEncode; } } /* Read appropriate number of bytes, break into RGB. */ switch ( Size ) { case 8: /* Grey scale, read and triplicate. */ Red = Grn = Blu = l = getbyte( ptr ); break; case 16: /* 5 bits each of red green and blue. */ case 15: /* Watch byte order. */ j = getbyte( ptr ); k = getbyte( ptr ); l = ( (unsigned int) k << 8 ) + j; Red = ( k & 0x7C ) >> 2; Grn = ( ( k & 0x03 ) << 3 ) + ( ( j & 0xE0 ) >> 5 ); Blu = j & 0x1F; break; case 32: case 24: /* 8 bits each of blue green and red. */ Blu = getbyte( ptr ); Grn = getbyte( ptr ); Red = getbyte( ptr ); if ( Size == 32 ) Alpha = getbyte( ptr ); /* Read alpha byte & throw away. */ l = 0; break; default: //mfSet_error (eIFE_BadFormat, "Unknown pixel size"); return; } PixEncode: if ( mapped ) *dest = ColorMap[l]; else { dest->red=Red;dest->green=Grn;dest->blue=Blu; dest->alpha = Alpha; } } static byte getbyte (byte*& ptr) { byte c = *ptr++; return c; } //============================================================= #if !defined(LINUX) #include #include #endif static FILE *sFileData; static int src_bits_per_pixel; static void bwrite(unsigned char data) { fputc(data, sFileData); } void wwrite(unsigned short data) { unsigned char h, l; l = data & 0xFF; h = data >> 8; bwrite(l); bwrite(h); } static void WritePixel(int depth, unsigned long a, unsigned long r, unsigned long g, unsigned long b) { DWORD color16; switch(depth) { case 32: bwrite((byte)b); // b bwrite((byte)g); // g bwrite((byte)r); // r bwrite((byte)a); // a break; case 24: bwrite((byte)b); // b bwrite((byte)g); // g bwrite((byte)r); // r break; case 16: r >>= 3; g >>= 3; b >>= 3; r &= 0x1F; g &= 0x1F; b &= 0x1F; color16 = (r << 10) | (g << 5) | b; wwrite((unsigned short)color16); break; } } static void GetPixel(unsigned char * data, int depth, unsigned long &a, unsigned long &r, unsigned long &g, unsigned long &b) { switch(depth) { case 32: r = *data++; g = *data++; b = *data++; a = *data++; break; case 24: r = *data++; g = *data++; b = *data++; a = 0xFF; break; default: assert(0); break; } } void WriteTGA8(byte *data8, int width, int height, char *filename) { unsigned char * data32 = new unsigned char [width*height*4]; for(int i=0; i