转贴:BMP格式详解 二 (转载)
http://www.360doc.com/content/10/1107/14/1188581_67355279.shtml
調色板
當bpp小于等于8時,BMP使用調色板記錄色彩信息,調色板中每條數據(即每種色彩值)都是一個uint型數據。當調色板存在時,圖像數據塊中存 儲的只是各個像素的色彩在調色板中的索引值,必須通過在調色板中查表,才能獲知各個像素的真實顏色。若引入調色板,則調色板數據塊緊隨在圖像參數數據塊之 后。
當bpp == 1時,調色板合法索引值只有0和1。因此調色板中只有2個色彩值,分別表示索引值為0和1時的色彩信息。
當bpp == 4或bpp == 8時,合法索引值范圍擴大為[0,15]和[0,255]。但圖像中不一定使用到了全部16種或256種顏色。第47-50字節存儲的uint型數據指出 圖像中實際應用的色彩數,也即調色板中的色彩值數目。當然,它不應超出調色板的合法索引值的范圍。
當bpp == 4或bpp == 8時,可以采用Run-Length-Encoding方式壓縮圖像的存儲空間,即壓縮方式選項的值為1或2(當選項值為0時,不壓縮)。這種編碼格式所 考慮的情況是,若4bpp或8bpp位圖的尺寸較大時,由于色彩總數非常有限,所以圖像中必然會有很多顏色重復的像素。因此BMP圖像格式的設計者決定采 取一個簡單的措施來挽回一些被浪費掉的存儲空間。這個簡單的措施就是RLE壓縮方法。
RLE, Run-Length-Encoding
如果你是一個有優化癖的程序員,當你發現一張8bpp、寬300像素的位圖中有一行像素只有兩種顏色:前100個像素是紅色,后200個像素是藍 色,然而你的位圖卻忠實地用100字節來存儲前半行重復的紅色,又用200字節來存儲后半行重復的藍色,那么你一定會抓狂到大罵BMP格式的設計者是白 癡。
為了避免被罵,BMP格式的設計者想出了這樣的辦法:先用一個字節來存儲重復色彩的數量,再用一個字節來存儲這個色彩的值,即用兩個字節代表一段顏 色重復的像素。并且,他們給重復色彩的數量起了個名字,叫做Run-Length,可能是因為只有在運行時我們才能知道這段重復色彩的長度。由于 runlength為0時沒有意義,因此設計者把runlength=0當做每行的終止符。于是,同樣存儲一行300個像素,原先需要300字節,現在只 用5個字節就搞定!
| 字節 | 1 | 2 | 3 | 4 | 5 |
| 內容 | 100 | red | 200 | blue | 0 |
由于你是一個有著嚴重優化癖的程序員,所以你對這樣粗制濫造的優化方法并不滿足,因為你很快發現,如果一張位圖中沒有連續重復的像素(例如紅藍像素 點陣),那么用剛才發明的這個辦法存儲300個像素,居然要用601字節!當然,這種情況下最好的辦法是不用壓縮算法。可是,如果既有重復像素,又有點陣 的情況呢?比如前150像素是重復的綠色,后150像素是紅藍相間的像素點陣。
為了滿足你變態的優化癖,BMP格式的設計者只好繼續發展這個算法。首先,他們保留了“用一個字節來存儲重復色彩的數量,再用一個字節來存儲這個色 彩的值”的設計思路,然后修改了runlength為0的含義。設計者規定,當遇到runlength==0時,我們要繼續讀取下一個字節,若該字節值為 n,意味著后面的n個像素將采用“逐字翻譯”的方式來解析,也就是說,這n個像素的前面沒有runlength這個字節。用這種方法壓縮“前150像素是 重復的綠色,后150像素是紅藍相間的像素點陣”的300個像素,只需要154個字節。
| 字節 | 1 | 2 | 3 | 4 | 后150個字節 |
| 內容 | 150 | green | 0 | 150 | red,blue,…,red,blue |
這個近似完美的結果中有個小問題:設計BMP格式的天才們把runlength==0的含義修改后,我們就沒有行終結符了。不過天才終歸是天才,他 們發現“逐字翻譯”的像素數必須大于2才有意義(想想這是為什么?),因此runlength==0之后的那個字節的值為0、1或2時,目前還沒有意義。 于是天才們規定,當這個值為0時,表示行結束符;當這個值為1時,表示文件結束符;當這個值為2時,似乎仍然沒有什么意義;只有當該值大于等于3時,才是 “逐字翻譯”。完整的壓縮結果是:
| 字節 | 1 | 2 | 3 | 4 | 后150個字節 | 155 | 156 |
| 內容 | 150 | green | 0 | 150 | red,blue,…,red,blue | 0 | 0 |
這就是傳說中的Run-Length-Encoding for 8bpp。4bpp的RLE跟8bpp時沒有本質差別。
RGB與Bit-Fields
當圖像中引用的色彩超過256種時,我們就需要16bpp或更高bpp的位圖。調色板不適合bpp較大的位圖,因此16bpp以上的位圖都不使用調色板。不使用調色板的位圖圖像有兩種編碼格式:RGB和Bit-Fields(下稱BF)。
RGB編碼格式是一種均分的思想,使Red、Green、Blue三個顏色分量所包含的信息容量盡可能一樣大。
16bpp-RGB:在每個像素所占的16bits中,低5位表示Blue分量;中5為表示Green分量;高5位表示Red分量;最高1位無意義 (后來有些應用程序將其視為透明度Alpha分量,但這并不是標準)。所以從低到高的順序實際上是B-G-R,這也是我在BMP簡介的表格里,把RGB的 編碼方式都寫成BGR的原因。
24bpp-RGB:24bpp的位圖又稱為真彩位圖,它通常只有這一種編碼格式,在24bits中,低8位表示Blue分量;中8為表示Green分量;高8位表示Red分量。
32bpp-RGB:在32bits中,低24位的編碼方式與24bpp位圖相同,最高8位用來表示透明度Alpha分量。32bpp的位圖尺寸太大,一般只有在圖像處理的中間過程中使用。對于需要半透過效果的圖像,更好的選擇是PNG格式。
BF編碼格式與RGB不同,它利用位域操作,人為地確定RGB三分量所包含的信息容量。在圖像參數信息模塊的介紹中提及,當壓縮方式選項置為BF 時,圖像參數結構體將比平時多出16字節。這16字節實際上是4個dword的位域掩碼。按照先后順序,它們分別是R、G、B、A四個分量的位域掩碼。當 然如果沒有Alpha分量,則Alpha掩碼沒有實際意義。
位域掩碼的作用是:指出像素色彩值中RGB分量,就像子網掩碼指出子網網段一樣。
16bpp-BF-565:這是BF編碼格式最著名和最普遍的應用。它的Red、Green和Blue分量的位域掩碼分別是0xF800、0x07E0和0x001F。
我們平時所能夠見到的位圖中使用BF編碼格式的并不多,因為它看上去比較麻煩,而效果也不見得比RGB要好(你能用肉眼分辨出16bpp-RGB和16bpp-BF-565之間的區別嗎?)。
BF編碼格式的重要應用在于游戲軟件。游戲軟件通常包含數量龐大的小尺寸圖片。如果一張圖片中幾乎沒有Blue分量,那么使用16bpp-RGB格 式顯然會浪費掉B分量所占的5位。此時若采用16bpp-BF-772格式,只給B分量2位,那么R與G分量都擁有7位的容量,幾乎接近真彩圖像。因此存 儲空間小、仿真彩能力強的特點使BF編碼格式仍然有著獨特的用武之地。
32bpp-BF-xxx:我一直不明白為什么會存在32bpp的位圖。如果說32bpp-RGB格式的存在是因為在圖像處理過程中存儲起來比較高效(不用壓縮),那么32bpp-BF又是為什么存在呢?
圖像數據塊
圖像數據塊從文件頭中起始偏移量字段所指出的位置開始,其中存放著位圖圖像的數據,數據格式由圖像參數信息塊中的壓縮方式選項的取值決定。操作圖像數據塊時,有一些注意事項:
當壓縮方式為RGB時,圖像數據塊以“行”為單位雙字對齊。例如一張寬度為5像素的8bpp的圖像,其實際使用的存儲空間是每行8個字節。又如一幅4bpp、寬度為5像素的位圖圖像,其實際使用的存儲空間是每行4個字節。
當bpp < 8時,每個字節將存放多個像素的色彩索引,則先出現的像素存放在高位中。例如某4bpp圖像第一行像素的順序是red, green, blue, yellow, …則圖像數據塊中第一個字節的高4位值為red,低4位值為green;第二字節高4位值為blue,低4位值為yellow。1bpp時的情況以此類 推。
還記得前面提到,圖像參數里,高度有可能是負值嗎?這看上去很逗,但事實是,你見過的大多數位圖,其圖像參數里的高度都是負值。BMP格式設計者規 定,當高度為正值時,圖像數據塊中記錄的第一行像素數據是圖像的最后一行;而數據塊中最后一行數據才是實際圖像的第一行,也就是說,數據塊中的行記錄與實 際圖像反序。而當高度為負值時,數據塊中的行記錄與實際圖像才是同序的。
如果你覺得這太奇怪了,我很理解。不過我們必須懷著無比崇敬之情接受這個看似滑稽的規定。這是因為在那個年代里,那些設計BMP格式的天才首先都是 數學家,讓天才的數學家們習慣左上角為原點,并且y軸方向向下的二維直角坐標系的格局顯然是很困難的,既然他們手上又有設計BMP的大權,于是……唉,這 就是歷史。
轉載于:https://blog.51cto.com/jkers/677107
總結
以上是生活随笔為你收集整理的转贴:BMP格式详解 二 (转载)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SilverLigth的Chart不要图
- 下一篇: 编写DLL所学所思(2)——导出类