Jump to content
  • Advertisement
Sign in to follow this  

Loading compressed TGA into OpenGL texture

This topic is 2492 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm in need of some assistance on a pretty specific problem. I'm trying to read a compressed 24-bit TGA file and pass it into OpenGL as a texture. I've been almost successful, except that the resulting rendered texture is somewhat... skewed, and off-color. I'm skipping pixels when reading the actual image data or something, but I swear I have the right idea since everything looks fine when I step through the code while loading a small image file. So maybe my OpenGL/texture settings are wrong? Ugh, I just can't figure out where the problem is.

Here's my TGA reading code. Note that the code is actually supposed to be designed to take either a 24-bit truecolor TGA or an 8-bit indexed TGA, but right now I'm just testing the reading of a 24-bit truecolor TGA.

int loadTGAToTexture(string filePath, HSTexture * hsTex)
//the data that OpenGL ultimately needs
GLushort imageWidth = 0;
GLushort imageHeight = 0;
GLenum format = GL_RGB;
GLubyte * imageData;
GLuint textureID;
GLuint bufferID;

//variables to help us get the above data
FILE * file;
GLubyte imageIDLength;
GLubyte colorMapType;
GLubyte imageType;
GLushort colorMapLength;
GLubyte colorMapEntrySize;
GLubyte pixelDepth;
GLubyte bytesPerPixel;
GLubyte imageDescriptor;
bool topAlign = true;
bool rightAlign = true;

//open the file
if(GLuint error = fopen_s(&file, filePath.data(), "rb") != 0)
return error; //couldn't open the file

//gather all the general info about the TGA file
fseek(file, 0L, SEEK_SET); //move to the beginning of the file
fread(&imageIDLength, 1, 1, file); //get the length of the image id field
fread(&colorMapType, 1, 1, file); //see if the file has a color map in it or not
fread(&imageType, 1, 1, file); //find out what kind of image file this is
if(imageType == 9)
format = GL_COLOR_INDEX; //its an indexed RLE format
else if(imageType != 10)
return -1; //this needs to be RLE, either indexed or truecolor
fseek(file, 2L, SEEK_CUR); //skip the first two bytes of the color map specification
fread(&colorMapLength, 2, 1, file); //get the length of the color map
fread(&colorMapEntrySize, 1, 1, file); //get the size of each color map entry
if(colorMapEntrySize == 15)
colorMapEntrySize = 16; //make sure it's a multiple of 8
fseek(file, 4L, SEEK_CUR); //skip the first four bytes of the image specification
fread((void *)&imageWidth, 2, 1, file); //get the image width
fread((void *)&imageHeight, 2, 1, file); //get the image height
fread(&pixelDepth, 1, 1, file); //get the size of each pixel in bits
bytesPerPixel = pixelDepth/8; //get the size of each pixel in bytes
if((format == GL_COLOR_INDEX && pixelDepth != 8) || (format == GL_RGB && pixelDepth != 24))
return -1; //only allowed configurations are: indexed with 8-bit, or truecolor with 24 bit
fread(&imageDescriptor, 1, 1, file); //get the image descriptor, and pull some data from it
if((imageDescriptor & ATTRIBUTE_BITS) > 0)
return -1; //no attributes should be defined
if((imageDescriptor & TOP_ALIGN) == 0)
topAlign = false; //the pixels are bottom-aligned
if((imageDescriptor & RIGHT_ALIGN) == 0)
rightAlign = false; //the pixels are left-aligned
fseek(file, (long)imageIDLength, SEEK_CUR); //skip the imageID
if(colorMapType != 0)
fseek(file, (long)(colorMapLength * colorMapEntrySize), SEEK_CUR); //skip the color map data

//okay, time for the fun part: picking through the image data.
//it's ALWAYS going to be in RLE format so we can't just grab the raw data.
GLuint maxPixels = imageWidth * imageHeight; //we need this so we know when to stop
GLuint curPixels = 0; //keeps track of how far we've gone through the actual pixels
imageData = (GLubyte*)malloc(maxPixels * bytesPerPixel); //set up our image data buffer
GLubyte repCount; //this'll hold each repetition count field
GLubyte * runColor = (GLubyte*)malloc(bytesPerPixel); //create a buffer to hold the color data of a run
while(curPixels < maxPixels)
if(fread(&repCount, 1, 1, file) != 1) { return -1;} //get the repetition count field
if((repCount & RL_PACKET) == 0)
//this is a raw packet.
//Just shove the pixel data into the image data buffer
repCount = repCount & REP_COUNT_BITS; //get the actual number of following pixels

