Skip to main content
GameDev.net gamedev.net
🔒 Locked

TGA File Loader

Started by Nacho Jul 18, 2003 at 8:47 PM 3 replies 1.1k views
Original Post
Nacho
Nacho
Hi there! I´m making a function to load a 32 x 32 pixel TGA file (with an alpha channel) and store it in a structure. Since I already know that all the TGA files I´ll load with that function are 32x32 pixels, I´ve decided to optimize the function a little bit by skiping all the header information and extracting just the color data. The function is quite short and the data structure is very simple but, although I´ve spent countless hours trying to make it work, I´m afraid I have not been successful since the data is not properly loaded inside the data structure. Can somebody help me? I´ll post the function and the data structure: typedef struct _Tile { unsigned char ucImageData[32*32*3]; // Allocate space for the R,G,B channels. unsigned char ucCollisionMask[32*32]; // Allocate space for the alpha channel. } Tile,*lpTile; int LoadTileTGA(char *lpFileName,Tile &rTile) { FILE *lpFile; int iCounter,iCounter2,iCounter3; unsigned char buffer[32*32*4]; lpFile = fopen(lpFileName,"rb"); if(!lpFile) return 0; fseek(lpFile,-(4096),SEEK_END); // 32*32*4 = 4096 fread(buffer,sizeof(unsigned char),4096,lpFile); for(iCounter=0,iCounter2=0,iCounter3=0;iCounter<1024; iCounter+=4,iCounter2+=3,iCounter3++) { rTile.ucImageData[iCounter2] = buffer[iCounter+2]; rTile.ucImageData[iCounter2+1] = buffer[iCounter+1]; rTile.ucImageData[iCounter2+2] = buffer[iCounter]; rTile.ucCollisionMask[iCounter3] = buffer[iCounter+3]; } // End for iCounter. return 1; } // End LoadTileTGA(). Thanks in advance for your help!! --Ignacio Liverotti iliverotti@hotmail.com
S1CA
S1CA
Take a look at the TGA format specification.

1) Any software made after 1989 is very likely to use the extended version of TGA. The extensions include a file footer , i.e. at the END of the file, AFTER the image data.

2) Surely the offset you''re passing to fseek() should be positive because the origin you''ve specified is the END of the file.

3) As you''ll also find in the file format, as well as the footer, TGA also supports other information after the image data. For example: Author Name, Comments, Scanline table, Postage stamp image, Colour correction table etc in the Extension area [after the image data]. There is also the [possible] Developer area before the extension area [but after the image data]. The only way to tell which are present in the file is to look at the footer and header.

4) The offsets from the start of the file to the image data can be variable because they''re in the header - so the possibility is there for you to find palette data AFTER the image data for example.

5a) If you want to access a file with no regard to format specific information you should use raw files. Or better, use binary PPM/PBM/PGM files - those are raw files with an extremely simple header (you can save a PPM file including header to disk with two lines of C code!).

5b) Making assumptions about strong file formats like TGA is a bad idea for future compatibility (ie. you release the code, someone saves a TGA using a different art package than you did, then the "working" code blows up for no apparent reason). If you want to access in a simpler way, use a simpler file format.

--
Simon O''Connor
ex -Creative Asylum
Programmer &
Microsoft MVP
Simon O'Connor | Technical Director (Newcastle) Lockwood Publishing | LinkedIn | Personal site
Nacho
Nacho
Thanks for your answer S1CA! Your tips were very helpful so I decided to re-write the function reading the header first and then extracting the color data. The problem is that the new function seems to have the same problem than the previous version, that is, the first pixel is correctly extracted but the subsequent ones are not. I´ll be very glad if you can find the mistake in this one, Once again, while you read it keep in mind that it is designed to load 32x32 pixels TGA images with an alpha channel:

