Sign in to follow this  
Tsumuji

OpenGL c++ texture class - Mine contribution to the community.

Recommended Posts

Here comes mine contribution to the forum. Since I started to post threads here, I killed a bunch of doubts, and I'm very grateful. So, I remember a thing. We have allways people around searching for a loader of some type of image(file format), but allways get a library that do that. Well, not allways people want a library for some reason, so I get some ideas from internet and maked some adaptations to my needs. Conclusion: I writed a c++ class that load image from disk, and generate the openGL texture for you. The class supports .bmp, .png, .jpg, .tga, .pcx, anf tif. The only missing is .dds from directx. I think is support enough to the class :) The only requisites to the png, jpg, and tif, is that you will need the respective libraries to load that complex files. So you'll need libpng, libjpg, and libtiff to work with these. Ah! And if you use this class, you don't need to send me a e-mail, just post here that you are using this class. I want a feedback ;) If anyone want help to implement this source code, tell me. Some adjustments can be needed. The licence is GPL. And here comes the code: HEADER .hpp
/*
The Open Engine
Copyright (C) 2006 Johnny Birnfeld  https://sourceforge.net/projects/theopenengine

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

*/


#ifndef TOE_TEXTURE_H
#define TOE_TEXTURE_H

#include <string>
using namespace std;

class TOETexture{
	string _fileName;
	int _w;
	int _h;
	short int _bpp;
	unsigned int _id;
	unsigned char *_data;
	unsigned long _dataSize;
	
	float _OGLfilter;
	
	int readBMP( const char* );
	int readJPG( const char* );
	int readPNG( const char* );
	int readTIF( const char* );
	int readTGA( const char* );
	int readPCX( const char* );
	int readDDS( const char* );
	int generateTexture();
	void* combine( void*, size_t, void*, size_t );
	int tgaLoadRawData( unsigned char* );
	int tgaLoadRLEData( unsigned char* );
	void tgaBGRtoRGB();
	void flip();
	public:
		TOETexture();
		int load( string );
		int load( string, float );
		string fileName();
		unsigned int w();unsigned int h();
		unsigned int bpp();
		unsigned int id();
};

#endif


BODY .cpp
/*
The Open Engine
Copyright (C) 2006 Johnny Birnfeld  https://sourceforge.net/projects/theopenengine

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

*/


#include "TOETexture.hpp"
#include <iostream>
#include <fstream>
#include <GL/glu.h>

extern "C" {
	#include <jpeglib.h>
	#include <jerror.h>
}
#include <png.h>
#include <tiffio.h>


int TOETexture::readBMP( const char *filename ){
	unsigned short int bfType;
	long int bfOffBits;
	short int biPlanes;
	
	ifstream ifile(filename);
	if ( !ifile.good() )
		return 1; //File not found or corrupted.
	ifile.read( (char*)&bfType, sizeof(short int) );
	if (bfType != 19778){
		return 2; //Not a Bitmap-File
	}
	
	ifile.seekg(8, ios::cur); //skip file size and reserved fields of bitmap file header
	ifile.read( (char*)&bfOffBits, sizeof(long int) ); //get the position of the actual bitmap data
	ifile.seekg(4, ios::cur); //skip size of bitmap info header
	ifile.read( (char*)&this->_w, sizeof(int) ); //get the width of the bitmap
	ifile.read( (char*)&this->_h, sizeof(int) ); //get the heigth of the bitmap
	ifile.read( (char*)&biPlanes, sizeof(short int) ); //get the number of planes (must be set to 1)
	if (biPlanes != 1){
		return 3; //number of Planes not 1!
	}
	ifile.read( (char*)&this->_bpp, sizeof(short int) ); //get the number of bits per pixel
	if (this->_bpp != 24){
		return 4; //Bits per Pixel not 24!
	}
	
	_dataSize = this->_w * this->_h * 3; //calculate the size of the image in bytes
	_data = new unsigned char[_dataSize];
	
	ifile.seekg(bfOffBits, ios::beg); //seek to the actual data
	ifile.read( (char*)_data, _dataSize ); //read data
	
	//swap red and blue (bgr -> rgb)
	unsigned char temp;
	for ( unsigned int i = 0; i < _dataSize; i += 3 ){
		temp = _data[i];
		_data[i] = _data[i + 2];
		_data[i + 2] = temp;
	}
	
	ifile.close();
	
	this->_fileName = filename;
	
	return 0;
}

int TOETexture::readJPG( const char* filename ){
	
	FILE *file;
	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
	volatile JSAMPROW row = 0;
	JSAMPROW rowptr[1];
	uint i,j,nrows;
	
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);

	/* make sure the file is there and open it read-only (binary) */
	if ((file = fopen(filename, "rb")) == NULL){
		return 1; //File not found.
	}
	jpeg_stdio_src(&cinfo, file);
	jpeg_read_header(&cinfo, true);
	jpeg_start_decompress(&cinfo);
	
	this->_w = cinfo.output_width;
	this->_h = cinfo.output_height;
	//printf("Colorspace %d\n",cinfo.jpeg_color_space);
	this->_bpp = cinfo.jpeg_color_space*8; //JPEG images do not handle alpha channel.
	
	row = (JSAMPROW)calloc(1,cinfo.image_width * cinfo.output_components
			* sizeof(JSAMPLE));
	
	_dataSize = cinfo.image_width * cinfo.image_height * cinfo.output_components * sizeof(JSAMPLE);
	_data = (unsigned char*)malloc(_dataSize);
	rowptr[0] = row;
	
	for ( i = 0; i < cinfo.output_height; i++ ) {
		nrows = jpeg_read_scanlines(&cinfo, rowptr, 1);
		if (nrows != 1) {
			fprintf(stderr, "jpeg_read_scanlines"
					" returns %u, expected 1\n", nrows);
			return 5;
		}
		
		for ( j = 0; j < cinfo.output_width*cinfo.output_components; j++ )
			_data[(cinfo.output_height-1-i)*cinfo.output_width*cinfo.output_components+j] = row[j];
	
	}
	
	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);
	free(row);
	
	this->_fileName = filename;
	
	return 0;
}

