Description of bmp graphic file

Materials

File format description can be found in many places (Internet, books). Below there are some examples:

  1. http://www.fastgraph.com/help/bmp_header_format.html
    local version:bmp_header_format.html
  2. http://web.uccs.edu/wbahn/ECE1021/STATIC/REFERENCES/bmpfileformat.htm
    local version:bmpfileformat.htm
  3. http://local.wasp.uwa.edu.au/~pbourke/dataformats/bmp/
  4. 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):
  1. the file header - store most important information about file;
  2. the image header - store information about picture;
  3. the color table - store information about colors (not always present);
  4. the pixel data.
In our description we use traditional definition: 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;

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;

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