Loading a image

I’ve manages to create a image loading function that works. As references here it is with comments.



ImageBMP::ImageBMP(){
	/*
		Zero everything out
	*/
	mBitmap	= NULL;
	mWidth	= 0;
	mHeight	= 0;
	mStride	= 0;
	mType	= 0;

	GdiFlush();
}
ImageBMP::~ImageBMP(){
	FreeBMP();
}
void ImageBMP::FreeBMP(){
	if(mBitmap)
		DeleteObject(mBitmap);

	mBitmap	= NULL;
	mWidth	= 0;
	mHeight	= 0;
	mStride = 0;
	mType	= 0;
	imgData	= NULL;
}
unsigned char* ImageBMP::GetImageData(int which_line){
	return (imgData + mStride * which_line);
}
bool ImageBMP::SizeImg(int w, int h, int channels){
	/*
		To begin with, we clear the memory of the image incase anything
			was already loaded in. We then create a temporary device
			context and initialize our width, height, and type variables.
			We also initialize our stride variable, but read further down for
			more information on that.
	*/
	FreeBMP();
	HDC tmpDC = CreateCompatibleDC(NULL);
	if(!tmpDC){
		MessageBox(NULL, "CreateCompatibleDC() failed!", "Error SizeImg()", MB_OK);
		return false;
	}

	mWidth	= w;
	mHeight	= h;
	mType	= channels;

	/*
		In windows and most other OS's. Some things are far easier
			to accomplish when the data is dword aligned. dword = 2 words
			or 2 words = 4 bytes (Some asm knowledge there). Anyhow, this
			helps speed up drawing to the screen, we we get the true stride, 
			then modify it until it is evenly divided by 4. IF you ever 
			wondered when the hell you would use the modulus symbol, here it
			is!
	*/
	mStride = mWidth * mType;
	while((mStride % 4) != 0)
		mStride++;

	BITMAPINFO bInfo = {0};

	/*
		We set up the bitmap data here for when we create the image itself 
			from the file data. biSize is the size of the header. WE just pass
			in the BitmapInfoHeader struct for this. biWidth and biHeight should
			not need explanation. biPlanes will always be 1 regardless of what type
			of image you load. Even MSDN says it MUST be 1. Next we have biBitCount 
			which is just the number of channels we have multiplied by 8 (8 bits per byte).
			If you wanted to be an [censored] to someone, you could always ask "Hey, can you
				save that in 3 byte format?" (24bit)
			Anyways, moving along... We come across biCompression. Since no compression
			should have happened to the image we are loading, we just tell it no compression
			occured to the image data. Finally, biClrUsed is just a indicies tracker for images
			in the 8 bit range for color platelets. We don't need it though so set it to 0.
	*/
	bInfo.bmiHeader.biSize			= sizeof BITMAPINFOHEADER;
	bInfo.bmiHeader.biWidth			= mWidth;
	bInfo.bmiHeader.biHeight		= mHeight;
	bInfo.bmiHeader.biPlanes		= 1;			// This will always be 1
	bInfo.bmiHeader.biBitCount		= mType * 8;	// 3 channels = 24, 4 = 32
	bInfo.bmiHeader.biCompression	= BI_RGB;		// No compression
	bInfo.bmiHeader.biClrUsed		= 0;			// Always 0 when working with 24/32 bit bmps


	/*
		Finally, we actually create the image to store our data into. After creating it, we
			check to see if it finished properly, then we delete the temporary device
			context created earlier. Do a final check to make sure all is okay, then return true!
	*/
	mBitmap = CreateDIBSection(tmpDC, &bInfo, DIB_RGB_COLORS, (void**)&imgData, 0, 0);

	DeleteDC(tmpDC);
	if(!mBitmap){
		MessageBox(NULL, "CreateDIBSection() failed!", "Error SizeImg()", MB_OK);
		return false;
	}
	return true;
}
bool ImageBMP::LoadBMP(const char *filename){
	/*
		To start off, we erase any memory or garbage memory that might be in the
			bitmap class already. After that we proceed to check and ensure a filename was
			ACTUALLY passed into the function, if it was, we open the file and proceed to 
			read the data into our holder and then into OpenGL.
	*/
	FreeBMP();
	if(!filename){
		MessageBox(NULL, "Bad file name!", "Error LoadBMP()", MB_OK);
		return false;
	}

	FILE * img = NULL;
	fopen_s(&img, filename, "rb");	// Notice we read the file in  byte by byte. 'rb' means Read Byte mode.	

	if(!img){
		MessageBox(NULL, "fopen() failed!", "Error LoadBMP()", MB_OK);
		return false;
	}

	// Stores information on the bitmap header when we load it in.
	BITMAPFILEHEADER bHeader;

	if(!fread(&bHeader, sizeof bHeader, 1, img)){
		MessageBox(NULL, "fread() [BITMAPFILEHEADER] failed!", "Error LoadBMP()", MB_OK);
		return false;
	}

	/*
		Here, we check to make sure this is ACTUALLY a bmp file. You can rename a .jpg
			to .bmp, but the compression still exists inside it. If we tried to load it 
			while it was still compressed, bad things will happen. We check to avoid that
			bad situation. Note that memcmp returns true if the memory does NOT match. Don't 
			ask me why.
	*/
	if(memcmp(&bHeader.bfType, "BM", 2)){
		MessageBox(NULL, "memcmp() detected false file type!", "Error LoadBMP()", MB_OK);
		fclose(img); // Close the file since it is the wrong filetype.
		return false;
	}

	// This contains information on the bitmap itself
	BITMAPINFOHEADER bInfo; 

	// Read in the info for the bitmap.
	if(!fread(&bInfo, sizeof(BITMAPINFOHEADER), 1, img))
	{
		MessageBox(NULL, "fread() [BITMAPINFOHEADER] failed!", "Error LoadBMP()", MB_OK);
		fclose(img);
		return false;
	}

	if((bInfo.biBitCount != 24) && (bInfo.biBitCount != 32)){
		/*
			Here, we checked if the image was 24 or 32 bits. If it is neither, then we just return false after
				closing the file. This loader is currently unable to load monochrome, or 8 bit bmp images. We
				will simply stick to 24 or 32.
		*/
		MessageBox(NULL, "Not a valid 24 | 32 bit image type!", "Error LoadBMP()", MB_OK);
		fclose(img);
		return false;
	}

	/*
		After getting all the information from the header, we then set the image data up 
			and prepare it for loading in the actual image data itself. That is done
			with the function call we created earlier in the class. We pass in the height
			and the width. To send in the number of channels, it is the bit count / 8 (8 bits per byte),
			and 3 bytes is the number of channels. Or 4...
	*/
	if(!SizeImg(bInfo.biWidth, bInfo.biHeight, bInfo.biBitCount/8)){
		MessageBox(NULL, "Size() failed!", "Error LoadBMP()", MB_OK);
		fclose(img);
		return false;
	}

	/*
		What is about to happen might look confusing but it is very straight forward! 
			First, we need to find out how many bits are on each line in the image.
			Second, we need to find out how many of those bytes are padded since we want to avoid reading those in.
			Finally, we read the data into the image handler.
	*/
	unsigned int bytesPerLine	= mWidth * mType;			// Get the number of bytes per line
	unsigned int padCount		= mStride - bytesPerLine;	// Get the amount of padding involved.

	// Loop through and read the data in
	for(int i=0; i<mHeight; i++){
		/*
			i is the line number in the image we are on. In this case, i 
				will start at 0 being the top of the file. From each line, we need 
				to relocate our position
		*/
		unsigned char* pos = GetImageData(i);			// Relocate our position

		/*
			Read in the image data
		*/
		if(!fread(pos, bytesPerLine, 1, img)){
			MessageBox(NULL, "fread() at ImageData failed!", "Error LoadBMP()", MB_OK);
			fclose(img);
			return false;
		}

		/*
			Skip over padding, remember that fseek returns 0 on succes, and negative values
				for errors.
		*/
		if(fseek(img, 0, SEEK_CUR)){
			MessageBox(NULL, "fread() at padding failed!", "Error LoadBMP()", MB_OK);
			fclose(img);
			return false;
		}
	}

	// Loaded the image successfully, we now just do what we want with the image data
	fclose(img);
	return true;
}

