Saving to a TGA file

I give up. Ive been trying for hours to get this to work. I want it to take a pic of the screen, using GBRA buffers, and save it to a 128x128 uncompressed tga.

Heres what I have:

(18 bytes)
typedef struct
BYTE IDLength;
BYTE ColormapType;
BYTE ImageType;
WORD ColorMapStart;
WORD ColorMapLength;
BYTE ColorMapDepth;
WORD XOrigin;
WORD YOrigin;
WORD ImageWidth;
WORD ImageHeight;
BYTE PixelDepth;
BYTE ImageDescriptor;
} tga_header;

unsigned char image = (unsigned char)malloc(sizeof(unsigned char)128128*4);
FILE *file = fopen(“screenshot.tga”, “wb”);

tga_header savetga;
glReadPixels( 0, 0, 128, 128, GL_BGRA_EXT, GL_UNSIGNED_BYTE, image );
savetga.IDLength = 0;
savetga.ColormapType = 0;
savetga.ImageType = 0;
savetga.ColorMapStart = 0;
savetga.ColorMapLength = 0;
savetga.ColorMapDepth = 0;
savetga.XOrigin = 0;
savetga.YOrigin = 0;
savetga.ImageWidth = 128;
savetga.ImageHeight = 128;
savetga.PixelDepth = 32;
savetga.ImageDescriptor = 8;
fwrite( &savetga, sizeof(savetga), 1, file );
fwrite( image, sizeof(unsigned char), 1281284, file );
fclose( file );
free( image );

What is wrong?

This will work better:
savetga.ImageType = 2;

Although we needed that, it still doesn’t work. Must be something else…

what is the savetga function?
do the saved textures open at all?
if so what do they look like?

its hard to help without knowing this

(Hint: you probably need Visual Studio’s equivalent to GNU’s attribute((packed)))

[This message has been edited by rts (edited 03-24-2002).]

try clearing the TGA header and only setting the minimal number of fields like so:

memset(&savetga, 0, sizeof(tga_header));
savetga.ImageType = 2;
savetga.ImageWidth = 128;
savetga.ImageHeight = 128;
savetga.PixelDepth = 32;

Also make sure that glReadPixels is working by creating a texture using the data returned and drawing it on a quad.

‘rta’ has the right answer: you need to surround the struct in the correct #pragma pack options. You also need to post in an on-topic forum, or just read the source of one of the 103 (last count) TGA readers/writers available through a Google search on the web.

oh god, dont start with the “this isn’t related” crap. See, by the time you get into the advanced stuff, you know how to work opengl, its just the theories and stuff. So, i think a topic on how to save TGA’s is appropiate.

Anyways, thanks, the header I was using was wrong or something.

Originally posted by rts:
(Hint: you probably need Visual Studio’s equivalent to GNU’s attribute((packed)))

#pragma Pack()


Following everyone’s posts there are only a couple things I see as being wrong.

One (please correct me if I am wrong) I don’t believe TGA’s are in BGRA format. I thought they were actually RGBA. But I haven’t looked at my TGA loader in a really really long time. And I’m not home to take a look. either way the code should still work it will just look weird ( :

The second thing. I don’t believe that you should have to align the struct and or allocated image data. It would be nicer to do so but shouldn’t be necessary. I don’t see why fwrite would care either way. Performance is probably significantly better with aligned data.

I am impressed to see that you set image descriptor equal to 8 (for the 8 bits of alpha). That is good but you missed one thing that i don’t think you are aware of.

TGA’s are meant to be platform independant. The height and width are specified as low byte then high byte. I would change your code as follows.

WORD ImageWidth;
WORD ImageHeight;

BYTE ImageWidthLow;
BYTE ImageWidthHigh;
BYTE ImageHeightLow;
BYTE ImageHeightHigh;

in your case with a 128 x 128 image its easy enough to go

ImageWidthLow = 128;
ImageHeightLow = 128;

Let me know if that helps.

Happy Coding.

I don’t believe TGA’s are in BGRA format

Yes they are stored in BGRA format.

I don’t believe that you should have to align the struct

The alignment can cause severe problems. Memory access is faster on 4 byte boundary than on 2 and 1 byte boundary. A compiler can align the data members so that they are stored on 4 byte boundary. That means that a structure may be padded with extra bytes so that the elements in the structure can be accessed faster (at the cost of greater memory usage). Problem occurs when you try to write the structure directly to a file, passing a pointer to the structure and the size of the structure. First, the sizeof operator will return the size of the structure including the size of the pad bytes, and when you write to the file, these pad bytes are also written. You solve this by either save each datamember separately, or by forcing the compiler to align the structure to 1 byte boudary.