Jump to content
  • Advertisement
Sign in to follow this  
relpats_eht

A bmp loader (for those that want one)

This topic is 4832 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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]

Share this post


Link to post
Share on other sites
Advertisement
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 loadGLTexture
GLTexture::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[i + 2]) right?
data ^= data[i+2] ^= data ^= data[i+2];
}
}else if(bpp == 8){
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;
GLubyte* ptr;

if(!tga.is_open()){
return FILE_NOT_FOUND;
}

fileSize = (GLint)tga.tellg();
tga.seekg(0, ios::beg);

buffer = new GLubyte[fileSize];
tga.read((char*)buffer, fileSize);
if(tga.bad()){
// leak
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{
// leak
return INVALID_FILE;
}
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){
// leak
return ILLEGAL_SIZE;
}

if(bpp == 24){
format = GL_RGB;
}else if(bpp == 32){
format = GL_RGBA;
}else{
// leak
return IMAGE_UNSUPORTED;
}

bpp /= 8;

data = new GLubyte[width*height*bpp];

if(compressed == false){
memcpy(data, ptr, width*height*bpp);

for(i=0; i<width*height*bpp; i+=bpp){
// slow
data ^= data[i+2] ^= 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;
}


Enigma

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!