int TOETexture::readPNG( const char* filename ){
	unsigned int y;
	
	png_structp png_ptr;
	png_infop info_ptr;
	int number_of_passes;
	png_bytep * row_pointers;
	
	char header[8];	// 8 is the maximum size that can be checked
	
	/* open file and test for it being a png */
	FILE *fp = fopen(filename, "rb");
	if (!fp)
		return 1; //File not found
	fread(header, 1, 8, fp);
	if (png_sig_cmp((png_byte*)header, 0, 8))
		return 2; //Not a valid file
	
	/* initialize stuff */
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	
	if (!png_ptr)
		return -1; //png_create_read_struct failed
	
	info_ptr = png_create_info_struct(png_ptr);
	if (!info_ptr)
		return -1; //png_create_info_struct failed
	
	if (setjmp(png_jmpbuf(png_ptr)))
		return -1; //Error during init_io
	
	png_init_io(png_ptr, fp);
	png_set_sig_bytes(png_ptr, 8);
	
	png_read_info(png_ptr, info_ptr);
	
	number_of_passes = png_set_interlace_handling(png_ptr);
	png_read_update_info(png_ptr, info_ptr);
	
	/* read file */
	if (setjmp(png_jmpbuf(png_ptr)))
		return -1; //Error during read_image
	
	row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * info_ptr->height);
	for ( y=0; y<info_ptr->height; y++ )
		row_pointers[y] = (png_byte*) malloc(info_ptr->rowbytes);
	
	png_read_image(png_ptr, row_pointers);
	
	
	
	this->_w = info_ptr->width;
	this->_h = info_ptr->height;
	this->_bpp = info_ptr->bit_depth;
	if ( info_ptr->color_type == PNG_COLOR_TYPE_RGBA ){
		this->_bpp *= 4;
	}else{
		if ( info_ptr->color_type == PNG_COLOR_TYPE_RGB ){
			this->_bpp *= 3;
		}else{
			std::cout << "Undefined color type for the png file." << std::endl;
		}
	}
	_dataSize = this->_h * info_ptr->rowbytes;
	_data = new unsigned char[_dataSize];
	
	int tszdata=0;
	for ( int c=0; c<this->_h; c++ ){
		_data = (unsigned char*)combine( _data, tszdata, row_pointers[c], info_ptr->rowbytes );
		tszdata += info_ptr->rowbytes;
	}
	
	this->_fileName = filename;
	
	
	
	
	/* finish decompression and release memory */
	png_read_end (png_ptr, NULL);
	png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
	/* we don't need row pointers anymore */
	free (row_pointers);
	
	fclose(fp);
	//Done reading .png file
	
	return 0;
}

int TOETexture::readTIF( const char *filename ){
	TIFFRGBAImage img;
	uint32 *raster;
	size_t npixels;
	
	TIFF *tif;
	char emsg[1024];
	tif = TIFFOpen(filename, "r");
	if (tif == NULL) {
		fprintf(stderr, "Problem loading %s\n", filename);
		return 1;
	}
	if ( TIFFRGBAImageBegin( &img, tif, 0, emsg ) ){
		npixels = img.width * img.height;
		raster = (uint32 *) _TIFFmalloc(npixels * /*img.samplesperpixel*/sizeof(uint32));
		if (raster != NULL) {
			if (TIFFRGBAImageGet(&img, raster, img.width, img.height) == 0) {
				TIFFError(filename, emsg);
				return 2;
			}
		}
		TIFFRGBAImageEnd(&img);
	}else{
		TIFFError(filename, emsg);
		return -1;
	}
	
	this->_w = img.width;
	this->_h = img.height;
	this->_bpp = 32/*img.bitspersample * img.samplesperpixel*/;
	_dataSize = this->_w * this->_h * this->_bpp/8;
	_data = new unsigned char[_dataSize];
	memcpy( _data, raster, _dataSize );
	this->_fileName = filename;
	
	
	TIFFClose(tif);
	return 0;
}

int TOETexture::readTGA( const char *filename ){
	
	ifstream ifile;
	unsigned long fileSize;
	unsigned char *pData;
	char bEnc;
	
	ifile.open( filename,ios::binary );
	if(ifile==NULL)
		return 1;
	
	// Get file size
	ifile.seekg(0,ios_base::end);
	fileSize=ifile.tellg();
	ifile.seekg(0,ios_base::beg);
	
	pData = new unsigned char[fileSize];
	if(pData==NULL){
		ifile.close();
		return -1;
	}
	
	// Read the file into memory
	ifile.read((char*)pData,fileSize);
	ifile.close();
	
	
	//Read Header
	short ColMapStart,ColMapLen;
	short x1,y1,x2,y2;
	
	if( pData[1] > 1 )    // 0 (RGB) and 1 (Indexed) are the only types we know about
		return 2; //Not a TGA file, or not supported format.
	
	bEnc = pData[2];     // Encoding flag  1 = Raw indexed image
                      //                2 = Raw RGB
                      //                3 = Raw greyscale
                      //                9 = RLE indexed
                      //               10 = RLE RGB
                      //               11 = RLE greyscale
                      //               32 & 33 Other compression, indexed
	
	if( bEnc > 11 or bEnc == 1 or bEnc == 9 ) // We don't want 32 or 33. And engine does not read indexed images too.
		return 2;
	
	
	// Get palette info
	memcpy(&ColMapStart,&pData[3],2);
	memcpy(&ColMapLen,&pData[5],2);
	
	// Reject indexed images if not a VGA palette (256 entries with 24 bits per entry)
	if(pData[1]==1) // Indexed
	{
		if(ColMapStart!=0 || ColMapLen!=256 || pData[7]!=24)
			return 2;
	}
	
	// Get image window and produce width & height values
	memcpy(&x1,&pData[8],2);
	memcpy(&y1,&pData[10],2);
	memcpy(&x2,&pData[12],2);
	memcpy(&y2,&pData[14],2);
	
	this->_w = (x2-x1);
	this->_h = (y2-y1);
	
	if(this->_w<1 || this->_h<1)
		return 2;
	
	// Bits per Pixel
	this->_bpp = pData[16];
	
	// Check flip / interleave byte
	if(pData[17]>32) // Interleaved data
		return 2;
	
	// Calculate image size
	this->_dataSize=(this->_w * this->_h * (this->_bpp/8));
	//Read Header
	
	
	switch(bEnc)
	{
		case 2: // Raw RGB
		{
			// Check filesize against header values
			if((this->_dataSize+18+pData[0])>fileSize)
				return 2;
			
			// Double check image type field
			if(pData[1]!=0)
				return 2;
			
			// Load image data
			if( tgaLoadRawData( pData )!=0 )
				return -1;
			
			tgaBGRtoRGB(); // Convert to RGB
			break;
		}
		
		case 10: // RLE RGB
		{
			// Double check image type field
			if( pData[1] != 0 )
				return 2;
			
			// Load image data
			if( tgaLoadRLEData( pData ) != 0 )
				return -1;
			
			tgaBGRtoRGB(); // Convert to RGB
			break;
		}
		
		default:
			return 2;
	}
	
	// Check flip bit
	//if((pData[17] & 0x20)==0){
	//	flip();
	//}
	
	// Release file memory
	delete [] pData;
	pData = NULL;
	
	this->_fileName = filename;
	
	return 0;
}

