bool LoadUncompressedTGA(Texture * texture, char * filename, FILE * fTGA) {
This function takes three parameters. The first two are the same as from LoadTGA, and are simply passed on. The third is the file pointer from the previous function so that we dont lose our place.
Next we try to read the next 6 bytes of the file, and store them in tga.header. If it fails, we run some error code, and return false.
// Attempt To Read Next 6 Bytes
if (fread(tga.header, sizeof(tga.header), 1, fTGA) == 0) {
…Error code here…
return false; // Return False
}
Now we have all the information we need to calculate the height, width and bpp of our image. We store it in both the texture and local structure.
texture->width = tga.header[1] * 256 + tga.header[0]; // Calculate Height
texture->height = tga.header[3] * 256 + tga.header[2]; // Calculate The Width
texture->bpp = tga.header[4]; // Calculate Bits Per Pixel
tga.Width = texture->width; // Copy Width Into Local Structure
tga.Height = texture->height; // Copy Height Into Local Structure
tga.Bpp = texture->bpp; // Copy Bpp Into Local Structure
Now we check to make sure that the height and width are at least one pixel, and that the bpp is either 24 or 32. If any of the values are outside their boundries we again display an error, close the file, and leave the function.
// Make Sure All Information Is Valid
if ((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp !=32))) {
…Error code here…
return false; // Return False
}
Next we set the type of the image. 24 bit images are type GL_RGB and 32 bit images are type GL_RGBA
if (texture->bpp == 24) // Is It A 24bpp Image?
{
texture->type = GL_RGB; // If So, Set Type To GL_RGB
} else // If It's Not 24, It Must Be 32
{
texture->type = GL_RGBA; // So Set The Type To GL_RGBA
}
Now we caclulate the BYTES per pixel and the total size of the image data.
tga.bytesPerPixel = (tga.Bpp / 8); // Calculate The BYTES Per Pixel
// Calculate Memory Needed To Store Image
tga.imageSize = (tga.bytesPerPixel * tga.Width * tga.Height);
We need some place to store all that image data so we will use malloc to allocate the right amount of memory.
Then we check to make sure memory was allocated, and is not NULL. If there was an error, run error handling code.
// Allocate Memory
texture->imageData = (GLubyte *)malloc(tga.imageSize);
if (texture->imageData == NULL) // Make Sure It Was Allocated Ok
{
…Error code here…
return false; // If Not, Return False
}
Here we try to read all the image data. If we can't, we trigger error code again.
// Attempt To Read All The Image Data
if (fread(texture->imageData, 1, tga.imageSize, fTGA) != tga.imageSize) {
…Error code here…
return false; // If We Cant, Return False
}
TGA files store their image in reverse order than what OpenGL wants so we much change the format from BGR to RGB. To do this we swap the first and third bytes in every pixel.
Steve Thomas Adds: I've got a little speedup in TGA loading code. It concerns switching BGR into RGB using only 3 binary operations. Instead of using a temp variable you XOR the two bytes 3 times.
Then we close the file, and exit the function successfully.
// Start The Loop
for (GLuint cswap = 0; cswap < (int)tga.imageSize; cswap += tga.bytesPerPixel) {
// 1st Byte XOR 3rd Byte XOR 1st Byte XOR 3rd Byte
texture->imageData[cswap] ^= texture->imageData[cswap+2] ^= texture->imageData[cswap] ^= texture->imageData[cswap+2];
}
fclose(fTGA); // Close The File
return true; // Return Success
}
Thats all there is to loading an uncompressed TGA file. Loading a RLE compressed one is only slightly harder. We read the header and collect height/width/bpp as usual, sme as the uncompressed version, so i will just post the code, you can look in the previous pages for a complete explanation.
bool LoadCompressedTGA(Texture * texture, char * filename, FILE * fTGA) {
if (fread(tga.header, sizeof(tga.header), 1, fTGA) == 0) {
…Error code here…
}
texture->width = tga.header[1] * 256 + tga.header[0];
texture->height = tga.header[3] * 256 + tga.header[2];
texture->bpp = tga.header[4];
tga.Width = texture->width;
tga.Height = texture->height;
tga.Bpp = texture->bpp;
if ((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp !=32))) {
…Error code here…
}
tga.bytesPerPixel = (tga.Bpp / 8);
tga.imageSize = (tga.bytesPerPixel * tga.Width * tga.Height);
Now we need to allocate the amount of storage for the image to use AFTER we uncompress it, we will use malloc. If memory fails to be allocated, run error code, and return false.
// Allocate Memory To Store Image Data
texture->imageData = (GLubyte *)malloc(tga.imageSize);
if (texture->imageData == NULL) // If Memory Can Not Be Allocated…
{
…Error code here…
return false; // Return False
}
Next we need to determine how many pixels make up the image. We will store it in the variable "pixelcount"
We also need to store which pixel we are currently on, and what byte of the imageData we are writing to, to avoid overflows and overwriting old data.
We will allocate enough memory to store one pixel.
GLuint pixelcount = tga.Height * tga.Width; // Number Of Pixels In The Image
GLuint currentpixel = 0; // Current Pixel We Are Reading From Data
GLuint currentbyte = 0; // Current Byte We Are Writing Into Imagedata
// Storage For 1 Pixel
GLubyte * colorbuffer = (GLubyte *)malloc(tga.bytesPerPixel);
Next we have a big loop.
Lets break it down into more manageable chunks.
First we declare a variable in order to store the chunk header. A chunk header dictates wheather the following section is RLE, or RAW, and how long it is. If the one byte header is less than or equal to 127, then it is a RAW header. The value of the header is the number of colors, minus one, that we read ahead and copy into memory, before we hit another header byte. So we add one to the value we get, and then read that many pixels and copy them into the ImageData, just like we did with the uncompressed ones. If the header is ABOVE 127, then it is the number of times that the next pixel value is repeated consequtively. To get the actual number of repetitions we take the value returned and subtract 127 to get rid of the one bit header identifier. Then we read the next one pixel and copy it the said number of times consecutively into the memory.