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]