int TOETexture::readPCX( const char *filename ){
	
	struct PCXheader {
		unsigned char Manufacturer;
		unsigned char Version;
		unsigned char Encoding;
		unsigned char BitsPerPixel;
		short int Xmin, Ymin, Xmax, Ymax;
		short int HDpi, VDpi;
		unsigned char Colormap[48];
		unsigned char Reserved;
		unsigned char NPlanes;
		short int BytesPerLine;
		short int PaletteInfo;
		short int HscreenSize;
		short int VscreenSize;
		unsigned char Filler[54];
	};
	
	struct PCXheader pcxh;
	int bpl;
	unsigned char *row, *buf = NULL;
	
	ifstream ifile(filename);
	if ( !ifile.good() ){
		return 1;
	}
	
	ifile.read( (char*)&pcxh, sizeof(pcxh) );
	
	this->_w = (pcxh.Xmax - pcxh.Xmin) + 1;
	this->_h = (pcxh.Ymax - pcxh.Ymin) + 1;
	this->_bpp = pcxh.BitsPerPixel * pcxh.NPlanes;
	if ( this->_bpp != 24 ){
		ifile.close();
		return 2;
	}
	
	this->_dataSize = this->_w*this->_h*(this->_bpp/8);
	this->_data = new unsigned char[this->_dataSize];
	
	
	bpl = pcxh.NPlanes * pcxh.BytesPerLine;
	buf = new unsigned char[bpl];
	row = this->_data;
	for ( int y=0; y<this->_h; ++y ) {
		/* decode a scan line to a temporary buffer first */
		int i, count = 0;
		unsigned char ch;
		unsigned char *dst = buf;
		for(i = 0; i < bpl; i++) {
			if(!count) {
				ifile.read( (char*)&ch, 1 );
				if( (ch & 0xc0) == 0xc0) {
					count = ch & 0x3f;
					ifile.read( (char*)&ch, 1 );
				} else
					count = 1;
			}
			dst[i] = ch;
			count--;
		}
		
		/* de-interlace planes */
		unsigned char *src = buf;
		int plane;
		for(plane = 0; plane < pcxh.NPlanes; plane++){
			int x;
			dst = row + plane;
			for(x = 0; x < this->_w; x++) {
				*dst = *src++;
				dst += pcxh.NPlanes;
			}
		}
		row += this->_dataSize/this->_h;
	}
	delete[] buf;
	ifile.close();
	
	flip();
	
	this->_fileName = filename;
	
	return 0;
}

int TOETexture::readDDS( const char *filename ){
	return 1;
}

int TOETexture::generateTexture(){
	
	glGenTextures( 1, &this->_id );
	glBindTexture( GL_TEXTURE_2D, this->_id );
	
	if ( this->_OGLfilter == GL_NEAREST ){ //NORMAL
		if ( this->_bpp != 32 ){ //imagem não tem canal alfa
			glTexImage2D( GL_TEXTURE_2D, 0, 3,
					this->_w, this->_h,
					0, GL_RGB, GL_UNSIGNED_BYTE,
					_data );
		}else{
			glTexImage2D( GL_TEXTURE_2D, 0, 4,
					this->_w, this->_h,
					0, GL_RGBA, GL_UNSIGNED_BYTE,
					_data );
		}
	}else{ //MIPMAP
		if ( this->_bpp != 32 ){ //imagem não tem canal alfa
			gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
					this->_w, this->_h,
					GL_RGB, GL_UNSIGNED_BYTE,
					_data);
		}else{
			gluBuild2DMipmaps(GL_TEXTURE_2D, 4,
					this->_w, this->_h,
					GL_RGBA, GL_UNSIGNED_BYTE,
					_data);
		}
		
	}
	
	if ( this->_OGLfilter == GL_NEAREST ){
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	}else{
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	}
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, this->_OGLfilter);
	
	
	return 0;
}

void* TOETexture::combine (void *o1, size_t s1, void *o2, size_t s2){
	void *result = malloc(s1 + s2);
	if (result != NULL)
		mempcpy(mempcpy(result, o2, s2), o1, s1);
	return result;
}

int TOETexture::tgaLoadRawData( unsigned char* ppData ) // Load uncompressed image data
{
	short iOffset;
	
	this->_data=new unsigned char[this->_dataSize];
	
	if(this->_data==NULL)
		return -1;
	
	iOffset = ppData[0]+18; // Add header to ident field size
	
	if(ppData[1]==1) // Indexed images
		iOffset+=768;  // Add palette offset
	
	memcpy(this->_data,&ppData[iOffset],this->_dataSize);
	
	return 0;
}

int TOETexture::tgaLoadRLEData( unsigned char* ppData ) // Load RLE compressed image data
{
	short iOffset,iPixelSize;
	unsigned char *pCur;
	unsigned long Index=0;
	unsigned char bLength,bLoop;
	
	// Calculate offset to image data
	iOffset=ppData[0]+18;
	
	// Add palette offset for indexed images
	if(ppData[1]==1)
		iOffset+=768; 
	
	// Get pixel size in bytes
	iPixelSize=this->_bpp/8;
	
	// Set our pointer to the beginning of the image data
	pCur=&ppData[iOffset];
 
	// Allocate space for the image data
	this->_data=new unsigned char[this->_dataSize];
 
	if(this->_data==NULL)
		return -1;
 
	// Decode
	while(Index<this->_dataSize) 
	{
		if(*pCur & 0x80) // Run length chunk (High bit = 1)
		{
			bLength=*pCur-127; // Get run length
			pCur++;            // Move to pixel data  
			
			// Repeat the next pixel bLength times
			for(bLoop=0;bLoop!=bLength;++bLoop,Index+=iPixelSize)
				memcpy(&this->_data[Index],pCur,iPixelSize);
			
			pCur+=iPixelSize; // Move to the next descriptor chunk
		}
		else // Raw chunk
		{
			bLength=*pCur+1; // Get run length
			pCur++;          // Move to pixel data
			
			// Write the next bLength pixels directly
			for(bLoop=0;bLoop!=bLength;++bLoop,Index+=iPixelSize,pCur+=iPixelSize)
				memcpy(&this->_data[Index],pCur,iPixelSize);
		}
	}
 
	return 0;
}

void TOETexture::tgaBGRtoRGB() // Convert BGR to RGB (or back again)
{
	unsigned long Index,nPixels;
	unsigned char *bCur;
	unsigned char bTemp;
	short iPixelSize;
	
	// Set ptr to start of image
	bCur = this->_data;
	
	// Calc number of pixels
	nPixels = this->_w*this->_h;
	
	// Get pixel size in bytes
	iPixelSize = this->_bpp/8;
	
	for(Index=0;Index!=nPixels;Index++)  // For each pixel
	{
		bTemp=*bCur;      // Get Blue value
		*bCur=*(bCur+2);  // Swap red value into first position
		*(bCur+2)=bTemp;  // Write back blue to last position
		
		bCur+=iPixelSize; // Jump to next pixel
	}
	
}

void TOETexture::flip( ) // Flips the image vertically (Why store images upside down?)
{
	unsigned char bTemp;
	unsigned char *pLine1, *pLine2;
	int iLineLen,iIndex;
 
	iLineLen=this->_w*(this->_bpp/8);
	pLine1=this->_data;
	pLine2=&this->_data[iLineLen * (this->_h - 1)];
 
	for( ;pLine1<pLine2;pLine2-=(iLineLen*2))
	{
		for(iIndex=0;iIndex!=iLineLen;pLine1++,pLine2++,iIndex++)
		{
			bTemp=*pLine1;
			*pLine1=*pLine2;
			*pLine2=bTemp;
		}
	}
 
}







