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
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.