PNG懂版、JPEG鹃栽、BMP等幾種圖片格式詳解(三)—— BMP

版本記錄

版本號(hào) 時(shí)間
V1.0 2017.09.06

前言

只要是做圖片的或者與圖片相關(guān)的,那么圖片的格式就是一個(gè)不可以繞過(guò)的問題躯畴,我們見過(guò)很多的圖片格式民鼓,但是具體不同的圖片格式是如何定義的,又具有什么特點(diǎn)蓬抄,很多時(shí)候我們都沒有深入研究過(guò)丰嘉,下面我們就開始深入研究。感興趣的可以看我上面這篇嚷缭。
1. PNG供嚎、JPEG、BMP等幾種圖片格式詳解(一)—— PNG
2. PNG峭状、JPEG克滴、BMP等幾種圖片格式詳解(二)—— JPEG

BMP

以下部分內(nèi)容來(lái)自百度百科,還會(huì)有一部分是自己的見解优床,我寫這篇文章的目的是既讓大家可以了解百度上已有的知識(shí)劝赔,但是不用再去找百度,還有就是可以看到我關(guān)于這種圖片格式的深層次的了解胆敞,看完這篇着帽,包括我在內(nèi),會(huì)感覺到即使很小很小的一個(gè)知識(shí)點(diǎn)移层,深入以后都是非常深的仍翰,底層和深層次原理是我的最愛,這也是我寫博客的初衷和目的观话。

BMP(全稱Bitmap)是Windows操作系統(tǒng)中的標(biāo)準(zhǔn)圖像文件格式予借,可以分成兩類:設(shè)備相關(guān)位圖(DDB)和設(shè)備無(wú)關(guān)位圖(DIB),使用非常廣。它采用位映射存儲(chǔ)格式灵迫,除了圖像深度可選以外秦叛,不采用其他任何壓縮,因此瀑粥,BMP文件所占用的空間很大挣跋。BMP文件的圖像深度可選lbit、4bit狞换、8bit及24bit避咆。BMP文件存儲(chǔ)數(shù)據(jù)時(shí),圖像的掃描方式是按從左到右修噪、從下到上的順序牌借。由于BMP文件格式是Windows環(huán)境中交換與圖有關(guān)的數(shù)據(jù)的一種標(biāo)準(zhǔn),因此在Windows環(huán)境中運(yùn)行的圖形圖像軟件都支持BMP圖像格式割按。

BMP文件通常是不壓縮的膨报,所以它們通常比同一幅圖像的壓縮圖像文件格式要大很多。例如适荣,一個(gè)800×600的24位幾乎占據(jù)1.4MB空間现柠。因此它們通常不適合在因特網(wǎng)或者其它低速或者有容量限制的媒介上進(jìn)行傳輸。根據(jù)顏色深度的不同弛矛,圖像上的一個(gè)像素可以用一個(gè)或者多個(gè)字節(jié)表示够吩,它由n/8所確定(n是位深度,1字節(jié)包含8個(gè)數(shù)據(jù)位)丈氓。圖片瀏覽器等基于字節(jié)的ASCII值計(jì)算像素的顏色周循,然后從調(diào)色板中讀出相應(yīng)的值。


格式組成

典型的BMP圖像文件由四部分組成:

  • 位圖頭文件數(shù)據(jù)結(jié)構(gòu)万俗,它包含BMP圖像文件的類型湾笛、顯示內(nèi)容等信息;
  • 位圖信息數(shù)據(jù)結(jié)構(gòu)闰歪,它包含有BMP圖像的寬嚎研、高、壓縮方法库倘,以及定義顏色等信息临扮;
  • 調(diào)色板,這個(gè)部分是可選的教翩,有些位圖需要調(diào)色板杆勇,有些位圖,比如真彩色圖(24位的BMP)就不需要調(diào)色板饱亿;
  • 位圖數(shù)據(jù)蚜退,這部分的內(nèi)容根據(jù)BMP位圖使用的位數(shù)不同而不同闰靴,在24位圖中直接使用RGB,而其他的小于24位的使用調(diào)色板中顏色索引值关霸。

格式類型

