Bitmap Tutorial



1. Introduction
2. Loading a bitmap to a DC
3. Loading a bitmap
4. Converting Bitmap data to an RGB array
5. Saving a Bitmap
6. Converting RGB data to saveable bmp data
7. Conclusion
DOWNLOAD the code


1. Introduction

The windows bitmap file format (.bmp) is the most widely used image file format on windows (next to .jpg) and there are many occasions a program or game has to be able to load or save bitmaps ( raytracers and other non-realtime renderers should be able to save their output in .bmp format, games might have to load them as textures etc. ).
Unfortunately .bmp files are not as straightforward as for example .png image files and provide quite a problem for newbies since it's not that easy to figure out how to use them.
On the gamedev.net forums the question how to load/save bitmaps is asked every few days and there are not many tutorials out there that really tell you how to save something in a .bmp and the MSDN doesn't give you any usable code for it at all. I searched around quite a bit and didn't find any tutorial that told you in one place how to load, save and handle bmp data, thus i decided to write this bitmap tutorial.

Two more things before i start:
1) I'm dealing with 24bit bmps only here since bmps in other resolutions are almost never used. But it shouldn't be hard to change the code to support palettized formats.
2) For clarity's sake i'm only doing a little bit of error checking in the code. On errors like supplying a wrongsized array the code is likely to crash the program when it tries to access the array, so you better add some exception handling.

Ok then lets begin

2. Loading a bitmap to a DC

I'm going to start this easy by showing you how to use the WinAPI to load a bitmap onto a device context.

First we declare the function that takes as input a device context and a filename:
bool LoadBMPIntoDC ( HDC hDC, char* bmpfile )
Now we check if the supplied arguments are valid. If not we return false.
{
	if ( ( NULL == hDC  ) || ( NULL == bmpfile ) ) 
		return false; 
We then use the windows function LoadImage to load the bitmap into a Bitmap Handle:
	HBITMAP hBmp = LoadImage ( NULL, bmpfile, IMAGE_BITMAP, 0, 0,
		LR_LOADFROMFILE );
The first parameter is the instance that contains the image, but since we are loading it from a file and not from a resource this is NULL. The second param is the filename, then we tell the function to load a bitmap (it can also load icons and corsors). The next two params are desired width and height. 0 means that we want the actual bitmap size. Finally we have to tell the function that we want to load the bitmap from a file. To load an image from a resource file we would use the function like this:
	HBITMAP hBmp = LoadImage ( hInstance, MAKEINTRESOURCE(imageid), IMAGE_BITMAP, 0, 0,
		LR_DEFAULTCOLOR );
where hInstance is the handle of the instance of the program (the HINSTANCE in WinMain), imageid is the identifier of the image in the resourcefile and LR_DEFAULTCOLOR is the standard do-nothing flag.
One more thing: it might not be possible to use this function to load a bitmap from a file on all versions of windows. My helpfiles tell me that LR_LOADFROMFILE is not supported on WinNT, but i can't test it. But don't worry, later on i will show you how to read .bmp files directly.
Now we do a quick check if the function returned a usable HBITMAP:
	if ( NULL == hBmp )
		return false; 
At this point we have a valid image handle. Now we want to select it into a device context. Unfortunately it is not possible to select it directly into a visible DC, but only into a memory DC, so we have to create that first:
HDC dcmem = CreateCompatibleDC ( NULL );	
This function created a memory device context that is compatible to the screen.
Now we can select the bitmap into it:
	if ( NULL == SelectObject ( dcmem, hBmp ) )
	{	
		DeleteDC ( dcmem ); 
		return false; 
	}
If the function fails it deletes the memory dc and returns from the function.
If it was successful we can now blit the image from the memory dc to the visible dc we supplied as the first parameter to our LoadBMPIntoDC function.
To blit the entire image we first have to find out it's size though, but the WinAPI supplies us with an easy way to get the width and height of a HBITMAP:
	BITMAP bm;
	GetObject ( hBmp, sizeof(bm), &bm );
This function loads the information from the HBITMAP, which is not much more then a pointer, into a BITMAP structure that lets us access the width and height of the image, stored in bm.bmWidth and bm.bmHeight. With those values we can now blit the whole image to the visible DC:
	if ( BitBlt ( hDC, 0, 0, bm.bmWidth, bm.bmHeight, dcmem,
		0, 0, SRCCOPY ) == 0 )
	{	
		DeleteDC ( dcmem ); 
		return false; 
	}
The function blits the entire image to the upper left of the visible DC and cleans up on failure.

On success we can now see the image on the screen.

Now we don't need the memory dc anymore and can finish the function:
	DeleteDC ( dcmem );
	
	return true;
}
Now to display an image we can use the function like this (hWnd is the window handle):
	HDC dc = GetDC ( hWnd );           // get device context
	LoadBMPIntoDC ( dc, "test.bmp"  ); // display test.bmp on it
	ReleaseDC ( hWnd, dc );            // release dc
