Source code for this:
The TGA file format (I love the 'Don't ask me' bit) is pretty simple - simply a header specifying what sort of TARGA file it is, then the data for each pixel. If we only handle uncompressed RGBA files, with 32 bits per pixel, it is very easy.
We need to read that pixel data into a ByteBuffer, then construct the texture using that ByteBuffer as we did programmatically in Textures 1. The ByteBuffer is a linear stream, whereas the image has a width and hight, which we need to remember. Consequently it is neat to have a TGABuffer class:
package org.waltermilner;
import java.nio.ByteBuffer;
public class TGABuffer {
private ByteBuffer imageBuffer;
private int width;
private int height;
TGABuffer(ByteBuffer b, int width, int height) {
imageBuffer = b;
this.width = width;
this.height = height;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public ByteBuffer getBuffer() {
return imageBuffer;
}
A class to read in a TGA file and make one of these is:
package org.waltermilner;
import com.sun.opengl.util.BufferUtil;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class TGABufferMaker {
public static TGABuffer make() {
ByteBuffer imageBuffer;
File f = new File("C:/Documents and Settings/Walter Milner/My Documents/NetBeansProjects/JOGLPrograms/build/classes/org/waltermilner/tga1.tga");
try {
// Open the file and then get a channel from the stream
FileInputStream fis = new FileInputStream(f);
FileChannel fc = fis.getChannel();
int sz = (int) fc.size();
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);
byte b = bb.get(2); // this is the image type : 2 = unmapped RGB
if (b != 2) {
System.out.println("Only handles unmapped RGB format");
throw new Exception();
}
int width = getInt(bb, 12);
int height = getInt(bb, 14);
byte bpp = bb.get(16); // bytes per pixel : RGBA = 4 bytes per pixel
if (bpp != 32) {
System.out.println("Only handles 32 bit RGBA format");
throw new Exception();
}
// here we have a 32bit RGB width X height sequence of pixels
bb.position(18); // go past header
imageBuffer = BufferUtil.newByteBuffer(width * height * 4);
for (int i = 0; i < height; i++) { // read from the input buffer and write to the image buffer we are making
for (int j = 0; j < width; j++)
byte blue = bb.get(); // in TGA, in BGRA order!!
byte green = bb.get();
byte red = bb.get();
byte alpha = bb.get();
imageBuffer.put(red);
imageBuffer.put(green);
imageBuffer.put(blue);
imageBuffer.put(alpha);
}
}
imageBuffer.rewind();
return new TGABuffer(imageBuffer, width, height); // make it and return it
} catch (Exception x) {
System.err.println( x);
System.exit(0);
}
return null;
}
private static int getInt(MappedByteBuffer bb, int offset) {
// TGA stores 16bit ints as unsigned, LoHi order, but
// getShort would expect to read signed 2's complement in HiLo order
// so we need this..
int bLo = bb.get(offset);
if (bLo < 0) {
bLo = 256 + bLo;
}
int bHi = (int) bb.get(offset + 1);
if (bHi < 0) {
bHi = 256 + bHi;
}
int result = (bHi << 8) | bLo;
return result;
}
}
The only awkward part of this is reading the height and width. TGA stores these as two bytes, in lo-hi order, each byte an unsigned integer - but Java's short expects 2 bytes in hi-lo order, in signed 2's complement format. So we need getInt to twiddle the bytes.
Once we have the ByteBuffer, we can use it in as follows. We declare a TGABuffer:
TGABuffer buffer;
Then in init we make one, and use it to make a texture (lots of code missed out - like before
public void init(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
..
buffer = TGABufferMaker.make();
..
// connect to data
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, buffer.getWidth(),
buffer.getHeight(), 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE,
buffer.getBuffer());
}
Then in display we draw a quad and stick the texture on it. The bottom left corner is blue, and the rest yellow:
public void display(GLAutoDrawable drawable) {
final GL gl = drawable.getGL();
gl.glLoadIdentity();
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_DECAL);
gl.glBindTexture(GL.GL_TEXTURE_2D, texName);
gl.glBegin(GL.GL_QUADS);
gl.glColor3f(0.0f, 0.0f, 1.0f);
gl.glTexCoord2f(0.0f, 0.0f);
gl.glVertex3f(-1.0f, -1.0f, -3.0f);
gl.glColor3f(1.0f, 1.0f, 0.0f);
gl.glTexCoord2f(0.0f, 1.0f);
gl.glVertex3f(-1.0f, 1.0f, -3.0f);
gl.glTexCoord2f(1.0f, 1.0f);
gl.glVertex3f(1.0f, 1.0f, -3.0f);
gl.glTexCoord2f(1.0f, 0.0f);
gl.glVertex3f(1.0f, -1.0f, -3.0f);
gl.glEnd();
gl.glFlush();
gl.glDisable(GL.GL_TEXTURE_2D);
}
This displays 
The original tga file, in GIMP 2, looked like this:
Initially the background was white. In the GIMP Filters.. Colours.. Colour to Alpha creates an alpha channel and replaces the selected colour with an alpha value of 0. This is why in the screen shot the quad colour shows through.