位圖一共有兩種類型,即:設(shè)備相關(guān)位圖(DDB)和設(shè)備無(wú)關(guān)位圖(DIB - Device-Independent Bitmap)杰扫。DDB位圖在早期的Windows系統(tǒng)(Windows 3.0以前)中是很普遍的队寇,事實(shí)上它也是唯一的。然而章姓,隨著顯示器制造技術(shù)的進(jìn)步佳遣,以及顯示設(shè)備的多樣化,DDB位圖的一些固有的問題開始浮現(xiàn)出來(lái)了凡伊。比如零渐,它不能夠存儲(chǔ)(或者說(shuō)獲取)創(chuàng)建這張圖片的原始設(shè)備的分辨率系忙,這樣诵盼,應(yīng)用程序就不能快速的判斷客戶機(jī)的顯示設(shè)備是否適合顯示這張圖片。為了解決這一難題银还,微軟創(chuàng)建了DIB位圖格式风宁。

1. 設(shè)備無(wú)關(guān)位圖 (Device-Independent Bitmap)

DIB位圖包含下列的顏色和尺寸信息:

  • 原始設(shè)備(即創(chuàng)建圖片的設(shè)備)的顏色格式。
  • 原始設(shè)備的分辨率蛹疯。
  • 原始設(shè)備的調(diào)色板
  • 一個(gè)位數(shù)組戒财,由紅、綠捺弦、藍(lán)(RGB)三個(gè)值代表一個(gè)像素饮寞。
  • 一個(gè)數(shù)組壓縮標(biāo)志,用于表明數(shù)據(jù)的壓縮方案(如果需要的話)列吼。

以上這些信息保存在BITMAPINFO結(jié)構(gòu)中幽崩,該結(jié)構(gòu)由BITMAPINFOHEADER結(jié)構(gòu)和兩個(gè)或更多個(gè)RGBQUAD結(jié)構(gòu)所組成。BITMAPINFOHEADER結(jié)構(gòu)所包含的成員表明了圖像的尺寸寞钥、原始設(shè)備的顏色格式歉铝、以及數(shù)據(jù)壓縮方案等信息。RGBQUAD結(jié)構(gòu)標(biāo)識(shí)了像素所用到的顏色數(shù)據(jù)凑耻。

DIB位圖也有兩種形式太示,即:底到上型DIB(bottom-up),和頂?shù)较滦?code>DIB(top-down)香浩。底到上型DIB的原點(diǎn)(origin)在圖像的左下角类缤,而頂?shù)较滦虳IB的原點(diǎn)在圖像的左上角。如果DIB的高度值(由BITMAPINFOHEADER結(jié)構(gòu)中的biHeight成員標(biāo)識(shí))是一個(gè)正值邻吭,那么就表明這個(gè)DIB是一個(gè)底到上型DIB餐弱,如果高度值是一個(gè)負(fù)值,那么它就是一個(gè)頂?shù)较滦虳IB。注意:頂?shù)较滦偷腄IB位圖是不能被壓縮的膏蚓。

位圖的顏色格式是通過(guò)顏色面板值(planes)和顏色位值(bitcount)計(jì)算得來(lái)的瓢谢,顏色面板值永遠(yuǎn)是1,而顏色位值則可以是1驮瞧、4氓扛、8、16论笔、24采郎、32其中的一個(gè)。如果它是1狂魔,則表示位圖是一張單色位圖(譯者注:通常是黑白位圖蒜埋,只有黑和白兩種顏色,當(dāng)然它也可以是任意兩種指定的顏色)最楷,如果它是4整份,則表示這是一張VGA位圖,如果它是8籽孙、16皂林、24、或是32蚯撩,則表示該位圖是其他設(shè)備所產(chǎn)生的位圖础倍。如果應(yīng)用程序想獲取當(dāng)前顯示設(shè)備(或打印機(jī))的顏色位值(或稱位深度),可調(diào)用API函數(shù)GetDeviceCaps()胎挎,并將第二個(gè)參數(shù)設(shè)為BITSPIXEL即可沟启。

