BMP格式

BMP(Bitmap-File)圖形檔案是Windows採用的圖形檔案格式,在Windows環境下運行的所有圖象處理軟體都支持BMP圖象檔案格式。

BMP檔案格式分析

簡介
BMP(Bitmap-File)圖形檔案是Windows採用的圖形檔案格式,在Windows環境下運行的所有圖象處理軟體都支持BMP圖象檔案格式。Windows系統內部各圖像繪製操作都是以BMP為基礎的。windows 3.0以前的BMP圖檔案格式與顯示設備有關,因此把這種BMP圖象檔案格式稱為設備相關點陣圖DDB(device-dependent bitmap)檔案格式。Windows 3.0以後的BMP圖象檔案與顯示設備無關,因此把這種BMP圖象檔案格式稱為設備無關點陣圖dib(device-independent bitmap)格式(註:Windows 3.0以後,在系統中仍然存在DDB點陣圖,象BitBlt()這種函式就是基於DDB點陣圖的,只不過如果你想將圖像以BMP格式保存到磁碟檔案中時,微軟極力推薦你以DIB格式保存),目的是為了讓Windows能夠在任何類型的顯示設備上顯示所存儲的圖象。BMP點陣圖檔案默認的檔案擴展名是BMP或者bmp(有時它也會以.DIB或.rle作擴展名)。
1.2 檔案結構
點陣圖檔案可看成由4個部分組成:點陣圖檔案頭(bitmap-file header)、點陣圖信息頭(bitmap-information header)、彩色表(color table)和定義點陣圖的位元組陣列,它具有如下所示的形式。
點陣圖檔案的組成
結構名稱 符 號
點陣圖檔案頭 (bitmap-file header) BITMAPFILEHEADER bmfh
點陣圖信息頭 (bitmap-information header) BITMAPINFOHEADER bmih
彩色表 (color table) RGBQUAD aColors[]
圖象數據陣列位元組 BYTE aBitmapBits[]
點陣圖檔案結構可綜合在表6-01中。
表01 點陣圖檔案結構內容摘要
偏移量 域的名稱 大小 內容
圖象檔案頭
0000h 檔案標識 2 bytes 兩位元組的內容用來識別點陣圖的類型:
‘BM’ :Windows 3.1x,95,NT,…
‘BA’ :OS/2 Bitmap Array
‘CI’ :OS/2 Color Icon
‘CP’ :OS/2 Color Pointer
‘IC’ :OS/2 Icon
‘PT’ :OS/2 Pointer
註:因為OS/2系統並沒有被普及開,所以在編程時,你只需判斷第一個標識“BM”就行。
0002h File Size 1 dword 用位元組表示的整個檔案的大小
0006h Reserved 1 dword 保留,必須設定為0
000Ah Bitmap Data Offset 1 dword 從檔案開始到點陣圖數據開始之間的數據(bitmap data)之間的偏移量
圖象信息頭
000Eh Bitmap Header Size 1 dword 點陣圖信息頭(Bitmap Info Header)的長度,用來描述點陣圖的顏色、壓縮方法等。下面的長度表示:
28h - Windows 3.1x,95,NT,…
0Ch - OS/2 1.x
F0h - OS/2 2.x
註:在Windows95、98、2000等作業系統中,點陣圖信息頭的長度並不一定是28h,因為微軟已經制定出了新的BMP檔案格式,其中的信息頭結構變化比較大,長度加長。所以最好不要直接使用常數28h,而是應該從具體的檔案中讀取這個值。這樣才能確保程式的兼容性。
0012h Width 1 dword 點陣圖的寬度,以象素為單位
0016h Height 1 dword 點陣圖的高度,以象素為單位
001Ah Planes 1 word 點陣圖的位面數(註:該值將總是1)
001Ch Bits Per Pixel 1 word 每個象素的位數
1 - 單色點陣圖(實際上可有兩種顏色,預設情況下是黑色和白色。你可以自己定義這兩種顏色)
4 - 16 色點陣圖
8 - 256 色點陣圖
16 - 16bit 高彩色點陣圖
24 - 24bit 真彩色點陣圖
32 - 32bit 增強型真彩色點陣圖
001Eh Compression 1 dword 壓縮說明:
0 - 不壓縮 (使用BI_RGB表示)
1 - RLE 8-使用8位RLE壓縮方式(用BI_RLE8表示)
2 - RLE 4-使用4位RLE壓縮方式(用BI_RLE4表示)
3 - Bitfields-位域存放方式(用BI_BITFIELDS表示)
0022h Bitmap Data Size 1 dword 用位元組數表示的點陣圖數據的大小。該數必須是4的倍數
0026h HResolution 1 dword 用象素/米表示的水平解析度
002Ah VResolution 1 dword 用象素/米表示的垂直解析度
002Eh Colors 1 dword 點陣圖使用的顏色數。如8-比特/象素表示為100h或者 256.
0032h Important Colors 1 dword 指定重要的顏色數。當該域的值等於顏色數時(或者等於0時),表示所有顏色都一樣重要
調色板數據 根據BMP版本的不同而不同 Palette N * 4 byte 調色板規範。對於調色板中的每個表項,這4個位元組用下述方法來描述RGB的值:1位元組用於藍色分量
1位元組用於綠色分量
1位元組用於紅色分量
1位元組用於填充符(設定為0)
圖象數據 根據BMP版本及調色板尺寸的不同而不同 Bitmap Data xxx bytes 該域的大小取決於壓縮方法及圖像的尺寸和圖像的位深度,它包含所有的點陣圖數據位元組,這些數據可能是彩色調色板的索引號,也可能是實際的RGB值,這將根據圖像信息頭中的位深度值來決定。
構件詳解
 1. 點陣圖檔案頭
