Problems loading BMP for texture mapping

HELP! I have 4 books on OpenGL, and not one of their implementations for loading .BMP files for texture mapping works correctly. They all compile, but fail to read the file. Using “step-into” (Borland C++ 6.0) I noticed that many of the values in BITMAPHEADERINFO etc… are way off – i.e. showing .biImageSize field value of 882938471991 for a 128x128 bitmap. Can anyone point me in the right direction where I could get source code for a loader that actually WORKS??? Thanks…

What books do you have?
Are you sure they are for loading BMP files and not TGA?

nehe.gamedev.net has a BMP loading information and routines

Originally posted by xpnctoc:
HELP! I have 4 books on OpenGL, and not one of their implementations for loading .BMP files for texture mapping works correctly. They all compile, but fail to read the file. Using “step-into” (Borland C++ 6.0) I noticed that many of the values in BITMAPHEADERINFO etc… are way off – i.e. showing .biImageSize field value of 882938471991 for a 128x128 bitmap. Can anyone point me in the right direction where I could get source code for a loader that actually WORKS??? Thanks…

I have the OpenGL Super Bible (Richard S Wright & Michael Sweet), OpenGL Game Programming (Kevin Hawkins & Dave Astle), and the “Red Book” and “Blue Book” to make sure I am using the texture routines correctly. I am positive the loading routines are for bitmaps.

I tried out that NeHe tutorial – a direct copy-and-paste and even that didn’t work.

I also tried using the image loading routine in glaux.h/lib, and that only causes an access violation and crashes my whole program.

Does anybody else using Borland C++ Builder 6.0 Personal have a lot of problems, either with OpenGL or programming in general?

[This message has been edited by xpnctoc (edited 09-21-2002).]

well reading .BMP files is not difficult … If you can’t follow the book that shows you step by step on how to load .BMP files, I don’t think anyone here can be of MORE help to you …

Go back to your book and make SURE you understand the code … Don’t just COPY and paste the text … In most cases the authors use personal function, or custom defined objects, that could be defined in different chapters of the book … Maybe that is what you are missing … or could be something else

If you can’t understand the Code, you will never learn

As for the #s being WAY off , it seems that you are reading incorrectly … For example, Header file holds the info about .BMP file… Some of the objects are shorts, others are long… Maybe you are reading 2 bytes where in fact you should be reading 4 bytes or vice versa … This could easily couse the rest of the data to be read incorrectly .

[This message has been edited by jubei_GL (edited 09-21-2002).]

I can vouch for what jubei says… I tried working out a BMP read routine after reading through some examples and documentation. But regardless of the variable types I used in the structures the values just refused to line up properly. I ended up dropping BMP for now and concentrating on TGA which I figured out a lot quicker thankfully. Well, the non compressed kind anyway :wink: Eventually I will have to get back to BMP loading but there’s no hurry yet :wink:

Tina

TINAK: THAT’S WHAT I’M TALKING ABOUT I’m glad to see I’m not the only one with this problem. I DO understand the code. I’m not a newbie to programming – just to OpenGL. The problem is that when I read the various header structures of a BMP file, the data REFUSES to line up properly. I’m using those predefined structures (like BITMAPINFOHEADER) with the sizeof() function to read the supposedly correct number of bytes. But they refuse to line up.

And whether or not I understand the code still doesn’t excuse the fact that the glaux.lib function for loading BMPs causes access violations. Isnt’ a .lib supposed to be a file of functions that WORK CORRECTLY?

[This message has been edited by xpnctoc (edited 09-22-2002).]

[This message has been edited by xpnctoc (edited 09-22-2002).]

“Isnt’ a .lib supposed to be a file of functions that WORK CORRECTLY?” That’s so funny…

Functions only work correctly when you use ithem n the contexted and limitations they were designed for. If you are using them incorrectly, say by passing in bad data or using a set of functions in a wrong order etc… then of course they won’t work as expected.

But why not simply post your code and then any errors could be pointed out

so if the data does not line up properly its Obvious that you don’t read it correctly … Reading 4 bytes instead of 2 or vice versa , as I have stated before …

