Lesson 33
Loading Uncompressed and RunLength Encoded TGA images By Evan "terminate" Pipho.
I've seen lots of people ask around in #gamdev, the gamedev forums, and other places about TGA loading. The following code and explanation will show you how to load both uncompressed TGA files and RLE compressed files. This particular tutorial is geared toward OpenGL, but I plan to make it more universal in the future.
We will begin with the two header files. The first file will hold our texture structure, the second, structures and variables used by the loading code.
Like every header file we need some inclusion guards to prevent the file from being included multiple times.
At the top of the file add these lines:
#ifndef __TEXTURE_H__ // See If The Header Has Been Defined Yet
#define __TEXTURE_H__ // If Not, Define It.
Then scroll all the way down to the bottom and add:
#endif // __TEXTURE_H__ End Inclusion Guard
These three lines prevent the file from being included more than once into a file. The rest of the code in the file will be between the first two, and the last line.
Into this header file we we will insert the standard headers we will need for everything we do. Add the following lines after the #define __TGA_H__ command.
#pragma comment(lib, "OpenGL32.lib") // Link To Opengl32.lib
#include <windows.h> // Standard Windows header
#include <stdio.h> // Standard Header For File I/O
#include <gl\gl.h> // Standard Header For OpenGL
The first header is the standard windows header, the second is for the file I/O functions we will be using later, and the 3rd is the standard OpenGL header file for OpenGL32.lib.
We will need a place to store image data and specifications for generating a texture usable by OpenGL. We will use the following structure.
typedef struct {
GLubyte* imageData; // Hold All The Color Values For The Image.
GLuint bpp; // Hold The Number Of Bits Per Pixel.
GLuint width; // The Width Of The Entire Image.
GLuint height; // The Height Of The Entire Image.
GLuint texID; // Texture ID For Use With glBindTexture.
GLuint type; // Data Stored In * ImageData (GL_RGB Or GL_RGBA)
} Texture;
Now for the other, longer head file. Again we will need some includsion guards, same as the last one.
Next come two more structures used during processing of the TGA file.
typedef struct {
GLubyte Header[12]; // File Header To Determine File Type
} TGAHeader;
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 type; // The Type Of Image, GL_RGB Or GL_RGBA
GLuint Height; // Height Of Image
GLuint Width; // Width Of Image
GLuint Bpp; // Number Of BITS Per Pixel (24 Or 32)
} TGA;
Now we declare some instances of our two structures so we can use them within our code.
TGAHeader tgaheader; // Used To Store Our File Header
TGA tga; // Used To Store File Information
We need to define a couple file headers so we can tell the program what kinds of file headers are on valid images. The first 12 bytes will be 0 0 2 0 0 0 0 0 0 0 0 0 if it is uncompressed TGA image and 0 0 10 0 0 0 0 0 0 0 0 0 if it an RLE compressed one. These two values allow us to check to see if the file we are reading is valid.
// 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};
Finally we declare a pair of functions to use in the loading process.
// Load An Uncompressed File
bool LoadUncompressedTGA(Texture *, char *, FILE *);
// Load A Compressed File
bool LoadCompressedTGA(Texture *, char *, FILE *);
Now, on to the cpp file, and the real brunt of the code. I will leave out some of the error message code and the like to make the tutorial shorter and more readable. You may look in the included file (link at the bottom of the article)
Right off the bat we have to include the file we just made so at the top of the file put.
#include "tga.h" // Include The Header We Just Made
We don't have to include any other files, because we included them inside our header we just completed.
The next thing we see is the first function, which is called LoadTGA(…).
// Load A TGA File!
bool LoadTGA(Texture * texture, char * filename) {
It takes two parameters. The first is a pointer to a Texture structure, which you must have declared in your code some where (see included example). The second parameter is a string that tells the computer where to find your texture file.
The first two lines of the function declare a file pointer and then open the file specified by "filename" which was passed to the function in the second parem for reading.
FILE * fTGA; // Declare File Pointer
fTGA = fopen(filename, "rb"); // Open File For Reading
The next few lines check to make sure that the file opened correctly.
if (fTGA == NULL) // If Here Was An Error
{
…Error code…
return false; // Return False
}
Next we try to read the first twelve bytes of the file and store them in out TGAHeader structure so we can check on the file type. If fread fails, the file is closed, an error displayed, and the function returns false.
// Attempt To Read The File Header
if (fread(&tgaheader, sizeof(TGAHeader), 1, fTGA) == 0) {
…Error code here…
return false; // Return False If It Fails
}
Next we try to determine what type of file it is by comparing our newly aquired header with our two hard coded ones. This will tell us if its compressed, uncompressed, or even if its the wrong file type. For this purpose we will use the memcmp(…) function.
// If The File Header Matches The Uncompressed Header
if (memcmp(uTGAcompare, &tgaheader, sizeof(tgaheader)) == 0) {
// Load An Uncompressed TGA
LoadUncompressedTGA(texture, filename, fTGA);
}
// If The File Header Matches The Compressed Header
else if(memcmp(cTGAcompare, &tgaheader, sizeof(tgaheader)) == 0) {
// Load A Compressed TGA
LoadCompressedTGA(texture, filename, fTGA);
} else // If It Doesn't Match Either One
{
…Error code here…
return false; // Return False
}
We will begin this section with the loading of an UNCOMPRESSED file. This function is heavily based on NeHe's code in lesson 25.
First thing we come to, as always, is the function header.
// Load An Uncompressed TGA!