>I was trying to find a good image format to easily load images in c++. I use GIMP for image editing and it offers a variety of image formats to use. TGA is a pretty common file format so I set out to read TGA images.
The first thing I noticed was that java does not have TGA as one of the default image formats. I started trying to learn the file structure with java because it is a bit easier for me.
I saved a TGA file from gimp, then I read that file with java and noticed the file ends with a string.
TRUEVISION-XFILE.
I used that as an offset for the footer, I knew the size of the image, and I assumed format of the pixels is 8 bit per channel with 4 channels (red, blue, green and alpha). With these assumptions was able to find the image.
Of course after getting the image back I decided to check wikipedia for the file format. This way I was able to find out where the important values of the file are stored. The type of image is stored from byte 3 and the size of the image, contained in bytes 13 and 14 for the width, bytes 15 and 16 for the height.
Extracting these values in java looked like:
//image dimensions int width = (stack.getUnsigned(13)<<8) + stack.getUnsigned(12); int height = (stack.getUnsigned(15)<<8) + stack.getUnsigned(14);
At the time of writing this java doesn't have an unsigned byte so I made a utility function to get an int version of the bytes that goes from 0->255.
int getUnsigned(int dex){ int row = dex/chunk; int column = dex%chunk; int b = stack.get(row)[column]; return b<0?256 + b:b; }
The row and column are used because I read the file in 2048 byte chunks and stored all of the byte[]s into an ArrayList<byte[]>.
Since I know the footer and the size of the image, I can extract all of the image data. Each pixel is stored as 4 bytes and the reading process ended up looking like.
for(int i = 0; i<width; i++){ for(int j = 0; j<height; j++){ int dex = (j*width + i)*byte_per_pixel + offset; int b = stack.getUnsigned(dex); int g = stack.getUnsigned(dex+1); int r = stack.getUnsigned(dex+2); int a = stack.getUnsigned(dex+3); bufferedImage.setRGB(i, y, (a<<24) + (r<<16) + (g<<8) + b); } }
I know the pixel format and the file description I went on to do the exact same thing in c++. Well not quite exact. I made a structure to store my data in c++.
One of the nice things about c++ is that I just read data into a character array and use casting for the type. For example.
std::ifstream infile( file_name.c_str(), std::ifstream::binary ); char* header = new char[18]; if(infile){ infile.read(header, 18); }
That way I read the header and then to extract values from it I just cast the values to their appropriate type.
int width = ((unsigned char)header[13]<<8) + (unsigned char)header[12]; int height = ((unsigned char)header[15]<<8) + (unsigned char)header[14];
To store the texture I did the same thing. Read all of the bytes into a char array then just cast the texture to a GLubyte*.
GLubyte* texture = (GLubyte*)back;
To use the texture I buffered it as described in my previous post on a minimum texture example. The only difference was that I needed to specify the byte format.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img->width, img->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, img->texture);
Note that img is a struct with the elements int width, height; and GLubyte* texture.
All of this code is available in a github repository. github.com/odinsbane/tga_reader
Here is a picture of the almost full moon.