common/LoadTGA.c
b23636c9
 // TGA loader, by Ingemar 2009, based on "tga.c" and some other sources.
 // Updated 2013 to use modern mipmapping
 // 130423: Added LoadTGATextureData
 // 130905: Fixed a bug in LoadTGATextureSimple
 // 140212: Fixed a bug in loading of uncompressed images.
 // 140520: Supports grayscale images (loads to red channel).
 // 141007: Added SaveTGA. (Moved from the obsolete LoadTGA2.)
 // 160302: Uses fopen_s on Windows, as suggested by Jesper Post. Should reduce warnings a bit.
 // 170220: Changed fopen_s to "rb". This made it fail for some textures.
 // 170331: Cleaned up a bit to remove warnings.
 // 170419: Fixed a bug that prevented monochrome images from loading.
 // 180216: calloc is better than malloc when you may have undefined data in parts of the texture!
 // 190416: Skipped testing the last four bytes of the 12-byte heading. That is the origin of the
 // image which is usually 0. It is now ignored.
e797f203
 // 220218: Big change: Removed the power of 2. GPUs with that limitation are gone now.
 // This simplifies both the code and the interface.
b23636c9
 
 // NOTE: LoadTGA does NOT support all TGA variants! You may need to re-save your TGA
 // with different settings to find a suitable format.
 
 #include "LoadTGA.h"
 
 static bool gMipmap = true;
 
 // Note that turning mimpapping on and off refers to the loading stage only.
 // If you want to turn off mipmapping later, use 
 // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 
 void LoadTGASetMipmapping(bool active)
 {
 	gMipmap = active;
 }
 
 bool LoadTGATextureData(const char *filename, TextureData *texture)	// Loads A TGA File Into Memory
 {
 	GLuint i;
 	GLubyte
 		TGAuncompressedheader[12]={ 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0},	// Uncompressed TGA Header
 		TGAcompressedheader[12]={ 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0},	// Compressed TGA Header
 		TGAuncompressedbwheader[12]={ 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0},	// Uncompressed grayscale TGA Header
 		TGAcompressedbwheader[12]={ 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0},	// Compressed grayscale TGA Header
 		actualHeader[12],	// Used To Compare TGA Header
 		header[6];		// First 6 Useful Bytes From The Header
 	GLuint bytesPerPixel,		// Holds Number Of Bytes Per Pixel Used In The TGA File
 		imageSize,		// Used To Store The Image Size When Setting Aside Ram
 		temp;			// Temporary Variable
 	long rowSize, stepSize, bytesRead;
e797f203
 //	long w, h;
b23636c9
 	GLubyte *rowP;
 	int err;
 	GLubyte rle;
e8d73fad
 	GLuint b;
b23636c9
 	long row, rowLimit;
 	GLubyte pixelData[4];	
 	
7dcf10dc
 	// Nytt för flipping-stöd 111114
b23636c9
 	char flipped;
 	long step;
 	
 	// It seems Windows/VS doesn't like fopen any more, but fopen_s is not on the others.
 	FILE *file;
 	#if defined(_WIN32)
 		fopen_s(&file, filename, "rb");
 	#else
 		file = fopen(filename, "rb"); // rw works everywhere except Windows?
 	#endif
 //	FILE *file = fopen(filename, "rb");			// Open The TGA File
 	err = 0;
 	if (file == NULL) err = 1;				// Does File Even Exist?
 	else if (fread(actualHeader, 1, sizeof(actualHeader), file) != sizeof(actualHeader)) err = 2; // Are There 12 Bytes To Read?
 	else if (
 	// Edit 2019: Ignore the origin!
 				(memcmp(TGAuncompressedheader, actualHeader, sizeof(TGAuncompressedheader)-4) != 0) &&
 				(memcmp(TGAcompressedheader, actualHeader, sizeof(TGAcompressedheader)-4) != 0) &&
 				(memcmp(TGAuncompressedbwheader, actualHeader, sizeof(TGAuncompressedheader)-4) != 0) &&
 				(memcmp(TGAcompressedbwheader, actualHeader, sizeof(TGAcompressedheader)-4) != 0)
 			)
 			{
 				err = 3; // Does The Header Match What We Want?
 				for (i = 0; i < 12; i++)
 					printf("%d ", actualHeader[i]);
 				printf("\n");
 			}
 	else if (fread(header, 1, sizeof(header), file) != sizeof(header)) err = 4; // If So Read Next 6 Header Bytes
 	
 	if (err != 0)
 	{
 		switch (err)
 		{
 			case 1: printf("could not open file %s\n", filename); break;
 			case 2: printf("could not read header of %s\n", filename); break;
 			case 3: printf("unsupported format in %s\n", filename); break;
 			case 4: printf("could not read file %s\n", filename); break;
 		}
 		
 		if(file == NULL)		// Did The File Even Exist? *Added Jim Strong*
 			return false;
 		else
 		{
 			fclose(file);		// If Anything Failed, Close The File
 			return false;
 		}
 	}
 	texture->width  = header[1] * 256 + header[0];	// Determine The TGA Width (highbyte*256+lowbyte)
 	texture->height = header[3] * 256 + header[2];	// Determine The TGA Height (highbyte*256+lowbyte)
 	if (texture->width <= 0 ||	// Is The Width Less Than Or Equal To Zero
 	texture->height <= 0 ||		// Is The Height Less Than Or Equal To Zero
 	(header[4] != 24 && header[4] != 32 && header[4] != 8))			// Is The TGA 24 or 32 Bit?
 	{
 		fclose(file);		// If Anything Failed, Close The File
 		return false;
 	}
 	flipped = (header[5] & 32) != 0; // Testa om flipped
e797f203
 
 // That old power of 2 is no longer needed!
 //	w = 1;
 //	while (w < texture->width) w = w << 1;
 //	h = 1;
 //	while (h < texture->height) h = h << 1;
 //	texture->texWidth = (GLfloat)texture->width / w;
 //	texture->texHeight = (GLfloat)texture->height / h;
b23636c9
 	
 	
 	texture->bpp = header[4];		// Grab The TGA's Bits Per Pixel (24 or 32)
 	bytesPerPixel = texture->bpp/8;		// Divide By 8 To Get The Bytes Per Pixel
e797f203
 	imageSize = texture->width * texture->height * bytesPerPixel;	// Calculate The Memory Required For The TGA Data
b23636c9
 	rowSize	= texture->width * bytesPerPixel;	// Image memory per row
e797f203
 	stepSize = texture->width * bytesPerPixel;		// Memory per row
b23636c9
 	texture->imageData = (GLubyte *)calloc(1, imageSize);	// Reserve Memory To Hold The TGA Data
 	if (texture->imageData == NULL)				// Does The Storage Memory Exist?
 	{
 		fclose(file);
 		return false;
 	}
 
 	// Set rowP and step depending on direction
 	// Inverted this 120131, since a texture came out wrong.
 	// I am still not sure this is OK.
e797f203
 	if (flipped)
b23636c9
 	{
 		step = -stepSize;
 		rowP = &texture->imageData[imageSize - stepSize];
 		row = 0 + (texture->height -1) * stepSize;
 	}
 	else
 	{
 		step = stepSize;
 		rowP = &texture->imageData[0];
 		row = 0;
 	}
 
 	if (actualHeader[2] == 2 || actualHeader[2] == 3) // uncompressed
 	{
 		for (i = 0; i < texture->height; i++)
 		{
 			bytesRead = fread(rowP, 1, rowSize, file);
 			rowP += step;
 			if (bytesRead != rowSize)
 			{
 				free(texture->imageData);	// If So, Release The Image Data
 				fclose(file);			// Close The File
 				return false;			// Return False
 			}
 		}
 	}
 	else
 	{ // compressed
 //		row = 0 + (texture->height -1) * stepSize;
 		i = row;
 		rowLimit = row + rowSize;
 		do
 		{
 			bytesRead = fread(&rle, 1, 1, file);
 			if (rle < 128)
 			{ // rle+1 raw pixels
 				bytesRead = fread(&texture->imageData[i], 1, (rle+1)*bytesPerPixel, file);
 				i += bytesRead;
 				if (bytesRead == 0)
 					i = imageSize;
 			}
 			else
 			{ // range of rle-127 pixels with a color that follows
 				bytesRead = fread(&pixelData, 1, bytesPerPixel, file);
 				do
 				{
 					for (b = 0; b < bytesPerPixel; b++)
 						texture->imageData[i+b] = pixelData[b];
 					i += bytesPerPixel;
 					rle = rle - 1;
 				} while (rle > 127);
 			}
 			if (i >= rowLimit)
 			{
 				row = row + step; // - stepSize;
 				rowLimit = row + rowSize;
 				i = row;
 			}
 		} while (i < imageSize);
 	}
 
 	if (bytesPerPixel >= 3) // if not monochrome	
e8d73fad
 	for (i = 0; i < imageSize; i += bytesPerPixel)	// Loop Through The Image Data
b23636c9
 	{		// Swaps The 1st And 3rd Bytes ('R'ed and 'B'lue)
 		temp = texture->imageData[i];		// Temporarily Store The Value At Image Data 'i'
 		texture->imageData[i] = texture->imageData[i + 2];	// Set The 1st Byte To The Value Of The 3rd Byte
 		texture->imageData[i + 2] = temp;	// Set The 3rd Byte To The Value In 'temp' (1st Byte Value)
 	}
 	fclose (file);
 
e797f203
 //texture->w = w;
 //texture->h = h;
b23636c9
 
 	return true;				// Texture loading Went Ok, Return True
 }
 
 bool LoadTGATexture(const char *filename, TextureData *texture)	// Loads A TGA File Into Memory and creates texture object
 {
 	char ok;
 	GLuint type = GL_RGBA;		// Set The Default GL Mode To RBGA (32 BPP)
 	
 	ok = LoadTGATextureData(filename, texture);	// Loads A TGA File Into Memory
 	if (!ok)
 		return false;
 
 	// Build A Texture From The Data
 	glGenTextures(1, &texture->texID);			// Generate OpenGL texture IDs
 	glBindTexture(GL_TEXTURE_2D, texture->texID);		// Bind Our Texture
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);	// Linear Filtered
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	// Linear Filtered
 	if (texture->bpp == 8)						// Was The TGA 8 Bits? Should be grayscale then.
 	{
 		type=GL_RED;			// If So Set The 'type' To GL_RED
 	}
 	if (texture->bpp == 24)						// Was The TGA 24 Bits?
 	{
 		type=GL_RGB;			// If So Set The 'type' To GL_RGB
 	}
