Advertisement

A bmp loader (for those that want one)

Started by August 24, 2005 05:57 PM
1 comment, last by relpats_eht 19 years, 3 months ago
A while back i started writing a texture class, i got bored after making a bmp loader and an (in my opinion) improved version of the tga loader. The bmp loader can only load a bitmap with a width and height that are both multiples of 4, and it only loads 8 and 24 bit images, but id say a good 99% of bitmaps used as openGL textures meet that criteria. So, for those that want it, here is my texture class the .h
#ifndef _TEXTURE_H_
#define _TEXTURE_H_

#include <string>
#include <fstream>
#include <algorithm>
#include <gl/gl.h>

#define TEXTURE_LOADED   0
#define FILE_NOT_FOUND   1
#define INVALID_FILE     2
#define READ_ERROR       3
#define IMAGE_UNSUPORTED 4
#define ILLEGAL_SIZE     5
#define MEMORY_ERROR     6

using namespace std;

typedef unsigned char byte;
typedef unsigned short word;

class GLTexture{
    private:
        GLint loadBMP(char* filename);
        GLint loadTGA(char* filename);

    public:
        GLint    bpp;
        GLsizei  width;
        GLsizei  height;
        GLenum   format;
        GLenum   dataType;
        GLubyte* data;
        
        GLTexture(){}
        GLTexture(char* filename);
        ~GLTexture();
        GLint loadGLTexture(char* filename);
        GLvoid generateTexture();
};

#endif
and the .cpp
#include "texture.h"
GLTexture::GLTexture(char* filename){
    bpp = 3;
    width = 64;
    height = 64;
    format = GL_RGB;
    dataType = GL_UNSIGNED_BYTE;
    data = NULL;
        
    loadGLTexture(filename);
}

GLTexture::~GLTexture(){
    if(data != NULL){
        delete [] data;
    }
}

GLint GLTexture::loadGLTexture(char* filename){
    GLint i;
    GLint result;
    string extension;

    extension = strrchr(filename, '.')+1;
    
    transform(extension.begin(), extension.end(), extension.begin(), (int(*)(int))tolower);
    
    bpp = 3;
    width = 64;
    height = 64;
    format = GL_RGB;
    dataType = GL_UNSIGNED_BYTE;
    
    if(data != NULL){
        delete [] data;
    }
    
    if(extension == "bmp" == 0){
        result = loadBMP(filename);
    }else if(extension == "tga" == 0){
        result = loadTGA(filename);
    }else{
        return IMAGE_UNSUPORTED;
    }
    
    return result;
}

GLvoid GLTexture::generateTexture(){
    glTexImage2D(GL_TEXTURE_2D, 0, bpp, width, height, 0, format, dataType, data);
}