///public methods
TOETexture::TOETexture(){
	this->_OGLfilter = GL_LINEAR_MIPMAP_LINEAR; //Default value to the texture filter(Trilinear).
}

int TOETexture::load( string filename ){
	return this->load( filename, this->_OGLfilter );
}

int TOETexture::load( string filename, float filterToUse ){
	this->_OGLfilter = filterToUse;
	int returnValue = 0;
	string ext = filename.substr( filename.length()-3, filename.length()-1 );
	string extl = filename.substr( filename.length()-4, filename.length()-1 );
	
	if ( ext == "bmp" ){
		returnValue = readBMP( filename.c_str() );
		if ( returnValue == 0 ){
			returnValue = generateTexture();
		}
	}
	
	if ( ext == "jpg" or extl == "jpeg" ){
		returnValue = readJPG( filename.c_str() );
		if ( returnValue == 0 ){
			returnValue = generateTexture();
		}
	}
	
	if ( ext == "png" ){
		returnValue = readPNG( filename.c_str() );
		if ( returnValue == 0 ){
			returnValue = generateTexture();
		}
	}
	
	if ( ext == "tif" or extl == "tiff" ){
		returnValue = readTIF( filename.c_str() );
		if ( returnValue == 0 ){
			returnValue = generateTexture();
		}
	}
	
	if ( ext == "tga" ){
		returnValue = readTGA( filename.c_str() );
		if ( returnValue == 0 ){
			returnValue = generateTexture();
		}
	}
	
	
	if ( ext == "pcx" ){
		returnValue = readPCX( filename.c_str() );
		if ( returnValue == 0 ){
			returnValue = generateTexture();
		}
	}
	
	if ( ext == "dds" ){
		returnValue = readDDS( filename.c_str() );
		cout << "INFO: " << this->_w << endl;
		cout << "INFO: " << this->_h << endl;
		cout << "INFO: " << this->_bpp << endl;
		cout << "INFO: " << this->_fileName << endl;
		if ( returnValue == 0 ){
			returnValue = generateTexture();
		}
	}
	
	switch( returnValue ){
		case -1:
			cout << "Error loading " << filename << ". Internal error reading image." << endl;
			break;
		case 1:
			cout << "Error loading " << filename << ". File not found." << endl;
			break;
		case 2:
			cout << "Error loading " << filename << ". Unexpected file format. Wrong extension?" << endl;
			break;
		case 3:
			cout << "Error loading " << filename << ". Number of Planes not 1." << endl;
			break;
		case 4:
			cout << "Error loading " << filename << ". Unexpected bits per pixel." << endl;
			break;
		case 5:
			cout << "Error loading " << filename << ". Unexpected number of rows." << endl;
			break;
		case 50:
			cout << "Error loading " << filename << ". Could not generate texture." << endl;
			break;
	}
	
	return returnValue;
}

string TOETexture::fileName(){
	return this->_fileName;
}

unsigned int TOETexture::w(){
	return this->_w;
}

unsigned int TOETexture::h(){
	return this->_h;
}

unsigned int TOETexture::bpp(){
	return this->_bpp;
}

unsigned int TOETexture::id(){
	return this->_id;
}



Share this post


Link to post
Share on other sites
Quote:

The licence is GPL.

:(

But anyway, here are my comments.


  • How about some documentation?

  • Don't put "using namespace std;" in header files! You've polluted the global namespace and defeated the entire purpose of namespaces.

  • What's with TOETexture? Just call it Texture and place it in a namespace. Prefixing class names is generally sort of useless; namespaces do a much better job.

  • You #include <string> but you use C strings for all your function parameters. Why?

  • Your public interface has very poor naming conventions. "w()" and "h()" and "bpp()" are terrible names.

  • You don't have a way of selectively disabling the loads I don't want to use. For example, lets say I only want to use the PNG functionality. You should have preprocessor switches I can set to skip the TIFF, et cetera, functionality so I don't have to link with all these extra libraries that I will never use.

  • Why is _OGLfilter a float?

  • You handle errors by returning magic numbers. You should really throw exceptions in many of these cases, or at the very least return enumeration values. When I recieve a return code of 4 from readBMP(), I have no idea what it means. This is not useful at all.

  • Your bitmap handling is pretty restrictive, isn't it? You only support certain bitmap color formats and such. Other types, like TGA, seem equally artificially restricted.

  • You mix C style IO (the JPEG loader) and C++ style IO (the bitmap loader). This has rather negative implications... the overall lack of consistency in the style of the code and the comments suggests this code is based primarily on tutorials or sample code, which implies that its probably not particularly robust.

  • Similarly, you inconsitantly print to stderr, stdout and sometimes not at all. Again, it's probably better just to throw an exception. Printing from a low-level interface like a texture is undesirable.

  • You don't have any way to bind the texture to make it current so it can be used. This essentially renders every texture you load except for the last one useless.

  • load() has a very poor method of extracting the extension that can result in naughty behavior if the string isn't well formed (for example, if the string is less than three characters). This is unstable.

  • From a high level perspective, I don't think binding together the texture itself, and the loading of that texture, is good design. Among other issues, it makes for some obnoxious rentrancy issues. For example, right now if I call load() twice, memory is leaked.

  • In fact, since you don't have a destructor, you leak memory anyway.



Overall, it's a respectable first attempt but needs a lot of work before it can compete with other available texture loading utility code for OpenGL (such as the OpenIL library). Keep at it!

Share this post


Link to post
Share on other sites
Quote:

* How about some documentation?

Maybe in the future? Is just a class!
Quote:

* Don't put "using namespace std;" in header files! You've polluted the global namespace and defeated the entire purpose of namespaces.

My mistake. Now on .cpp.
Quote:

* What's with TOETexture? Just call it Texture and place it in a namespace. Prefixing class names is generally sort of useless; namespaces do a much better job.

I name all my classes in that way(of this project). I see no problem with that.
Quote:

* You #include <string> but you use C strings for all your function parameters. Why?

I use std::string just for the load method and _fileName attribute. Pass const char* for the other methods because they need to. ifstream just accept const char* as a parameter to open() or constructor. And fopen too. So...
Quote:

* Your public interface has very poor naming conventions. "w()" and "h()" and "bpp()" are terrible names.

This is a texture class. So a texture.w() is a little obvious you don't think? But I can change this. No problem.
( I wrote in that way because I saw in platically all library loaders, they implement member of a struct or method of a class named w or w(), to call image width. )
Quote:

* You don't have a way of selectively disabling the loads I don't want to use. For example, lets say I only want to use the PNG functionality. You should have preprocessor switches I can set to skip the TIFF, et cetera, functionality so I don't have to link with all these extra libraries that I will never use.

I'll fix this.
Quote:

* Why is _OGLfilter a float?

Because void glTexParameterf( GLenum target, GLenum pname, GLfloat param ).
Sure, I can call glTexParameteri(). Humm, ok, changed this. But I do not saw problem.
Quote:

* You handle errors by returning magic numbers. You should really throw exceptions in many of these cases, or at the very least return enumeration values. When I recieve a return code of 4 from readBMP(), I have no idea what it means. This is not useful at all.

Yeah, was thinking in a good way to do that... try - catch()?
Quote:

* Your bitmap handling is pretty restrictive, isn't it? You only support certain bitmap color formats and such. Other types, like TGA, seem equally artificially restricted.

You mean only to support 24/32 bits RGB/BGR compressed or uncompressed formats? I think is enought. I'll not put support here for indexed images. Anybody still use that?
I was thinking in support Greyscale images, but I'm not sure.
Quote:

* You mix C style IO (the JPEG loader) and C++ style IO (the bitmap loader). This has rather negative implications... the overall lack of consistency in the style of the code and the comments suggests this code is based primarily on tutorials or sample code, which implies that its probably not particularly robust.

Because void jpeg_stdio_src(j_decompress_ptr cinfo, FILE * infile); I cannot use fstream here(I thinked in that problem yes, but I have no choice), or can I?
Quote:

* Similarly, you inconsitantly print to stderr, stdout and sometimes not at all. Again, it's probably better just to throw an exception. Printing from a low-level interface like a texture is undesirable.

Forget to throw out these things. Now is clean.
Quote:

* You don't have any way to bind the texture to make it current so it can be used. This essentially renders every texture you load except for the last one useless.

No, was thinking on that functionality too.
Quote:

* load() has a very poor method of extracting the extension that can result in naughty behavior if the string isn't well formed (for example, if the string is less than three characters). This is unstable.

Don't thinked on that. Man, this is why open source is do great! Thanks for that!
The name now must have at least 5 characters.
Quote:

* From a high level perspective, I don't think binding together the texture itself, and the loading of that texture, is good design. Among other issues, it makes for some obnoxious rentrancy issues. For example, right now if I call load() twice, memory is leaked.

Quote:

* In fact, since you don't have a destructor, you leak memory anyway.

I made a fix. Look and tell me what you think.


Thank you man! This was of great help! hummmm your name in credits? :)

