Full Version: How to make sense of the .bmp format
Dream.In.Code > Programming Tutorials > C++ Tutorials
WolfCoder
I haven't written a tutorial in quite some time, but since I keep seeing people having problems when they try to write and read bitmaps, I have decided to clear up the confusion about it all.

I would read up on using structs in C++ if you do not know how to use them. This is important for this tutorial.

.bmp files (Windows BMP) commonly come in 8-bit or 24-bit formats. Working with 8-bit images can actually be difficult so I will cover 24-bit bitmaps in this tutorial since they are easier to understand (and look nicer too smile.gif ). The first thing you should always do when handling a program that uses Windows is to include the windows.h header.

CODE

#include <windows.h> // This is a must


Now that is out of the way. The structure for the bitmap file is already handled for you! You simply create two variables (one for each structure using the structure itself as the variable type) to hold the format for the .bmp file. For writing an image, you fill them out and write them to the disk along with the image data. For reading an image, you read them into the variables from disk in order. There are two structures that you need to look at when handling Windows bitmap files. Here's the one that comes first when reading the file:

CODE

typedef struct tagBITMAPFILEHEADER {
  WORD    bfType;
  DWORD   bfSize;
  WORD    bfReserved1;
  WORD    bfReserved2;
  DWORD   bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;


Here's a breakdown of the structure:
bfType contains the ID for a .bmp file. This is always equal to the string 'BM'. If not, then it isn't a bitmap file. This can help you check to see if the user accidentally opened some other kind of file.
bfSize this should always be equal to the size of the structure itself. Simply set this to sizeof(BITMAPINFOHEADER).
bfReserved1 bfReserved2 These should always be 0.
bfOffBits This should contain the offset in the file where the image data actually begins. For a 24-bit bitmap, simply set this to sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER). In simpler terms, this contains the size of the header for the bitmap file (whatever is not image data).

And the other structure is this:

CODE

typedef struct tagBITMAPINFOHEADER {
    DWORD  biSize;          // size of structure, in bytes
    DWORD  biWidth;         // bitmap width, in pixels
    DWORD  biHeight;        // bitmap height, in pixels
    WORD   biPlanes;        // see below
    WORD   biBitCount;      // see below
    DWORD  biCompression;   // see below
    DWORD  biSizeImage;     // see below
    DWORD  biXPelsPerMeter; // see below
    DWORD  biYPelsPerMeter; // see below
    DWORD  biClrUsed;       // see below
    DWORD  biClrImportant;  // see below
} BITMAPINFOHEADER;


biSize this should always be sizeof(BITMAPINFOHEADER) just like the other one.
biWidth biHeight this should be the physical size of the bitmap image in pixels. Set them to your heart's content.
biPlanes don't even worry about this one, just set it to 1.
biBitCount for a 24-bit image, set this to 24. For an 8-bit image, set this to 8. (24-bit means a true-color image and an 8-bit is a palletized image).
biCompression since you aren't going to fiddle with compression, set this to BI_RGB (it's a macro in the windows header).
biSizeImage this should be set to the size of the image. Use sizeof(RGBTRIPLE)*width*height for this one if you don't want to use a calculator.
biXPelsPerMeter biYPelsPerMeter this is the resolution setting for printers. It's the number of pixels per meter. Unless you want to calculate stuff, I would just put 2400 for both of them.
biClrUsed biClrImportant these don't matter for a 24-bit image.

Now the image itself is an array of RGBTRIPLES. Sometimes padding is required, but you shouldn't encounter that problem with bitmaps that have a width that is a multiple of 4. For a 128*128 pixel image, you would declare:
CODE

RGBTRIPLE image[128*128];


I'm sure you want a simple example to help you, so look over the following example:

CODE

#include <windows.h>
#include <iostream.h>

HANDLE hfile;
DWORD written;
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
RGBTRIPLE *image;

void()
{
// Open the file
hfile = CreateFile("source.bmp",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,NULL,NULL);
// Read the header
ReadFile(hfile,&bfh,sizeof(bfh),&written,NULL);
ReadFile(hfile,&bih,sizeof(bih),&written,NULL);
// Read image
imagesize = bih.biWidth*bih.biHeight; // Helps you allocate memory for the image
image = new RGBTRIPLE[imagesize]; // Create a new image (I'm creating an array during runtime)
ReadFile(hfile,image,imagesize*sizeof(RGBTRIPLE),&written,NULL); // Reads it off the disk
// Close source file
CloseHandle(hfile);
// Now for some information
cout<<"The image width is "<<bih.biWidth<<"\n"; // Will output the width of the bitmap
cout<<"The image height is "<<bih.biHeight<<"\n"; // Will output the height of the bitmap
}


Running through the following code, you see me open the file, read all the bytes from the beginning of the file into two variables (defined by the structs), and then I make an array during runtime and fill it with the rest of the file.

Now, if you wanted to modify the bitmap and save it back, it gets a little confusing. For some reason (actually it's a long story) the bitmap image is upside down. Don't ask me why, I am not a mathematician. To get and set pixels, you would have to do the following:

CODE

RGBTRIPLE get_pixel(int x,int y)
{
// Image define from earlier
return image[(bih.biHeight-1-y)*bih.biWidth+x];
}
RGBTRIPLE set_pixel(int x,int y,RGBTRIPLE color)
{
// Image define from earlier
image[(bih.biHeight-1-y)*bih.biWidth+x] = color;
}


Now if you take a close look, I take the maximum height and subtract it by 1. For example, you enter 5 as the y value, you would get 5 from the bottom of the image. However, since the image is upside down, it is actually 5 from the top of the image. You can breathe a sigh of relief since it's only like that in the y direction of the bitmap.

So much for just a .bmp file! Fiddle around with the example program and see what you can do. For an exercise to help you understand this format, try modifying the program to alter the bitmap file and save it back to the disk. For some reference material on the .bmp format, visit this link.
sourabh009
hi, i want to know 1 more thing that how to convert a .bmp file (256 color) into grayscale image.


nirvanarupali
Can you also make in .jpg or .gif format? I am more interested in jpg and gif.
jpr0
"For some reason (actually it's a long story) the bitmap image is upside down. Don't ask me why, I am not a mathematician."

This is because matrices are indexed as "top to bottom, left to right", i.e. M(n,m) means nth row, mth column. Mapping this to what you see as a rectangular bitmap image, the index n corresponds to (Y - y), and m to x, where Y is the largest Y value, and (x,y) are cartesian coordinates (the origin chosen at the bottom left hand corner of the bmp, the x-axis points east, and y-axis points north).
calvinthedestroyer
Wow thats allot different from the 3 pages of code it takes to do that in QBasic.
How do you adjust for the different .bmp files? such as 2bit, 4bit, and .bmp's with compression?
Were did you load the pallet? If I remember right 24bit has no pallet just raw data right?
thanks
Andy
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Invision Power Board © 2001-2008 Invision Power Services, Inc.