You can of course modify the code to suit your needs, for example you can change the BitBlt to only copy parts of the image or to copy it to another place then (0,0) on the destination DC.


3. Loading a Bitmap


Now most of the time we want to work with the bitmap data directly (e.g. use it as a texture) and not just display it on a window dc, so the next thing i'll show you is how to load a bitmap.
Before we attempt to load it we should take a short look at the structure of a .bmp file.
A 24 bit bmp consists of 3 parts:
1) The file header, that holds information about type, size and layout of the file
2) The info header, that holds information about dimension and colorformat of the image
3) The image data

A palettized .bmp ( 4 or 8 bit ) additionally holds a colorpalette between info header and image data, but we're only looking at the usual 24 bit bmp in this tutorial.
The WinAPI is again nice enough to supply us with the structs for the file and info header, but you can as well declare them yourself if you don't want to use any windows functions or want to load .bmps on an other OS. They look like this:
typedef struct tagBITMAPFILEHEADER 
{
   WORD    bfType;        // must be 'BM' 
   DWORD   bfSize;        // size of the whole .bmp file
   WORD    bfReserved1;   // must be 0
   WORD    bfReserved2;   // must be 0
   DWORD   bfOffBits;     
} BITMAPFILEHEADER;  
The bfOffBits member is a bit confusing due to it's name. Actually it is the distance to the beginning of the image data, in bytes, from the beginning of the file. This is due to the fact that the size of the info header is not guaranteed to be a certain size, since on newer windows versions there might be an extended version of the header, thus it is not guaranteed that the imaga data starts at sizeof(BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER) (although you can always read in a BITMAPINFOHEADER since the expanded info headers have the same layout). So, to read the image data later on we will move the file pointer to bfOffBits and start reading there.
Now lets take a look at the info header:
typedef struct tagBITMAPINFOHEADER
{
   DWORD  biSize;            // size of the structure
   LONG   biWidth;           // image width
   LONG   biHeight;          // image height
   WORD   biPlanes;          // bitplanes
   WORD   biBitCount         // resolution 
   DWORD  biCompression;     // compression
   DWORD  biSizeImage;       // size of the image
   LONG   biXPelsPerMeter;   // pixels per meter X
   LONG   biYPelsPerMeter;   // pixels per meter Y
   DWORD  biClrUsed;         // colors used
   DWORD  biClrImportant;    // important colors
} BITMAPINFOHEADER;  
A little note: if you don't use the struct declarations from windows.h but paste the above into your code you need to make sure that your compiler aligns data structures on 2-byte boundaries, else the loading and saving will not work correctly since the compiler usually pads the structures to the next 4-byte boundary. Setting a 2-byte align is luckily very easy with VC++: just add #pragma pack(2) before the struct declaration. If you use another compiler you will have to consult it's documents on how to change the alignment.

