a721e321 |
// 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.
|
0915175c |
// 220218: Big change: Removed the power of 2. GPUs with that limitation are gone now.
// This simplifies both the code and the interface.
|
a721e321 |
// 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;
}
|
0915175c |
bool LoadTGATextureData(const char *filename, TextureData *texture) // Loads A TGA File Into Memory
|
a721e321 |
{
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;
|
0915175c |
// long w, h;
|
a721e321 |
GLubyte *rowP;
int err;
GLubyte rle;
int b;
long row, rowLimit;
GLubyte pixelData[4];
// Nytt f�r flipping-st�d 111114
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
|
0915175c |
// 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;
|
a721e321 |
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
|
0915175c |
imageSize = texture->width * texture->height * bytesPerPixel; // Calculate The Memory Required For The TGA Data
|
a721e321 |
rowSize = texture->width * bytesPerPixel; // Image memory per row
|
0915175c |
stepSize = texture->width * bytesPerPixel; // Memory per row
|
a721e321 |
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.
|
0915175c |
if (flipped)
|
a721e321 |
{
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
for (i = 0; i < (int)(imageSize); i += bytesPerPixel) // Loop Through The Image Data
{ // 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);
|
0915175c |
//texture->w = w;
//texture->h = h;
|
a721e321 |
return true; // Texture loading Went Ok, Return True
}
|
0915175c |
bool LoadTGATexture(const char *filename, TextureData *texture) // Loads A TGA File Into Memory and creates texture object
|
a721e321 |
{
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
}
|
0915175c |
glTexImage2D(GL_TEXTURE_2D, 0, type, texture->width, texture->height, 0, type, GL_UNSIGNED_BYTE, texture[0].imageData);
|
a721e321 |
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
}
|
0915175c |
void LoadTGATextureSimple(const char *filename, GLuint *tex) // If you really only need the texture object.
|
a721e321 |
{
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
|
0915175c |
// No, this is the responsability of the host!
// free(imageData);
|
a721e321 |
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);
}
|