That loads the image and this actually makes the texture.

bool LoadImage(char* file, GLuint tex[], GLuint id){
	ImageBMP img;
	if(!img.LoadBMP(file))
		return false;

	glGenTextures(1, &tex[id]);
	glBindTexture(GL_TEXTURE_2D, tex[id]);

	glTexImage2D(	GL_TEXTURE_2D,		
					0,				
					img.GetType(),		
					img.GetWidth(),			
					img.GetHeight(),		
					0,						
					GL_BGR_EXT,				
					GL_UNSIGNED_BYTE,		
					img.GetImageData(0));

	gluBuild2DMipmaps(	GL_TEXTURE_2D,
						img.GetType(),
						img.GetWidth(),
						img.GetHeight(),
						GL_BGR_EXT,			
						GL_UNSIGNED_BYTE,
						img.GetImageData(0));

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

	/*
		The functions below cause the image to be stretched properly to cover the border lines preventing
			black lines from showing. Try commenting these out to see the effect.
	*/
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);


	return true;
}

For some reason though, I get a strange error. I am unable to draw any image that is loaded before the last image loaded in. So I can load in 3 images, but no matter which one I bind, only the last one will be drawn. Here is my draw function.


glEnable(GL_TEXTURE_2D);
	glPushMatrix();
		glTranslatef(m_position.x(), m_position.y(), m_position.z());
		glRotatef(90, 1, 0, 0);
		glRotatef(m_facingAngle, 0, 0, 1);
		glColor3f(1, 1, 1);
		glBegin(GL_QUADS);
			glBindTexture(GL_TEXTURE_2D, textures[0]);
			glTexCoord2i(0, 1); glVertex3f(-.8, -.5, 0);
			glTexCoord2i(1, 1); glVertex3f(.8, -.5, 0);
			glTexCoord2i(1, 0); glVertex3f(.8, .5, 0);
			glTexCoord2i(0, 0); glVertex3f(-.8, .5, 0);
		glEnd();
	glPopMatrix();
glDisable(GL_TEXTURE_2D);

Does anyone have any idea on why this happens? Do you need more info?

You are always binding same texture:
glBindTexture(GL_TEXTURE_2D, textures[0]);

Do not call glBindTexture in glBegin/glEnd block.

GL_INVALID_OPERATION is generated if glBindTexture is executed between the execution of glBegin and the corresponding execution of glEnd.

Thanks, the error was I was calling it after glBegin. Thanks for the follow up.