GLint GLTexture::loadBMP(char* filename){
    ifstream bmp(filename, ios::in | ios::ate | ios::binary);
    GLint i,l;
    GLint fileSize;
    GLubyte* buffer = NULL;
    GLubyte* ptr;
    
    if(!bmp.is_open()){
        return FILE_NOT_FOUND;
    }
    
    fileSize = (GLint)bmp.tellg();
    bmp.seekg(0, ios::beg);
    
    buffer = new GLubyte[fileSize];
    if(buffer == NULL){
        return MEMORY_ERROR;
    }
    
    bmp.read((char*)buffer, fileSize);
    if(bmp.bad()){
        delete [] buffer;
        return READ_ERROR;
    }
    bmp.close();
    
    ptr = buffer;
    
    if(ptr[0]!='B' || ptr[1]!='M'){
        delete [] buffer;
        return INVALID_FILE;
    }

    ptr += sizeof(GLushort); // type
    ptr += sizeof(GLint); // size
    ptr += sizeof(GLuint); // reserved
    ptr += sizeof(GLint); // dataOff
    ptr += sizeof(GLint); // size

    width= GLsizei(*(GLint*)ptr);
    ptr += sizeof(GLint);

    height= GLsizei(*(GLint*)ptr);
    ptr += sizeof(GLint);

    if(width <= 0 || height <= 0 || (width % 4) != 0 || (height % 4) != 0){
        delete [] buffer;
        return ILLEGAL_SIZE;
    }
    
    data = new GLubyte[width*height*3];
    if(data == NULL){
        delete [] buffer;
        return MEMORY_ERROR;
    }

    ptr += sizeof(GLshort); // planes

    bpp = GLint(*(GLshort*)ptr);
    ptr += sizeof(GLshort);
    if(bpp != 24 && bpp != 8){
        delete [] buffer;
        return IMAGE_UNSUPORTED;
    }
    
    ptr += sizeof(GLint); // compression
    ptr += sizeof(GLint); // sizeImage
    ptr += sizeof(GLint); // xPelsPerMeter
    ptr += sizeof(GLint); // yPelsPerMeter
    ptr += sizeof(GLint); // clrUsed
    ptr += sizeof(GLint); // clrImportant
    
    if(bpp == 24){
        memcpy(data, ptr, width*height*3);
        
        for(i=0; i<width*height*3; i+=3){
            swap(data, data[i+2]);
        }
    }else if(bpp == 8){
        GLubyte palette[1024];
        memcpy(palette, ptr, sizeof(GLubyte)*256*4);
        ptr += sizeof(GLubyte)*256*4;
        
        for(i=0; i<width*height*3; i+=3){
            for(l=0; l<3; l++){
                data[i+l] = palette[((*(GLubyte*)ptr)*4)+l];
            }
            ptr += sizeof(GLubyte);
        }
    }        

    bpp = 3;
    format = GL_RGB;
    
    delete [] buffer;
    
    return TEXTURE_LOADED;
}
  
