Materiały
Opis formatu pliku można znaleźć bez problemu w sieci. Poniżej podaję kilka przykładowych adresów, które znalazłem
-
http://www.fastgraph.com/help/bmp_header_format.html
wersja lokalna:bmp_header_format.html
-
http://web.uccs.edu/wbahn/ECE1021/STATIC/REFERENCES/bmpfileformat.htm
wersja lokalna:bmpfileformat.htm
-
http://local.wasp.uwa.edu.au/~pbourke/dataformats/bmp/
-
http://en.wikipedia.org/wiki/Windows_bitmap
Opis formatu pliku
Wprowadzenie
Pliki BMP są jednymi z prostszych plików służących do zapisu obrazów graficznych. Wykorzystywane mogą być do przechowywania szerokiej gamy obrazów poczynając od 1-bitowych (czyli czarno-białych) na 24-bitowych (czyli zawir
erających 16 777 216 kolorów) kończąc. Jest to bezstratny format zapisu danych i choć teoretycznie możliwy jest zapis obrazu poddanego kompresji to zwykle się tego nie stosuje.
Ogólna struktura pliku
W pliku BMP możemy wyróżnić maksymalnie 4 części choć nie zawsze wszystkie występują. I tak w kolejności ich występowania są to
- nagłówek pliku - przechowuje najważniejsze informacje związane z plikiem jako takim;
- nagłówek obrazu - przechowuje informacje o obrazie;
- paleta kolorów - definiuje wykorzystane kolory (nie zawsze występuje);
- dane obrazu.
W opisie przyjmujemy następujące, tradycyjnie używane, definicje
- byte - typ o wielkości równej 1 bajtowi (8 bitów),
- word - typ o wielkości równej 2 bajtom (16 bitów),
- dword - typ o wielkości równej 4 bajtom (32 bitów).
Ponadto zakładamy, że kolejność bajtów w zmiennej word i dword jest kolejnością typową dla architektury intelowskiej, tj. mniej znaczący bajt umieszczony jest jako pierwszy bajt z lewej strony (tzw. little endian).
Nagłówek pliku (czerwony w poniższym przykładzie)
typedef struct
{
word bfType;
dword bfSize;
word bfReserved1;
word bfReserved2;
dword bfOffBits;
}
FileHeader;
- bfType - sygnatura pliku; powinno to być "BM",
- bfSize - rozmiar pliku wyrażony w bajtach,
- bfReserved1 - pole zarezerwowane, jego wartość powinna wynosić 0,
- bfReserved2 - j.w.,
- bfOffBits - ilość bajtów poprzedzających dane o obrazie.
Nagłówek obrazu (niebieski w poniższym przykładzie)
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 - rozmiar nagłówka PictureHeader,
- biWidth - szerokość obrazka w pixelach,
- biHeight - wysokość obrazka w pixelach,
- biPlanes - ilość planów (warstw), musi mieć wartość 1,
- biBitCount - ilość bitów opisujących pixel (1,2,4,6,8,24,32),
- biCompression - rodzaj kompresji (0 oznacza brak kompresji),
- biSizeImage - rozmiar obrazka; może mieć wartość równą 0 dla nieskompresowanych obrazów,
- biXPelsPerMeter - rozdzielczość w pixelach na metr dla osi OX;
- biYPelsPerMeter - analogocznie j.w. dla osi OY,
- biClrUsed - ilość kolorów która faktycznie została użyta,
- biClrImportant - ilość kolorów znaczących.
Paleta kolorów
Dane obrazu (czarny w poniższym przykładzie)
Ilość bajtów opisujących wiersz obrazu musi być podzielna przez 4. Jeśli tak nie jest to na koniec każdego wiersza dodawane są bajty o wartości 0 w takiej ilości aby tak zmieniona ilość bajtów w wierszu była podzielna przez 4.
Dane o pixelach zapisywane są od lewej do prawej i od dołu do góry obrazka.
Zauważmy jeszcze że składowe kolorów reprezentowane są ,,od końca'', czyli jeśli w pliku mamy ff 00 00 to oznacza to kolor 00 00 ff.
Przykład 1
Przeanalizujmy bardzo prosty obrazek 24-bitowy (czyli na zapisanie koloru jednego pixela żywać będziemy 24 bitów):

