C++ OpenGL TGA Loading Failing

3k views Asked by At

I've been working through a basic OpenGl tutorial on loading a TGA file, to be used as a texture on a 3d object. I've been able to load data from the TGA header, but when I attempt to load the actual image data, it fails. I'm not sure where it is going wrong. Here is my texture loading class:

Header file:

    struct TGA_Header 
{
    GLbyte  ID_Length;
    GLbyte  ColorMapType;
    GLbyte  ImageType;
    // Color map specifications
    GLbyte firstEntryIndex[2];      
    GLbyte colorMapLength[2];
    GLbyte colorMapEntrySize;

    //image specification
    GLshort xOrigin;
    GLshort yOrigin;
    GLshort ImageWidth;
    GLshort ImageHeight;
    GLbyte  PixelDepth;
    GLbyte ImageDescriptor;
};

class Texture
{
public:
    Texture(string in_filename, string in_name = "");
    ~Texture();

public:
    unsigned short  width;
    unsigned short  height;
    unsigned int    length;
    unsigned char   type;   
    unsigned char   *imageData;
    unsigned int    bpp;
    unsigned int    texID;

    string          name;

    static vector<Texture *> textures;

private:
    bool loadTGA(string filename);
    bool createTexture(unsigned char *imageData, int width, int height, int type);

    void swap(unsigned char * ori, unsigned char * dest, GLint size);
    void flipImage(unsigned char * image, bool flipHorizontal, bool flipVertical, GLushort width, GLushort height, GLbyte bpp);
};

Here is the load TGA function in the cpp:

bool Texture::loadTGA(string filename)
{
    TGA_Header TGAheader;

    ifstream file( filename.data(), std::ios::in, std::ios::binary );

    //make sure the file was opened properly
    if (!file.is_open() )
        return false;

    if( !file.read( (char *)&TGAheader, sizeof(TGAheader) ) )
        return false;


    //make sure the image is of a type we can handle
    if( TGAheader.ImageType != 2 )
        return false;

    width = TGAheader.ImageWidth;
    height = TGAheader.ImageHeight;
    bpp = TGAheader.PixelDepth;

    if( width < 0   ||              // if the width or height is less than 0, than
        height <= 0 ||              // the image is corrupt
        (bpp != 24 && bpp != 32) )  // make sure we are of the correct bit depth
    {
        return false;
    }

    //check for an alpha channel
    GLuint type = GL_RGBA;
    if ( bpp == 24 )
        type = GL_RGB;

    GLuint bytesPerPixel = bpp / 8;

    //allocate memory for the TGA so we can read it
    GLuint imageSize = width * height * bytesPerPixel;
    imageData = new GLubyte[imageSize];

    if ( imageData == NULL )
        return false;

    //make sure we are in the correct position to load the image data
    file.seekg(-imageSize, std::ios::end);

    // if something when wrong, make sure we free up the memory
    //NOTE: It never gets past this point. The conditional always fails.
    if ( !file.read( (char *)imageData, imageSize ) )
    {
        delete imageData;

        return false;
    }

    //more code is down here, but it doesnt matter because it does not pass the above function
}

It seems to load some data, but it keeps returning that it failed. Any help on why would be greatly appreciated. Appologies if it gets a bit wordy, but I'm not sure what is or is not significant.

UPDATE: So, I just rewrote the function. The ifsteam I was using, seemed to be the cause of the problem. Specifically, it would try and load far more bytes of data than I had entered. I don't know the cause of the behavior, but I've listed my functioning code below. Thank you every one for your help.

3

There are 3 answers

0
Donutfiend84 On BEST ANSWER

So, I changed from using an ifstream to a FILE. The ifstream, was trying to load far more bytes than I had listed in the arguments. Here is the new code. (NOTE: It still needs optomized. I believe there are some unused variables floating around, but it works perfectly.). Thanks again everyone for your help.

The header file:

    //struct to hold tga data
struct TGA_Header
{
    GLbyte  ID_Length;
    GLbyte  ColorMapType;
    GLbyte  ImageType;
    // Color map specifications
    GLbyte firstEntryIndex[2];      
    GLbyte colorMapLength[2];
    GLbyte colorMapEntrySize;