GLint GLTexture::loadTGA(char* filename){
    ifstream tga(filename, ios::in | ios::ate | ios::binary);
    GLint i,l;
    GLuint currentByte = 0;
    GLboolean compressed;
    GLint fileSize;
    GLubyte colorBuffer[4];
    GLubyte chunkHeader;
    GLubyte* buffer = NULL;
    GLubyte* ptr;
    
    if(!tga.is_open()){
        return FILE_NOT_FOUND;
    }
    
    fileSize = (GLint)tga.tellg();
    tga.seekg(0, ios::beg);
    
    buffer = new GLubyte[fileSize];
    if(buffer == NULL){
        return MEMORY_ERROR;
    }
    
    tga.read((char*)buffer, fileSize);
    if(tga.bad()){
        delete [] buffer;
        return READ_ERROR;
    }
    tga.close();
    
    ptr = buffer;
    
    
    
    ptr += sizeof(GLubyte); // idLen
    ptr += sizeof(GLubyte); // colMapPresent
    
    if(*(GLubyte*)ptr == 2){
        compressed = false;
    }else if(*(GLubyte*)ptr == 10){
        compressed = true;
    }else{
        delete [] buffer;
        return IMAGE_UNSUPORTED;
    }
    ptr += sizeof(GLubyte); // imageType
    
    ptr += sizeof(GLshort); // firstEntry
    ptr += sizeof(GLshort); // colMapLen
    ptr += sizeof(GLubyte); // colMapEntSize
    ptr += sizeof(GLshort); // originX
    ptr += sizeof(GLshort); // originY
    
    width = GLsizei(*(GLushort*)ptr);
    ptr += sizeof(GLushort);
    
    height = GLsizei(*(GLushort*)ptr);
    ptr += sizeof(GLushort);
    
    bpp = GLint(*(GLubyte*)ptr);
    ptr += sizeof(GLubyte);
    
    ptr += sizeof(GLubyte); // imageDesc
    
    if(width <= 0 || height <= 0){
        delete [] buffer;
        return ILLEGAL_SIZE;
    }
    
    if(bpp == 24){
        format = GL_RGB;
    }else if(bpp == 32){
        format = GL_RGBA;
    }else{
        delete [] buffer;
        return IMAGE_UNSUPORTED;
    }
    
    bpp /= 8;
    
    data = new GLubyte[width*height*bpp];
    if(data == NULL){
        delete [] buffer;
        return MEMORY_ERROR;
    }
    
    if(compressed == false){
        memcpy(data, ptr, width*height*bpp);
        
        for(i=0; i<width*height*bpp; i+=bpp){
            swap(data, data[i+2]);
        }
    }else{
        for(i=0; i<(width*height); i++){
            chunkHeader = 0;
            
            chunkHeader = *(GLubyte*)ptr;
            ptr += sizeof(GLubyte);
            
            if(chunkHeader < 128){
                chunkHeader++;
                
                for(l=0; l<chunkHeader; l++){
                    memcpy(colorBuffer, ptr, bpp);
                    ptr += bpp;
                    
                    data[currentByte] = colorBuffer[2];
                    data[currentByte+1] = colorBuffer[1];
                    data[currentByte+2] = colorBuffer[0];
                    
                    if(bpp == 4){
                        data[currentByte+3] = colorBuffer[3];
                    }
                    
                    currentByte += bpp;
                    i++;
                }
            }else{
                chunkHeader -= 127;
                
                memcpy(colorBuffer, ptr, bpp);
                ptr += bpp;
                
                for(l=0; l<chunkHeader; l++){                    
                    data[currentByte] = colorBuffer[2];
                    data[currentByte+1] = colorBuffer[1];
                    data[currentByte+2] = colorBuffer[0];
                    
                    if(bpp == 4){
                        data[currentByte+3] = colorBuffer[3];
                    }
                    
                    currentByte += bpp;
                    i++;
                }
            }
        }
    }              

    delete [] buffer;

    return TEXTURE_LOADED;
}
using it is simple enough, just call the loadGLTexture function and the image will be loaded into the respective vars (you should be able to figure them out from their names) eh, hope that helps anyone who wanted a small(ish) cross platfrom(ish) bmp loader... *edit* updated to the current version (ie: the one without memory leaks) did som e of the stuff enigma suggested. (i know there are more inefficies with it, some are intentional (eg, adding adding the sizeof(int) over and over, some are from laziness (basically, i only fixed what had to be fixed, and stuff that could be improved is still there)) [Edited by - relpats_eht on August 24, 2005 7:32:10 PM]
- relpats_eht
It's great that you're willing to share your code like that, but there are some serious errors in your implementation, not to mention that it's quite inefficient. I've highlighted some (but not all) of the errors and inefficiencies below:
// you never initialise data, which causes problems in loadGLTextureGLTexture::GLTexture(char* filename){    loadGLTexture(filename);}GLTexture::~GLTexture(){    delete [] data;}GLint GLTexture::loadGLTexture(char* filename){    GLint i;    GLint result;    char extension[6] = "";    // what about files with multiple '.'s?  Use std::string, std::find and std::string::rbegin()/std::string::rend() to find the last '.' in a string.    strncpy(extension, strrchr(filename, '.')+1, 5);    // only works for english ascii.  Try std::transform(extension.begin(), extension.end(), extension.begin(), std::tolower)    for(i=0; i<strlen(extension); i++){        if(extension >= 65 && extension <= 90){            extension += 32;        }    }        bpp = 3;    width = 64;    height = 64;    format = GL_RGB;    dataType = GL_UNSIGNED_BYTE;    // you never initialised data, so this deletes an uninitialised pointer    delete [] data;        if(strcmp(extension, "bmp") == 0){        result = loadBMP(filename);    }else if(strcmp(extension, "tga") == 0){        result = loadTGA(filename);    }else{        return IMAGE_UNSUPORTED;    }        return result;}GLvoid GLTexture::generateTexture(){    glTexImage2D(GL_TEXTURE_2D, 0, bpp, width, height, 0, format, dataType, data);}GLint GLTexture::loadBMP(char* filename){    ifstream bmp(filename, ios::in | ios::ate | ios::binary);    GLint i,l;    GLint fileSize;    GLubyte* buffer;    // might not be needed.  Declare it just before you first need it and let the compiler decide when to allocate it.    GLubyte palette[1024];    GLubyte* ptr;        if(!bmp.is_open()){        return FILE_NOT_FOUND;    }        fileSize = (GLint)bmp.tellg();    bmp.seekg(0, ios::beg);        buffer = new GLubyte[fileSize];    bmp.read((char*)buffer, fileSize);    if(bmp.bad()){        // you never delete buffer, so you have a memory leak        return READ_ERROR;    }    bmp.close();        ptr = buffer;        if(ptr[0]!='B' || ptr[1]!='M'){        // see previous comment        return INVALID_FILE;    }    ptr += sizeof(GLushort); // type    ptr += sizeof(GLint); // size    ptr += sizeof(GLuint); // reserved    ptr += sizeof(GLint); // dataOff    ptr += sizeof(GLint); // size    width= GLsizei(*(GLint*)ptr);    ptr += sizeof(GLint);    height= GLsizei(*(GLint*)ptr);    ptr += sizeof(GLint);    if(width <= 0 || height <= 0 || (width % 4) != 0 || (height % 4) != 0){        // see previous comment        return ILLEGAL_SIZE;    }        // if new throws you leak buffer    data = new GLubyte[width*height*3];    ptr += sizeof(GLshort); // planes    bpp = GLint(*(GLshort*)ptr);    ptr += sizeof(GLshort);    if(bpp != 24 && bpp != 8){        // see previous comment        return IMAGE_UNSUPORTED;    }        ptr += sizeof(GLint); // compression    ptr += sizeof(GLint); // sizeImage    ptr += sizeof(GLint); // xPelsPerMeter    ptr += sizeof(GLint); // yPelsPerMeter    ptr += sizeof(GLint); // clrUsed    ptr += sizeof(GLint); // clrImportant        if(bpp == 24){        memcpy(data, ptr, width*height*3);                for(i=0; i<width*height*3; i+=3){            // ahh, the infamous "xor-swap".  You do realise that on modern compilers this is less efficient than std::swap(data, data) right?</span><br>            data<span style="font-weight:bold;"> ^= data[i+<span class="cpp-number">2</span>] ^= data<span style="font-weight:bold;"> ^= data[i+<span class="cpp-number">2</span>];<br>        }<br>    }<span class="cpp-keyword">else</span> <span class="cpp-keyword">if</span>(bpp == <span class="cpp-number">8</span>){<br>        memcpy(palette, ptr, <span class="cpp-keyword">sizeof</span>(GLubyte)*<span class="cpp-number">256</span>*<span class="cpp-number">4</span>);<br>        ptr += <span class="cpp-keyword">sizeof</span>(GLubyte)*<span class="cpp-number">256</span>*<span class="cpp-number">4</span>;<br>        <br>        <span class="cpp-keyword">for</span>(i=<span class="cpp-number">0</span>; i&lt;width*height*<span class="cpp-number">3</span>; i+=<span class="cpp-number">3</span>){<br>            <span class="cpp-keyword">for</span>(l=<span class="cpp-number">0</span>; l&lt;<span class="cpp-number">3</span>; l++){<br>                data[i+l] = palette[((*(GLubyte*)ptr)*<span class="cpp-number">4</span>)+l];<br>            }<br>            ptr += <span class="cpp-keyword">sizeof</span>(GLubyte);<br>        }<br>    }        <br><br>    bpp = <span class="cpp-number">3</span>;<br>    format = GL_RGB;<br>    <br>    <span class="cpp-keyword">delete</span> [] buffer;<br>    <br>    <span class="cpp-keyword">return</span> TEXTURE_LOADED;<br>}<br><br>    <br>    <br>GLint GLTexture::loadTGA(<span class="cpp-keyword">char</span>* filename){<br>    ifstream tga(filename, ios::in | ios::ate | ios::binary);<br>    GLint i,l;<br>    GLuint currentByte = <span class="cpp-number">0</span>;<br>    GLboolean compressed;<br>    GLint fileSize;<br>    GLubyte colorBuffer[<span class="cpp-number">4</span>];<br>    GLubyte chunkHeader;<br>    GLubyte* buffer;<br>    GLubyte* ptr;<br>    <br>    <span class="cpp-keyword">if</span>(!tga.is_open()){<br>        <span class="cpp-keyword">return</span> FILE_NOT_FOUND;<br>    }<br>    <br>    fileSize = (GLint)tga.tellg();<br>    tga.seekg(<span class="cpp-number">0</span>, ios::beg);<br>    <br>    buffer = <span class="cpp-keyword">new</span> GLubyte[fileSize];<br>    tga.read((<span class="cpp-keyword">char</span>*)buffer, fileSize);<br>    <span class="cpp-keyword">if</span>(tga.bad()){<br>        <span class="cpp-comment">// leak</span><br>        <span class="cpp-keyword">return</span> READ_ERROR;<br>    }<br>    tga.close();<br>    <br>    ptr = buffer;<br>    <br>    <br>    <br>    ptr += <span class="cpp-keyword">sizeof</span>(GLubyte); <span class="cpp-comment">// idLen</span><br>    ptr += <span class="cpp-keyword">sizeof</span>(GLubyte); <span class="cpp-comment">// colMapPresent</span><br>    <br>    <span class="cpp-keyword">if</span>(*(GLubyte*)ptr == <span class="cpp-number">2</span>){<br>        compressed = <span class="cpp-keyword">false</span>;<br>    }<span class="cpp-keyword">else</span> <span class="cpp-keyword">if</span>(*(GLubyte*)ptr == <span class="cpp-number">10</span>){<br>        compressed = <span class="cpp-keyword">true</span>;<br>    }<span class="cpp-keyword">else</span>{<br>        <span class="cpp-comment">// leak</span><br>        <span class="cpp-keyword">return</span> INVALID_FILE;<br>    }<br>    ptr += <span class="cpp-keyword">sizeof</span>(GLubyte); <span class="cpp-comment">// imageType</span><br>    <br>    ptr += <span class="cpp-keyword">sizeof</span>(GLshort); <span class="cpp-comment">// firstEntry</span><br>    ptr += <span class="cpp-keyword">sizeof</span>(GLshort); <span class="cpp-comment">// colMapLen</span><br>    ptr += <span class="cpp-keyword">sizeof</span>(GLubyte); <span class="cpp-comment">// colMapEntSize</span><br>    ptr += <span class="cpp-keyword">sizeof</span>(GLshort); <span class="cpp-comment">// originX</span><br>    ptr += <span class="cpp-keyword">sizeof</span>(GLshort); <span class="cpp-comment">// originY</span><br>    <br>    width = GLsizei(*(GLushort*)ptr);<br>    ptr += <span class="cpp-keyword">sizeof</span>(GLushort);<br>    <br>    height = GLsizei(*(GLushort*)ptr);<br>    ptr += <span class="cpp-keyword">sizeof</span>(GLushort);<br>    <br>    bpp = GLint(*(GLubyte*)ptr);<br>    ptr += <span class="cpp-keyword">sizeof</span>(GLubyte);<br>    <br>    ptr += <span class="cpp-keyword">sizeof</span>(GLubyte); <span class="cpp-comment">// imageDesc</span><br>    <br>    <span class="cpp-keyword">if</span>(width &lt;= <span class="cpp-number">0</span> || height &lt;= <span class="cpp-number">0</span>){<br>        <span class="cpp-comment">// leak</span><br>        <span class="cpp-keyword">return</span> ILLEGAL_SIZE;<br>    }<br>    <br>    <span class="cpp-keyword">if</span>(bpp == <span class="cpp-number">24</span>){<br>        format = GL_RGB;<br>    }<span class="cpp-keyword">else</span> <span class="cpp-keyword">if</span>(bpp == <span class="cpp-number">32</span>){<br>        format = GL_RGBA;<br>    }<span class="cpp-keyword">else</span>{<br>        <span class="cpp-comment">// leak</span><br>        <span class="cpp-keyword">return</span> IMAGE_UNSUPORTED;<br>    }<br>    <br>    bpp /= <span class="cpp-number">8</span>;<br>    <br>    data = <span class="cpp-keyword">new</span> GLubyte[width*height*bpp];<br>    <br>    <span class="cpp-keyword">if</span>(compressed == <span class="cpp-keyword">false</span>){<br>        memcpy(data, ptr, width*height*bpp);<br>        <br>        <span class="cpp-keyword">for</span>(i=<span class="cpp-number">0</span>; i&lt;width*height*bpp; i+=bpp){<br>            <span class="cpp-comment">// slow</span><br>            data<span style="font-weight:bold;"> ^= data[i+<span class="cpp-number">2</span>] ^= data<span style="font-weight:bold;"> ^= data[i+<span class="cpp-number">2</span>];<br>        }<br>    }<span class="cpp-keyword">else</span>{<br>        <span class="cpp-keyword">for</span>(i=<span class="cpp-number">0</span>; i&lt;(width*height); i++){<br>            chunkHeader = <span class="cpp-number">0</span>;<br>            <br>            chunkHeader = *(GLubyte*)ptr;<br>            ptr += <span class="cpp-keyword">sizeof</span>(GLubyte);<br>            <br>            <span class="cpp-keyword">if</span>(chunkHeader &lt; <span class="cpp-number">128</span>){<br>                chunkHeader++;<br>                <br>                <span class="cpp-keyword">for</span>(l=<span class="cpp-number">0</span>; l&lt;chunkHeader; l++){<br>                    memcpy(colorBuffer, ptr, bpp);<br>                    ptr += bpp;<br>                    <br>                    data[currentByte] = colorBuffer[<span class="cpp-number">2</span>];<br>                    data[currentByte+<span class="cpp-number">1</span>] = colorBuffer[<span class="cpp-number">1</span>];<br>                    data[currentByte+<span class="cpp-number">2</span>] = colorBuffer[<span class="cpp-number">0</span>];<br>                    <br>                    <span class="cpp-keyword">if</span>(bpp == <span class="cpp-number">4</span>){<br>                        data[currentByte+<span class="cpp-number">3</span>] = colorBuffer[<span class="cpp-number">3</span>];<br>                    }<br>                    <br>                    currentByte += bpp;<br>                    i++;<br>                }<br>            }<span class="cpp-keyword">else</span>{<br>                chunkHeader -= <span class="cpp-number">127</span>;<br>                <br>                memcpy(colorBuffer, ptr, bpp);<br>                ptr += bpp;<br>                <br>                <span class="cpp-keyword">for</span>(l=<span class="cpp-number">0</span>; l&lt;chunkHeader; l++){                    <br>                    data[currentByte] = colorBuffer[<span class="cpp-number">2</span>];<br>                    data[currentByte+<span class="cpp-number">1</span>] = colorBuffer[<span class="cpp-number">1</span>];<br>                    data[currentByte+<span class="cpp-number">2</span>] = colorBuffer[<span class="cpp-number">0</span>];<br>                    <br>                    <span class="cpp-keyword">if</span>(bpp == <span class="cpp-number">4</span>){<br>                        data[currentByte+<span class="cpp-number">3</span>] = colorBuffer[<span class="cpp-number">3</span>];<br>                    }<br>                    <br>                    currentByte += bpp;<br>                    i++;<br>                }<br>            }<br>        }<br>    }              <br><br>    <span class="cpp-keyword">delete</span> [] buffer;<br><br>    <span class="cpp-keyword">return</span> TEXTURE_LOADED;<br>}<br></pre></div><!–ENDSCRIPT–><br><br>Enigma
Advertisement
oops, that was an older version, which explains some (but not all) of the problems

ill fix as many as i see...
- relpats_eht

This topic is closed to new replies.

Advertisement