//put the pixels into the image data buffer
for(int i = 0; i <= repCount; i++)
//fread(imageData + (curPixels * bytesPerPixel) + (i * bytesPerPixel), bytesPerPixel, 1, file);
for(int j = bytesPerPixel - 1; j >= 0; j--)
//gotta read each individual pixel in backwards since TGA stores color backwards
if(fread(imageData + (curPixels * bytesPerPixel) + (i * bytesPerPixel) + j, 1, 1, file) != 1) { return -1; }
//this is a run-length packet.
//Put the specificed number of pixels of the specified color into the image data buffer
repCount = repCount & REP_COUNT_BITS; //get the actual number of following pixels

//get the color if this run
//fread(runColor, bytesPerPixel, 1, file);
for(int i = bytesPerPixel - 1; i >= 0; i--)
//since TGA stores colors as BGR, we need to read it backwards to get RGB
if(fread(runColor + i, 1, 1, file) != 1) { return -1; }

for(int i = 0; i <= repCount; i++)
//put a pixel of the specified packet color into the image data buffer
memcpy(imageData + (curPixels * bytesPerPixel) + (i * bytesPerPixel), runColor, bytesPerPixel);
curPixels += repCount + 1; //update how far we've moved through the actual pixels



//create and bind the texture
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);

//set some options

//put everything in the texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imageWidth, imageHeight, 0, format, GL_UNSIGNED_BYTE, imageData);

glBindTexture(GL_TEXTURE_2D, 0);

//make an array of coordinates based on the texture dimensions
GLshort * dim = new GLshort[12];
dim[0] = 0; dim[1] = 0; dim[2] = 0;
dim[3] = imageWidth; dim[4] = 0; dim[5] = 0;
dim[6] = imageWidth; dim[7] = imageHeight; dim[8] = 0;
dim[9] = 0; dim[10] = imageHeight; dim[11] = 0;

//make a buffer object
glGenBuffers(1, &bufferID);
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
glBufferData(GL_ARRAY_BUFFER, 12*sizeof(GLushort), dim, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);

delete[] dim;

//now, put all the results in the texture struct
hsTex->defined = true;
hsTex->textureID = textureID;
hsTex->bufferID = bufferID;
hsTex->rightAlign = rightAlign;
hsTex->topAlign = topAlign;

return 0;

Attached are four files. test.bmp and john.bmp is what the files look like before being turned into a 24 bit compressed truecolor TGA (I know that part of the process works fine because I can open them back up in a TGA viewer and they look correct) and then run through my code. testResult.bmp and johnResult.bmp is what I get back out as an on-screen texture. I'm rendering in orthographic mode and the surface I'm drawing on has the same dimensions as the texture that belongs to it.

Any ideas? Any help would be appreciated.


Share this post

Link to post
Share on other sites
you may want to add some UVs to your Quad, I'm not even sure how this is working at all. Is this all the code?

Your TGA loading is probably fine, look into building a correct Quad to apply the texture to.

Share this post

Link to post
Share on other sites
Depending on the values of imageWidth and imageHeight you may need to change your unpack alignment: http://www.opengl.org/wiki/Common_Mistakes#Texture_upload_and_pixel_reads

Share this post

Link to post
Share on other sites
"However, the program crashes on upload, or there seems to be diagonal lines going through the resulting image."

Okay, that might actually be the problem, since the image looks "torn" across a diagonal. I'll look into it. Thanks.

Assuming the solutions suggested in this section fixes it, then I have to ask: which of the two solutions is more efficient performance-wise? Should I change the unpack alignment, or should I just slap an extra byte on my pixels and make the input data RGBA?

Share this post

Link to post
Share on other sites
Most efficient would probably be to add the extra byte but use BGRA, not RGBA. If you use RGBA your texture upload will need to go through an additional software stage to convert it to BGRA (which is what your GPU is more than likely using internally, despite an internalFormat of GL_RGBA8 in this case - legacy OpenGL is weird like that). Unpack alignment 1 would likely incur additional penalties as it may only transfer in chunks of 1 byte at a time (rather than 4). You'd really need to profile to get the full picture, but my gut says extra byte + BGRA.

Share this post

Link to post
Share on other sites

But you can write your own benchmark code if you wish.

Share this post

Link to post
Share on other sites
Well at any rate, if I have some kind of performance issue during image loading in the future, I'll come back and look at this subject and try different things. Until then I'll just go ahead and assume using BGRA is my best bet, since all info sources (and some common sense) seems to point towards it.

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!