    //image specification
    GLshort xOrigin;
    GLshort yOrigin;
    GLshort ImageWidth;
    GLshort ImageHeight;
    GLbyte  PixelDepth;
    GLbyte ImageDescriptor;
};

class Texture
{
public:
    //functions
    Texture(string in_filename, string in_name = "");
    ~Texture();

public:
    //vars
    unsigned char   *imageData;
    unsigned int    texID;

    string          name;

    //temp global access point for accessing all loaded textures
    static vector<Texture *> textures;

private:
    //can add additional load functions for other image types
    bool loadTGA(string filename);
    bool createTexture(unsigned char *imageData, int width, int height, int type);

    void swap(unsigned char * ori, unsigned char * dest, GLint size);
    void flipImage(unsigned char * image, bool flipHorizontal, bool flipVertical, GLushort width, GLushort height, GLbyte bpp);
};

#endif

Here is the load TGA function:

    bool Texture::loadTGA(string filename)
{
    //var for swapping colors
    unsigned char colorSwap = 0;

    GLuint type;
    TGA_Header TGAheader;

    FILE* file = fopen(filename.c_str(), "rb");

    unsigned char Temp_TGAheader[18];

    //check to make sure the file loaded
    if( file == NULL )
        return false;   

    fread(Temp_TGAheader, 1, sizeof(Temp_TGAheader), file);

    //pull out the relavent data. 2 byte data (short) must be converted
    TGAheader.ID_Length = Temp_TGAheader[0];
    TGAheader.ImageType = Temp_TGAheader[2];
    TGAheader.ImageWidth = *static_cast<unsigned short*>(static_cast<void*>(&Temp_TGAheader[12]));
    TGAheader.ImageHeight = *static_cast<unsigned short*>(static_cast<void*>(&Temp_TGAheader[14]));
    TGAheader.PixelDepth = Temp_TGAheader[16];


    //make sure the image is of a type we can handle
    if( TGAheader.ImageType != 2 || TGAheader.ImageWidth <= 0 || TGAheader.ImageHeight <= 0 )
    {
        fclose(file);
        return false;
    }

    //set the type
    if ( TGAheader.PixelDepth == 32 )
    {
        type = GL_RGBA;
    } 
    else if ( TGAheader.PixelDepth == 24 ) 
    {
        type = GL_RGB;
    }
    else 
    {
        //incompatable image type
        return false;
    }


    //remember bits != bytes. To convert we need to divide by 8
    GLuint bytesPerPixel = TGAheader.PixelDepth / 8;

    //The Memory Required For The TGA Data  
    unsigned int imageSize = TGAheader.ImageWidth * TGAheader.ImageHeight * bytesPerPixel;// Calculate 

    //request the needed memory
    imageData = new GLubyte[imageSize];

    if ( imageData == NULL ) // just in case
        return false;

    if( fread(imageData, 1, imageSize, file) != imageSize )
    {       
        //Kill it
        delete [] imageData;
        fclose(file);                                           
        return false;   
    }       

    fclose(file);

    for (unsigned int x = 0; x < imageSize; x +=bytesPerPixel)  
    {
        colorSwap = imageData[x];       
        imageData[x] = imageData[x + 2];        
        imageData[x + 2] = colorSwap;   
    }   

    createTexture( imageData, TGAheader.ImageWidth, TGAheader.ImageHeight, type );

    return true;
}
1
Maurizio Benedetti On

The problem could be depending on the TGA algorithm which do not support compressed TGA.

Make sure you do not compress the TGA and that the TGA order (less important) is in a Bottom Left origin.

I usually work with GIMP and at the moment of the same, uncheck the RLE compression and put the Bottom Left alignment.

5
Kromster On

I'm not familiar with C++, sorry.

Are you sure this line file.seekg(-imageSize, std::ios::end); is not supposed to be file.seekg(headerSize, std::ios::start); ?

Makes more sense to seek from start than from end.

You should also check for ColorMapType != 0.

P.S. Here if( width < 0 || height <=0 width check should be <= as well.