顯示設(shè)備的分辨率是以每米多少個(gè)像素來(lái)表明的,應(yīng)用程序可以通過(guò)以下三個(gè)步驟來(lái)獲取顯示設(shè)備或打印機(jī)的水平分辨率

  • 調(diào)用GetDeviceCaps()函數(shù)犹菇,指定第二個(gè)參數(shù)為HORZRES德迹。
  • 再次調(diào)用GetDeviceCaps()函數(shù),指定第二個(gè)參數(shù)為HORZSIZE揭芍。
  • 用第一個(gè)返回值除以第二個(gè)返回值胳搞。即:GetDeviceCaps(hDC,HORZRES)/GetDeviceCaps(hDC,HORZSIZE);

應(yīng)用程序也可以使用相同的三個(gè)步驟來(lái)獲取設(shè)備的垂直分辨率,不同之處只是要將HORZRES替換為VERTRES称杨,把HORZSIZE替換為VERTSIZE肌毅,即可。

應(yīng)用程序可以從一個(gè)DDB位圖創(chuàng)建出一個(gè)DIB位圖姑原,步驟是悬而,先初始化一些必要的結(jié)構(gòu),然后再調(diào)用GetDIBits()函數(shù)锭汛。不過(guò)笨奠,有些顯示設(shè)備有可能不支持這個(gè)函數(shù)袭蝗,你可以通過(guò)調(diào)用GetDeviceCaps()函數(shù)來(lái)確定一下(GetDeviceCaps()函數(shù)在調(diào)用時(shí)指定RC_DI_BITMAP作為RASTERCAPS的標(biāo)志)。

應(yīng)用程序可以用DIB去設(shè)置顯示設(shè)備上的像素(譯者注:也就是顯示DIB)般婆,方法是調(diào)用SetDIBitsToDevice()函數(shù)或調(diào)用StretchDIBits()函數(shù)到腥。同樣,有些顯示設(shè)備也有可能不支持以上這兩個(gè)函數(shù)蔚袍,這時(shí)你可以指定RC_DIBTODEV作為RASTERCAPS標(biāo)志乡范,然后調(diào)用GetDeviceCaps()函數(shù)來(lái)判斷該設(shè)備是否支持SetDIBitsToDevice()函數(shù)。也可以指定RC_STRETCHDIB作為RASTERCAPS標(biāo)志來(lái)調(diào)用GetDeviceCaps()函數(shù)页响,來(lái)判斷該設(shè)備是否支持StretchDIBits()函數(shù)篓足。

如果應(yīng)用程序只是要簡(jiǎn)單的顯示一個(gè)已經(jīng)存在的DIB位圖段誊,那么它只要調(diào)用SetDIBitsToDevice()函數(shù)就可以闰蚕。比如一個(gè)電子表格軟件,它可以打開一個(gè)圖表文件连舍,在窗口中簡(jiǎn)單的調(diào)用SetDIBitsToDevice()函數(shù)没陡,將圖形顯示在窗口中。但如果應(yīng)用程序要重復(fù)的繪制位圖的話索赏,則應(yīng)該使用BitBlt()函數(shù)盼玄,因?yàn)锽itBlt()函數(shù)的執(zhí)行速度要比SetDIBitsToDevice()函數(shù)快很多。

2. 設(shè)備相關(guān)位圖 (Device-Dependent Bitmaps)

設(shè)備相關(guān)位圖(DDB)之所以現(xiàn)在還被系統(tǒng)支持潜腻,只是為了兼容舊的Windows 3.0軟件埃儿,如果程序員現(xiàn)在要開發(fā)一個(gè)與位圖有關(guān)的程序吐葵,則應(yīng)該盡量使用或生成DIB格式的位圖践险。
DDB位圖是被一個(gè)單個(gè)結(jié)構(gòu)BITMAP所描述,這個(gè)結(jié)構(gòu)的成員標(biāo)明了該位圖的寬度恕齐、高度威鹿、設(shè)備的顏色格式等信息剃斧。