(obrazek widoczny na tej stronie jest powiększony 6 razy: z 5x5 do 30x30).
Oto zawartość pliku przedstawiona w postaci liczb szesnastkowych (przypominam, że dwie cyfry szesnastkowe reprezentują jeden bajt):
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
A teraz spróbujmy odczytać zawartość obrazka ( zapis (i,j) oznacza bajt leżący w i-tym wierszu i j-tej kolumnie)
(0,0)-(0,1): pole bfType, zawartość pola: 42 4d co oznacza odpowiednio BM.(2,2)-(2,5): pole biSizeImage
(0,2)-(0,5): pole bfSize, zawartość pola: 86 00 00 00 co daje dziesiętną wartość liczbową 134 która jest równa wielkości pliku.
(0,6)-(0,7): pole bfReserved1, zawartość pola: 00 00 co daje dziesiętną wartość liczbową 0.
(0,8)-(0,9): pole bfReserved2, zawartość pola: 00 00 co daje dziesiętną wartość liczbową 0.
(0,10)-(0,13): pole bfOffBits, zawartość pola: 36 00 00 00 co daje dziesiętną wartość liczbową 54 a zatem blok
z danymi obrazka powinien zacząć się od bajtu (3,6).
(0,14)-(1,1): pole biSize, zawartość pola: 28 00 00 00 co daje dziesiętną wartość liczbową 40 a zatem dlugosc tego naglowka wynosi 40 bajtow.
czyli od pola (3,6) powinna zacząć się paleta kolorów jeśli będzie występować lub dane obrazka (porównaj z wartością wyliczoną dla pola bfOffBits).
(1,2)-(1,5): pole biWidth, zawartość pola: 05 00 00 00 co daje dziesiętną wartość liczbową 5 a zatem szerokość bitmapy wynosi 5 pixeli.
(1,6)-(1,9): pole biHeight, zawartość pola: 05 00 00 00 co daje dziesiętną wartość liczbową 5 a zatem wysokość bitmapy wynosi 5 pixeli.
(1,10)-(1,11): pole biPlanes, zawartość pola: 01 00 co daje dziesiętną wartość liczbową 1, która zgodna jest z podaną powyżej specyfikacją
(1,12)-(1,13): pole biBitCount, zawartość pola: 18 00 co daje dziesiętną wartość liczbową 24 a zatem w pliku nie będzie przechowywana paleta kolorów.
(1,14)-(2,1): pole biCompression, zawartość pola: 00 00 00 00 co daje dziesiętną wartość liczbową 0 a zatem jest to plik nieskompresowany.
(2,2)-(2,5): pole biSizeImage, zawartość pola: 50 00 00 00 co daje dziesiętną wartość liczbową 80
a zatem obrazek powinien być opisany przez 80 bajtów (uwzględniając ewentualne dopełnienia wynikające z konieczności zapewnienia, iż ilość bajtów w linii podzielna jest przez 4)
(2,6)-(2,9): pole biXPelsPerMeter, zawartość pola: 00 00 00 00 co daje dziesiętną wartość liczbową 0.
(2,10)-(2,13): pole biYPelsPerMeter, zawartość pola: 00 00 00 00 co daje dziesiętną wartość liczbową 0.
(2,14)-(3,1): pole biClrUsed, zawartość pola: 00 00 00 00 co daje dziesiętną wartość liczbową 0.
(3,2)-(3,5): pole biClrImportant, zawartość pola: 00 00 00 00 co daje dziesiętną wartość liczbową 0.
Wartość 0 w ostatnich dwóch polach nie powinna dziwić. Jest to tylko dodatkowa informacja dla systemów, które nie mogą operować całą gamą kolorów. Powinna wówczas pomóc w wyborze ,,ważniejszych'' kolorów. Wartość 0 w tych polach zwykle rozumiana jest w ten sposób, że wszystkie kolory są istotne.
Zgodnie z tym co policzono powyżej od tego pola zaczynają się dane obrazka. Palety kolorów nie ma gdyż jest to obrazek 24-bitowy.
Zgodnie z tym co wyliczono powyżej mamy 24-bitowy obrazek (zatem 3 bajty na pixel). Pixeli w linii jest 5, co łącznie daje 5x3=15 bajtów.
Liczbę tą musimy dopełnić jeszcze tyloma bajtami aby uzyskać liczbę podzielną przez 4 - w tym wypadku wystarczy 1 bajt co łącznie daje 16 bajtów na linie.
Dlatego bajty jakie nam pozostały podzielone zostały na bloki po 16. Łącznie wyszło 5 pełnych bloków co zgodne jest z ilością wierszy w naszym obrazku. Zgodne jest to także z liczbą 80 zapisaną w polu biSizeImage (od (2,2) do (2,5)).
Pamiętając o tym, że dane zapisywane są od lewego dolnego rogu spróbujemy teraz utworzyć obrazek
(przyjmujemy numeracje pixeli zaczynającą się od 0 w lewym górnym rogu)
(3,6)-(3,8): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (4,0)
(3,9)-(3,11): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (4,1)
(3,12)-(3,14): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (4,2)
(3,15)-(4,1): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (4,3)
(4,2)-(4,4): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (4,4)
(4,5): zawartość pola: 00 - dopełnienie
(4,6)-(4,8): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (3,0)
(4,9)-(4,11): zawartość pola: 00 ff ff = RGB(255,255,0) = zółty, pixel (3,1)
(4,12)-(4,14): zawartość pola: ff ff ff = RGB(255,255,255) = biały, pixel (3,2)
(4,15)-(5,1): zawartość pola: 00 ff 00 = RGB(0,255,0) = zielony, pixel (3,3)
(5,2)-(5,4): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (3,4)
(5,5): zawartość pola: 00 - dopełnienie
(5,6)-(5,8): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (2,0)
(5,9)-(5,11): zawartość pola: ff ff ff = RGB(255,255,255) = biały, pixel (2,1)
(5,12)-(5,14): zawartość pola: ff ff ff = RGB(255,255,255) = biały, pixel (2,2)
(5,15)-(6,1): zawartość pola: ff ff ff = RGB(255,255,255) = biały, pixel (2,3)
(6,2)-(6,4): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (2,4)
(6,5): zawartość pola: 00 - dopełnienie
(6,6)-(6,8): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (1,0)
(6,9)-(6,11): zawartość pola: 00 00 ff = RGB(255,0,0) = czerwony, pixel (1,1)
(6,12)-(6,14): zawartość pola: ff ff ff = RGB(255,255,255) = biały, pixel (1,2)
(6,15)-(7,1): zawartość pola: ff ff 00 = RGB(0,255,255) = jasny niebieski, pixel (1,3)
(7,2)-(7,4): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (1,4)
(7,5): zawartość pola: 00 - dopełnienie
(7,6)-(7,8): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (0,0)
(7,9)-(7,11): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (0,1)
(7,12)-(7,14): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (0,2)
(7,15)-(8,2): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (0,3)
(8,2)-(8,4): zawartość pola: 00 00 00 = RGB(0,0,0) = czarny, pixel (0,4)
(8,5): zawartość pola: 00 - dopełnienie
Przykład 2
Przeanalizujmy teraz ten sam obrazek ale wykorzystujący 8-bitową definicję koloru:

Oto zawartość pliku przedstawiona w postaci liczb szesnastkowych:
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
Przeanalizujmy jeszcze raz ten sam obrazek, ale wykorzystując 4-bitową definicję koloru:

Oto zawartość pliku przedstawiona w postaci liczb szesnastkowych:
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