HEADER .hpp

/*
The Open Engine
Copyright (C) 2006 Johnny Birnfeld https://sourceforge.net/projects/theopenengine

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

*/



#ifndef TOE_TEXTURE_H
#define TOE_TEXTURE_H

#include <string>

class TOETexture{
std::string _fileName;
int _w;
int _h;
short int _bpp;
unsigned int _id;
unsigned char *_data;
unsigned long _dataSize;

int _OGLfilter;

int readBMP( const char* );
int readJPG( const char* );
int readPNG( const char* );
int readTIF( const char* );
int readTGA( const char* );
int readPCX( const char* );
int readDDS( const char* );
int generateTexture();
void* combine( void*, size_t, void*, size_t );
int tgaLoadRawData( unsigned char* );
int tgaLoadRLEData( unsigned char* );
void tgaBGRtoRGB();
void flip();
public:
TOETexture();
~TOETexture();
int load( std::string );
int load( std::string, int );
std::string fileName();
unsigned int width();unsigned int height();
unsigned int bpp();
unsigned int id();

void createInMemoryTexture( void* );
};

#endif




BODY .cpp

/*
The Open Engine
Copyright (C) 2006 Johnny Birnfeld https://sourceforge.net/projects/theopenengine

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

*/



#include "TOETexture.hpp"
#include <iostream>
#include <fstream>
#include <GL/glu.h>

extern "C" {
#include <jpeglib.h>
#include <jerror.h>
}
#include <png.h>
#include <tiffio.h>

using namespace std;

int TOETexture::readBMP( const char *filename ){
unsigned short int bfType;
long int bfOffBits;
short int biPlanes;

ifstream ifile(filename);
if ( !ifile.good() )
return 1; //File not found or corrupted.
ifile.read( (char*)&bfType, sizeof(short int) );
if (bfType != 19778){
return 2; //Not a Bitmap-File
}

ifile.seekg(8, ios::cur); //skip file size and reserved fields of bitmap file header
ifile.read( (char*)&bfOffBits, sizeof(long int) ); //get the position of the actual bitmap data
ifile.seekg(4, ios::cur); //skip size of bitmap info header
ifile.read( (char*)&this->_w, sizeof(int) ); //get the width of the bitmap
ifile.read( (char*)&this->_h, sizeof(int) ); //get the heigth of the bitmap
ifile.read( (char*)&biPlanes, sizeof(short int) ); //get the number of planes (must be set to 1)
if (biPlanes != 1){
return 3; //number of Planes not 1!
}
ifile.read( (char*)&this->_bpp, sizeof(short int) ); //get the number of bits per pixel
if (this->_bpp != 24){
return 4; //Bits per Pixel not 24!
}

_dataSize = this->_w * this->_h * 3; //calculate the size of the image in bytes
_data = new unsigned char[_dataSize];

ifile.seekg(bfOffBits, ios::beg); //seek to the actual data
ifile.read( (char*)_data, _dataSize ); //read data

//swap red and blue (bgr -> rgb)
unsigned char temp;
for ( unsigned int i = 0; i < _dataSize; i += 3 ){
temp = _data[i];
_data[i] = _data[i + 2];
_data[i + 2] = temp;
}

ifile.close();

this->_fileName = filename;

return 0;
}

int TOETexture::readJPG( const char* filename ){

FILE *file;
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
volatile JSAMPROW row = 0;
JSAMPROW rowptr[1];
uint i,j,nrows;

cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);

/* make sure the file is there and open it read-only (binary) */
if ((file = fopen(filename, "rb")) == NULL){
return 1; //File not found.
}
jpeg_stdio_src(&cinfo, file);
jpeg_read_header(&cinfo, true);
jpeg_start_decompress(&cinfo);

this->_w = cinfo.output_width;
this->_h = cinfo.output_height;
this->_bpp = cinfo.jpeg_color_space*8; //JPEG images do not handle alpha channel.

row = (JSAMPROW)calloc(1,cinfo.image_width * cinfo.output_components
* sizeof(JSAMPLE));

_dataSize = cinfo.image_width * cinfo.image_height * cinfo.output_components * sizeof(JSAMPLE);
_data = (unsigned char*)malloc(_dataSize);
rowptr[0] = row;

for ( i = 0; i < cinfo.output_height; i++ ) {
nrows = jpeg_read_scanlines(&cinfo, rowptr, 1);
if (nrows != 1) {
return 5;
}

for ( j = 0; j < cinfo.output_width*cinfo.output_components; j++ )
_data[(cinfo.output_height-1-i)*cinfo.output_width*cinfo.output_components+j] = row[j];

}

jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
free(row);

this->_fileName = filename;

return 0;
}

int TOETexture::readPNG( const char* filename ){
unsigned int y;

png_structp png_ptr;
png_infop info_ptr;
int number_of_passes;
png_bytep * row_pointers;

char header[8]; // 8 is the maximum size that can be checked

/* open file and test for it being a png */
FILE *fp = fopen(filename, "rb");
if (!fp)
return 1; //File not found
fread(header, 1, 8, fp);
if (png_sig_cmp((png_byte*)header, 0, 8))
return 2; //Not a valid file

/* initialize stuff */
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

if (!png_ptr)
return -1; //png_create_read_struct failed

info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
return -1; //png_create_info_struct failed

if (setjmp(png_jmpbuf(png_ptr)))
return -1; //Error during init_io

png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, 8);

