熱線電話:0755-23712116
郵(you)箱:contact@legoupos.cn
地(di)址:深(shen)圳市寶安區(qu)沙井街道(dao)后亭(ting)茅洲山工業園工業大(da)廈(sha)全至科技創新園科創大(da)廈(sha)2層2A
設備無關位圖(tu)(Device Independent Bitmap)是可(ke)(ke)以保(bao)存在(zai)磁(ci)盤的(de)位圖(tu)文(wen)件(jian)(jian),可(ke)(ke)以從磁(ci)盤讀取到(dao)內存或(huo)者(zhe)從內存保(bao)存到(dao)磁(ci)盤上。它的(de)文(wen)件(jian)(jian)結(jie)構是標準化的(de),可(ke)(ke)以在(zai)Windows/Linux/Unix等(deng)平臺上顯示相(xiang)同的(de)效果。本文(wen)主要介紹了
1. 如(ru)果將(jiang)位圖文件從(cong)磁盤讀到內(nei)存中
2. 在(zai)內存中對(dui)位(wei)圖文件進行操作后(hou),如何將位(wei)圖保存到(dao)磁盤
1 讀取位圖到內存中
1.1 DIB文件結構
要(yao)將(jiang)位(wei)圖文件(jian)(.bmp)從磁盤(pan)讀取(qu)到內(nei)存,首(shou)先要(yao)了解其(qi)文件(jian)結(jie)構(gou)。DIB的(de)文件(jian)組成(cheng)有以下(xia)4個部分:
1. 文(wen)(wen)件(jian)表頭,主要包含了(le)文(wen)(wen)件(jian)的類型(必(bi)須是BM),文(wen)(wen)件(jian)的大小(xiao)(所占(zhan)用的字節數)和位(wei)圖的像(xiang)素矩陣的便宜(yi)量。
2. 信(xin)息(xi)表(biao)頭,包含(han)了兩部(bu)分(fen)內容(rong):位圖的(de)相關(guan)信(xin)息(xi)(位圖的(de)大小、位深度、位面(mian)數、壓縮和編碼等(deng))和指(zhi)向RGB顏色表(biao)(調色盤)的(de)指(zhi)針(zhen)。
3. RGB色彩(cai)對照表,也就是調色板,不(bu)一定會有。16位及以上直(zhi)接使(shi)用RGB通道(dao)表示顏(yan)色,一般不(bu)需要(yao)調色板。
4. 位圖(tu)的像(xiang)(xiang)素(su)信息矩陣,表示具(ju)體(ti)的像(xiang)(xiang)素(su)。1,4,8位顏色(se),保存的是調色(se)板(ban)的索引(yin),具(ju)體(ti)的顏色(se)根據索引(yin)在調色(se)板(ban)中查(cha)找;16位及其以(yi)上不(bu)使用(yong)調色(se)板(ban),直接使用(yong)RGB組成像(xiang)(xiang)素(su)顏色(se)。
1.2 在Windows下DIB的內存結構
要將DIB數據讀取到內存,就需要在內存中分配相應的空間。Windows提供了幾種結構體,結構體中的字段對應著DIB文件的各個信息值,具體如下
引用自 //blog.csdn.net/wenzhou1219/article/details/26162869
將DIB讀取到內存只需要將磁盤數據填充到相應到結構體即可。在磁盤上DIB需要連續的結構存儲,在內存中則不需要連續的存儲空間,可以分段將數據讀取到相應的結構體中。
讀(du)取DIB到內(nei)存的具體(ti)步(bu)驟:
1. 將文件頭(tou)信息讀取到(dao)BITMAPFILEHEADER結構(gou)體中。
2. 將位圖(tu)頭信息(xi)讀取到BITMAPINFOHEADER結構體中(zhong)。
3. 如果有(you)調色板,則將其信(xin)息讀取到RGBQUAD中。
4. 讀取(qu)位圖像(xiang)素信息到(dao)像(xiang)素矩(ju)陣中(zhong)。
fp.Read(&bmfileHeader, sizeof(BITMAPFILEHEADER)); // 讀取BMP文(wen)件頭
...
//讀取文(wen)件(jian)信息(xi)頭
ret = fp.Read(&bmHeader, sizeof(BITMAPINFOHEADER));
...
fp.Read(m_dibBits, GetBodySize()); //讀取像(xiang)素信息
1.3 結構體各字段信(xin)息
BITMAPFILEHEADER
代表(biao)文件(jian)頭信息的(de)結構(gou)體BITMAPFILEHEADER的(de)聲明如下:
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER
其中,
· bfType是文件類型,該字段必須是BM,如果(guo)不(bu)是(shi)則(ze)說(shuo)明該文件不(bu)是(shi)DIB。
· bfSize是位圖(tu)文件的大小(字節數(shu))
· bfReserved1和bfReserved2是保留字段
· bfOffBits 從BITMAPFILEHEADER的(de)起(qi)始位置(zhi)到位圖(tu)像素的(de)字(zi)節偏(pian)移量。
BITMAPINFO
在上面提到,位圖信息和位圖的調色板是存放在同一個(ge)結(jie)構體(ti)中的,該結(jie)構體(ti)就是BITMAPINFO,其聲(sheng)明如下
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO, *PBITMAPINFO;
bmiHeader是位圖頭信息
bmiColors是調色板(ban)
BITMAPINFOHEADER
位(wei)圖的頭信(xin)息結(jie)構BITMAPINFOHEADER,該(gai)結(jie)構包(bao)含了DIB的尺寸和顏(yan)色格式等信(xin)息,聲明如下
ypedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER
其中,
· biSize是該結構(gou)體(ti)所(suo)占用(yong)的字節說
· biWidth DIB的寬(kuan)(以像(xiang)素為單位),如果biCompression是(shi)(shi)BI_JPEG或(huo)者(zhe)BI_PNG,則(ze)biWidth是(shi)(shi)解壓縮后JPEG或(huo)者(zhe)PNG圖像(xiang)的寬(kuan)度(du)。
· biHeight,DIB的(de)高(以(yi)像素為單位)。
o 如果biHeight是正的(de),則DIB的(de)像(xiang)(xiang)素是按照從(cong)下往上(bottom-up,也就是像(xiang)(xiang)素數組的(de)第一行(xing)保存的(de)實際是DIB的(de)最后一行(xing)像(xiang)(xiang)素值),圖像(xiang)(xiang)源(yuan)點在左(zuo)下角。
o 如果(guo)biHeight是(shi)負的,則DIB的像素是(shi)按照從上往(wang)下(xia)保(bao)存的(up-bottom),而且像素的數據是(shi)不能被壓縮(suo)的其biCompression必須是(shi)BI_RGB或者BI_FIELDS
o 如(ru)果biCompression是BI_JPEG或者BI_PNG,則biHeight是解壓縮后JPEG或者PNG的高
· biPlanes 目(mu)標設備的(de)平面(mian)數(shu),總(zong)是設為1.
· biBitCount,每(mei)(mei)個(ge)像素(su)(su)所占用(yong)(yong)的位數。0,表示(shi)JPEG或者(zhe)PNG指定(ding)每(mei)(mei)個(ge)像素(su)(su)所占用(yong)(yong)的位數。還可以是1,4,8,16,24,32。
· biCompression,數據的壓縮(suo)方(fang)法(fa)(up-down的DIB不能被壓縮(suo)),可(ke)以是以下值:
o BI_RGB / BI_FIELDS未被壓縮
o BI_RLE4 / BI_RLE8 使用游程長(chang)度編碼 (RLE,run-length encode)
o BI_JPEG / BI_PNG 指示該圖像(xiang)是(shi)JPEG或者PNG圖像(xiang)。
· biSizeImage 圖像的字節數,對(dui)于BI_RGB的DIB其(qi)值為(wei)0.
· biXPelsPerMeter / biYPelsPerMeter 顯示該DIB的(de)目標(biao)設(she)備所需的(de)分(fen)辨(bian)率(單位是(shi)像(xiang)素每米)
· biClrUsed DIB實際使(shi)用調色(se)板中的(de)顏(yan)色(se)個數,通(tong)常(chang)為0表(biao)示使(shi)用調色(se)板中的(de)全部顏(yan)色(se)。
· biClrImportant 顯示DIB所必須(xu)的顏(yan)色(se)個數,通常為(wei)0表示全(quan)部顏(yan)色(se)都(dou)是(shi)必須(xu)的。
詳細的解釋參見 //msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx
RGBQUAD
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
注意,其存(cun)儲(chu)順序是BGR。
1.4 DIB的結(jie)構實例(li)
· 首(shou)先是(shi)文件的(de)頭信(xin)息 BITMAPFILEINFO 共有14個(ge)(ge)字(zi)節(0x00-0x0D),其起始的(de)兩個(ge)(ge)字(zi)節為0x4d42(大端存儲,高位在(zai)前)表示文件類型為BM,最后4個(ge)(ge)字(zi)節 0x0076是(shi)位圖的(de)像(xiang)(xiang)素信(xin)息相對于文件頭的(de)偏移量,也就是(shi)從0x0076為圖像(xiang)(xiang)的(de)像(xiang)(xiang)素信(xin)息。
· 下面是(shi)位圖的頭信(xin)息 BITMAPINFOHEADER共有40個字(zi)(zi)節(jie)(0x0E-0x35),起始的4個字(zi)(zi)節(jie)是(shi)0x0028就是(shi)該(gai)結構(gou)體(ti)的大小,緊接(jie)著的4個字(zi)(zi)節(jie)是(shi)圖像(xiang)的寬0x0020
· 跟著是圖像的(de)調(diao)色(se)板(ban),0x36 - 0x75。調(diao)色(se)板(ban)共有16種(zhong)顏色(se),也就是說(shuo)有調(diao)色(se)板(ban)中有16個RGBQUAD,其大小為16 *4.
· 最后是位(wei)圖的(de)數據 0x76-0x275
1.5 讀取
CFile fp(dibName, CFile::modeRead | CFile::typeBinary);
BITMAPFILEHEADER bmfileHeader;
BITMAPINFOHEADER bmHeader;
ULONGLONG headpos;
int paletteSize = 0;
int ret, cbHeaderSize;
headpos = fp.GetPosition(); // 獲(huo)取(qu)文件(jian)指針的位置
ret = fp.Read(&bmfileHeader, sizeof(BITMAPFILEHEADER)); // 讀取BMP文件頭
if (bmfileHeader.bfType != 0x4d42) //判斷文件(jian)(jian)類型(xing)(xing)標頭是不(bu)是x4d42,表(biao)示該文件(jian)(jian)為BMP類型(xing)(xing)文件(jian)(jian)
{
AfxMessageBox(_T("文(wen)件(jian)不(bu)是bmp!"));
return;
}
//讀取(qu)文件信(xin)息(xi)頭(tou)
ret = fp.Read(&bmHeader, sizeof(BITMAPINFOHEADER));
// 計算RGBQUAD的大(da)小
switch (bmHeader.biBitCount)
{
case 1:
paletteSize = 2;
break;
case 4:
paletteSize = 16;
break;
case 8:
paletteSize = 256;
break;
}
// 為BITMAPINFO分配存儲(chu)空間
cbHeaderSize = sizeof(BITMAPINFOHEADER)+paletteSize * sizeof(RGBQUAD);
m_dibInfo = (BITMAPINFO*) new char[cbHeaderSize];
m_dibInfo->bmiHeader = bmHeader;
if (paletteSize) //是(shi)否有調色板
{
ret = fp.Read(&(m_dibInfo->bmiColors[0]), paletteSize * sizeof(RGBQUAD));
if (ret != int(paletteSize * sizeof(RGBQUAD) ) )
{
delete[] m_dibInfo;
m_dibInfo = NULL;
return;
}
}
//為(wei)像素數(shu)組分配存儲空間,大(da)小(xiao)由GetBodySize決定
m_dibBits = (void*) new char[GetBodySize()];
fp.Seek(headpos + bmfileHeader.bfOffBits, CFile::begin); // 將(jiang)文件指針移動(dong)到DIB像素數組
ret = fp.Read(m_dibBits, GetBodySize());
if (ret != int(GetBodySize()))
{
delete[] m_dibInfo;
delete[] m_dibBits;
m_dibInfo = NULL;
m_dibBits = NULL;
}
fp.Close();
知(zhi)道了DIB的(de)(de)文件結構后,讀取其(qi)(qi)到內(nei)存(cun)還是(shi)(shi)挺簡(jian)單(dan)的(de)(de),需要注意的(de)(de)是(shi)(shi)DIB的(de)(de)像(xiang)素數組(zu)的(de)(de)大小(xiao)。由于DIB的(de)(de)寬度需要時4的(de)(de)倍(bei)數,不是(shi)(shi)的(de)(de)話需要填充(chong)0將其(qi)(qi)湊成4的(de)(de)倍(bei)數,所以其(qi)(qi)像(xiang)素數組(zu)的(de)(de)大小(xiao)不能簡(jian)單(dan)的(de)(de)width * height * biBitCount / 8,其(qi)(qi)中biBitCount是(shi)(shi)每個像(xiang)素占用的(de)(de)位數。其(qi)(qi)具體的(de)(de)計(ji)算方法如下
1. 首先計算一行所占用的字節數bytesPerLine
bytesPerLine =((m_dibInfo->bmiHeader.biWidth * m_dibInfo->bmiHeader.biBitCount + 31) /32 ) * 4
2. 將bytesPerLine乘以圖像的高
bytesPerLine *m_dibInfo->bmiHeader.biHeight
1.6 總結
本文主(zhu)要對(dui)DIB的文件結(jie)構以及(ji)其(qi)對(dui)應的內存中(zhong)的結(jie)構體做了一個(ge)總結(jie),并對(dui)一個(ge)具(ju)體的DIB16進制數據(ju)結(jie)構進行分析(xi),最后(hou)實現了如何將一個(ge)DIB數據(ju)讀取到內存中(zhong)。
一直(zhi)對(dui)(dui)位(wei)圖(tu)結構不是很了(le)解,趁著在(zai)公(gong)司實習沒有具體的(de)工作安排,對(dui)(dui)DIB的(de)結構作了(le)個總結。至于如(ru)何(he)將處(chu)理后的(de)位(wei)圖(tu)數據寫(xie)回磁盤文(wen)件,在(zai)知道(dao)位(wei)圖(tu)結構的(de)情況下(xia),只需要填充相應的(de)結構字段(duan)就(jiu)行了(le),需要注意的(de)還(huan)是位(wei)圖(tu)的(de)寬需要是4的(de)倍數,不是的(de)話(hua)要用0補齊。