Materials
File format description can be found in many places (Internet, books). Below there are some examples:
-
http://www.fastgraph.com/help/bmp_header_format.html
local version:bmp_header_format.html
-
http://web.uccs.edu/wbahn/ECE1021/STATIC/REFERENCES/bmpfileformat.htm
local version:bmpfileformat.htm
-
http://local.wasp.uwa.edu.au/~pbourke/dataformats/bmp/
-
http://en.wikipedia.org/wiki/Windows_bitmap
File format description
Introduction
BMP graphic file is one of the simplest graphic format. Using it we can store a wide variety of graphic starting from 1-bit (i.e. blacka and white) to 24-bit (16 777 216 colors). BMP is a lossless format and although theoretically compression can be used it is very rare situation.
General file structure
In BMP format we can distinct at most 4 parts (in order from the start of the file):
- the file header - store most important information about file;
- the image header - store information about picture;
- the color table - store information about colors (not always present);
- the pixel data.
In our description we use traditional definition:
- byte - typ equal to one byte (8 bits),
- word - typ equal to 2 bytes (16 bits),
- dword - typ equal to 4 bytes (32 bits).
Furthermore we take, that order of bytes in word and dword is typical for intel's architecture, i.e. less important byte is placed as first byte from left side (so-called little endian).
The file header (red in our example)
typedef struct
{
word bfType;
dword bfSize;
word bfReserved1;
word bfReserved2;
dword bfOffBits;
}
FileHeader;
- bfType - file's signature; it should be "BM",
- bfSize - size of file expressed in bytes,
- bfReserved1 - reserved field; it should be 0,
- bfReserved2 - see above,
- bfOffBits - number of bytes before picture data.
The image header (blue in our example)
typedef struct
{
dword biSize;
dword biWidth;
dword biHeight;
word biPlanes;
word biBitCount;
dword biCompression;
dword biSizeImage;
dword biXPelsPerMeter;
dword biYPelsPerMeter;
dword biClrUsed;
dword biClrImportant;
}
PictureHeader;
- biSize - size of PictureHeader,
- biWidth - picture width in pixels,
- biHeight - picture height in pixels,
- biPlanes - number of plans (layers), it should be equal to 1,
- biBitCount - number of bits per pixel (1,2,4,6,8,24,32),
- biCompression - kind of compression (0 oznacza brak kompresji),
- biSizeImage - size of picture; it can be equal to 0 for noncomressed pictures,
- biXPelsPerMeter - resolution expressed in pixels per meter for OX axis;
- biYPelsPerMeter - resolution expressed in pixels per meter for OY axis;
- biClrUsed - number of realy used colors,
- biClrImportant - number of important colors.
The color table
The pixel data (black in our example)
Number of bytes for each row of picture has to be divisible by 4. If not, at the end of each row it should be added bytes of value 0, so the modified row can be divisible by 4.
Pixel's data are stored from left to right and from bottom to the top of the picture.
At the end let's notice that components of each color are stored in oposite order, so if in our file there is ff 00 00 it means 00 00 ff.
Example 1
Let's try to analyse very simple 24-bits picture (it means that for each pixel we have to use 24 bits to describe color):