png_read_info(png_ptr, info_ptr);

number_of_passes = png_set_interlace_handling(png_ptr);
png_read_update_info(png_ptr, info_ptr);

/* read file */
if (setjmp(png_jmpbuf(png_ptr)))
return -1; //Error during read_image

row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * info_ptr->height);
for ( y=0; y<info_ptr->height; y++ )
row_pointers[y] = (png_byte*) malloc(info_ptr->rowbytes);

png_read_image(png_ptr, row_pointers);



this->_w = info_ptr->width;
this->_h = info_ptr->height;
this->_bpp = info_ptr->bit_depth;
if ( info_ptr->color_type == PNG_COLOR_TYPE_RGBA ){
this->_bpp *= 4;
}else{
if ( info_ptr->color_type == PNG_COLOR_TYPE_RGB ){
this->_bpp *= 3;
}else{
return 2;
}
}
_dataSize = this->_h * info_ptr->rowbytes;
_data = new unsigned char[_dataSize];

int tszdata=0;
for ( int c=0; c<this->_h; c++ ){
_data = (unsigned char*)combine( _data, tszdata, row_pointers[c], info_ptr->rowbytes );
tszdata += info_ptr->rowbytes;
}

this->_fileName = filename;




/* finish decompression and release memory */
png_read_end (png_ptr, NULL);
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
/* we don't need row pointers anymore */
free (row_pointers);

fclose(fp);
//Done reading .png file

return 0;
}

int TOETexture::readTIF( const char *filename ){
TIFFRGBAImage img;
uint32 *raster;
size_t npixels;

TIFF *tif;
char emsg[1024];
tif = TIFFOpen(filename, "r");
if (tif == NULL) {
return 1;
}
if ( TIFFRGBAImageBegin( &img, tif, 0, emsg ) ){
npixels = img.width * img.height;
raster = (uint32 *) _TIFFmalloc(npixels * /*img.samplesperpixel*/sizeof(uint32));
if (raster != NULL) {
if (TIFFRGBAImageGet(&img, raster, img.width, img.height) == 0) {
TIFFError(filename, emsg);
return 2;
}
}
TIFFRGBAImageEnd(&img);
}else{
TIFFError(filename, emsg);
return -1;
}

this->_w = img.width;
this->_h = img.height;
this->_bpp = 32/*img.bitspersample * img.samplesperpixel*/;
_dataSize = this->_w * this->_h * this->_bpp/8;
_data = new unsigned char[_dataSize];
memcpy( _data, raster, _dataSize );
this->_fileName = filename;


TIFFClose(tif);
return 0;
}

int TOETexture::readTGA( const char *filename ){

ifstream ifile;
unsigned long fileSize;
unsigned char *pData;
char bEnc;

ifile.open( filename,ios::binary );
if(ifile==NULL)
return 1;

// Get file size
ifile.seekg(0,ios_base::end);
fileSize=ifile.tellg();
ifile.seekg(0,ios_base::beg);

pData = new unsigned char[fileSize];
if(pData==NULL){
ifile.close();
return -1;
}

// Read the file into memory
ifile.read((char*)pData,fileSize);
ifile.close();


//Read Header
short ColMapStart,ColMapLen;
short x1,y1,x2,y2;

if( pData[1] > 1 ) // 0 (RGB) and 1 (Indexed) are the only types we know about
return 2; //Not a TGA file, or not supported format.

bEnc = pData[2]; // Encoding flag 1 = Raw indexed image
// 2 = Raw RGB
// 3 = Raw greyscale
// 9 = RLE indexed
// 10 = RLE RGB
// 11 = RLE greyscale
// 32 & 33 Other compression, indexed

if( bEnc > 11 or bEnc == 1 or bEnc == 9 ) // We don't want 32 or 33. And engine does not read indexed images too.
return 2;


// Get palette info
memcpy(&ColMapStart,&pData[3],2);
memcpy(&ColMapLen,&pData[5],2);

// Reject indexed images if not a VGA palette (256 entries with 24 bits per entry)
if(pData[1]==1) // Indexed
{
if(ColMapStart!=0 || ColMapLen!=256 || pData[7]!=24)
return 2;
}

// Get image window and produce width & height values
memcpy(&x1,&pData[8],2);
memcpy(&y1,&pData[10],2);
memcpy(&x2,&pData[12],2);
memcpy(&y2,&pData[14],2);

this->_w = (x2-x1);
this->_h = (y2-y1);

if(this->_w<1 || this->_h<1)
return 2;

// Bits per Pixel
this->_bpp = pData[16];

// Check flip / interleave byte
if(pData[17]>32) // Interleaved data
return 2;

// Calculate image size
this->_dataSize=(this->_w * this->_h * (this->_bpp/8));
//Read Header


switch(bEnc)
{
case 2: // Raw RGB
{
// Check filesize against header values
if((this->_dataSize+18+pData[0])>fileSize)
return 2;

// Double check image type field
if(pData[1]!=0)
return 2;

// Load image data
if( tgaLoadRawData( pData )!=0 )
return -1;

tgaBGRtoRGB(); // Convert to RGB
break;
}

case 10: // RLE RGB
{
// Double check image type field
if( pData[1] != 0 )
return 2;

// Load image data
if( tgaLoadRLEData( pData ) != 0 )
return -1;

tgaBGRtoRGB(); // Convert to RGB
break;
}

default:
return 2;
}

// Check flip bit
//if((pData[17] & 0x20)==0){
// flip();
//}

// Release file memory
delete [] pData;
pData = NULL;

this->_fileName = filename;

return 0;
}

int TOETexture::readPCX( const char *filename ){

struct PCXheader {
unsigned char Manufacturer;
unsigned char Version;
unsigned char Encoding;
unsigned char BitsPerPixel;
short int Xmin, Ymin, Xmax, Ymax;
short int HDpi, VDpi;
unsigned char Colormap[48];
unsigned char Reserved;
unsigned char NPlanes;
short int BytesPerLine;
short int PaletteInfo;
short int HscreenSize;
short int VscreenSize;
unsigned char Filler[54];
};

struct PCXheader pcxh;
int bpl;
unsigned char *row, *buf = NULL;

ifstream ifile(filename);
if ( !ifile.good() ){
return 1;
}

ifile.read( (char*)&pcxh, sizeof(pcxh) );

this->_w = (pcxh.Xmax - pcxh.Xmin) + 1;
this->_h = (pcxh.Ymax - pcxh.Ymin) + 1;
this->_bpp = pcxh.BitsPerPixel * pcxh.NPlanes;
if ( this->_bpp != 24 ){
ifile.close();
return 2;
}

this->_dataSize = this->_w*this->_h*(this->_bpp/8);
this->_data = new unsigned char[this->_dataSize];


bpl = pcxh.NPlanes * pcxh.BytesPerLine;
buf = new unsigned char[bpl];
row = this->_data;
for ( int y=0; y<this->_h; ++y ) {
/* decode a scan line to a temporary buffer first */
int i, count = 0;
unsigned char ch;
unsigned char *dst = buf;
for(i = 0; i < bpl; i++) {
if(!count) {
ifile.read( (char*)&ch, 1 );
if( (ch & 0xc0) == 0xc0) {
count = ch & 0x3f;
ifile.read( (char*)&ch, 1 );
} else
count = 1;
}
dst[i] = ch;
count--;
}

/* de-interlace planes */
unsigned char *src = buf;
int plane;
for(plane = 0; plane < pcxh.NPlanes; plane++){
int x;
dst = row + plane;
for(x = 0; x < this->_w; x++) {
*dst = *src++;
dst += pcxh.NPlanes;
}
}
row += this->_dataSize/this->_h;
}
delete[] buf;
ifile.close();

flip();

this->_fileName = filename;

return 0;
}