Ok now we can write a function to load the bitmap ( i'm using windows file I/O here, but you can use fopen/fread etc. or STL functions as well..whatever you like.
Lets declare the function and some variables:
BYTE* LoadBMP ( int* width, int* height, long* size, char* bmpfile )
{
	BITMAPFILEHEADER bmpheader;
	BITMAPINFOHEADER bmpinfo;
	DWORD bytesread;
Note that we take three pointers as parameters for width, height and size, since we will return the image dimensions and size in these variables. bmpfile is of course the filename of the bitmap, and the return value of the function will be a pointer to the image data.
First lets try to open the file:
	HANDLE file = CreateFile ( bmpfile , GENERIC_READ, FILE_SHARE_READ,
		 NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL );
	if ( NULL == file )
		return NULL; 	
Just a quick note here: it's useful to write if ( NULL == file ) instead of if ( file == NULL ) to prevent bugs, since on accidently typing if ( file = NULL ) the compiler will not complain but assign NULL to the file handle. if ( NULL = file ) will spawn a compiler error so you can prevent bugs easily this way.
Back to the topic: now we opened the file and can read the file header. On error we will close the file and return from the function.
	if ( ReadFile ( file, &bmpheader, sizeof ( BITMAPFILEHEADER ), 
		&bytesread, NULL ) == false )
	{
		CloseHandle ( file );
		return NULL;
	}
Now we can read the info header:
	if ( ReadFile ( file, &bmpinfo, sizeof ( BITMAPINFOHEADER ), 
		&bytesread, NULL ) == false )
	{
		CloseHandle ( file );
		return NULL;
	}
Since we are only going to load 24bit .bmps here we now do some checking of the header contents.
First check if the file is actually a bitmap:
	if ( bmpheader.bfType != 'MB' )
	{
		CloseHandle ( file );
		return NULL;
	}
check if it's uncompressed
	if ( bmpinfo.biCompression != BI_RGB )
	{
		CloseHandle ( file );
		return NULL;
	}
and check if it's 24bit
	if ( bmpinfo.biBitCount != 24 )
	{
		CloseHandle ( file );
		return NULL;
	}
When we are here we actually have a 24 bit bmp so lets get its size and dimensions. We'll already store them in the supplied variables:
	*width   = bmpinfo.biWidth;
	*height  = bmpinfo.biHeight;
	*size 	 = bmpheader.bfSize - bmpheader.bfOffBits;
To be independent of the type of info header we compute the imaga data size as the whole file size - the distance from file origin to start of image data.
Now we create a buffer to hold the data
BYTE* Buffer = new BYTE[ *size ];
Again to be independent of info header version we set the file pointer to the start of image data as told by the bfOffBits:
	SetFilePointer ( file, bmpheader.bfOffBits, NULL, FILE_BEGIN );
And now we can read in the data. We make sure that on error the Buffer gets deleted so we don't create memory leaks
	if ( ReadFile ( file, Buffer, *size, &bytesread, NULL ) == false )
	{
		delete [] Buffer;
		CloseHandle ( file );
		return NULL;
	}
and finish the function
	CloseHandle ( file );
	return Buffer;
}
A quick example of how to use the function:
	int x, y;
	long size;
	BYTE* Buffer = LoadBMP ( &x, &y, &size, "test.bmp" );
Now that didn't seem that complicated, did it?
Unfortunately we have another problem now. The data we read from the image and returned from the function isn't quite what one might expect it to be.
One would expect that we now have a Bufer of width*height RGB triplets, but unfortunately bitmaps store their data a little bit un-straightforward:
Basically they store everything from back to front: the image is stored upside-down in the file, and not in RGB triplets, but in BGR triplets.
And another thing comes into play: scanlines in .bmps are DWORD aligned. This means that every scanline ( there are (height) scanlines in an image ) that is not a multiple of 4 bytes long ( DWORD == 4bytes ) is filled up with zeros to the next multiple of 4.
Thus if the image would be just one black pixel it is not stored as one RGB triplet ( FF, FF, FF ) in the file, but as a DWORD ( FF, FF, FF, 00 ). This means if we treat the data as an RGB array we get some wrong colors since we must not interprete the added zeros as color values.
Luckily the graphics APIs can deal with the unusual structure of .bmp image data:
In OpenGL it is possible to assign the buffer returned by LoadBMP directly to a texture if we add the following code before calls to glTexImage2D:
    glPixelStorei ( GL_UNPACK_ALIGNMENT,   4 );
    glPixelStorei ( GL_UNPACK_ROW_LENGTH,  0 );
    glPixelStorei ( GL_UNPACK_SKIP_ROWS,   0 );
    glPixelStorei ( GL_UNPACK_SKIP_PIXELS, 0 );
I don't know how to do it with D3D but it shouldn't be to hard to find out :)