e797f203
 	glTexImage2D(GL_TEXTURE_2D, 0, type, texture->width, texture->height, 0, type, GL_UNSIGNED_BYTE, texture[0].imageData);
b23636c9
 	
 	if (gMipmap)
 	{
 		glGenerateMipmap(GL_TEXTURE_2D);
 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);	// Linear Filtered
 	}
 	
 	return true;				// Texture Building Went Ok, Return True
 }
 
 void LoadTGATextureSimple(const char *filename, GLuint *tex) // If you really only need the texture object.
 {
 	TextureData texture;
 	memset(&texture, 0, sizeof(texture)); // Bug fix 130905.
 	
 	if (LoadTGATexture(filename, &texture))
 	{
 		if(texture.imageData != NULL)
 			free(texture.imageData);
 		*tex = texture.texID;
 	}
 	else
 		*tex = 0;
 }
 
 
 // saves an array of pixels as a TGA image
 // Was tgaSave, found in some reusable code.
 // Limitation: Can NOT save with transparency! Only RGB, not RGBA!
 // But it should! Why not?
 int SaveDataToTGA(char			*filename, 
 			 short int		width, 
 			 short int		height, 
 			 unsigned char	pixelDepth,
 			 unsigned char	*imageData)
 {
 	unsigned char cGarbage = 0, mode,aux;
 	int i, w, ix;
 	FILE *file;
 	char /*GLubyte*/ TGAuncompressedheader[12]={ 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};	// Uncompressed TGA Header
 
 // open file and check for errors
 #if defined(_WIN32)
 	fopen_s(&file, filename, "w");
 #else
 	file = fopen(filename, "w"); // rw works everywhere except Windows?
 #endif
 //	file = fopen(filename, "w");
 	if (file == NULL)
 		return(TGA_ERROR_FILE_OPEN);
 
 // compute image type: 2 for RGB(A), 3 for greyscale
 	mode = pixelDepth / 8;
 // NOT YET IMPLEMENTED
 //	if ((pixelDepth == 24) || (pixelDepth == 32))
 //		type = 2;
 //	else
 //		type = 3;
 
 // write the header
 	fwrite(&TGAuncompressedheader, 12, 1, file);
 	fwrite(&width, sizeof(short int), 1, file);
 	fwrite(&height, sizeof(short int), 1, file);
 	fwrite(&pixelDepth, sizeof(unsigned char), 1, file);
 
 	fwrite(&cGarbage, sizeof(unsigned char), 1, file);
 
 // convert the image data from RGB(a) to BGR(A)
 	if (mode >= 3)
 	for (i=0; i < width * height * mode ; i+= mode)
 	{
 		aux = imageData[i];
 		imageData[i] = imageData[i+2];
 		imageData[i+2] = aux;
 	}
 
 // save the image data
 	w = 1;
 	while (w < width) w = w << 1;
 //	bytesPerPixel = pixelDepth/8;	
 //	row = width * bytesPerPixel;
 	
 // Write one row at a time
 	for (i = 0; i < height; i++)
 	{
 		ix = i * w * mode;
 		fwrite(&imageData[ix], sizeof(unsigned char), width * mode, file);
 	}
 
 	fclose(file);
 // release the memory
e797f203
 // No, this is the responsability of the host!
 //	free(imageData);
b23636c9
 
 	return(TGA_OK);
 }
 
 // Save a TextureData
 // Problem: Saves upside down!
 void SaveTGA(TextureData *tex, char *filename)
 {
 	SaveDataToTGA(filename, tex->width, tex->height, 
 			tex->bpp, tex->imageData);
 }
 
 void SaveFramebufferToTGA(char *filename, GLint x, GLint y, GLint w, GLint h)
 {
 	int err;
 	void *buffer = malloc(h*w*3);
 	glReadPixels(x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, buffer);
 	err = SaveDataToTGA(filename, w, h, 
 			3*8, (unsigned char *)buffer);
 //	free(buffer); already done
 	printf("SaveDataToTGA returned %d\n", err);
 }