• # How to Load a Bitmap

General and Gameplay Programming

# Bitmap file format

There are four sections that make up a bitmap file. They are the bitmap header, bitmap info, colour palette and the bitmap data. These sections will always appear in this order, but the colourpalette will not always be present. For anyone programming on the Windows platform, there are already structures available to load this information. All you have to do is #include to get access to thee structures. For those not programming in Windows, you will have to define these structures yourself. Below you can see these structures as they are defined in windows.h.

//File information header //provides general information about the file typedef struct tagBITMAPFILEHEADER { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER, *PBITMAPFILEHEADER; //Bitmap information header //provides information specific to the image data typedef struct tagBITMAPINFOHEADER{ DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER, *PBITMAPINFOHEADER; //Colour palette typedef struct tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD;
Note that there is no structure for the data. That is because the data is simple a run of bytes.

# Opening a file

The first thing we need to do to read in a bitmap file is to open the file. The function we will use to open the file is:

FILE *fopen(const char *filename, const char mode).
The first parameter is the name of the file and the second parameter is the mode to open with. Here is the code snippet you need to read from a file.

//include the header for file access#include //function to load the bitmap loadBMP(char *file) { //file handle used in all file operations FILE *in; //open the file for reading in binary mode in=fopen(file,"rb");
The "rb" portion is very important in the fopen function. This is what allows us to read in binary format. If this is not set properly you will not be able to read the file correctly.

That's all there is. The file is now open and ready for reading.

The first thing you need to read from the file is the file header. The function we will use for reading is:

size_t fread(void *ptr, size_t size, size_t nelem, FILE *stream);
The first parameter is a pointer to where you want the data stored. The second parameter is the size of each element that you want to read. The third is the number of elements of that size toread. The final parameter a pointer to the stream obtained from fopen.

BITMAPFILEHEADER bmfh; fread(&bmfh,sizeof(BITMAPFILEHEADER),1,in);
As you can see, it only takes one read to fill all the information in the structure. Using sizeof() on BITMAPFILEHEADER will return a size of 16 bytes. This is also the exact size of the fileheader. Since there is only one header, the third element is set to one. And of course, the stream we are using is the last element.

Now that we have loaded the file header, let's examine each element for their meaning.

WORD bfType;
This will tell you if the file is a bitmap type or not. This number is always 19778. If it is not, then the file is not a bitmap file.

DWORD bfSize;
This is the total size of the file, including all the headers.

WORD bfReserved1; WORD bfReserved2;
These two are reserved and should contain all zeros.

DWORD bfOffBits;
This is the offset to the image data from the start of the file. This will vary depending on whether or not there is a colour palette.

Here you use the same method that was used to load BITMAPFILEHEADER:

BITMAPINFOHEADER bmih; fread(&bmih,sizeof(BITMAPINFOHEADER),1,in);
The sizeof() should return 40 bytes on this read. Since there is only one BITMAPFILEHEADER, the number to read in is 1.

Now let's look at the elements of BITMAPFILEHEADER:

DWORD biSize;
This is the size of BITMAPFILEHEADER. It should be 40.

LONG biWidth;
This is the width of the image in pixels.

LONG biHeight;
This is the height of the image in pixels.

WORD biPlanes;
This is the number of bit planes in the image. It should always be 1.

WORD biBitCount;
This is the number of bits per pixel for colours. It will be 1,4,8 or 24.

DWORD biCompression;
This is the compression, if any, used. It will be set to 0 for no compression.

DWORD biSizeImage;
This should be set to the size of the image data. Sometimes it may be set to 0.

LONG biXPelsPerMeter; LONG biYPelsPerMeter;
These set the horizontal and vertical resolution in pixels per meter.

DWORD biClrUsed;
This specifies the number of colours used from the colour palette. If the image is 24 bits per pixel, then this is usually 0.

DWORD biClrImportant;
This is the number of colour indexes that are important. If it is set to 0, all indexes are important.

This information is only read in from the file if there is a colour table. There is only a colour table if the image is less than 24 bits per pixel. The code below will load the colour table:

//set the number of colours numColours=1 << bmih.biBitCount; //load the palette for 8 bits per pixel if(bmih.biBitCount == 8) { colours=new RGBQUAD[numColours]; fread(colours,sizeof(RGBQUAD),numColours,in); }
In this article, I will only be dealing with 8bit or 24bit bitmaps, so there will only be a colour table for the 8bit variety. With the code above, it can be easily expanded to accommodate 4bit and 1bit colour tables as well.

The sizeof() here will return 4. The variable numColours will be 256 for an 8bit image. So this will load in a 256 colour palette. On a Windows PC, most colour palettes are stored in RGB format. In a bitmap file, this is reversed. There is also a reserved byte at the end of each colour. The palette loaded will look like BGRr. Where the 'r' is the reserved byte.

# Image data

