Skip to main content
gamedev.net
🔒 Locked

Having problems with TGA loader

Started by p4n1c Dec 22, 2002 at 1:31 PM 6 replies 600+ views
Original Post
p4n1c
p4n1c
Hi all, What I have done is taken the TGA loading tutorial on gametutorials.com and switched to using fstream instead of the standard C file routines. The uncompressed TGA displays fine, but the compressed one just shows a band of RGB noise on the top part and blank on the bottom. My load function:
  
// Load a TGA file

bool OdinTGA::loadtga(const char *filename) {
	short width = 0, height = 0;
	char length = 0;						// Length in bytes of pixels

	char iType = 0;							// Type of image(RLE, RGB, RGBA)

	char bits = 0;							// Bits per pixel of image

	int channels = 0;						// Number of channels

	int stride = 0;							// channels * width


	ifstream tga(filename, ios::in | ios::binary);
	if(tga.is_open() == false)
		return false;

	// Allocate memory for tgadata structure

	tgadata = new otga_s[sizeof(otga_s)];

	// Read in useful header information

	tga.read(&length, sizeof(char));	// Read in the length of bytes from the header

	tga.seekg(1, ios::cur);				// Move ahead one byte

	tga.read(&iType, sizeof(char));		// Read in image type

	tga.seekg(9, ios::cur);				// Move ahead nine bytes

	// Read in width, height and bpp

	tga.read((char *)&width, sizeof(short));
	tga.read((char *)&height, sizeof(short));
	tga.read((char *)&bits, sizeof(char));
	// Move to the image data

	tga.seekg(length + 1, ios::cur);

	// Check if the image is RLE compressed or not

	if(iType != TGA_RLE) {
		// Check if the image is a 24 or 32-bit image

		if(bits == 24 || bits == 32) {
			// Calculate the channels(3 or 4)

			// Then calculate stride and allocate memory for pixels

			channels = bits / 8;
			stride = channels * width;
			tgadata->data = new unsigned char[stride * height];

			// Load in the image data line by line

			for(int y = 0; y < height; y++) {
				// Store a pointer to current line

				unsigned char *tmpline = &(tgadata->data[stride * y]);

				tga.read((char *)tmpline, stride);
				// Byte swapping between blue and red values

				for(int i = 0; i < stride; i += channels) {
					int temp		= tmpline[i];
					tmpline[i]		= tmpline[i + 2];
					tmpline[i + 2]	= temp;
				}
			}
		} else if(bits == 16) {
			unsigned short pixels = 0;
			int r = 0, g = 0, b = 0;

			// Hardcode the channels since we are converting to 24-bit

			channels = 3;
			stride = channels * width;
			tgadata->data = new unsigned char[stride * height];

			for(int i = 0; i < width * height; i++) {
				tga.read((char *)&pixels, sizeof(unsigned short));

				// To convert we need to mask to isolate each colour value

				// 0x1F = 11111 in binary, since 5 bits are reserved in

				// each unsigned short for the R, G and B, we bit shift and

				// mask.  Shift up by 3 to get the full colour.

				b = (pixels & 0x1F) << 3;
				g = ((pixels >> 5) & 0x1F) << 3;
				r = ((pixels >> 10) & 0x1F) << 3;

				tgadata->data[i * 3 + 0] = r;
				tgadata->data[i * 3 + 1] = g;
				tgadata->data[i * 3 + 2] = b;
			}
		} else
			return false;
	} else {
		// Run length encoded TGA file

		char rleid = 0;
		int coloursread = 0;
		int i = 0;
		channels = bits / 8;
		stride = channels * width;

		tgadata->data = new unsigned char[stride * height];
		char *colours = new char[channels];

		// Load in image data

		while(i < width * height) {
			// Read in current colour count + 1

			tga.read(&rleid, sizeof(char));

			// Is the string of colours encoded?

			if(rleid < 128) {
				rleid++;

				while(rleid) {
					tga.read(colours, sizeof(char) * channels);

					// Store the current pixel in our image array

					tgadata->data[coloursread + 0] = colours[2];
					tgadata->data[coloursread + 1] = colours[1];
					tgadata->data[coloursread + 2] = colours[0];

					if(bits == 32)
						tgadata->data[coloursread + 3] = colours[3];

					i++;
					rleid--;
					coloursread += channels;
				}
			} else {
				// Minus the 128 ID + 1(127) to get the colour count that needs

				// to be read

				rleid -= 127;

				// Read in current colour

				tga.read(colours, sizeof(char) * channels);

				while(rleid) {
					tgadata->data[coloursread + 0] = colours[2];
					tgadata->data[coloursread + 1] = colours[1];
					tgadata->data[coloursread + 2] = colours[0];

					if(bits == 32)
						tgadata->data[coloursread + 3] = colours[3];

					i++;
					rleid--;
					coloursread += channels;
				}
			}
		}
	}

	tga.close();
	tgadata->channels = channels;
	tgadata->width	= width;
	tgadata->height	= height;

	return true;
}
  
My create GL textures function:
  
// Create an OpenGL texture from TGA