int LoadTileTGA(char *lpFileName,Tile &rTile)
{
FILE *lpFile;
int iCounter,iCounter2,ic3;
short int sib;
unsigned char ucb,imagetypecode;

lpFile = fopen(lpFileName,"rb");
if(!lpFile)
return 0;

fread(&ucb,sizeof(unsigned char),1,lpFile);
fread(&ucb,sizeof(unsigned char),1,lpFile);

fread(&imagetypecode,sizeof(unsigned char),1,lpFile);

fread(&sib,sizeof(short int),1,lpFile);
fread(&sib,sizeof(short int),1,lpFile);
fread(&ucb,sizeof(unsigned char),1,lpFile);
fread(&sib,sizeof(short int),1,lpFile);
fread(&sib,sizeof(short int),1,lpFile);

short int width,height;

fread(&width,sizeof(short int),1,lpFile);
fread(&height,sizeof(short int),1,lpFile);

unsigned char bitcount;

fread(&bitcount,sizeof(unsigned char),1,lpFile);
fread(&ucb,sizeof(unsigned char),1,lpFile);

int colormode = bitcount / 8;

unsigned char buffer[32*32*4]; // buffer for the whole image.

fread(buffer,sizeof(unsigned char),32*32*4,lpFile);

for(iCounter=0,iCounter2=0,ic3=0;iCounter<4096;iCounter+=4,iCounter2+=3,ic3++) { // 32*32*4 = 4096
rTile.ucImageData[iCounter2] = buffer[iCounter+2];
rTile.ucImageData[iCounter2+1] = buffer[iCounter+1];
rTile.ucImageData[iCounter2+2] = buffer[iCounter];
rTile.ucCollisionMask[ic3] = buffer[iCounter+3];
} // End for iCounter.

return 1;
} // End LoadTileTGA().

Thanks again for all your help!

--Ignacio Liverotti
iliverotti@hotmail.com
foofightr
foofightr
Maybe your TGA file is compressed. I don''t see where you''re taking that into account.

Anyways, here is a straight dump of my TGA loader that I wrote a few years ago. It works fine, but do not hold me accountable for the coding style

You won''t be able to compile this straight up, because it has slight dependencies on other code, but it shouldn''t be too hard to figure those out.


// by Brian Segall


//

// tga header

typedef struct TGA_h_s
{
byte id_length;
byte color_map_type;
byte image_type;
u16 color_map_origin;
u16 color_map_length;
byte color_map_entry_size;
u16 x_origin;
u16 y_origin;
u16 width;
u16 height;
byte pixel_depth;
byte image_descriptor;
} TGA_h;

//

// TGA v2.0 extension area

typedef struct TGA_ext_s
{
u16 extension_size;
char author_name[41];
char author_comments[324];
u16 date_time_stamp[6];
char job_name[41];
u16 job_time[3];
char software_id[41];
char software_version[3];
u32 key_color;
u16 pixel_aspect_ratio[2];
u16 gamma_value[2];
u32 color_correction_offset;
u32 postage_stamp_offset;
u32 scan_line_offset;
byte attributes_type;
} TGA_ext;

//

// TGA developer area

typedef struct TGA_dev_s
{
// variablelength fields

byte *developer_fields;
byte *developer_directory;
} TGA_dev;

//

// TGA file footer

typedef struct TGA_f_s
{
u32 extension_area_offset;
u32 developer_directory_offset;
char signature[18];
} TGA_f;

//

// tga struct

typedef struct TGA_s {
TGA_h header;
byte *image_id;
byte *color_map_data;
byte *image_data;
TGA_dev developer;
TGA_ext extension;
byte *scan_line_table;
byte *postage_stamp_image;
u16 color_correction_table[1024];
TGA_f footer;
RGB *palette;
byte *data;
} TGA;

//

// Reads a .tga file and stores info in TGA pointer

//