點陣圖檔案頭包含有關於檔案類型、檔案大小、存放位置等信息,在Windows 3.0以上版本的點陣圖檔案中用BITMAPFILEHEADER結構來定義:
typedef struct tagBITMAPFILEHEADER { /* bmfh */
UINT bfType;
DWORD bfSize;
UINT bfReserved1;
UINT bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
其中:
bfType
說明檔案的類型.(該值必需是0x4D42,也就是字元'BM'。我們不需要判斷OS/2的點陣圖標識,這么做現在來看似乎已經沒有什麼意義了,而且如果要支持OS/2的點陣圖,程式將變得很繁瑣。所以,在此只建議你檢察'BM'標識)
注意:asciiB 0x42,M0x4d,bfType 為兩個位元組,Blow位元組,M為high位元組所以bfType=0x4D42,而不是0x424D,但注意
bfSize
說明檔案的大小,用位元組為單位
bfReserved1
保留,必須設定為0
bfReserved2
保留,必須設定為0
bfOffBits
說明從檔案頭開始到實際的圖象數據之間的位元組的偏移量。這個參數是非常有用的,因為點陣圖信息頭和調色板的長度會根據不同情況而變化,所以你可以用這個偏移值迅速的從檔案中讀取到位數據。
2. 點陣圖信息頭
點陣圖信息用BITMAPINFO結構來定義,它由點陣圖信息頭(bitmap-information header)和彩色表(color table)組成,前者用BITMAPINFOHEADER結構定義,後者用RGBQUAD結構定義。BITMAPINFO結構具有如下形式:
typedef struct tagBITMAPINFO { /* bmi */
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
其中:
bmiHeader
說明BITMAPINFOHEADER結構,其中包含了有關點陣圖的尺寸及位格式等信息
bmiColors
說明彩色表RGBQUAD結構的陣列,其中包含索引圖像的真實RGB值。
BITMAPINFOHEADER結構包含有點陣圖檔案的大小、壓縮類型和顏色格式,其結構定義為:
typedef struct tagBITMAPINFOHEADER { /* bmih */
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
其中:
biSize
說明BITMAPINFOHEADER結構所需要的字數。註:這個值並不一定是BITMAPINFOHEADER結構的尺寸,它也可能是sizeof(BITMAPV4HEADER)的值,或是sizeof(BITMAPV5HEADER)的值。這要根據該點陣圖檔案的格式版本來決定,不過,就現在的情況來看,絕大多數的BMP圖像都是BITMAPINFOHEADER結構的(可能是後兩者太新的緣故吧:-)。
biWidth
說明圖象的寬度,以象素為單位
biHeight
說明圖象的高度,以象素為單位。註:這個值除了用於描述圖像的高度之外,它還有另一個用處,就是指明該圖像是倒向的點陣圖,還是正向的點陣圖。如果該值是一個正數,說明圖像是倒向的,如果該值是一個負數,則說明圖像是正向的。大多數的BMP檔案都是倒向的點陣圖,也就是時,高度值是一個正數。(註:當高度值是一個負數時(正向圖像),圖像將不能被壓縮(也就是說biCompression成員將不能是BI_RLE8或BI_RLE4)。
biPlanes
為目標設備說明位面數,其值將總是被設為1
biBitCount
說明比特數/象素,其值為1、4、8、16、24、或32
biCompression
說明圖象數據壓縮的類型。其值可以是下述值之一:
BI_RGB:沒有壓縮;
BI_RLE8:每個象素8比特的RLE壓縮編碼,壓縮格式由2位元組組成(重複象素計數和顏色索引);
BI_RLE4:每個象素4比特的RLE壓縮編碼,壓縮格式由2位元組組成
BI_BITFIELDS:每個象素的比特由指定的掩碼決定。
biSizeImage
說明圖象的大小,以位元組為單位。當用BI_RGB格式時,可設定為0
biXPelsPerMeter
說明水平解析度,用象素/米表示
biYPelsPerMeter
說明垂直解析度,用象素/米表示
biClrUsed
說明點陣圖實際使用的彩色表中的顏色索引數(設為0的話,則說明使用所有調色板項)
biClrImportant
說明對圖象顯示有重要影響的顏色索引的數目,如果是0,表示都重要。
現就BITMAPINFOHEADER結構作如下說明:
(1) 彩色表的定位
應用程式可使用存儲在biSize成員中的信息來查找在BITMAPINFO結構中的彩色表,如下所示:
pColor = ((LPSTR) pBitmapInfo + (WORD) (pBitmapInfo->bmiHeader.biSize))
(2) biBitCount
biBitCount=1 表示點陣圖最多有兩種顏色,預設情況下是黑色和白色,你也可以自己定義這兩種顏色。圖像信息頭裝調色板中將有兩個調色板項,稱為索引0和索引1。圖象數據陣列中的每一位表示一個象素。如果一個位是0,顯示時就使用索引0的RGB值,如果位是1,則使用索引1的RGB值。
biBitCount=4 表示點陣圖最多有16種顏色。每個象素用4位表示,並用這4位作為彩色表的表項來查找該象素的顏色。例如,如果點陣圖中的第一個位元組為0x1F,它表示有兩個象素,第一象素的顏色就在彩色表的第2表項中查找,而第二個象素的顏色就在彩色表的第16表項中查找。此時,調色板中預設情況下會有16個RGB項。對應於索引0到索引15。
biBitCount=8 表示點陣圖最多有256種顏色。每個象素用8位表示,並用這8位作為彩色表的表項來查找該象素的顏色。例如,如果點陣圖中的第一個位元組為0x1F,這個象素的顏色就在彩色表的第32表項中查找。此時,預設情況下,調色板中會有256個RGB項,對應於索引0到索引255。
biBitCount=16 表示點陣圖最多有65536種顏色。每個色素用16位(2個位元組)表示。這種格式叫作高彩色,或叫增強型16位色,或64K色。它的情況比較複雜,當biCompression成員的值是BI_RGB時,它沒有調色板。16位中,最低的5位表示藍色分量,中間的5位表示綠色分量,高的5位表示紅色分量,一共占用了15位,最高的一位保留,設為0。這種格式也被稱作555 16位點陣圖。如果biCompression成員的值是BI_BITFIELDS,那么情況就複雜了,首先是原來調色板的位置被三個DWORD變數占據,稱為紅、綠、藍掩碼。分別用於描述紅、綠、藍分量在16位中所占的位置。在Windows 95(或98)中,系統可接受兩種格式的位域:555和565,在555格式下,紅、綠、藍的掩碼分別是:0x7C00、0x03E0、0x001F,而在565格式下,它們則分別為:0xF800、0x07E0、0x001F。你在讀取一個像素之後,可以分別用掩碼“與”上像素值,從而提取出想要的顏色分量(當然還要再經過適當的左右移操作)。在NT系統中,則沒有格式限制,只不過要求掩碼之間不能有重疊。(註:這種格式的圖像使用起來是比較麻煩的,不過因為它的顯示效果接近於真彩,而圖像數據又比真彩圖像小的多,所以,它更多的被用於遊戲軟體)。
biBitCount=24 表示點陣圖最多有1670萬種顏色。這種點陣圖沒有調色板(bmiColors成員尺寸為0),在位數組中,每3個位元組代表一個象素,分別對應於顏色R、G、B。
biBitCount=32 表示點陣圖最多有4294967296(2的32次方)種顏色。這種點陣圖的結構與16位點陣圖結構非常類似,當biCompression成員的值是BI_RGB時,它也沒有調色板,32位中有24位用於存放RGB值,順序是:最高位—保留,紅8位、綠8位、藍8位。這種格式也被成為888 32點陣圖。如果 biCompression成員的值是BI_BITFIELDS時,原來調色板的位置將被三個DWORD變數占據,成為紅、綠、藍掩碼,分別用於描述紅、綠、藍分量在32位中所占的位置。在Windows 95(or 98)中,系統只接受888格式,也就是說三個掩碼的值將只能是:0xFF0000、0xFF00、0xFF。而在NT系統中,你只要注意使掩碼之間不產生重疊就行。(註:這種圖像格式比較規整,因為它是DWORD對齊的,所以在記憶體中進行圖像處理時可進行彙編級的代碼最佳化(簡單))。

bmp點陣圖和調色板的概念

如今Windows(3.x以及95,98,NT)系列已經成為絕大多數用戶使用的作業系統,它比DOS成功的一個重要因素是它可視化的漂亮界面。那么Windows是如何顯示圖象的呢?這就要談到點陣圖(bitmap)。
我們知道,普通的顯示器螢幕是由許許多多點構成的,我們稱之為象素。顯示時採用掃描的方法:電子槍每次從左到右掃描一行,為每個象素著色,然後從上到下這樣掃描若干行,就掃過了一屏。為了防止閃爍,每秒要重複上述過程幾十次。例如我們常說的螢幕解析度為640×480,刷新頻率為70Hz,意思是說每行要掃描640個象素,一共有480行,每秒重複掃描螢幕70次。
我們稱這種顯示器為位映象設備。所謂位映象,就是指一個二維的象素矩陣,而點陣圖就是採用位映象方法顯示和存儲的圖象。舉個例子,圖1.1是一幅普通的黑白點陣圖,圖1.2是被放大後的圖,圖中每個方格代表了一個象素。我們可以看到:整個骷髏就是由這樣一些黑點和白點組成的。

圖1.1 骷髏
1.2 放大後的骷髏點陣圖
那么,彩色圖是怎么回事呢?
我們先來說說三元色RGB概念。
我們知道,自然界中的所有顏色都可以由紅、綠、藍(R,G,B)組合而成。有的顏色含有紅色成分多一些,如深紅;有的含有紅色成分少一些,如淺紅。針對含有紅色成分的多少,可以分成0到255共256個等級,0級表示不含紅色成分;255級表示含有100%的紅色成分。同樣,綠色和藍色也被分成256級。這種分級概念稱為量化。
這樣,根據紅、綠、藍各種不同的組合我們就能表示出256×256×256,約1600萬種顏色。這么多顏色對於我們人眼來說已經足夠豐富了。
1.1 常見顏色的RGB組合值
顏色 R G B
255 0 0
0 0 255
0 255 0
255 255 0
255 0 255
0 255 255
255 255 255
0 0 0
128 128 128
你大概已經明白了,當一幅圖中每個象素賦予不同的RGB值時,能呈現出五彩繽紛的顏色了,這樣就形成了彩色圖。的確是這樣的,但實際上的做法還有些差別。
讓我們來看看下面的例子。
有一個長寬各為200個象素,顏色數為16色的彩色圖,每一個象素都用R、G、B三個分量表示。因為每個分量有256個級別,要用8位(bit),即一個位元組(byte)來表示,所以每個象素需要用3個位元組。整個圖象要用200×200×3,約120k位元組,可不是一個小數目呀!如果我們用下面的方法,就能省的多。
因為是一個16色圖,也就是說這幅圖中最多只有16種顏色,我們可以用一個表:表中的每一行記錄一種顏色的R、G、B值。這樣當我們表示一個象素的顏色時,只需要指出該顏色是在第幾行,即該顏色在表中的索引值。舉個例子,如果表的第0行為255,0,0(紅色),那么當某個象素為紅色時,只需要標明0即可。
讓我們再來計算一下:16種狀態可以用4位(bit)表示,所以一個象素要用半個位元組。整個圖象要用200×200×0.5,約20k位元組,再加上表占用的位元組為3×16=48位元組.整個占用的位元組數約為前面的1/6,省很多吧?
這張R、G、B的表,就是我們常說的調色板(Palette),另一種叫法是顏色查找表LUT(Look Up Table),似乎更確切一些。Windows點陣圖中便用到了調色板技術。其實不光是Windows點陣圖,許多圖象檔案格式如pcx、tif、gif等都用到了。所以很好地掌握調色板的概念是十分有用的。
有一種圖,它的顏色數高達256×256×256種,也就是說包含我們上述提到的R、G、B顏色表示方法中所有的顏色,這種圖叫做真彩色圖(true color)。真彩色圖並不是說一幅圖包含了所有的顏色,而是說它具有顯示所有顏色的能力,即最多可以包含所有的顏色。表示真彩色圖時,每個象素直接用R、G、B三個分量位元組表示,而不採用調色板技術。原因很明顯:如果用調色板,表示一個象素也要用24位,這是因為每種顏色的索引要用24位(因為總共有2種顏色,即調色板有2行),和直接用R,G,B三個分量表示用的位元組數一樣,不但沒有任何便宜,還要加上一個256×256×256×3個位元組的大調色板。所以真彩色圖直接用R、G、B三個分量表示,它又叫做24位色圖。RGB顏色陣列?

有關RGB三色空間我想大家都很熟悉,這裡我想說的是在Windows下,RGB顏色陣列存儲的格式其實BGR。也就是說,對於24位的RGB點陣圖像素數據格式是:

藍色B值 綠色g值 紅色R值
對於32位的RGB點陣圖像素數據格式是:
藍色B值 綠色G值 紅色R值 透明通道A值
透明通道也稱Alpha通道,該值是該像素點的透明屬性,取值在0(全透明)到255(不透明)之間。對於24位的圖像來說,因為沒有Alpha通道,故整個圖像都不透明。

載入檔案

載入檔案的目的是要得到圖片屬性,以及RGB數據,然後可以將其繪製在DC上(GDI),或是生成紋理對象(3D:OpenGL/Direct3D)。這兩種用途在數據處理上有點區別,我們主要按前一種用法講,在和3D有不同的地方,我們再提出來。

1、載入檔案頭

//Load the file header
BITMAPFILEHEADER header;
memset(&header,0,sizeof(header));
inf.read((char*)&header,sizeof(header));
if(header.bfType != 0x4D42)
return false;
這個很簡單,沒有什麼好說的。

2、載入點陣圖信息頭

//Load the image information header
BITMAPINFOHEADER infoheader;
memset(&infoheader,0,sizeof(infoheader));
inf.read((char*)&infoheader,sizeof(infoheader));
m_iImageWidth = infoheader.biWidth;
m_iImageHeight = infoheader.biHeight;
m_iBitsPerPixel = infoheader.biBitCount;
這裡我們得到了3各重要的圖形屬性:寬,高,以及每個像素顏色所占用的位數。

3、行對齊

由於Windows在進行行掃描的時候最小的單位為4個位元組,所以當
圖片寬 X 每個像素的位元組數 != 4的整數倍
時要在每行的後面補上缺少的位元組,以0填充(一般來說當圖像寬度為2的冪時不需要對齊)。點陣圖檔案里的數據在寫入的時候已經進行了行對齊,也就是說載入的時候不需要再做行對齊。但是這樣一來圖片數據的長度就不是:寬 X 高 X 每個像素的位元組數了,我們需要通過下面的方法計算正確的數據長度:
//Calculate the image data size
int iLineByteCnt = (((m_iImageWidth*m_iBitsPerPixel) + 3) >> 2) << 2;
m_iImageDataSize = iLineByteCnt * m_iImageHeight;

4、載入圖片數據

對於24位和32位的點陣圖檔案,點陣圖數據的偏移量為sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER),也就是說現在我們可以直接讀取圖像數據了。
if(m_pImageData) delete &#91;&#93;m_pImageData;
m_pImageData = new unsigned char&#91;m_iImageDataSize&#93;;
inf.read((char*)m_pImageData,m_iImageDataSize);
如果你足夠細心,就會發現記憶體m_pImageData里的數據的確是BGR格式,可以用個純藍色或者是純紅色的圖片測試一下。

5、繪製

好了,數據和屬性我們都有了,現在就可以拿來隨便用了,就和吃饅頭一樣,愛粘白糖粘白糖,愛粘紅糖粘紅糖。下面是我的GDI繪製代碼,僅作參考。
void cimage::DrawImage(HDC hdc,int iLeft,int ITOP,int iWidth,int iHeight)
{
if(!hdc || m_pImageData == NULL)
return;
BITMAPINFO bmi;
memset(&bmi,0,sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFO);
bmi.bmiHeader.biWidth = m_iImageWidth;
bmi.bmiHeader.biHeight = m_iImageHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = m_iBitsPerPixel;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = m_iImageDataSize;
StretchDIBits(hdc,iLeft,iTop,iWidth,iHeight,
0,0,m_iImageWidth,m_iImageHeight,
m_pImageData,&bmi,DIB_RGB_COLORS,SRCCOPY);
}

6、3D(OpenGL)的不同之處

如果你是想用剛才我們得到的數據生成紋理對象,那么你還要請出下面的問題。
首先,用來生成紋理的數據不需要對齊,也就是說不能在每行的後面加上對齊的位元組。當然在OpenGL里要求紋理圖片的尺寸為2的冪,所以這個問題實際上不存在;
其次,我們得到的圖形數據格式是BGR(BGRA),所以在生成紋理的時候,需指定格式為GL_BGR_EXT(GL_BGRA_EXT);否則需要做BGR->RGB(BGRA->RGBA)的轉化。

相關搜尋

熱門詞條

聯絡我們