But this tutorial is also for people who don't use an API for it, so lets move on to the next section.


4. Converting Bitmap data to an RGB array


Now i will show you how to take the padded upside-down BGR image data and convert it into a RGB buffer.
The function will take as arguments the BYTE array, the width and the height returned from LoadBMP. The output will be an array of RGB triplets and thus have the size 3 * width * height.
This means if Buffer is the output of the function, the RGB color of the first pixel in the image will be (Buffer[0], Buffer[1], Buffer[2]), the second pixel's (Buffer[3], Buffer[4], Buffer[5]) etc.
It is possible, and perhaps more straightforward, to create a struct like this:
typedef struct tagRGBTriplet
{
	BYTE red;
	BYTE green;
	BYTE blue;
} RGBTriplet;
and then make an array of such structs and assign the output from the function to it like this:
RGBTriplet* buffer = (RGBTriplet*)ConvertBMPToRGBBuffer 
		( imagedata, width, height );
buffer will then consist of width*height RGBTriplet structures and the color of the first pixel in the image would be ( buffer[0].red, buffer[0].green, buffer[0].blue ).

Now lets look at the code:
First declare the function and make sure we have no invalid params:
BYTE* ConvertBMPToRGBBuffer ( BYTE* Buffer, int width, int height )
{
	if ( ( NULL == Buffer ) || ( width == 0 ) || ( height == 0 ) )
		return NULL;		
Now we have to find out the number of bytes every scanline is padded with
	int padding = 0;
	int scanlinebytes = width * 3;
	while ( ( scanlinebytes + padding ) % 4 != 0 )  // DWORD = 4 bytes
		padding++;
At the end of the while loop padding will hold the number of padding bytes

Now we can get the length in bytes of a padded scanline:
	int psw = scanlinebytes + padding;
And construct the buffer to hold the output
	BYTE* newbuf = new BYTE[width*height*3];
The 3 stands for the number of bytes in one RGBTriplet of course.

Now comes the heart of the function:
	long bufpos = 0;   
	long newpos = 0;
	for ( int y = 0; y < height; y++ )
		for ( int x = 0; x < 3 * width; x+=3 )
		{
			newpos = y * 3 * width + x;     
			bufpos = ( height - y - 1 ) * psw + x;
			// swap R and B values
			newbuf[newpos] 	   = Buffer[bufpos + 2];       
			newbuf[newpos + 1] = Buffer[bufpos + 1]; 
			newbuf[newpos + 2] = Buffer[bufpos];     
		}
What exactly happens in this loop?
For clear code and some more speed we declare two variables that will hold the buffer indexes.
The first for loop loops trough each scanline in the image data, the second loop hits every 3rd byte in a scanline, meaning the start of every RGB triplet(representing a pixel).
Now we compute the index the current pixel will have in the new RGB buffer as current scanline * imagewidth * numberofbytesperpixel + x position of current pixel.
Next we compute the position we have to look at for the current pixel in the image data. The image was stored upside down in the .bmp, thus if we want to find a pixel color in the first line we have to look at the last scanline in the image data. Thus, and because we start indexing arrays with 0, the scanline to look for is imageheight - currentscanline (the y variable of the loop) - 1.
To get the exact pixel position we have to multiply the scanline # by the amount of bytes per scanline in the buffer, which we already computed in psw. And finally we add the x position of the current pixel.
So now we have the position the pixel (x, y) will have in the new buffer in newpos, and the position the color values for this pixel are at in the image data is in bufpos.
Now we could just assign those values, but remember that the color values themselves are stored in BGR format in the image and we want them in RGB format, so we have to swap the bytes at our position (red value) and the one at our poition+2 (blue value).
I hope that was halfway clear :)