void OdinTGA::creategltex(const char *filename, int texID) {
	if(!filename)
		return;

	if(loadtga(filename) == false)
		return;

	glGenTextures(1, &tID);			// Generate a texture

	glBindTexture(GL_TEXTURE_2D, tID);

	// Set texture type to GL_RGB as default

	int tType = GL_RGB;
	if(tgadata->channels == 4)
		tType = GL_RGBA;

	// Build mipmaps

	gluBuild2DMipmaps(GL_TEXTURE_2D, tgadata->channels, tgadata->width, tgadata->height, tType, GL_UNSIGNED_BYTE, tgadata->data);

	// Setup quality of texture map

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);

	// Free image data

	if(tgadata) {
		if(tgadata->data)
			delete[] tgadata->data;
		delete tgadata;
	}
}
  
Is there anything I''m doing wrong in either of those functions, that it would create the noise for the compressed texture? Please don''t nitpick the code unless it''s related to this problem. Thanks in advance ----- For I have seen the face of evil and it will not soon be forgotten.
-----For I have seen the face of evil and it will not soon be forgotten.
p4n1c
p4n1c
Nevermind, fixed. Problem with using a wrong type.
-----For I have seen the face of evil and it will not soon be forgotten.
xg0blin
xg0blin
Mind letting me in on the trick? I tried to do the same thing and got the same kind of problem. What did you do to fix it?
p4n1c
p4n1c
I misinterpreted "byte" as being just a char. It's actually an unsigned char in the code. So if you look at rleid, it's assigned as a char, it should be an unsigned char. That completely cleared up the problem.

This:

      
char rleid = 0;
int coloursread = 0;
int i = 0;
channels = bits / 8;
stride = channels * width;

tgadata->data = new unsigned char[stride * height];
char *colours = new char[channels];

// Load in image data

while(i < width * height) {
// Read in current colour count + 1

tga.read(&rleid, sizeof(char));

// Is the string of colours encoded?

if(rleid < 128) {
rleid++;

while(rleid) {
tga.read(colours, sizeof(char) * channels);

// Store the current pixel in our image array

tgadata->data[coloursread + 0] = colours[2];
tgadata->data[coloursread + 1] = colours[1];
tgadata->data[coloursread + 2] = colours[0];

if(bits == 32)
tgadata->data[coloursread + 3] = colours[3];

i++;
rleid--;
coloursread += channels;
}
} else {
// Minus the 128 ID + 1(127) to get the colour count that needs

// to be read

rleid -= 127;

// Read in current colour

tga.read(colours, sizeof(char) * channels);

while(rleid) {
tgadata->data[coloursread + 0] = colours[2];
tgadata->data[coloursread + 1] = colours[1];
tgadata->data[coloursread + 2] = colours[0];

if(bits == 32)
tgadata->data[coloursread + 3] = colours[3];

i++;
rleid--;
coloursread += channels;
}
}
}
}


Should be this(you'll notice rleid and colours have been changed to unsigned chars and the fstream read has been casted because of the change to unsigned):

  
// Run length encoded TGA file

unsigned char rleid = 0;
int coloursread = 0;
int i = 0;
channels = bits / 8;
stride = channels * width;

tgadata->data = new unsigned char[stride * height];
unsigned char *colours = new unsigned char[channels];

// Load in image data

while(i < width * height) {
// Read in current colour count + 1

tga.read((char *)&rleid, sizeof(unsigned char));

// Is the string of colours encoded?

if(rleid < 128) {
rleid++;

while(rleid) {
tga.read((char *)colours, sizeof(unsigned char) * channels);

// Store the current pixel in our image array

tgadata->data[coloursread + 0] = colours[2];
tgadata->data[coloursread + 1] = colours[1];
tgadata->data[coloursread + 2] = colours[0];

if(bits == 32)
tgadata->data[coloursread + 3] = colours[3];

i++;
rleid--;
coloursread += channels;
}
} else {
// Minus the 128 ID + 1(127) to get the colour count that needs

// to be read

rleid -= 127;

// Read in current colour

tga.read((char *)colours, sizeof(unsigned char) * channels);

while(rleid) {
tgadata->data[coloursread + 0] = colours[2];
tgadata->data[coloursread + 1] = colours[1];
tgadata->data[coloursread + 2] = colours[0];

if(bits == 32)
tgadata->data[coloursread + 3] = colours[3];

i++;
rleid--;
coloursread += channels;
}
}
}


[edited by - p4n1c on December 22, 2002 6:11:35 PM]
-----For I have seen the face of evil and it will not soon be forgotten.
xg0blin
xg0blin
I tried it a little differently. I tried to use

file >> stuff >> morestuff

and of course it didn''t work. I then discovered c++ also has a read function (not that you can''t use fread of course), but I am more comfortable using c++ style io. I was under the impression that you couldn''t use the ifstream::read function because the first argument is a char *, and not a void * or template. I thought I''d have to do a cast on every data type so I thought that was out. You just case non-character data types to char * and it works? I don''t see you casting back to unsigned int''s or anything. How does this work cause I''m really confused
p4n1c
p4n1c
Sorry for not responding last night, christmas things had to be done. Anonymous is correct. My understanding is you cast to what the function wants but when it comes through it''s converted to the data type that it is storing in.
-----For I have seen the face of evil and it will not soon be forgotten.
xg0blin
xg0blin
That just seems really weird to me I guess, that you could cast the data type and it would come out right. I guess that''s pretty cool though. I''ll have to try it for myself. Thanks.

Topic Locked

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

Sign in to reply to this topic.