PDA

View Full Version : Loading a Compressed TGA



nukem
09-27-2002, 02:15 PM
I followed the NeHe tutorial to load TGA files, but I get some errors. I changed the code around a little bit because I'm making this class load other file formats. When I load uncompressed TGA files it works fine but when I try to load a compressed file I get the error "Error reading pixel" I've gone over the code countless times and I see nothing wrong with it.

Can someone please help me?

Thanks,
Nuke




class TEXTURE
{
private:

typedef struct
{
GLubyte Header[6]; //holds the first 6 useful bytes of the file
GLuint bytesPerPixel; //number of BYTES Per Pixel (3 or 4)
GLuint imagesize; //amount of memory needed to hold the image
GLuint temp;
GLuint type; //the type of image, GL_RGB or GL_RGBA
GLuint h; //height of image
GLuint w; //width of image
GLuint bpp; //number of BITS Per Pixel (24, or 32)
} TGA;

TGA tga; //used to store file info

bool LoadUncompressedTGA(FILE* file)
{
if(fread(tga.Header, sizeof(tga.Header), 1, file) == 0)
{
fprintf(stderr, "Error Opening UnCompressed TGA File\n");
return false;
}

x = tga.Header[1] * 256 + tga.Header[0];
y = tga.Header[3] * 256 + tga.Header[2];
bpp = tga.Header[4];
tga.w = x;
tga.h = y;
tga.bpp = bpp;

if((x <= 0) &amp;#0124; &amp;#0124; (y <= 0) &amp;#0124; &amp;#0124; ((bpp != 24) &amp;&amp; (bpp != 32)))
{
fprintf(stderr, "Error x, y, and/or, bpp has in invalid value\n");
return false;
}

if(bpp == 24)
{
type = GL_RGB;
}else{
type = GL_RGBA;
}

tga.bytesPerPixel = (tga.bpp / 8);
tga.imagesize = (tga.bytesPerPixel * tga.w * tga.h);

//data = (GLubyte*)malloc(tga.imagesize);
data = (char*)malloc(tga.imagesize);

if(data == 0)
{
fprintf(stderr, "Error in memory!");
return false;
}

if(fread(data, 1, tga.imagesize, file) != tga.imagesize)
{
fprintf(stderr, "Image not the right size\n", file);
return false;
}

return true;
}

bool LoadCompressedTGA(FILE* file)
{
if(fread(tga.Header, sizeof(tga.Header), 1, file) == 0)
{
fprintf(stderr, "Error Opening UnCompressed TGA File\n");
return false;
}

x = tga.Header[1] * 256 + tga.Header[0];
y = tga.Header[3] * 256 + tga.Header[2];
bpp = tga.Header[4];
tga.w = x;
tga.h = y;
tga.bpp = bpp;

if((x <= 0) &amp;#0124; &amp;#0124; (y <= 0) &amp;#0124; &amp;#0124; ((bpp != 24) &amp;&amp; (bpp != 32)))
{
fprintf(stderr, "Error x, y, and/or, bpp has in invalid value\n");
return false;
}

if(bpp == 24)
{
type = GL_RGB;
}else{
type = GL_RGBA;
}

tga.bytesPerPixel = (tga.bpp / 8);
tga.imagesize = (tga.bytesPerPixel * tga.w * tga.h);

//data = (GLubyte*)malloc(tga.imagesize);
data = (char*)malloc(tga.imagesize);

if(data == 0)
{
fprintf(stderr, "Error in memory!\n");
return false;
}

GLuint pixelcount = tga.h * tga.w; //number of pixels in the image
GLuint currentpixel = 0; //current pixel we are reading data from
GLuint currentbyte = 0; //current byte we are writing into imagedata

//try char ! GLubyte
GLubyte* colorbuffer = (GLubyte*)malloc(tga.bytesPerPixel); //storage for one pixel

do
{
GLubyte chunkheader = 0; //var to store the value of the id chunk

if(fread(&amp;chunkheader, sizeof(GLubyte), 1, file) == 0) //try to read the chunks header
{
fprintf(stderr, "Error reading chunks header!\n");
return false;
}

if(chunkheader < 128) //if the chunk is a RAW chunk
{
chunkheader++; //add 1 to the value to get the total number of raw pixels

//start pixel reading loop
for(short counter = 0; counter < chunkheader; counter++)
{
//try to read 1 pixel
if(fread(colorbuffer, 1, tga.bytesPerPixel, file) != tga.bytesPerPixel)
{
fprintf(stderr, "Error reading pixel 1\n"); //the error is here
return false;
}

data[currentbyte] = colorbuffer[2]; //write the 'R' byte
data[currentbyte + 1] = colorbuffer[1]; //write the 'G' byte
data[currentbyte + 2] = colorbuffer[0]; //write the 'B' byte

//if its a 32bpp image
if(tga.bytesPerPixel == 4)
{
data[currentbyte + 3] = colorbuffer[3]; //write the 'A' byte
}

currentbyte += tga.bytesPerPixel; //increment teh byte counter by the number of bytes per pixel
currentpixel++; //increment by 1

}

}else{

chunkheader -= 127; //subtract 127 to get rid of the id bit

//read the next pixel
if(fread(colorbuffer, 1, tga.bytesPerPixel, file) != tga.bytesPerPixel)
{
fprintf(stderr, "Error Reading Pixel 2\n");
return false;
}

//start the loop
for(short counter =0; counter < chunkheader; counter++)
{
data[currentbyte] = colorbuffer[2]; //cpy the 'R' byte
data[currentbyte + 1] = colorbuffer[1]; //cpy the 'G' byte
data[currentbyte + 2] = colorbuffer[0]; // cpy the 'B' byte

if(tga.bytesPerPixel == 4)
{
data[currentbyte + 3] = colorbuffer[3];
}
}

currentbyte += tga.bytesPerPixel;
currentpixel++;
}

}while(currentpixel < pixelcount);

fclose(file);

return true;
}

public:

unsigned long x;
unsigned long y;
unsigned short int bpp;
char* data;
unsigned int ID;
GLuint type;

TEXTURE()
{
type = GL_RGB;
}

bool LoadTGA(char* FileName)
{
//find out if its compressed or not
GLubyte tgaheader[12];
//uncompressed tga header
GLubyte uTGAcompare[12] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};
//compressed tga header
GLubyte cTGAcompare[12] = {0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0 ,0};
FILE* file;

file = fopen(FileName, "rb");

if(file == 0)
{
fprintf(stderr, "Error invalid TGA file: %s\n", FileName);
return false;
}

if(fread(&amp;tgaheader, sizeof(tgaheader), 1, file) == 0)
{
fprintf(stderr, "Error invalid TGA file: %s\n", FileName);
fclose(file);
return false;
}

if(memcmp(uTGAcompare, &amp;tgaheader, sizeof(tgaheader)) == 0)
{
fprintf(stderr, "loading uncompressed...\n");
//load an uncompressed tga
if(LoadUncompressedTGA(file) == false)
{
fprintf(stderr, "Error Loading UnCompressed TGA File: %s\n", FileName);
fclose(file);
return false;
}
}else if(memcmp(cTGAcompare, &amp;tgaheader, sizeof(tgaheader)) == 0)
{
fprintf(stderr, "loading compressed...\n");
//load compressed tga
if(LoadCompressedTGA(file) == false)
{
fprintf(stderr, "Error Loading Compressed TGA File: %s\n", FileName);
fclose(file);
return false;
}
}else{
fprintf(stderr, "Error invalid TGA file: %s\n", FileName);
fclose(file);
return false;
}

fclose(file);
return true;
}
/*
"Both width and height must have the form 2^m + 2b, where m is a non-negative integer (which can have a
different value for width than for height) and b is the value of border. The maximum size of a texture
map depends on the implementation of OpenGL, but it must be at least 64 x 64 (or 66 x 66 with borders)."
--OpenGL Programming Guide

Meaning:

You can use width or height of 2, 4, 8, 16, 32, 64, (on new video cards) 128, 256, 512 (They dont have
to be the same)

If you have a texture border you add 2 to the size.
*/
int LoadTexture(char* FileName)
{

if(LoadTGA(FileName) == 0)
{
return 0;
}

glGenTextures(1, &amp;ID);
glBindTexture(GL_TEXTURE_2D, ID);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexImage2D(GL_TEXTURE_2D, 0, 3, x, y, 0, type, GL_UNSIGNED_BYTE, data);

return 1;
}
};

Nutty
09-27-2002, 02:18 PM
dunno m8.

try this code. works for rle compressed and uncompressd 24 and 32 bit tga's




int LoadTargaFile(const char *const filename, TGA_FILE *const targa)
{/*Loads a targa file.*/

FILE *fin;
int i, bytes_per_pixel;
unsigned char r, g, b, a;
unsigned char id, length;


//Attempt to open the file.
if((fin = fopen(filename, "rb")) == NULL)
{
return TGA_FAILED_OPEN;
}


//read in the header.
fread(&amp;targa->header, sizeof(TGA_HEADER), 1, fin);



//Check a few things out.
if(targa->header.color_map_type != 0)
{
return TGA_HAS_COLOR_MAP;
}


if(targa->header.image_type_code != 2 &amp;&amp; targa->header.image_type_code != 10)
{
return TGA_NOT_TYPE_2_OR_10;
}


//Must be 24 bit images.
if(targa->header.image_info.pixel_size != 24)
{
return TGA_NOT_24_BIT;
}


bytes_per_pixel = targa->header.image_info.pixel_size / 8;

//allocate some memory for the string id.
if(targa->header.id_length) //If there is one at all.
{
targa->image_id_field = new char [targa->header.id_length];

if(targa->image_id_field == NULL)
{
return FAILED_ID_ALLOCATION;
}


//read in the string id.
fread(targa->image_id_field, sizeof(char), targa->header.id_length, fin);
}
else
{
targa->image_id_field = NULL;
}


targa->image_data = new unsigned char [targa->header.image_info.width * targa->header.image_info.height * bytes_per_pixel];

if(targa->image_data == NULL)
{
return FAILED_DATA_ALLOCATION;
}

//Read in the data, there is no color map, so the next thing to get should be the data.
targa->color_map = NULL;


if(targa->header.image_type_code == 2)
{
//Just read int he data.
fread(targa->image_data, sizeof(unsigned char), targa->header.image_info.width*targa->header.image_info.height*bytes_per_pixel, fin);
}
else
{
//RLE decompression.

for(i=0; i<targa->header.image_info.width*targa->header.image_info.height*bytes_per_pixel; )
{
id = fgetc(fin);
//If bit 7 is set it's a run length data.
if(id & 128)
{
//Find length.
length = id - 127; //Remove high bit (128) and add 1.
//Get the next 3 bytes, which should be the values to duplicate.
r = fgetc(fin);
g = fgetc(fin);
b = fgetc(fin);
if(bytes_per_pixel == 4)
{
a = fgetc(fin);
}

//Do a loop to write the number of pels out.
while(length > 0)
{
targa->image_data[i++] = r;
targa->image_data[i++] = g;
targa->image_data[i++] = b;
if(bytes_per_pixel == 4)
{
targa->image_data[i++] = a;
}

length--;
}

}
else
{
//Find number of pixels to get.
length = id + 1;

while(length > 0)
{
r = fgetc(fin);
g = fgetc(fin);
b = fgetc(fin);
if(bytes_per_pixel == 4)
{
a = fgetc(fin);
}


targa->image_data[i++] = r;
targa->image_data[i++] = g;
targa->image_data[i++] = b;
if(bytes_per_pixel == 4)
{
targa->image_data[i++] = a;
}

length--;
}
}
}
}
//Close the file.
fclose(fin);


//convert the data to RGB. Green doesn't move, so don't bother copying.
for(i=0; i<targa->header.image_info.width*targa->header.image_info.height*bytes_per_pixel; i+=bytes_per_pixel)
{
b = targa->image_data[i+0];
//g = targa->image_data[i+1];
r = targa->image_data[i+2];
//a stays same place too.

targa->image_data[i+0] = r;
//targa->image_data[i+1] = g;
targa->image_data[i+2] = b;
//a stays same place too.
}


return 0;
}


Nutty

Miguel_dup1
09-28-2002, 08:27 AM
try my code from http://cheo.resnet.wayne.edu/miguel/

jwatte
09-28-2002, 08:45 AM
The bug is probably in this code inside LoadCompressedTGA():




//start the loop
for(short counter =0; counter < chunkheader; counter++) {
data[currentbyte] = colorbuffer[2]; //cpy the 'R' byte
data[currentbyte + 1] = colorbuffer[1]; //cpy the 'G' byte
data[currentbyte + 2] = colorbuffer[0]; // cpy the 'B' byte
if(tga.bytesPerPixel == 4) {
data[currentbyte + 3] = colorbuffer[3];
}
}
currentbyte += tga.bytesPerPixel;
currentpixel++;


The last two lines, which increment currentbyte and currentpixel, should probably run once per pixel you un-pack. Thus, they should go inside that for() block, rather than outside.

There may be other bugs as well, though this is a good first try.

When it comes to loading images, I'd suggest just using DevIL and be done with it; it's a decent enough image loader that loads a wide range of popular image formats.

The other alternative would be to roll your own .dds loader, as .dds supports uncompressed images as well as s3tc compressed images, and the s3tc compressed images can be immediately shuffled out to the card without re-compression, leading to a rather nice and fast load time (as well as rendering faster, as reading s3tc compressed images is less work for the card).

There are a bunch of other things in your code that you might want to think about:

Why fread() a byte at a time? I'd read the entire thing into memory and walk it with a pointer when parsing. MUCH more efficient.

Once you do that, you probably want to separate file loading from format un-packing, which will lead to a more flexible design.

Why malloc() space for ONE pixel? You know that one pixel won't be more than 4 bytes; thus, you can just declare 4 bytes of storage on the stack if you need space for one pixel.