Now we come to the most important part of the file. This is where all the pixels that make up the image will be stored. To find out the size of this section, you could look at bmih.biSizeImage. However, this value will sometimes be 0. To make sure you always have a valid value, you can use the following code:

DWORD size; size=bmfh.bfSize-bmfh.bfOffBits;
This takes the size of the file and subtracts the size of all the header information. This will always give you an accurate result. Next create a temporary variable to store image data in so you can work with it and read the image data from the file:

BYTE *tempPixelData; tempPixelData=new BYTE[size]; if(tempPixelData==NULL) { fclose(in); return false; } fread(tempPixelData,sizeof(BYTE),size,in);
And that's it. One read reads in the entire bitmap, no matter how large it is. The only consideration is if you have enough memory to hold the image.

Once you have the data loaded, you might think you are ready to use it. However, there are a couple of things to consider about how the data is formatted.

First, each line of data must end on a DWORD(4 byte) boundary. If the width of your image does not fall on a DWORD boundary, then it will be padded with zeros to fill it out. For example, if you have an image that is 254x254, the DWORD boundary for this image is 256. The image data loaded from this file will be 256x254. Before the data can be used, the padding must be removed. Also note that some image editors will also make sure the file ends on a DWORD boundary. This needs to be taken into account in the code. There are comments to show where this needs to be considered.

Second, the data can be stored either in forward order or reverse order. The way to tell is to check the bmih.biHeight value. If this value is negative, the data is stored in forward order. If it is positive, reverse order is used.

Now we'll set up a couple of variables to hold the widths:

//byteWidth is the width of the actual image in bytes //padWidth is the width of the image plus the extra padding LONG byteWidth,padWidth; //initially set both to the width of the image byteWidth=padWidth=(LONG)((float)width*(float)bpp/8.0); //add any extra space to bring each line to a DWORD boundary while(padWidth%4!=0) { padWidth++; }
I will just throw all the code out and then go through it:

DWORD diff; int offset; LONG height; height=bmih.biHeight; //set diff to the actual image size(no padding) diff=height*byteWidth; //allocate memory for the image pixelData=new BYTE[diff]; if(pixelData==NULL) { fclose(in); return false; } //bitmap is inverted, so the padding needs to be removed //and the image reversed //Here you can start from the back of the file or the front, //after the header. The only problem is that some programs //will pad not only the data, but also the file size to //be divisible by 4 bytes. if(height>0) { int j==size-3; offset=padWidth-byteWidth; for(int i=0;i if((i+1)%padWidth==0) { i+=offset; } *(pixelData+j+2)=*(tempPixelData+i); *(pixelData+j+1)=*(tempPixelData+i+1); *(pixelData+j)=*(tempPixelData+i+2); j++; } } //the image is not reversed. Only the padding needs to be removed. else { height=height*-1; offset=0; do { memcpy((pixelData+(offset*byteWidth)), (tempPixelData+(offset*padWidth)), byteWidth); offset++; } while(offset }
First the actual size of the image is determined and memory is allocated for it. Then the height is checked to see if the image is reversed or not. If the image is reversed, the data needs to be copied byte by byte into the final storage area. If the image is not reversed, then we can make use of memcpy() to quickly move the data. This is not a big deal as image loading should not occur in time critical code anyway.

# An example

Now that we have our bitmap loaded, I will show you how you can use this in an OpenGL program to load texture data from a bitmap image. First I will post the header file for my class to make things easier to follow:

#ifndef _BITMAP_H #define _BITMAP_H #include #include class Bitmap { public: //variables RGBQUAD *colours; BYTE *pixelData; bool loaded; LONG width,height; WORD bpp; //methods Bitmap(void); Bitmap(char *); ~Bitmap(); bool loadBMP(char *); private: //variables BITMAPFILEHEADER bmfh; BITMAPINFOHEADER bmih; //methods void reset(void); }; #endif //_BITMAP_H
As you can see, I left the data that needs to be accessed public to make it easier to use.

Now for an example of loading a texture into an OpenGL program:

bool loadTexture() { Bitmap *image; image=new Bitmap(); if(image==NULL) { return false; } if (image->loadBMP("mytexture.bmp")) { glGenTextures(1, &texture[0]); glBindTexture(GL_TEXTURE_2D, texture[0]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, 3, image->width, image->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->pixelData); } else { return false; } if (image) { delete image; } return true; }
As you can see, all you have to do is allocate space for the Bitmap object, tell it to load the bitmap and send it to OpenGL.

# Conclusion

I didn't cover all the aspects of bitmap files, but the ones that I left out are rarely used. It's pretty straightforward to load a bitmap, once you have the specification. You can easily add tothis class by adding a loadXXX for each type of file (TGA,PCX,JPG...) you want to load.

# Postscript

Some minor changes have been made to the code since the writing of this article. But essentially it is the same.

Report Article

## User Feedback

There are no comments to display.

## Create an account

Register a new account

×

## Important Information

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!