int TOETexture::readDDS( const char *filename ){
return 1;
}

int TOETexture::generateTexture(){

glGenTextures( 1, &this->_id );
glBindTexture( GL_TEXTURE_2D, this->_id );

if ( this->_OGLfilter == GL_NEAREST ){ //NORMAL
if ( this->_bpp != 32 ){ //image does not have alpha channel
glTexImage2D( GL_TEXTURE_2D, 0, 3,
this->_w, this->_h,
0, GL_RGB, GL_UNSIGNED_BYTE,
_data );
}else{
glTexImage2D( GL_TEXTURE_2D, 0, 4,
this->_w, this->_h,
0, GL_RGBA, GL_UNSIGNED_BYTE,
_data );
}
}else{ //MIPMAP
if ( this->_bpp != 32 ){ //image does not have alpha channel
gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
this->_w, this->_h,
GL_RGB, GL_UNSIGNED_BYTE,
_data);
}else{
gluBuild2DMipmaps(GL_TEXTURE_2D, 4,
this->_w, this->_h,
GL_RGBA, GL_UNSIGNED_BYTE,
_data);
}

}

if ( this->_OGLfilter == GL_NEAREST ){
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}else{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, this->_OGLfilter);

glBindTexture( GL_TEXTURE_2D, 0 ); //Unbind current texture.
return 0;
}

void* TOETexture::combine (void *o1, size_t s1, void *o2, size_t s2){
void *result = malloc(s1 + s2);
if (result != NULL)
mempcpy(mempcpy(result, o2, s2), o1, s1);
return result;
}

int TOETexture::tgaLoadRawData( unsigned char* ppData ) // Load uncompressed image data
{
short iOffset;

this->_data=new unsigned char[this->_dataSize];

if(this->_data==NULL)
return -1;

iOffset = ppData[0]+18; // Add header to ident field size

if(ppData[1]==1) // Indexed images
iOffset+=768; // Add palette offset

memcpy(this->_data,&ppData[iOffset],this->_dataSize);

return 0;
}

int TOETexture::tgaLoadRLEData( unsigned char* ppData ) // Load RLE compressed image data
{
short iOffset,iPixelSize;
unsigned char *pCur;
unsigned long Index=0;
unsigned char bLength,bLoop;

// Calculate offset to image data
iOffset=ppData[0]+18;

// Add palette offset for indexed images
if(ppData[1]==1)
iOffset+=768;

// Get pixel size in bytes
iPixelSize=this->_bpp/8;

// Set our pointer to the beginning of the image data
pCur=&ppData[iOffset];

// Allocate space for the image data
this->_data=new unsigned char[this->_dataSize];

if(this->_data==NULL)
return -1;

// Decode
while(Index<this->_dataSize)
{
if(*pCur & 0x80) // Run length chunk (High bit = 1)
{
bLength=*pCur-127; // Get run length
pCur++; // Move to pixel data

// Repeat the next pixel bLength times
for(bLoop=0;bLoop!=bLength;++bLoop,Index+=iPixelSize)
memcpy(&this->_data[Index],pCur,iPixelSize);

pCur+=iPixelSize; // Move to the next descriptor chunk
}
else // Raw chunk
{
bLength=*pCur+1; // Get run length
pCur++; // Move to pixel data

// Write the next bLength pixels directly
for(bLoop=0;bLoop!=bLength;++bLoop,Index+=iPixelSize,pCur+=iPixelSize)
memcpy(&this->_data[Index],pCur,iPixelSize);
}
}

return 0;
}

void TOETexture::tgaBGRtoRGB() // Convert BGR to RGB (or back again)
{
unsigned long Index,nPixels;
unsigned char *bCur;
unsigned char bTemp;
short iPixelSize;

// Set ptr to start of image
bCur = this->_data;

// Calc number of pixels
nPixels = this->_w*this->_h;

// Get pixel size in bytes
iPixelSize = this->_bpp/8;

for(Index=0;Index!=nPixels;Index++) // For each pixel
{
bTemp=*bCur; // Get Blue value
*bCur=*(bCur+2); // Swap red value into first position
*(bCur+2)=bTemp; // Write back blue to last position

bCur+=iPixelSize; // Jump to next pixel
}

}

void TOETexture::flip( ) // Flips the image vertically (Why store images upside down?)
{
unsigned char bTemp;
unsigned char *pLine1, *pLine2;
int iLineLen,iIndex;

iLineLen=this->_w*(this->_bpp/8);
pLine1=this->_data;
pLine2=&this->_data[iLineLen * (this->_h - 1)];

for( ;pLine1<pLine2;pLine2-=(iLineLen*2))
{
for(iIndex=0;iIndex!=iLineLen;pLine1++,pLine2++,iIndex++)
{
bTemp=*pLine1;
*pLine1=*pLine2;
*pLine2=bTemp;
}
}

}







///public methods
TOETexture::TOETexture(){
this->_fileName = "";
this->_w = 0;
this->_h = 0;
this->_bpp = 0;
this->_id = 0;
this->_data = NULL;
this->_dataSize = 0;

this->_OGLfilter = GL_LINEAR_MIPMAP_LINEAR; //Default value to the texture filter(Trilinear).
}

TOETexture::~TOETexture(){
delete[] this->_data;

this->_fileName = "";
this->_w = 0;
this->_h = 0;
this->_bpp = 0;
this->_id = 0;
this->_data = NULL;
this->_dataSize = 0;
}

int TOETexture::load( string filename ){
return this->load( filename, this->_OGLfilter );
}