Now we can finish the function
	return newbuf;
}
I have two more things to add:
1) As i stated in the introduction i'm only doing rudimentary error checking here. You can see that if the buffer passed to the function is smaller then width*height*3 the loop will try to access data that doesn't belong to the array which might crash the program.
To prevent this from happening it would be good to do some exception handling and wrap the function or at leat the loop in a __try __catch block and make sure you pass the exact output from LoadBMP to the function.
2) The function creates a new buffer which it then returns, thus the calling function has to make sure this buffer is [] deleted when not longer needed to prevent memory leaks.


5. Saving a Bitmap


Now that i covered how to load an image the next topic is how to save an image as a bitmap.
I already described the structure of .bmp files in section 3 so look there again if anything is unclear.

Lets begin by declaring the function
bool SaveBMP ( BYTE* Buffer, int width, int height, 
		long paddedsize, char* bmpfile )
{
Buffer is an array that contains the image data, width and height are the dimensions of the image to save and paddedsize is the size of Buffer in bytes. bmpfile is the filename to save to.
Now we declare the header structs and clear them.
	BITMAPFILEHEADER bmfh;
	BITMAPINFOHEADER info;
	unsigned long    bwritten;

	memset ( &bmfh, 0, sizeof ( BITMAPFILEHEADER ) );
	memset ( &info, 0, sizeof ( BITMAPINFOHEADER ) );
First we fill the file header with data:
	bmfh.bfType 	 = 0x4d42; // 0x4d42 = 'BM'
	bmfh.bfReserved1 = 0;
	bmfh.bfReserved2 = 0;
	bmfh.bfSize 	 = sizeof(BITMAPFILEHEADER) + 
		sizeof(BITMAPINFOHEADER) + paddedsize;
	bmfh.bfOffBits   = 0x36;  // (54) size of headers
and fill the info header
	info.biSize 	     = sizeof ( BITMAPINFOHEADER );
	info.biWidth	     = width;
	info.biHeight 	     = height;
	info.biPlanes        = 1;
	info.biBitCount      = 24;		
	info.biCompression   = BI_RGB;	
	info.biSizeImage     = 0;		
	info.biXPelsPerMeter = 0x0ec4;    
	info.biYPelsPerMeter = 0x0ec4;     
	info.biClrUsed       = 0;		
	info.biClrImportant  = 0;   
Some explanations: we want to save as a 24 bit RGB image, so we have to set biCompression to BI_RGB, biBitCount to 24 and biPlanes to 1.
In 24 bit images we can set the biSizeImage value to 0 since it is ignored.
The PelsPerMeter values are of no real importance, but windows Paint and PSP both use 0x0ec4 so it can't be wrong.
Since we have no palette we set the biClrUsed to 0, and biClrImportant being zero means that all colors are important.

Now we can open a file to save to ( again i'm using windows functions but it doesnt matter what file I/O functions you use of course)
	HANDLE file = CreateFile ( bmpfile , GENERIC_WRITE, FILE_SHARE_READ,
		 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( file == NULL )
	{
		CloseHandle ( file );
		return false;
	}
Now we write the file header:
	if ( WriteFile ( file, &bmfh, sizeof ( BITMAPFILEHEADER ), 
		&bwritten, NULL ) == false )
	{	
		CloseHandle ( file );
		return false;
	}
and the info header
	if ( WriteFile ( file, &info, sizeof ( BITMAPINFOHEADER ), 
		&bwritten, NULL ) == false )
	{	
		CloseHandle ( file );
		return false;
	}
and finally the image data
	if ( WriteFile ( file, Buffer, paddedsize, 
		&bwritten, NULL ) == false )
	{	
		CloseHandle ( file );
		return false;
	}
and finish the function
	CloseHandle ( file );

	return true;
}
Now to save a buffer with image data as a .bmp file you use the function like this: (Buffer holds the data, is s bytes in size and of dimension width*height)
	SaveBMP ( Buffer, width, height, s, "test.bmp" );
Unfortunately we now have the same problem as above: the weird format of the image data. To be opened and displayed in the right way by other programs we can't save an RGB buffer to a bmp file, but first have to convert it to BGR, flip it upside down and DWORD-align the scanlines.


6. Converting RGB data to saveable bmp data


Basically this function is the opposite of the ConvertBMPToRGBBuffer form above so i'm not going to explain every detail again.

First declare the function and check params for validity
BYTE* ConvertRGBToBMPBuffer ( BYTE* Buffer, int width, 
		int height, long* newsize )
{
	if ( ( NULL == Buffer ) || ( width == 0 ) || ( height == 0 ) )
		return NULL;
find number of bytes the buffer has to be padded with and length of padded scanline
	int padding = 0;
	int scanlinebytes = width * 3;
	while ( ( scanlinebytes + padding ) % 4 != 0 ) 
		padding++;
	int psw = scanlinebytes + padding;
calculate the size of the padded buffer and create it
	*newsize = height * psw;
	BYTE* newbuf = new BYTE[*newsize];
Now we could of course copy the old buffer to the new one, flip it and change RGB to GRB and then fill every scanline with zeros to the next DWORD boundary, but we are smart coders of course so we don't bother with any actual padding at all but just initialize the whole buffer with zeros and then copy the color values to their new positions without touching those padding-bytes anymore :)
	memset ( newbuf, 0, *newsize );

	long bufpos = 0;   
	long newpos = 0;
	for ( int y = 0; y < height; y++ )
		for ( int x = 0; x < 3 * width; x+=3 )
		{
			// calculate pixel positions in new
			// and old buffer
			bufpos = y * 3 * width + x;     
			newpos = ( height - y - 1 ) * psw + x;  
			// assign color values
			// and switch R and B
			newbuf[newpos]     = Buffer[bufpos + 2];      
			newbuf[newpos + 1] = Buffer[bufpos + 1]; 
			newbuf[newpos + 2] = Buffer[bufpos]; 
		}
and finish the function
	return newbuf;
}
Again remember to delete all buffers at end of program and think of adding some exception handling.


7. Conclusion


And that's all there is to .bmps. And that in less then 200 lines of code :)

Now i'll just give you some examples of how to use the functions and to see if you got the code right :)

The following code copies test.bmp to test2.bmp
	int x, y;
	long s;
	BYTE* b = LoadBMP ( &x, &y, &s, "test.bmp" );
	SaveBMP ( b, x, y, s, "test2.bmp" );
	delete [] b;
This does the same if you got it right:
	int x, y;
	long s, s2;
	BYTE* a = LoadBMP ( &x, &y, &s, "test.bmp" );
	BYTE* b = ConvertBMPToRGBBuffer ( a, x, y );
	BYTE* c = ConvertRGBToBMPBuffer ( b, x, y, &s2 );	
	SaveBMP ( c, x, y, s2, "test2.bmp" );
	delete [] a;
	delete [] b;
	delete [] c;
Now if you have a program that rendered an image of dimension x * y into an RGB buffer you save it to a .bmp file like this:
	long s;
	BYTE* b = ConvertRGBToBMPBuffer ( buffer, x, y, &s );
	SaveBMP ( b, x, y, s, "image.bmp" );
	delete [] b;	
and to load an image e.g. as a texture into an RGB buffer:
	int x, y;
	long s;
	BYTE* a = LoadBMP ( &x, &y, &s, "texture.bmp" );
	BYTE* TexBuf = ConvertBMPToRGBBuffer ( a, x, y );
	delete [] a;



Well i hope this tutorial was useful for you and you gained some insight on windows bitmaps and you forgive me any spelling and grammatical errors. I tested the code quite thoroughly ( spent quite some time with a hexeditor to see if my functions really create the exact same output as Windows Paint and PaintShopPro ) so the code should be correct.
But if you find any errors, or have other comments, critics, or something else ( like the code to tell D3D to use raw bmp data since i only know how to do it in OGL :) ) please mail me.

To make your life easier and so you don't have to copy/paste everything from this page you can get one .cpp file with the whole code (documented) here:
BMP.cpp


Click here to go back to runicsoft.com




Copyright 2002 by Andreas Hartl (A n d y a t r u n i c s o f t d o t c o m )