DDB位圖也有兩種類型,即:可廢棄的(discardable)DDB和不可廢棄的(nondiscardable)DDB忽你∮锥可廢棄的DDB位圖就是一種當(dāng)系統(tǒng)內(nèi)存缺乏,并且該位圖也沒有被選入設(shè)備描述表(DC)的時(shí)候科雳,系統(tǒng)就會(huì)把該DDB位圖從內(nèi)存中清除(即廢棄)根蟹。不可廢棄的DDB則是無(wú)論系統(tǒng)內(nèi)存多少都不會(huì)被系統(tǒng)清除的DDB。API函數(shù)CreateDiscardableBitmap()函數(shù)可用于創(chuàng)建可廢棄位圖糟秘。而函數(shù)CreateBitmap()娜亿、CreateCompatibleBitmap()、和CreateBitmapIndirect()可用于創(chuàng)建不可廢棄的位圖蚌堵。
應(yīng)用程序可以通過(guò)一個(gè)DIB位圖而創(chuàng)建一個(gè)DDB位圖买决,只要先初始化一些必要的結(jié)構(gòu)沛婴,然后再調(diào)用CreateDIBitmap()函數(shù)就可以。如果在調(diào)用該函數(shù)時(shí)指定了CBM_INIT標(biāo)志督赤,那么這一次調(diào)用就等價(jià)于先調(diào)用CreateCompatibleBitmap()創(chuàng)建當(dāng)前設(shè)備格式的DDB位圖嘁灯,然后又調(diào)用SetDIBits()函數(shù)轉(zhuǎn)換DIB格式到DDB格式。(可能有些設(shè)備并不支持SetDIBits()函數(shù)躲舌,你可以指定RC_DI_BITMAP作為RASTERCAPS的標(biāo)志丑婿,然后調(diào)用GetDeviceCaps()函數(shù)來(lái)判斷一下)。


對(duì)應(yīng)數(shù)據(jù)結(jié)構(gòu)

1. BMP文件組成

BMP文件由文件頭没卸、位圖信息頭羹奉、顏色信息和圖形數(shù)據(jù)四部分組成。

2. BMP文件頭(14字節(jié))

BMP文件頭數(shù)據(jù)結(jié)構(gòu)含有BMP文件的類型约计、文件大小和位圖起始位置等信息诀拭。

其結(jié)構(gòu)定義如下:

typedef struct tagBITMAPFILEHEADER
{
    WORD bfType;//位圖文件的類型,必須為BM(1-2字節(jié))
    DWORD bfSize;//位圖文件的大小煤蚌,以字節(jié)為單位(3-6字節(jié)耕挨,低位在前)
    WORD bfReserved1;//位圖文件保留字,必須為0(7-8字節(jié))
    WORD bfReserved2;//位圖文件保留字尉桩,必須為0(9-10字節(jié))
    DWORD bfOffBits;//位圖數(shù)據(jù)的起始位置筒占,以相對(duì)于位圖(11-14字節(jié),低位在前)
    //文件頭的偏移量表示蜘犁,以字節(jié)為單位
}__attribute__((packed)) BITMAPFILEHEADER;

具體如下圖所示翰苫。

3. 位圖信息頭(40字節(jié))

typedef struct tagBITMAPINFOHEADER{
DWORD biSize;//本結(jié)構(gòu)所占用字節(jié)數(shù)(15-18字節(jié))
LONG biWidth;//位圖的寬度,以像素為單位(19-22字節(jié))
LONG biHeight;//位圖的高度这橙,以像素為單位(23-26字節(jié))
WORD biPlanes;//目標(biāo)設(shè)備的級(jí)別奏窑,必須為1(27-28字節(jié))
WORD biBitCount;//每個(gè)像素所需的位數(shù),必須是1(雙色)析恋,(29-30字節(jié))
//4(16色)良哲,8(256色)16(高彩色)或24(真彩色)之一
DWORD biCompression;//位圖壓縮類型,必須是0(不壓縮)助隧,(31-34字節(jié))
//1(BI_RLE8壓縮類型)或2(BI_RLE4壓縮類型)之一
DWORD biSizeImage;//位圖的大小(其中包含了為了補(bǔ)齊行數(shù)是4的倍數(shù)而添加的空字節(jié))筑凫,以字節(jié)為單位(35-38字節(jié))
LONG biXPelsPerMeter;//位圖水平分辨率,每米像素?cái)?shù)(39-42字節(jié))
LONG biYPelsPerMeter;//位圖垂直分辨率并村,每米像素?cái)?shù)(43-46字節(jié))
DWORD biClrUsed;//位圖實(shí)際使用的顏色表中的顏色數(shù)(47-50字節(jié))
DWORD biClrImportant;//位圖顯示過(guò)程中重要的顏色數(shù)(51-54字節(jié))
}__attribute__((packed)) BITMAPINFOHEADER;