(picture which can be seen on this web page is 6 times grater than oryginal: from 5x5 to 30x30).
There is our file presented as hexadecimal numbers (two hexadecimal digits represents one byte):
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
0: 42 4d 86 00 00 00 00 00 00 00 36 00 00 00 28 00
1: 00 00 05 00 00 00 05 00 00 00 01 00 18 00 00 00
2: 00 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00
3: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
4: 00 00 00 00 00 00 00 00 00 00 ff ff ff ff ff 00
5: ff 00 00 00 00 00 00 00 00 ff ff ff ff ff ff ff
6: ff ff 00 00 00 00 00 00 00 00 00 ff ff ff ff ff
7: ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
8: 00 00 00 00 00 00
Now let's try to decode this file ( notation (i,j) means i-row and j-column byte)
(0,0)-(0,1): bfType field, content: 42 4d what is ASCII code for BM string.
(0,2)-(0,5): bfSize field, content: 86 00 00 00 what is equal to decimal value 134 which is equal to the size of the file.
(0,6)-(0,7): bfReserved1 field, content: 00 00 what is equal to decimal value 0.
(0,8)-(0,9): bfReserved2 field, content: 00 00 what is equal to decimal value 0.
(0,10)-(0,13): bfOffBits field, content: 36 00 00 00 what is equal to decimal value 54, so part with pixels data should start at byte (3,6).
(0,14)-(1,1): biSize field, content: 28 00 00 00 what is equal to decimal value 40, so length of this header is 40 bytes
and from byte (3,6) should start the color table if present or pixels data (compare with bfOffBits field).
(1,2)-(1,5): biWidth field, content: 05 00 00 00 what is equal to decimal value 5, so width of picture is equal to 5.
(1,6)-(1,9): biHeight field, content: 05 00 00 00 what is equal to decimal value 5, so hwight of picture is equal to 5.
(1,10)-(1,11): biPlanes field, content: 01 00 what is equal to decimal value 1, and that value agree with specification.
(1,12)-(1,13): biBitCount field, content: 18 00 what is equal to decimal value 24, so in our file there is no a color table.
(1,14)-(2,1): biCompression field, content: 00 00 00 00 what is equal to decimal value 0, so our file is not compressed.
(2,2)-(2,5): biSizeImage field, content: 50 00 00 00 what is equal to decimal value 80, so our picture should be described by 80 bytes
(consider any necessary padding/filling to make each row divisible by 4)
(2,6)-(2,9): biXPelsPerMeter field, content: 00 00 00 00 what is equal to decimal value 0.
(2,10)-(2,13): biYPelsPerMeter field, content: 00 00 00 00 what is equal to decimal value 0.
(2,14)-(3,1): biClrUsed field, content: 00 00 00 00 what is equal to decimal value 0.
(3,2)-(3,5): biClrImportant field, content: 00 00 00 00 what is equal to decimal value 0.
Zero value in two last fields is quite common situation. It is because information in that fields is onle auxiliary for systems
which can't operate whole palette of colors. In such cases it should help to choose mor important colors. Zero value means taht all colors are important.
Being in accordance with above text, now should start pixels data (in 24-bits we don't have a color table).
So, we have 24-bits picture (it means 3 bytes for pixel). We have 5 pixel in one line and it gives us 5x3=15 bytes.
That number we have to increase to such a number which can be divisible by 4 - in our case 1 byte is enough, because it gives us 16 bytes per line.
This is why rest of our data we can divide into 16 bytes blocks. We obtain 5 such a blocks (from (2,2) to (2,5))
what is correct with number 80 taken form biSizeImage field.
Having in mind that pixels are stored from bottom-left corner, let's try to decode pixels' color
(we start numbering pixels from 0 in top-left corner)
(3,6)-(3,8): field content: 00 00 00 = RGB(0,0,0) = black, pixel (4,0)
(3,9)-(3,11): field content: 00 00 00 = RGB(0,0,0) = black, pixel (4,1)
(3,12)-(3,14): field content: 00 00 00 = RGB(0,0,0) = black, pixel (4,2)
(3,15)-(4,1): field content: 00 00 00 = RGB(0,0,0) = black, pixel (4,3)
(4,2)-(4,4): field content: 00 00 00 = RGB(0,0,0) = black, pixel (4,4)
(4,5): field content: 00 - padding/filling
(4,6)-(4,8): field content: 00 00 00 = RGB(0,0,0) = black, pixel (3,0)
(4,9)-(4,11): field content: 00 ff ff = RGB(255,255,0) = yellow, pixel (3,1)
(4,12)-(4,14): field content: ff ff ff = RGB(255,255,255) = white, pixel (3,2)
(4,15)-(5,1): field content: 00 ff 00 = RGB(0,255,0) = green, pixel (3,3)
(5,2)-(5,4): field content: 00 00 00 = RGB(0,0,0) = black, pixel (3,4)
(5,5): field content: 00 - padding/filling
(5,6)-(5,8): field content: 00 00 00 = RGB(0,0,0) = black, pixel (2,0)
(5,9)-(5,11): field content: ff ff ff = RGB(255,255,255) = white, pixel (2,1)
(5,12)-(5,14): field content: ff ff ff = RGB(255,255,255) = white, pixel (2,2)
(5,15)-(6,1): field content: ff ff ff = RGB(255,255,255) = white, pixel (2,3)
(6,2)-(6,4): field content: 00 00 00 = RGB(0,0,0) = black, pixel (2,4)
(6,5): field content: 00 - padding/filling
(6,6)-(6,8): field content: 00 00 00 = RGB(0,0,0) = black, pixel (1,0)
(6,9)-(6,11): field content: 00 00 ff = RGB(255,0,0) = red, pixel (1,1)
(6,12)-(6,14): field content: ff ff ff = RGB(255,255,255) = white, pixel (1,2)
(6,15)-(7,1): field content: ff ff 00 = RGB(0,255,255) = light blue, pixel (1,3)
(7,2)-(7,4): field content: 00 00 00 = RGB(0,0,0) = black, pixel (1,4)
(7,5): field content: 00 - padding/filling
(7,6)-(7,8): field content: 00 00 00 = RGB(0,0,0) = black, pixel (0,0)
(7,9)-(7,11): field content: 00 00 00 = RGB(0,0,0) = black, pixel (0,1)
(7,12)-(7,14): field content: 00 00 00 = RGB(0,0,0) = black, pixel (0,2)
(7,15)-(8,2): field content: 00 00 00 = RGB(0,0,0) = black, pixel (0,3)
(8,2)-(8,4): field content: 00 00 00 = RGB(0,0,0) = black, pixel (0,4)
(8,5): field content: 00 - padding/filling
Example 2
Now let's examine the same picture but stored as 8-bits bitmap file:

. There is our file presented as hexadecimal numbers (two hexadecimal digits represents one byte):
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
0:42 4d 5e 04 00 00 00 00 00 00 36 04 00 00 28 00
1:00 00 05 00 00 00 05 00 00 00 01 00 08 00 00 00
2:00 00 28 00 00 00 00 00 00 00 00 00 00 00 00 00
3:00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80
4:00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80
5:00 00 c0 c0 c0 00 c0 dc c0 00 f0 ca a6 00 00 20
6:40 00 00 20 60 00 00 20 80 00 00 20 a0 00 00 20
7:c0 00 00 20 e0 00 00 40 00 00 00 40 20 00 00 40
8:40 00 00 40 60 00 00 40 80 00 00 40 a0 00 00 40
9:c0 00 00 40 e0 00 00 60 00 00 00 60 20 00 00 60
10:40 00 00 60 60 00 00 60 80 00 00 60 a0 00 00 60
11:c0 00 00 60 e0 00 00 80 00 00 00 80 20 00 00 80
12:40 00 00 80 60 00 00 80 80 00 00 80 a0 00 00 80
13:c0 00 00 80 e0 00 00 a0 00 00 00 a0 20 00 00 a0
14:40 00 00 a0 60 00 00 a0 80 00 00 a0 a0 00 00 a0
15:c0 00 00 a0 e0 00 00 c0 00 00 00 c0 20 00 00 c0
16:40 00 00 c0 60 00 00 c0 80 00 00 c0 a0 00 00 c0
17:c0 00 00 c0 e0 00 00 e0 00 00 00 e0 20 00 00 e0
18:40 00 00 e0 60 00 00 e0 80 00 00 e0 a0 00 00 e0
19:c0 00 00 e0 e0 00 40 00 00 00 40 00 20 00 40 00
20:40 00 40 00 60 00 40 00 80 00 40 00 a0 00 40 00
21:c0 00 40 00 e0 00 40 20 00 00 40 20 20 00 40 20
22:40 00 40 20 60 00 40 20 80 00 40 20 a0 00 40 20
23:c0 00 40 20 e0 00 40 40 00 00 40 40 20 00 40 40
24:40 00 40 40 60 00 40 40 80 00 40 40 a0 00 40 40
25:c0 00 40 40 e0 00 40 60 00 00 40 60 20 00 40 60
26:40 00 40 60 60 00 40 60 80 00 40 60 a0 00 40 60
27:c0 00 40 60 e0 00 40 80 00 00 40 80 20 00 40 80
28:40 00 40 80 60 00 40 80 80 00 40 80 a0 00 40 80
29:c0 00 40 80 e0 00 40 a0 00 00 40 a0 20 00 40 a0
30:40 00 40 a0 60 00 40 a0 80 00 40 a0 a0 00 40 a0
31:c0 00 40 a0 e0 00 40 c0 00 00 40 c0 20 00 40 c0
32:40 00 40 c0 60 00 40 c0 80 00 40 c0 a0 00 40 c0
33:c0 00 40 c0 e0 00 40 e0 00 00 40 e0 20 00 40 e0
34:40 00 40 e0 60 00 40 e0 80 00 40 e0 a0 00 40 e0
35:c0 00 40 e0 e0 00 80 00 00 00 80 00 20 00 80 00
36:40 00 80 00 60 00 80 00 80 00 80 00 a0 00 80 00
37:c0 00 80 00 e0 00 80 20 00 00 80 20 20 00 80 20
38:40 00 80 20 60 00 80 20 80 00 80 20 a0 00 80 20
39:c0 00 80 20 e0 00 80 40 00 00 80 40 20 00 80 40
40:40 00 80 40 60 00 80 40 80 00 80 40 a0 00 80 40
41:c0 00 80 40 e0 00 80 60 00 00 80 60 20 00 80 60
42:40 00 80 60 60 00 80 60 80 00 80 60 a0 00 80 60
43:c0 00 80 60 e0 00 80 80 00 00 80 80 20 00 80 80
44:40 00 80 80 60 00 80 80 80 00 80 80 a0 00 80 80
45:c0 00 80 80 e0 00 80 a0 00 00 80 a0 20 00 80 a0
46:40 00 80 a0 60 00 80 a0 80 00 80 a0 a0 00 80 a0
47:c0 00 80 a0 e0 00 80 c0 00 00 80 c0 20 00 80 c0
48:40 00 80 c0 60 00 80 c0 80 00 80 c0 a0 00 80 c0
49:c0 00 80 c0 e0 00 80 e0 00 00 80 e0 20 00 80 e0
50:40 00 80 e0 60 00 80 e0 80 00 80 e0 a0 00 80 e0
51:c0 00 80 e0 e0 00 c0 00 00 00 c0 00 20 00 c0 00
52:40 00 c0 00 60 00 c0 00 80 00 c0 00 a0 00 c0 00
53:c0 00 c0 00 e0 00 c0 20 00 00 c0 20 20 00 c0 20
54:40 00 c0 20 60 00 c0 20 80 00 c0 20 a0 00 c0 20
55:c0 00 c0 20 e0 00 c0 40 00 00 c0 40 20 00 c0 40
56:40 00 c0 40 60 00 c0 40 80 00 c0 40 a0 00 c0 40
57:c0 00 c0 40 e0 00 c0 60 00 00 c0 60 20 00 c0 60
58:40 00 c0 60 60 00 c0 60 80 00 c0 60 a0 00 c0 60
59:c0 00 c0 60 e0 00 c0 80 00 00 c0 80 20 00 c0 80
60:40 00 c0 80 60 00 c0 80 80 00 c0 80 a0 00 c0 80
61:c0 00 c0 80 e0 00 c0 a0 00 00 c0 a0 20 00 c0 a0
62:40 00 c0 a0 60 00 c0 a0 80 00 c0 a0 a0 00 c0 a0
63:c0 00 c0 a0 e0 00 c0 c0 00 00 c0 c0 20 00 c0 c0
64:40 00 c0 c0 60 00 c0 c0 80 00 c0 c0 a0 00 f0 fb
65:ff 00 a4 a0 a0 00 80 80 80 00 00 00 ff 00 00 ff
66:00 00 00 ff ff 00 ff 00 00 00 ff 00 ff 00 ff ff
67:00 00 ff ff ff 00 00 00 00 00 00 00 00 00 00 fb
68:ff fa 00 00 00 00 00 ff ff ff 00 00 00 00 00 f9
69:ff fe 00 00 00 00 00 00 00 00 00 00 00 00
Przykład 3
Now let's examine the same picture but stored as 4-bits bitmap file:

. There is our file presented as hexadecimal numbers (two hexadecimal digits represents one byte):
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
0: 42 4d 8a 00 00 00 00 00 00 00 76 00 00 00 28 00
1: 00 00 05 00 00 00 05 00 00 00 01 00 04 00 00 00
2: 00 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00
3: 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80
4: 00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80
5: 00 00 80 80 80 00 c0 c0 c0 00 00 00 ff 00 00 ff
6: 00 00 00 ff ff 00 ff 00 00 00 ff 00 ff 00 ff ff
7: 00 00 ff ff ff 00 00 00 00 00 0b fa 00 00 0f ff
8: 00 00 09 fe 00 00 00 00 00 00