If this is the case its easy to find the problem … Just debug the program, and look at the functions that read .BMP header files … You should first get char B then M, then #s … Just trace it, if you get good Values, and than all the sudden you get some Strange #, see what could couse this … EASY FIX

hehe, yep tried that jubei myself… I’d get so far and after trying several variable types with no success I just decided to leave it for another day. Ah, looking up one of my older projects when I used bmps and found that I did get bmps loading but not using a structure… I had to read it pretty much a byte at a time… And I probably copied that from someone else… The problem I had was when I tried to turn it into a single structure read like I have for tga. Oh, and I wasn’t using the bitmapinfoheader structures as I wanted the loading routine to be system independent. Although whether I had problems with the bitmapinfoheader when I did use it the first few times I don’t recall.

Here’s how I ended up reading in BMP files.

GLubyte* tx_image;
GLuint tex;
int i;
GLuint size;
unsigned short int planes;
unsigned short int bpp;
GLuint width;
GLuint height;
GLuint type;
char temp;
FILE f;
f = fopen(filename,“rb”);
if (f != NULL)
{
fseek(f,18,SEEK_CUR);
i = fread(&width,4,1,f);
i = fread(&height,4,1,f);
i = fread(&planes,2,1,f);
i = fread(&bpp,2,1,f);
fseek(f,24,SEEK_CUR);
size = height * width * (bpp / 8);
tx_image = (GLubyte
) malloc(size);
i = fread(tx_image,size,1,f);
fclose(f);
for (i=0;i<size;i+=bpp/8)
{
temp = tx_image[i];
tx_image[i] = tx_image[i+2];
tx_image[i+2] = temp;
}
return tex;
}

Not exactly how I wanted it but I’m sure I’ll figure it out when I need to at a later date.

Tina

It’s possible you guys are running into packing problems. By default, Windows compilers use an 8 byte packing alignment. What that means is that it aligns data so that if a member would cross an 8 byte border, it adds extra padding.

Just looking at the BITMAPINFOHEADER struct, it doesn’t appear it should have that problem, but when I did my own BMP loader I made my own struct and set the packing alignment to 1 just to be safe. You do that by using the #pragma directive. In VC++ you can use the following to set the packing alignment for a particular struct, then return it to whatever it was before. Other compilers like the GNU compiler doesn’t support the push,pop portion, though.

#pragma pack(push, 1)
// define the struct
#pragma pack(pop)

But anyway, here’s some basic code I’ve got in my own little library for doing some BMP loading. These functions aren’t entirely stand-alone as they use other functions I have for allocating memory for the image struct, etc. You should be able to figure that out for the most part, though.

One extra disclaimer about this code. I don’t have cases in there for every different variation of BMP yet. It seems to work well with 24-bit uncompressed BMP files, though.

// BMPFiles.h
#ifndef BMPFILES_H
#define BMPFILES_H

#ifdef __cplusplus
extern “C” {
#endif

#define BMP_BPP_MONOCHROME 1
#define BMP_BPP_16_COLOR 4
#define BMP_BPP_256_COLOR 8
#define BMP_BPP_16_BIT 16
#define BMP_BPP_24_BIT 24
#define BMP_BPP_32_BIT 32

#define BMP_COMPRESSION_NONE 0
#define BMP_COMPRESSION_RLE8 1
#define BMP_COMPRESSION_RLE4 2
#define BMP_COMPRESSION_BITFIELDS 3

#ifdef WIN32
#pragma pack(push,1)
#else
#pragma pack(1)
#endif

typedef struct
{
unsigned long size;
unsigned long width;
unsigned long height;
unsigned short planes;
unsigned short bpp;
unsigned long compression;
unsigned long dataSize;
unsigned long horizResolution;
unsigned long vertResolution;
unsigned long numColors;
unsigned long importantColors;
} BMP_info_header;

#define BMP_ID_WINDOWS “BM”
#define BMP_ID_OS2_BMP_ARRAY “BA”
#define BMP_ID_OS2_COLOR_ICON “CI”
#define BMP_ID_OS2_COLOR_POINTER “CP”
#define BMP_ID_OS2_ICON “IC”
#define BMP_ID_OS2_POINTER “PT”

typedef struct
{
unsigned char id[2];
unsigned long fileSize;
unsigned long reserved;
unsigned long offset;
BMP_info_header header;
} BMP_file_header;

#ifdef WIN32
#pragma pack(pop)
#else
#pragma pack(8)
#endif

Latrans_image_p imgLoadBMP(const char* filename);
int imgSaveBMP(Latrans_image_p lpImg, const char* filename, unsigned long dwFlags);

#ifdef __cplusplus
}
#endif