具體如下圖所示巍实。

4. 顏色表

顏色表用于說(shuō)明位圖中的顏色,它有若干個(gè)表項(xiàng)哩牍,每一個(gè)表項(xiàng)是一個(gè)RGBQUAD類型的結(jié)構(gòu)棚潦,定義一種顏色。RGBQUAD結(jié)構(gòu)的定義如下:

typedef struct tagRGBQUAD{
BYTE rgbBlue;//藍(lán)色的亮度(值范圍為0-255)
BYTE rgbGreen;//綠色的亮度(值范圍為0-255)
BYTE rgbRed;//紅色的亮度(值范圍為0-255)
BYTE rgbReserved;//保留膝昆,必須為0
}__attribute__((packed)) RGBQUAD;

顏色表中RGBQUAD結(jié)構(gòu)數(shù)據(jù)的個(gè)數(shù)有biBitCount來(lái)確定:

  • 當(dāng)biBitCount=1,4,8時(shí)丸边,分別有2,16,256個(gè)表項(xiàng)叠必;
  • 當(dāng)biBitCount=24時(shí),沒有顏色表項(xiàng)妹窖。

位圖信息頭和顏色表組成位圖信息纬朝,BITMAPINFO結(jié)構(gòu)定義如下:

typedef struct tagBITMAPINFO{
BITMAPINFOHEADER bmiHeader;//位圖信息頭
RGBQUAD bmiColors[1];//顏色表
}__attribute__((packed)) BITMAPINFO;

5. 位圖數(shù)據(jù)

位圖數(shù)據(jù)記錄了位圖的每一個(gè)像素值,記錄順序是在掃描行內(nèi)是從左到右骄呼,掃描行之間是從下到上共苛。位圖的一個(gè)像素值所占的字節(jié)數(shù):

  • 當(dāng)biBitCount=1時(shí),8個(gè)像素占1個(gè)字節(jié)蜓萄;
  • 當(dāng)biBitCount=4時(shí)隅茎,2個(gè)像素占1個(gè)字節(jié);
  • 當(dāng)biBitCount=8時(shí)嫉沽,1個(gè)像素占1個(gè)字節(jié)辟犀;
  • 當(dāng)biBitCount=24時(shí),1個(gè)像素占3個(gè)字節(jié),按順序分別為B,G,R耻蛇;

Windows規(guī)定一個(gè)掃描行所占的字節(jié)數(shù)必須是
4的倍數(shù)(即以long為單位)踪蹬,不足的以0填充胞此,

biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;

以上具體可以參考下圖臣咖。

參考文章

1. 百度百科
2. BMP文件格式詳解

后記

未完,待續(xù)~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末漱牵,一起剝皮案震驚了整個(gè)濱河市夺蛇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌酣胀,老刑警劉巖刁赦,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異闻镶,居然都是意外死亡甚脉,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門铆农,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)牺氨,“玉大人,你說(shuō)我怎么就攤上這事墩剖『锇迹” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵岭皂,是天一觀的道長(zhǎng)郊霎。 經(jīng)常有香客問我,道長(zhǎng)爷绘,這世上最難降的妖魔是什么书劝? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任进倍,我火速辦了婚禮,結(jié)果婚禮上购对,老公的妹妹穿的比我還像新娘背捌。我一直安慰自己,他們只是感情好洞斯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布毡庆。 她就那樣靜靜地躺著,像睡著了一般烙如。 火紅的嫁衣襯著肌膚如雪么抗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天亚铁,我揣著相機(jī)與錄音蝇刀,去河邊找鬼。 笑死徘溢,一個(gè)胖子當(dāng)著我的面吹牛吞琐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播然爆,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼站粟,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了曾雕?” 一聲冷哼從身側(cè)響起奴烙,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎剖张,沒想到半個(gè)月后切诀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搔弄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年幅虑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顾犹。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡倒庵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蹦渣,到底是詐尸還是另有隱情哄芜,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布柬唯,位于F島的核電站认臊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏锄奢。R本人自食惡果不足惜失晴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一剧腻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涂屁,春花似錦书在、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至帖族,卻和暖如春栈源,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背竖般。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工甚垦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涣雕。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓艰亮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親挣郭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子迄埃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容