版本記錄
版本號(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;
以上具體可以參考下圖臣咖。
參考文章
后記
未完,待續(xù)~~~