int TOETexture::load( string filename, int filterToUse ){
if ( filename.length() < 5 ){
cout << "Error loading " << filename << ". Filename is too short!" << endl;
return -2;
}
if ( this->_data ){
cout << "Error loading " << filename << ". File is already loaded! Will not load twice." << endl;
return -3;
}

this->_OGLfilter = filterToUse;
int returnValue = 0;
string ext = filename.substr( filename.length()-3, filename.length()-1 );
string extl = filename.substr( filename.length()-4, filename.length()-1 );

if ( ext == "bmp" ){
returnValue = readBMP( filename.c_str() );
if ( returnValue == 0 ){
returnValue = generateTexture();
}
}

if ( ext == "jpg" or extl == "jpeg" ){
returnValue = readJPG( filename.c_str() );
if ( returnValue == 0 ){
returnValue = generateTexture();
}
}

if ( ext == "png" ){
returnValue = readPNG( filename.c_str() );
if ( returnValue == 0 ){
returnValue = generateTexture();
}
}

if ( ext == "tif" or extl == "tiff" ){
returnValue = readTIF( filename.c_str() );
if ( returnValue == 0 ){
returnValue = generateTexture();
}
}

if ( ext == "tga" ){
returnValue = readTGA( filename.c_str() );
if ( returnValue == 0 ){
returnValue = generateTexture();
}
}


if ( ext == "pcx" ){
returnValue = readPCX( filename.c_str() );
if ( returnValue == 0 ){
returnValue = generateTexture();
}
}

if ( ext == "dds" ){
returnValue = readDDS( filename.c_str() );
cout << "INFO: " << this->_w << endl;
cout << "INFO: " << this->_h << endl;
cout << "INFO: " << this->_bpp << endl;
cout << "INFO: " << this->_fileName << endl;
if ( returnValue == 0 ){
returnValue = generateTexture();
}
}

switch( returnValue ){
case -1:
cout << "Error loading " << filename << ". Internal error reading image." << endl;
break;
case 1:
cout << "Error loading " << filename << ". File not found." << endl;
break;
case 2:
cout << "Error loading " << filename << ". Unexpected file format. Wrong extension?" << endl;
break;
case 3:
cout << "Error loading " << filename << ". Number of Planes not 1." << endl;
break;
case 4:
cout << "Error loading " << filename << ". Unexpected bits per pixel." << endl;
break;
case 5:
cout << "Error loading " << filename << ". Unexpected number of rows." << endl;
break;
case 50:
cout << "Error loading " << filename << ". Could not generate texture." << endl;
break;
}

return returnValue;
}

string TOETexture::fileName(){
return this->_fileName;
}

unsigned int TOETexture::width(){
return this->_w;
}

unsigned int TOETexture::height(){
return this->_h;
}

unsigned int TOETexture::bpp(){
return this->_bpp;
}

unsigned int TOETexture::id(){
return this->_id;
}

void TOETexture::createInMemoryTexture( void* ){

}





phantom, I'm just want to write a texture class, not a complete library :) Is a different approach.
Ah, I wanted to make you a question: Your project just runs on win32?

Share this post


Link to post
Share on other sites
Quote:

Maybe in the future? Is just a class!

Irrelevant. It should be documented. Especially since your public interface is next to incomprehensible. Especially since you are providing it for others to use.

Quote:

I name all my classes in that way(of this project). I see no problem with that.

It adds no useful information. The functionality can be duplicated (in a better fashion) by namespaces. It hinders readability. Depending the layout of your project files and their names, it can make project navigation a chore.

Quote:

I use std::string just for the load method and _fileName attribute. Pass const char* for the other methods because they need to. ifstream just accept const char* as a parameter to open() or constructor. And fopen too. So...

std::string has a method called c_str() that will return a const char* representation of the string. So in the case of fopen(), you can use:

FILE *fp = fopen(some_std_string.c_str(),"rb");

or something similar. Using const char* for strings is a chore.

Quote:

This is a texture class. So a texture.w() is a little obvious you don't think? But I can change this. No problem.
( I wrote in that way because I saw in platically all library loaders, they implement member of a struct or method of a class named w or w(), to call image width. )

No, I don't think it's obvious. It's bad practice, and it's not self-documenting. The fact that you've seen other code use it doesn't mean it's okay, it means that other code is poor.

w() and h() might be intuitable. But consider, for example, bpp()?

What on earth does that mean? Bits per pixel or bytes per pixel? both are perfectly useful numbers (neither is neccessarily more or less useful than the other). So which is it? The user won't know unless he digs through your source code.

Quote:

Sure, I can call glTexParameteri(). Humm, ok, changed this. But I do not saw problem.

Because it's silly to use a float to store values that are integers. You never need that value to be a float, so why make it a float?

Quote:

Yeah, was thinking in a good way to do that... try - catch()?

It is unlikely that you will need to catch any exceptions in this code, since it is very low level and would not know what to do about the failure (the only exception would be if you wanted to translate the exceptions and rethrow). Therefore you won't need to use try/catch blocks.

However, I may have misspoke. Writing exception-safe code is hard and might be something you want to postpone until you have more programming experience.

You should at least convert your returned magic numbers to values that are part of some kind of error code enumeration, however.

Quote:

You mean only to support 24/32 bits RGB/BGR compressed or uncompressed formats? I think is enought. I'll not put support here for indexed images. Anybody still use that?
I was thinking in support Greyscale images, but I'm not sure.

Yes, people still use indexed formats. They use grayscale formats, too. The key to writing code suitable for others to use is to realize that you generally can't expect your users to need to use the code in exactly the way you'd use it.

Quote:

Because void jpeg_stdio_src(j_decompress_ptr cinfo, FILE * infile); I cannot use fstream here(I thinked in that problem yes, but I have no choice), or can I?

Hmm, fair enough. I don't know enough about libjpeg, so perhaps that does mean FILE* are you only options here.

Quote:

Thank you man!

No problem.

Share this post


Link to post
Share on other sites
Quote:
Original post by Tsumuji
phantom, I'm just want to write a texture class, not a complete library :) Is a different approach.
Ah, I wanted to make you a question: Your project just runs on win32?


The only difference is that I split up what you do in one class over a number of logical units; the benfit of more software engineering experiance I guess [smile]

GTL has successfully been compiled and tested for Win32, Win64 (x64) and ARM9 for the GP2x handheld console (which runs linux).
In theory, because it uses C++ and crossplatorm libs it should work on any system which uses the same Endian as Intel/AMD CPUs, the BMP and DDS code might have some 'issues' with systems which don't use it (PPC for example...), I should fix that.

Share this post


Link to post
Share on other sites
You covered pretty much every point above, except one, which I would like to focus on a bit.
Quote:
Original post by Tsumuji
Quote:

* You mix C style IO (the JPEG loader) and C++ style IO (the bitmap loader). This has rather negative implications... the overall lack of consistency in the style of the code and the comments suggests this code is based primarily on tutorials or sample code, which implies that its probably not particularly robust.

Because void jpeg_stdio_src(j_decompress_ptr cinfo, FILE * infile); I cannot use fstream here(I thinked in that problem yes, but I have no choice), or can I?

Any library worth it's name that requires I/O of some kind should have a way to abstract the I/O. Never used the JPEG library you use, so can't say whether it have it or not, but look for some user I/O callbacks.

Common ways are to register read/write callbacks, which the library use for reading/writing/whatever. For example, the read callback prototype can be something like:

int read(int n, void *p, void *user)

The user pointer being some user defined value, so you can pass the address of a istream object, letting you read from any stream derived from the standard C++ input stream.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this