bmp(全稱bitmap)是window作業系統中的标準圖像檔案格式,可以分成兩類:裝置相關位圖(ddb)和裝置無關位圖(dib),使用非常廣。它采用位映射存儲格式,除了圖像深度可選以外,不采用其他任何壓縮,是以,bmp檔案所占用的空間很大。bmp檔案的圖像深度可選lbit、4bit、8bit及24bit。bmp檔案存儲資料時,圖像的掃描方式是按從左到右、從下到上的順序。由于bmp檔案格式是windows環境中交換與圖有關的資料的一種标準,是以在windows環境中運作的圖形圖像軟體都支援bmp圖像格式。
目錄 []
典型的bmp圖像檔案由四部分組成:
位圖頭檔案資料結構,它包含bmp圖像檔案的類型、顯示内容等資訊;
位圖資訊資料結構,它包含有bmp圖像的寬、高、壓縮方法,以及定義顔色等資訊;
調色闆,這個部分是可選的,有些位圖需要調色闆,有些位圖,比如真彩色圖(24位的bmp)就不需要調色闆;
位圖資料,這部分的内容根據bmp位圖使用的位數不同而不同,在24位圖中直接使用rgb,而其他的小于24位的使用調色闆中顔色索引值。
位圖一共有兩種類型,即:裝置相關位圖(ddb)和裝置無關位圖(dib)。ddb位圖在早期的windows系統(windows
3.0以前)中是很普遍的,事實上它也是唯一的。然而,随着顯示器制造技術的進步,以及顯示裝置的多樣化,ddb位圖的一些固有的問題開始浮現出來了。比如,它不能夠存儲(或者說擷取)建立這張圖檔的原始裝置的分辨率,這樣,應用程式就不能快速的判斷客戶機的顯示裝置是否适合顯示這張圖檔。為了解決這一難題,微軟建立了dib位圖格式。
裝置無關位圖
(device-independent bitmap)
dib位圖包含下列的顔色和尺寸資訊:
原始裝置(即建立圖檔的裝置)的顔色格式。
原始裝置的分辨率。
原始裝置的調色闆
一個位數組,由紅、綠、藍(rgb)三個值代表一個像素。
一個數組壓縮标志,用于表明資料的壓縮方案(如果需要的話)。
以上這些資訊儲存在bitmapinfo結構中,該結構由bitmapinfoheader結構和兩個或更多個rgbquad結構所組成。bitmapinfoheader結構所包含的成員表明了圖像的尺寸、原始裝置的顔色格式、以及資料壓縮方案等資訊。rgbquad結構辨別了像素所用到的顔色資料。
dib位圖也有兩種形式,即:底到上型dib(bottom-up),和頂到下型dib(top-down)。底到上型dib的原點(origin)在圖像的左下角,而頂到下型dib的原點在圖像的左上角。如果dib的高度值(由bitmapinfoheader結構中的biheight成員辨別)是一個正值,那麼就表明這個dib是一個底到上型dib,如果高度值是一個負值,那麼它就是一個頂到下型dib。注意:頂到下型的dib位圖是不能被壓縮的。
位圖的顔色格式是通過顔色面闆值(planes)和顔色位值(bitcount)計算得來的,顔色面闆值永遠是1,而顔色位值則可以是1、4、8、16、24、32其中的一個。如果它是1,則表示位圖是一張單色位圖(譯者注:通常是黑白位圖,隻有黑和白兩種顔色,當然它也可以是任意兩種指定的顔色),如果它是4,則表示這是一張vga位圖,如果它是8、16、24、或是32,則表示該位圖是其他裝置所産生的位圖。如果應用程式想擷取目前顯示裝置(或列印機)的顔色位值(或稱位深度),可調用api函數getdevicecaps(),并将第二個參數設為bitspixel即可。
顯示裝置的分辨率是以每米多少個像素來表明的,應用程式可以通過以下三個步驟來擷取顯示裝置或列印機的水準分辨率:
調用getdevicecaps()函數,指定第二個參數為horzres。
再次調用getdevicecaps()函數,指定第二個參數為horzsize。
用第一個傳回值除以第二個傳回值。即:getdevicecaps(hdc,horzres)/getdevicecaps(hdc,horzsize);
應用程式也可以使用相同的三個步驟來擷取裝置的垂直分辨率,不同之處隻是要将horzres替換為vertres,把horzsize替換為vertsize,即可。
調色闆是被儲存在一個rgbquad結構的數組中,該結構指出了每一種顔色的紅、綠、藍的分量值。位數組中的每一個索引都對應于一個調色闆項(即一個rgbquad結構),應用程式将根據這種對應關系,将像素索引值轉換為像素rgb值(真實的像素顔色)。應用程式也可以通過調用getdevicecaps()函數來擷取目前顯示裝置的調色闆尺寸(将該函數的第二個參數設為numcolors即可)。
win32
api支援位資料的壓縮(隻對8位和4位的底到上型dib位圖)。壓縮方法是采用運作長度編碼方案(rle),rle使用兩個位元組來描述一個句法,第一個位元組表示重複像素的個數,第二個位元組表示重複像素的索引值。有關壓縮位圖的詳細資訊請參見對bitmapinfoheader結構的解釋。
應用程式可以從一個ddb位圖建立出一個dib位圖,步驟是,先初始化一些必要的結構,然後再調用getdibits()函數。不過,有些顯示裝置有可能不支援這個函數,你可以通過調用getdevicecaps()函數來确定一下(getdevicecaps()函數在調用時指定rc_di_bitmap作為rastercaps的标志)。
應用程式可以用dib去設定顯示裝置上的像素(譯者注:也就是顯示dib),方法是調用setdibitstodevice()函數或調用stretchdibits()函數。同樣,有些顯示裝置也有可能不支援以上這兩個函數,這時你可以指定rc_dibtodev作為rastercaps标志,然後調用getdevicecaps()函數來判斷該裝置是否支援setdibitstodevice()函數。也可以指定rc_stretchdib作為rastercaps标志來調用getdevicecaps()函數,來判斷該裝置是否支援stretchdibits()函數。
如果應用程式隻是要簡單的顯示一個已經存在的dib位圖,那麼它隻要調用setdibitstodevice()函數就可以。比如一個電子表格軟體,它可以打開一個圖表檔案,在視窗中簡單的調用setdibitstodevice()函數,将圖形顯示在視窗中。但如果應用程式要重複的繪制位圖的話,則應該使用bitblt()函數,因為bitblt()函數的執行速度要比setdibitstodevice()函數快很多。
裝置相關位圖
(device-dependent bitmaps)
裝置相關位圖(ddb)之是以現在還被系統支援,隻是為了相容舊的windows
3.0軟體,如果程式員現在要開發一個與位圖有關的程式,則應該盡量使用或生成dib格式的位圖。
ddb位圖是被一個單個結構bitmap所描述,這個結構的成員标明了該位圖的寬度、高度、裝置的顔色格式等資訊。
ddb位圖也有兩種類型,即:可廢棄的(discardable)ddb和不可廢棄的(nondiscardable)ddb。可廢棄的ddb位圖就是一種當系統記憶體缺乏,并且該位圖也沒有被選入裝置描述表(dc)的時候,系統就會把該ddb位圖從記憶體中清除(即廢棄)。不可廢棄的ddb則是無論系統記憶體多少都不會被系統清除的ddb。api函數creatediscardablebitmap()函數可用于建立可廢棄位圖。而函數createbitmap()、createcompatiblebitmap()、和createbitmapindirect()可用于建立不可廢棄的位圖。
應用程式可以通過一個dib位圖而建立一個ddb位圖,隻要先初始化一些必要的結構,然後再調用createdibitmap()函數就可以。如果在調用該函數時指定了cbm_init标志,那麼這一次調用就等價于先調用createcompatiblebitmap()建立目前裝置格式的ddb位圖,然後又調用setdibits()函數轉換dib格式到ddb格式。(可能有些裝置并不支援setdibits()函數,你可以指定rc_di_bitmap作為rastercaps的标志,然後調用getdevicecaps()函數來判斷一下)。
對應資料結構
bmp檔案組成
bmp檔案由檔案頭、位圖資訊頭、顔色資訊和圖形資料四部分組成。
bmp檔案頭(14位元組)
bmp檔案頭資料結構含有bmp檔案的類型、檔案大小和位圖起始位置等資訊。
其結構定義如下:
typedef struct
tagbitmapfileheader
{
word bftype; // 位圖檔案的類型,必須為bm(1-2位元組)
dword
bfsize; // 位圖檔案的大小,以位元組為機關(3-6位元組)
word bfreserved1; //
位圖檔案保留字,必須為0(7-8位元組)
word bfreserved2; // 位圖檔案保留字,必須為0(9-10位元組)
bfoffbits; // 位圖資料的起始位置,以相對于位圖(11-14位元組)
// 檔案頭的偏移量表示,以位元組為機關
}
bitmapfileheader;
位圖資訊頭(40位元組)
bmp位圖資訊頭資料用于說明位圖的尺寸等資訊。
typedef struct tagbitmapinfoheader{
bisize; // 本結構所占用位元組數(15-18位元組)
long biwidth; // 位圖的寬度,以像素為機關(19-22位元組)
long
biheight; // 位圖的高度,以像素為機關(23-26位元組)
word biplanes; //
目标裝置的級别,必須為1(27-28位元組)
word bibitcount;// 每個像素所需的位數,必須是1(雙色),(29-30位元組)
//
4(16色),8(256色)或24(真彩色)之一
dword bicompression; // 位圖壓縮類型,必須是
0(不壓縮),(31-34位元組)
// 1(bi_rle8壓縮類型)或2(bi_rle4壓縮類型)之一
dword bisizeimage; //
位圖的大小,以位元組為機關(35-38位元組)
long bixpelspermeter; // 位圖水準分辨率,每米像素數(39-42位元組)
biypelspermeter; // 位圖垂直分辨率,每米像素數(43-46位元組)
dword biclrused;//
位圖實際使用的顔色表中的顔色數(47-50位元組)
dword biclrimportant;// 位圖顯示過程中重要的顔色數(51-54位元組)
bitmapinfoheader;
顔色表
顔色表用于說明位圖中的顔色,它有若幹個表項,每一個表項是一個rgbquad類型的結構,定義一種顔色。rgbquad結構的定義如下:
typedef
struct tagrgbquad {
byte rgbblue;// 藍色的亮度(值範圍為0-255)
byte rgbgreen; //
綠色的亮度(值範圍為0-255)
byte rgbred; // 紅色的亮度(值範圍為0-255)
byte rgbreserved;//
保留,必須為0
rgbquad;
顔色表中rgbquad結構資料的個數有bibitcount來确定:
當bibitcount=1,4,8時,分别有2,16,256個表項;
當bibitcount=24時,沒有顔色表項。
位圖資訊頭和顔色表組成位圖資訊,bitmapinfo結構定義如下:
struct tagbitmapinfo {
bitmapinfoheader bmiheader; // 位圖資訊頭
rgbquad
bmicolors[1]; // 顔色表
} bitmapinfo;
位圖資料
位圖資料記錄了位圖的每一個像素值,記錄順序是在掃描行内是從左到右,掃描行之間是從下到上。位圖的一個像素值所占的位元組數:
當bibitcount=1時,8個像素占1個位元組;
當bibitcount=4時,2個像素占1個位元組;
當bibitcount=8時,1個像素占1個位元組;
當bibitcount=24時,1個像素占3個位元組;
windows規定一個掃描行所占的位元組數必須是
4的倍數(即以long為機關),不足的以0填充,
bisizeimage
= ((((bi.biwidth * bi.bibitcount) + 31) & ~31) / 8) *
bi.biheight;
具體資料舉例:
如某bmp檔案開頭:
424d 4690 0000 0000 0000 4600 0000 2800
0000 8000 0000 9000 0000 0100*1000 0300 0000 0090 0000 a00f 0000 a00f 0000 0000
0000 0000 0000*00f8 e007 1f00 0000*02f1 84f1 04f1 84f1 84f1 06f2 84f1 06f2 04f2
86f2 06f2 86f2 86f2 …. ….
讀取方法
<code>1</code>
<code>/*</code>
<code>2</code>
<code>功能:在圖檔的第50行畫一條黑線</code>
<code>3</code>
<code>為簡化代碼,隻支援24位色的圖檔</code>
<code>4</code>
<code>codeblocks下正确運作。vc下需要将二維數組img改為malloc動态配置設定。</code>
<code>5</code>
<code>*/</code>
<code>6</code>
<code>#include</code>
<code>7</code>
<code>8</code>
<code>typedef</code> <code>struct</code><code>{</code>
<code>9</code>
<code>byte</code> <code>b;</code>
<code>10</code>
<code>byte</code> <code>g;</code>
<code>11</code>
<code>byte</code> <code>r;</code>
<code>12</code>
<code>}rgb;</code>
<code>13</code>
<code>int</code> <code>main(</code><code>void</code>
<code>)</code>
<code>14</code>
<code>{</code>
<code>15</code>
<code>bitmapfileheader fileheader;</code>
<code>16</code>
<code>bitmapinfoheader infoheader;</code>
<code>17</code>
<code>file</code><code>* pfin =</code><code>fopen</code><code>(</code><code>"原始圖像.bmp"</code><code>,</code><code>"rb"</code><code>);</code>
<code>18</code>
<code>file</code><code>* pfout =</code><code>fopen</code><code>(</code><code>"修改後的圖像.bmp"</code>
<code>,</code><code>"wb"</code><code>);</code>
<code>19</code>
<code>//read the bitmap file header;</code>
<code>20</code>
<code>fread</code><code>(&fileheader,</code><code>sizeof</code><code>(bitmapfileheader),1,pfin);</code>
<code>21</code>
<code>//read the bitmap info header;</code>
<code>22</code>
<code>fread</code><code>(&infoheader,</code><code>sizeof</code><code>(bitmapinfoheader),1,pfin);</code>
<code>23</code>
<code>//為簡化代碼,隻處理24位彩色</code>
<code>24</code>
<code>if</code><code>( infoheader.bibitcount == 24 )</code>
<code>25</code>
<code>26</code>
<code>int</code> <code>size = infoheader.biwidth*infoheader.biheight;</code>
<code>27</code>
<code>rgb img[infoheader.biheight][infoheader.biwidth];</code>
<code>28</code>
<code>fread</code><code>( img ,</code><code>sizeof</code><code>(rgb) , size , pfin );</code>
<code>29</code>
<code>//把第50行染成黑色</code>
<code>30</code>
<code>int</code> <code>i = 0;</code>
<code>31</code>
<code>for</code><code>( ; i < infoheader.biwidth ; i++ )</code>
<code>32</code>
<code>33</code>
<code>img[50][i].b =img[50][i].g=img[50][i].r= 0;</code>
<code>34</code>
<code>}</code>
<code>35</code>
<code>//将修改後的圖檔儲存到檔案</code>
<code>36</code>
<code>fwrite</code><code>( &fileheader ,</code><code>sizeof</code><code>(fileheader) , 1 , pfout );</code>
<code>37</code>
<code>fwrite</code><code>( &infoheader ,</code><code>sizeof</code><code>(infoheader) , 1 , pfout );</code>
<code>38</code>
<code>fwrite</code><code>( img ,</code><code>sizeof</code><code>(rgb) , size , pfout );</code>
<code>39</code>
<code>40</code>
1)1-2:(這裡的數字代表的是”字”,即兩個位元組,下同)圖像檔案頭。0x4d42=’bm’,表示是windows支援的bmp格式。(注意:查ascii表b
0×42,m0x4d,bftype
為兩個位元組,b為low位元組,m為high位元組是以bftype=0x4d42,而不是0x424d,但注意)
2)3-6:整個檔案大小。4690
0000,為00009046h=36934。
3)7-8:保留,必須設定為0。
4)9-10:保留,必須設定為0。
5)11-14:從檔案開始到位圖資料之間的偏移量(14+40+4*(2^bibitcount))。4600
0000,為00000046h=70,上面的檔案頭就是35字=70位元組。
6)15-18:位圖圖資訊頭長度。
7) 19-22:位圖寬度,以像素為機關。8000
0000,為00000080h=128。
8)23-26:位圖高度,以像素為機關。9000
0000,為00000090h=144。
9)27-28:位圖的位面數,該值總是1。0100,為0001h=1。
10)29-30:每個像素的位數。有1(單色),4(16色),8(256色),16(64k色,高彩色),24(16m色,真彩色),32(4096m色,增強型真彩色)。1000為0010h=16。
11)31-34:壓縮說明:有0(不壓縮),1(rle
8,8位rle壓縮),2(rle
4,4位rle壓縮,3(bitfields,位域存放)。rle簡單地說是采用像素數+像素值的方式進行壓縮。t408采用的是位域存放方式,用兩個位元組表示一個像素,位域配置設定為r5b6g5。圖中0300
0000為00000003h=3。
12)35-38:用位元組數表示的位圖資料的大小,該數必須是4的倍數,數值上等于(≥位圖寬度的最小的4的倍數)×位圖高度×每個像素位數。0090
0000為00009000h=80×90×2h=36864。
13)39-42:用象素/米表示的水準分辨率。a00f 0000為0000
0fa0h=4000。
14)43-46:用象素/米表示的垂直分辨率。a00f 0000為0000
15)47-50:位圖使用的顔色索引數。設為0的話,則說明使用所有調色闆項。
16)51-54:對圖象顯示有重要影響的顔色索引的數目。如果是0,表示都重要。
17)(55+0)到(50-1+2^bibitcount):彩色闆規範。對于調色闆中的每個表項,用下述方法來描述rgb的值:
1位元組用于藍色分量
1位元組用于綠色分量
1位元組用于紅色分量
1位元組用于填充符(設定為0)
對于24-位真彩色圖像就不使用彩色闆,因為位圖中的rgb值就代表了每個象素的顔色。
如,彩色闆為00f8
0000 e007 0000 1f00 0000 0000 0000,其中:
00f8為f800h = 1111 1000 0000
0000(二進制),是藍色分量的掩碼。
e007 為 07e0h = 0000 0111 1110
0000(二進制),是綠色分量的掩碼。
1f00為001fh = 0000 0000 0001
[1]1111(二進制),是紅色分量的掩碼。
0000
總設定為0。
将掩碼跟像素值進行“與”運算再進行移位操作就可以得到各色分量值。看看掩碼,就可以明白事實上在每個像素值的兩個位元組16位中,按從高到低取5、6、5位分别就是r、g、b分量值。取出分量值後把r、g、b值分别乘以8、4、8就可以補齊第個分量為一個位元組,再把這三個位元組按rgb組合,放入存儲器(同樣要反序),就可以轉換為24位标準bmp格式了。
圖像資料陣列
18)55(無調色闆)-bfsize:每兩個位元組表示一個像素。陣列中的第一個位元組表示位圖左下角的象素,而最後一個位元組表示位圖右上角的象素。
每兩個位元組表示一個像素。陣列中的第一個位元組表示位圖左下角的象素,而最後一個位元組表示位圖右上角的象素。
bmp檔案通常是不壓縮的,是以它們通常比同一幅圖像的壓縮圖像檔案格式要大很多。例如,一個800×600的24位幾乎占據1.4mb空間。是以它們通常不适合在網際網路或者其它低速或者有容量限制的媒介上進行傳輸。根據顔色深度的不同,圖像上的一個像素可以用一個或者多個位元組表示,它由n/8所确定(n是位深度,1位元組包含8個資料位)。圖檔浏覽器等基于位元組的ascii值計算像素的顔色,然後從調色闆中讀出相應的值。更為詳細的資訊請參閱下面關于位圖檔案的部分。n位2n種顔色的位圖近似位元組數可以用下面的公式計算:bmp檔案大小約等于
54+4*2的n次方+(w*h*n)/8,其中高度和寬度都是像素數。需要注意的是上面公式中的54是位圖檔案的檔案頭,是彩色調色闆的大小。另外需要注意的是這是一個近似值,對于n位的位圖圖像來說,盡管可能有最多2n中顔色,一個特定的圖像可能并不會使用這些所有的顔色。由于彩色調色闆僅僅定義了圖像所用的顔色,是以實際的彩色調色闆将小于。如果想知道這些值是如何得到的,請參考下面檔案格式的部分。由于存儲算法本身決定的因素,根據幾個圖像參數的不同計算出的大小與實際的檔案大小将會有一些細小的差别。
圖象資料bgra:預設的bmp是不支援alpha通道的,但對32位bmp而言,每個象素用32位(4個位元組)表示,前三個位元組表示rgb分量,最後一個位元組可以做為alpha通道的值,是以32位位圖可以存儲帶alpha通道的圖像,在檔案中,各分量的存儲順序為bgra,bgra,bgra,bgra…
另外要注意的是,bmp圖像的象素存儲順序是從下到上