BMP(全稱Bitmap)是Windows操作系統中的標準圖像文件格式,可以分成兩類:設備相關位圖(DDB)和設備無關位圖(DIB),使用非常廣。它采用位映射存儲格式,除了圖像深度可選以外,不采用其他任何壓縮,因此,BMP文件所占用的空間很大。BMP文件的圖像深度可選lbit、4bit、8bit及24bit。BMP文件存儲數據時,圖像的掃描方式是按從左到右、從下到上的順序。由于BMP文件格式是Windows環境中交換與圖有關的數據的一種標準,因此在Windows環境中運行的圖形圖像軟件都支持BMP圖像格式。
這里通過一個具體的例子對BMP格式做一個簡單的介紹。
1、整體信息
BMP格式的文件從頭到尾依次是如下信息:
- bmp文件頭(bmp file header):共14字節;
- 位圖信息頭(bitmap information):共40字節;
- 調色板(color palette):可選;
- 位圖數據;
最常見的就是24位圖,所謂的24位圖,就是說一個像素的顏色信息用24位來表示,也就是說,對于三原色BRG,每一個顏色都用以字節(8)位來表示。除了24位圖,還有1位(單色),2位(4色,CGA),4位(16色,VGA),8位(256色),16位(增強色),24位(真彩色)和32位等。
下面通過下面的圖片做詳細介紹:
? 圖像的部分信息如下:
2、bmp文件頭(bmp file header)
bmp文件頭包含如下信息:
- bfType:2字節,文件類型;
- bfSize:4字節,文件大小;
- bfReserved1:2字節,保留,必須設置為0;
- bfReserved2:2字節,保留,必須設置為0;
- bfOffBits:4字節,從頭到位圖數據的偏移;
下圖的數據就是bmp文件頭:
一共14字節,下面逐個解釋。
0-1:bfType,表示文件類型,BMP格式的文件這兩個字節是0x4D42,10進制就是19778,字符顯示就是‘BM’;
2-5:bfSize,表示文件的大小,這里的是0x0004B436,十進制是308278,也就是301kb,檢查文件信息,驗證正確;
6-7:bfReserved1,保留位,必須設置為0;
8-9:bfReserved2,保留位,必須設置為0;
a-d:bfOffBits,4字節的偏移,表示從文件頭到位圖數據的偏移,這里是0x00000436,十進制是1078,后面會做驗證;
3、位圖信息頭(bitmap information)
位圖信息頭一共40字節,包含如下內容:
- biSize:4字節,信息頭的大小,即40;
- biWidth:4字節,以像素為單位說明圖像的寬度;
- biHeight:4字節,以像素為單位說明圖像的高度,同時如果為正,說明位圖倒立(即數據表示從圖像的左下角到右上角),如果為負說明正向;
- biPlanes:2字節,為目標設備說明顏色平面數,總被設置為1;
- biBitCount:2字節,說明比特數/像素數,值有1、2、4、8、16、24、32;
- biCompression:4字節,說明圖像的壓縮類型,最常用的就是0(BI_RGB),表示不壓縮;
- biSizeImages:4字節,說明位圖數據的大小,當用BI_RGB格式時,可以設置為0;
- biXPelsPerMeter:表示水平分辨率,單位是像素/米,有符號整數;
- biYPelsPerMeter:表示垂直分辨率,單位是像素/米,有符號整數;
- biClrUsed:說明位圖使用的調色板中的顏色索引數,為0說明使用所有;
- biClrImportant:說明對圖像顯示有重要影響的顏色索引數,為0說明都重要;
下圖數據是位圖信息頭:
一共40字節,解釋如下:
0e-11:4字節的biSize,這里是0x28,即十進制的40,驗證正確;
12-15:4字節的biWidth,這里是0x00000280,即十進制的640,用像素表示圖像的寬度,查看文件信息驗證正確;
16-19:4字節的biHeight,這里是0x000001E0,即十進制的480,用像素表示圖像的高度,查看文件信息驗證正確;同時,這是一個正數,表示圖像是倒立的,即圖像數據是從左下角到右上角排列的;
1a-1b:2字節的biPlanes,值為0x0001;
1c-1d:2字節的biBitCount,值是0x0008,即8,表示每個像素用8位表示,一共有256個顏色;
1e-21:4字節的biCompression,值是0,即BI_RGB格式,不壓縮;
22-25:4字節的biSizeImage,圖像的大小,值是0x0004B000,十進制為307200,由上面的bfSize(文件大小)和bfOffBits(文件頭到數據的偏移)分別是308278和1078可以得到,biSizeImage=bfSize-bfOffBits,即圖像大小=文件大小-偏移量;
26-29:4字節的biXPelsPerMeter,水平分辨率,值是0x00000EC4,十進制3780;
2a-2d:4字節的biYPelsPerMeter,垂直分辨率,值是0x00000EC4,十進制3780;
2e-31:4字節的biClrUsed,使用的顏色索引數,值是0x00000100,十進制256,與1c-1d得到的結論一致;
32-35:4字節的biClrImportant,重要的顏色索引數,值是0x00000100,十進制256;
4、調色板(Color Palette)
調色板是可選的,不過這里的8位色圖有調色板。那么接下來的數據就是調色板了。調色板就是一個顏色的索引,這里是8位色圖,一共有256中顏色,由于每個顏色都有RGB三原色,也就是要3個字節表示,這樣的話256個顏色就不能表示所有的顏色,所以就需要一個索引,用一個字節的索引指向4個字節表示的顏色(RGB加上Alpha值)。如果把這4個字節表示為一個Color類型,那么調色板就是Color的數組。由于Color類型也是一個數組,調色板就像一個二維數組palette[N][4],其中N是顏色的數量,這里就是256。因此,這個例子中的調色板的大小就是256x4=1024字節,在調色板之前,有14字節的bmp文件頭,40字節的位圖信息頭,加上1024字節的調色板,一共1078字節,也就是說真正的圖像數據前面有1078字節,這和bmp文件頭中的bfOffBits相符,驗證了我們的討論。
有的圖像沒有調色板,比如下面的24位色圖:
頭部數據如下:
根據上面的討論可以知道,biBitCount是24(0x18),bfOffBits是54(0x36),即沒有調色板,位圖信息頭接下來就是圖像數據了。
調色板中的數據每4字節一組,分別表示藍、綠、紅和Alpha值。按照第一個圖像舉例來說:
索引 | 藍 | 綠 | 紅 | Alpha |
0 | 01 | 10 | 37 | 00 |
1 | 00 | 10 | 49 | 00 |
2 | 00 | 18 | 44 | 00 |
3 | 01 | 1D | 58 | 00 |
接下來就是位圖數據了。由于是8位色圖,所以每個像素用1個字節表示,取出每個字節,顯示到相應的設備上就可以了。
注意,這里的biHeight為正數,說明圖像倒立,從左下角開始到右上角,以行為主序排列。
如果是24位色圖,按照BGR的順序排列,32位色圖按照BGRAlpha排列。
位圖數據排列還有一個規則,就是對齊。
Windows默認的掃描的最小單位是4字節,如果數據對齊滿足這個值的話對于數據的獲取速度等都是有很大的增益的。因此,BMP圖像順應了這個要求,要求每行的數據的長度必須是4的倍數,如果不夠需要進行比特填充(以0填充),這樣可以達到按行的快速存取。這樣的話,位圖數據的大小就不一定是寬x高x每像素字節數了,因為每行還可能有0填充。
填充后的每行數據如下:
其中,BPP是每像素的比特數(Bits Per Pixel),即biBitCount,Width是寬度,單位是像素即bfWidth。
對于我們這個例子,BPP是8,Width是480,正好是4的倍數,也就是沒有填充。來計算一下:
RowSize=4*(8*480/32)=480字節,驗證沒有填充。
那么以上面第二個圖片24位色圖為例,按照數據可以得到:
- biBitCount=0x0018=24;
- bfWidth=0x000001c6=454;
- bfHeight=0x00000053=83;
- biSizeImage=0x0001BA3c=113212;
按照沒填充計算:454*83*3=113046 bytes,與真實值相差166字節。
按照填充公式,每行有數據4*(24*454/32)=1364 字節,真正的數據有454*3=1362字節,也就是說每行填充了2字節0,一共83行,共填充83*2=166字節,驗證了我們的討論。
在程序中,我們可以用下面的代碼計算每行的數據:
int bytesPerLine=((bfWidth*biBitCount+31)>>5)<<2;
那么,位圖數據大小為:
int imageSize=bytesPerLine*bfHeight;
這樣的話,每掃描完一行數據,最后的幾個字節可能是填充的0,需要跳過:
int skip=4-((bfWidth*biBitCount)>>3)&3;