#endif // BMPFILES_H

// BMPFiles.c
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include “LatransImg.h”
#include “LatransDraw.h”
#include “BMPFiles.h”

Latrans_image_p imgLoadBMP(const char* filename)
{
Latrans_image_p pImg = NULL;
BMP_file_header head;
char* pData;
char padding[4];
FILE* f;
unsigned int x,y;

f = fopen(filename, “rb”);

if (f)
{
fread(&head, sizeof(BMP_file_header), 1, f);

  if (head.header.bpp == BMP_BPP_32_BIT)
  {
  	pImg = imgCreateImage(head.header.width, head.header.height, IMG_RGBA);
  }
  else
  {
  	pImg = imgCreateImage(head.header.width, head.header.height, IMG_RGB);
  }

  pData = pImg->pData;

  if (head.header.bpp == BMP_BPP_24_BIT && head.header.compression == BMP_COMPRESSION_NONE)
  {
  	for (y=0;y<head.header.height;y++)
  	{
  		int index = head.header.width * y * 3;

  		fread(&pData[index], 3*head.header.width, 1, f);

  		if (head.header.width % 4 > 0)
  			fread(padding, head.header.width % 4, 1, f);
  	}

  	for(y=0;y<pImg->nHeight;y++)
  	{
  		for(x=0;x<pImg->nWidth;x++)
  		{
  			int index = (y*pImg->nWidth+x)*3;
  			char temp = pImg->pData[index];

  			pImg->pData[index] = pImg->pData[index+2];
  			pImg->pData[index+2] = temp;
  		}
  	}
  }

  imgFlipHorizontal(pImg);
  fclose(f);

}

return pImg;
}

int imgSaveBMP(Latrans_image_p lpImg, const char* filename, unsigned long dwFlags)
{
FILE* f;
BMP_file_header head;
unsigned int x, y;

if (lpImg->eType == IMG_GREY | | lpImg->eType == IMG_INDEX | | lpImg->eType == IMG_RGBA)
{
// Not supported yet
return -1;
}

f = fopen(filename, “wb”);

if (f)
{
memcpy(head.id, BMP_ID_WINDOWS, 2);
head.fileSize = sizeof(BMP_file_header) + lpImg->nHeight * lpImg->nWidth;
head.reserved = 0;
head.offset = sizeof(BMP_file_header);

  head.header.size = sizeof (BMP_info_header);
  head.header.width = lpImg->nWidth;
  head.header.height = lpImg->nHeight;
  head.header.planes = 1;
  head.header.bpp = BMP_BPP_24_BIT;
  head.header.compression = BMP_COMPRESSION_NONE;
  head.header.dataSize = lpImg->nWidth * lpImg->nHeight;
  head.header.horizResolution = 0;
  head.header.vertResolution = 0;
  head.header.numColors = 0;
  head.header.importantColors = 0;

  fwrite(&head, 1, sizeof(BMP_file_header), f);

  for(y=0;y<lpImg->nHeight;y++)
  {
  	for(x=0;x<lpImg->nWidth;x++)
  	{
  		int index = (y*lpImg->nWidth+x)*3;
  		char col[3];

  		col[0] = lpImg->pData[index+2];
  		col[1] = lpImg->pData[index+1];
  		col[2] = lpImg->pData[index];

  		fwrite(col, 1, 3, f);
  	}
  }
  fclose(f);

}

return 0;
}

You can also look at the file headers with octal dump on unix etc.