static int _LoadTGA (byte *filebuf, u32 filelen, TGA *tga)
{
int i, j;
TGA_h &h = tga->header; // header shortcut

int bytes_pp; // bytes per pixel shortcut

int width, height, area;
byte *src, *dest;
byte *imgbuf;
byte *uncompressed_buf;


// read header

memcpy(&tga->header, filebuf, sizeof(TGA_h));
tga->image_id = filebuf + sizeof(TGA_h);
tga->color_map_data = tga->image_id + h.id_length;
tga->image_data = tga->color_map_data + h.color_map_length * ROUND_UP(h.color_map_entry_size, 8) / 8;

bytes_pp = ROUND_UP(h.pixel_depth, 8) / 8;
width = h.width;
height = h.height;
area = width * height;

// see if it has a palette

if (h.color_map_type == 1)
{
tga->palette = new RGB[h.color_map_length];
byte *palinfo = tga->color_map_data;
for (i=0 ; i<h.color_map_length ; i++)
{
tga->palette[i + h.color_map_origin].blue = *palinfo++;
tga->palette[i + h.color_map_origin].green = *palinfo++;
tga->palette[i + h.color_map_origin].red = *palinfo++;
}
}
else // it doesn''t have one

{
tga->palette = NULL;
}

// allocate memory for the image data

imgbuf = new byte[area * bytes_pp];
if (imgbuf == NULL)
return 0;

// if there''s not even any image, don''t bother

if (h.image_type == 0)
{
delete[] imgbuf;
return 0;
}

// RLE compression, summary:

// 1st byte (packet) --> if bit 7 is 1, it''s RLE packet, if not, it''s raw packet

// RLE packet = bits 0-6 tell how many pixels (+1) in a row are the same in the source image

// = next byte (or more depending on bitdepth) tells what the consecutive pixel value actually is

// RAW packet = bits 0-6 tell how many pixels (+1) in a row are different in the source image

// = next series of data contains the pixel data (length of this = pixels encoded * pixel depth)

if (h.image_type == 9 || h.image_type == 10 || h.image_type == 11)
{
byte *compressed_buf;
byte *compressed_buf_end;
byte *uncompressed_buf_ptr;
int bytes_encoded;
int pixels_encoded;
byte packet;
byte pixel_value[16]; // now if you have more than 128-bit color, you''re screwed ;)


compressed_buf = tga->image_data;
compressed_buf_end = filebuf + filelen;

uncompressed_buf = new byte[area * bytes_pp];
uncompressed_buf_ptr = uncompressed_buf;

// uncompress whole file

while (compressed_buf < compressed_buf_end)
{
packet = *compressed_buf++;
pixels_encoded = (packet & 0x7F) + 1; // look at 7-bit only, and add 1 (since you can''t encode 0 bytes)

bytes_encoded = pixels_encoded * bytes_pp;

// RLE encoded packet

if (packet & 0x80)
{
// copy pixel

for (i=0 ; i<bytes_pp ; i++)
pixel_value[i] = compressed_buf[i];

// keep writing it out to the uncompressed stream (up to 128 times)

for (i=0 ; i<pixels_encoded ; i++)
{
for (j=0 ; j<bytes_pp ; j++)
{
*uncompressed_buf_ptr++ = pixel_value[j];
}
}

compressed_buf += bytes_pp;
}
// raw packet

else
{
memcpy(uncompressed_buf_ptr, compressed_buf, bytes_encoded);
compressed_buf += bytes_encoded;
uncompressed_buf_ptr += bytes_encoded;
}
}
}
else // no decompression required, so just point to uncompressed buffer (from file)

{
uncompressed_buf = tga->image_data;
}

// read in uncompressed data stream

if (TEST_BIT(h.image_descriptor, 0x20)) // starting from the top

{
if (TEST_BIT(h.image_descriptor, 0x10)) // starting from the right

{ // ugh...

dest = imgbuf;
src = &uncompressed_buf[(width * bytes_pp) - bytes_pp];
for (i=0 ; i<height ; i++)
{
for (j=0 ; j<width ; j++)
{
memcpy(dest, src, bytes_pp);
dest += bytes_pp;
src -= bytes_pp;
}
src += 2 * width * bytes_pp;
}
}
else
{ // straight copy (starting from top-left)

memcpy(imgbuf, uncompressed_buf, area * bytes_pp);
}
}
else // starting from the bottom

{
if (TEST_BIT(h.image_descriptor, 0x10))
{ // right

dest = &imgbuf[(area * bytes_pp) - bytes_pp];
src = &uncompressed_buf[(width * bytes_pp) - bytes_pp];
for (i=0 ; i<height ; i++)
{
for (j=0 ; j<width ; j++)
{
memcpy(dest, src, bytes_pp);
dest -= bytes_pp;
src -= bytes_pp;
}
src += 2 * width * bytes_pp;
}
}
else
{ // left

// (this is the one Paint Shop Pro 4.14 uses)

src = uncompressed_buf;
for (i=height-1 ; i>=0 ; i--)
{
dest = &imgbuf[i * width * bytes_pp];
memcpy(dest, src, width * bytes_pp);
src += width * bytes_pp;
}
}
}

// if we had to decompress, then delete the temporary buffer we used for decompression

if (h.image_type == 9 || h.image_type == 10 || h.image_type == 11)
{
delete[] uncompressed_buf;
}

tga->data = new byte[area * colorBytes];
if (tga->data == NULL)
return 0;
_ConvertImageBpp(colorDepth, h.pixel_depth, tga->palette, tga->data, imgbuf, area);

delete[] imgbuf;
return 1;
}

Nacho
Nacho
Thanks for your help foofightr! Fortunately after a couple of hours the function worked fine. Thanks for your answers again!

--Ignacio Liverotti
iliverotti@hotmail.com

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.