作者:
吉林大學 胡卓瑋一、位圖文件結(jié)構(gòu)- 位圖文件頭
- 位圖信息
2.1 位圖信息頭
2.2 顏色表 - 位圖數(shù)據(jù)
二、位圖文件讀寫操作
- 類的聲明
- 位圖的讀取
- 位圖讀取過程中的調(diào)色板的創(chuàng)建和調(diào)用
- 位圖的顯示
- 位圖的存儲
- 新位圖的創(chuàng)建
- 其它問題
三、CFG_DIB的使用
下載本文配套代碼
關于位圖文件操作的資料很多。為了方便開發(fā)人員的工作,寫下本文,介紹了位圖文件結(jié)構(gòu),在此基礎之上設計了通用類CFG_DIB,用于進行位圖文件的讀寫操作。
一、位圖文件結(jié)構(gòu)
位圖文件由三部分組成:文件頭 + 位圖信息 + 位圖像素數(shù)據(jù)
1、位圖文件頭。位圖文件頭主要用于識別位圖文件。以下是位圖文件頭結(jié)構(gòu)的定義:
typedef struct tagBITMAPFILEHEADER { // bmfh
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
其中的bfType值應該是“BM”(0x4d42),標志該文件是位圖文件。bfSize的值是位圖文件的大小。
2、位圖信息中所記錄的值用于分配內(nèi)存,設置調(diào)色板信息,讀取像素值等。
以下是位圖信息結(jié)構(gòu)的定義:
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
可見位圖信息也是由兩部分組成的:位圖信息頭 + 顏色表
2.1位圖信息頭。位圖信息頭包含了單個像素所用字節(jié)數(shù)以及描述顏色的格式,此外還包括位圖的寬度、高度、目標設備的位平面數(shù)、圖像的壓縮格式。以下是位圖信息頭結(jié)構(gòu)的定義:
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;
下表是對結(jié)構(gòu)體當中各個成員的說明:
結(jié)構(gòu)成員
| 說 明 |
biSize | 結(jié)構(gòu)BITMAPINFOHEADER的字節(jié)數(shù),即sizeof(BITMAPINFOHEADER)* |
biWidth
| 以像素為單位的圖像寬度* |
biHeight
| 以像素為單位的圖像長度* |
biplanes
| 目標設備的位平面數(shù) |
biBitCount
| 每個像素的位數(shù)*(1) |
biCompression
| 圖像的壓縮格式(這個值幾乎總是為0) |
biSizeImage
| 以字節(jié)為單位的圖像數(shù)據(jù)的大小(對BI_RGB壓縮方式而言) |
biXPelsPermeter
| 水平方向上的每米的像素個數(shù) |
biYpelsPerMeter
| 垂直方向上的每米的像素個數(shù) |
biClrused
| 調(diào)色板中實際使用的顏色數(shù)(2) |
biClrImportant
| 現(xiàn)實位圖時必須的顏色數(shù)(3) |
說明:*是需要加以注意的部分,因為它們是我們在進行位圖操作時經(jīng)常參考的變量
(1)對于每個像素的字節(jié)數(shù),分別有一下意義:
0,用在JPEG格式中
1,單色圖,調(diào)色板中含有兩種顏色,也就是我們通常說的黑白圖片
4,16色圖
8,256色圖,通常說的灰度圖
16,64K圖,一般沒有調(diào)色板,圖像數(shù)據(jù)中每兩個字節(jié)表示一個像素,5個或6個位表示一個RGB分量
24,16M真彩色圖,一般沒有調(diào)色板,圖像數(shù)據(jù)中每3個字節(jié)表示一個像素,每個字節(jié)表示一個RGB分量
32,4G真彩色,一般沒有調(diào)色板,每4個字節(jié)表示一個像素,相對24位真彩圖而言,加入了一個透明度,即RGBA模式
(2)這個值通常為0,表示使用biBitCount確定的全部顏色,例外是使用的顏色樹木小于制定的顏色深度的顏色數(shù)目的最大值。
(3)這個值通常為0,表示所有的顏色都是必需的
2.2顏色表。顏色表一般是針對16位一下的圖像而設置的,對于16位和16位以上的圖像,由于其位圖像素數(shù)據(jù)中直接對對應像素的RGB(A)顏色進行描述,因而省卻了調(diào)色板。而對于16位一下的圖像,由于其位圖像素數(shù)據(jù)中記錄的只是調(diào)色板索引值,因而需要根據(jù)這個索引到調(diào)色板去取得相應的RGB(A)顏色。顏色表的作用就是創(chuàng)建調(diào)色板。
下圖是帶調(diào)色板和不帶調(diào)色板的位圖的簡單示意圖

圖1 帶調(diào)色板和不帶調(diào)色板位圖之間的區(qū)別
顏色表是由顏色表項組成的,顏色表項結(jié)構(gòu)的定義如下:
typedef struct tagRGBQUAD { // rgbq
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
其中需要注意的問題是,RGBQUAD結(jié)構(gòu)中的顏色順序是BGR,而不是平常的RGB。
3、位圖數(shù)據(jù)。最后,在位圖文件頭、位圖信息頭、位圖顏色表之后,便是位圖的主體部分:位圖數(shù)據(jù)。根據(jù)不同的位圖,位圖數(shù)據(jù)所占據(jù)的字節(jié)數(shù)也是不同的,比如,對于8位位圖,每個字節(jié)代表了一個像素,對于16位位圖,每兩個字節(jié)代表了一個像素,對于24位位圖,每三個字節(jié)代表了一個像素,對于32位位圖,每四個字節(jié)代表了一個像素。
二、位圖文件讀寫操作
認識了位圖文件的結(jié)構(gòu)以后,對特定位圖文件進行讀寫操作就顯得簡單了。本文附帶的源代碼中包含了一個能夠方便進行位圖讀寫操作的C++類。以下給出該類的使用參考,對于實現(xiàn)代碼中的關鍵部分做出了講解。
1、類的聲明
class CFG_DIB : public CObject
{
public:
//默認構(gòu)造函數(shù)
CFG_DIB();
//構(gòu)造函數(shù),根據(jù)圖象寬和高,以及記錄每個象素所需字節(jié)數(shù)來初始化
CFG_DIB(int width, int height, int nBitCounts);
virtual ~CFG_DIB();
public:
HBITMAP m_hBitmap;
LPBYTE m_lpDIBits; //DIB位的起始位置
LPBITMAPINFOHEADER m_lpBMPHdr; //BITMAPINFOHEADER信息
LPVOID m_lpvColorTable; //顏色表信息
HPALETTE m_hPalette; //條調(diào)色板
private:
DWORD m_dwImageSize; //非BITMAPINFOHEADER或BITMAPFILEHEADER的位
int m_nColorEntries; //顏色表項的個數(shù)
//顯示參數(shù)
public:
CPoint m_Dest; //目的矩形域的左上角坐標
CSize m_DestSize; //顯示矩形的寬度和高度
CPoint m_Src; //原矩形左下角坐標
CSize m_SrcSize; //原矩形寬度和高度
public:
void InitDestroy(); //初始化變量
void ComputePaletteSize(int nBitCounts); //計算調(diào)色板大小
void ComputeImage(); //計算圖象大小
//從BMP文件中讀入DIB信息
BOOL ReadFile(CFile* pFile);
//從BMP文件中讀入DIB信息,與ReadFile不同的是使用CreateSection創(chuàng)建位圖位
BOOL ReadSection(CFile* pFile, CDC* pDC = NULL);
//將DIB寫入文件,保存成BMP圖片格式
BOOL WriteFile(CFile* pFile);
//創(chuàng)建新的位圖文件,根據(jù)參數(shù)width,height,nBitCounts分配內(nèi)存空間
BOOL NewFile(int width, int height, int nBitCounts);
//關閉位圖文件
BOOL CloseFile();
//顯示位圖
BOOL Display(CDC* pDC);
HBITMAP CreateBitmap(CDC* pDC); //用DIB創(chuàng)建DDB
HBITMAP CreateSection(CDC* pDC = NULL); //創(chuàng)建位圖位數(shù)據(jù),即象素數(shù)據(jù)
//如果DIB沒有顏色表,可以用邏輯調(diào)色板
BOOL SetLogPalette(CDC* pDC);
//如果DIB有顏色表,可以創(chuàng)建系統(tǒng)調(diào)色板
BOOL SetWinPalette();
//把DIB對象的邏輯調(diào)色板選進設備環(huán)境里,然后實現(xiàn)調(diào)色板
UINT UseLogPalette(CDC* pDC);
//得到BitmapInfoHeader的大小,包含顏色表數(shù)據(jù)
int GetHeaderSize()
{
return sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorEntries;
}
//得到圖像的高度
int GetHeight()
{
if(m_lpBMPHdr == NULL) return 0;
return m_lpBMPHdr->biHeight;
}
//得到圖像的寬度
int GetWidth()
{
if(m_lpBMPHdr == NULL) return 0;
return m_lpBMPHdr->biWidth;
}
//得到圖像的大小
int GetImageSize()
{
return m_dwImageSize;
}
long GetLineBit(); //得到一行的象素數(shù)
};
2、位圖的讀取。CFG_DIB提供了兩個從位圖文件讀取位圖數(shù)據(jù)的方法:ReadFile和ReadSection,二者不同之處,前者使用動態(tài)分配內(nèi)存的方法初始化存儲位位圖數(shù)據(jù)的指針,后者則使用API函數(shù),根據(jù)位圖信息初始化存儲位圖數(shù)據(jù)的指針。
方法1m_lpDIBits = (LPBYTE) new char[m_dwImageSize];
方法2m_hBitmap = ::CreateDIBSection(pDC->GetSafeHdc(),
(LPBITMAPINFO) m_lpBMPHdr, DIB_RGB_COLORS,
(LPVOID*) &m_lpDIBits, NULL, 0);
3、位圖讀取過程中的調(diào)色板的創(chuàng)建和調(diào)用。關于調(diào)色板的詳細情況,本文不作詳細介紹,只是對讀取位圖的過程中需要調(diào)用的對調(diào)色板進行操作的相關函數(shù)進行說明。
讀取文件的過程中,計算出調(diào)色板大小,然后調(diào)用創(chuàng)建調(diào)色板函數(shù):
ComputePaletteSize(m_lpBMPHdr->biBitCount);
SetWinPalette();
在顯示位圖之前,設置調(diào)色板:
if(m_hPalette != NULL) {
::SelectPalette(pDC->GetSafeHdc(), m_hPalette, TRUE);
}
4、位圖的顯示。位圖的顯示還是調(diào)用Windows的API函數(shù)來進行,需要傳遞的參數(shù)包括當前位圖信息頭,位圖數(shù)據(jù)等:
::StretchDIBits(pDC->GetSafeHdc(), m_Dest.x, m_Dest.y,
m_DestSize.cx, m_DestSize.cy,
m_Src.x, m_Src.y,
m_SrcSize.cx, m_SrcSize.cy,
m_lpDIBits, (LPBITMAPINFO) m_lpBMPHdr,
DIB_RGB_COLORS, SRCCOPY);
其中的m_Dest,m_DestSize,m_Src,m_SrcSize分別代表了圖像在當前設備上顯示的左上角坐標和范圍以及需要顯示的源圖像的左下角坐標和范圍。此處需要說明的是,位圖數(shù)據(jù)的字節(jié)數(shù)組是從圖像的最下面一行開始逐行想上存儲的,所以用戶在選取源位圖的現(xiàn)實范圍的時候需要特別注意!
m_Dest,m_DestSize,m_Src,m_SrcSize需要在現(xiàn)實之前設置好。
5、位圖的存儲。位圖的存儲用WriteFile實現(xiàn)。
6、新位圖的創(chuàng)建。新位圖的創(chuàng)建由NewFile實現(xiàn)。需要的參數(shù)是位圖的寬度、高度、以及位圖像素占用的位數(shù)。
7、其它問題。存取位圖數(shù)據(jù)的字節(jié)數(shù)組有個問題需要引起開發(fā)人員的注意:字節(jié)數(shù)組中每個掃描行的字節(jié)數(shù)必需是4的倍數(shù),如果不足要用0補齊。
以下是處理的辦法:
DWORD dwBytes = ((DWORD) m_lpBMPHdr->biWidth * m_lpBMPHdr->biBitCount) / 32;
if(((DWORD) m_lpBMPHdr->biWidth * m_lpBMPHdr->biBitCount) % 32) {
dwBytes++;
}
dwBytes *= 4;
m_dwImageSize = dwBytes * m_lpBMPHdr->biHeight;
這段代碼按照要求算出了用于記錄圖像數(shù)據(jù)的字節(jié)數(shù)組的大小。
三、CFG_DIB的使用 以下是CFG_DIB的使用示例代碼。
#include "fg_dib.h"
CFG_DIB m_fgdib;
//new file
m_fgdib.NewFile(width, height, nbitnum);
//open file
CFile* pf;
pf = new CFile;
pf->Open(sFileName, CFile::modeRead);
m_fgdib.ReadFile(pf);
pf->Close();
delete pf;
//draw BMP
m_fgdib.m_Dest.x = 0;
m_fgdib.m_Dest.y = 0;
m_fgdib.m_DestSize.cx = m_fgdib.GetWidth();
m_fgdib.m_DestSize.cy = m_fgdib.GetHeight();
m_fgdib.m_Src.x = 0;
m_fgdib.m_Src.y = 0;
m_fgdib.m_SrcSize.cx = m_fgdib.GetWidth();
m_fgdib.m_SrcSize.cy = m_fgdib.GetHeight();
CDC* pDC = GetDC();
m_fgdib.Display(pDC);
//close BMP
m_fgdib.CloseFile();
如果您在閱讀文章和使用代碼過程中遇到的問題,請與作者聯(lián)系:
吉林省長春市西民主大街6號地球探測科學與技術學院2001級碩士研究生(130026)
歡迎訪問作者的主頁:
Forevergis.6to23.comfrom:
http://www.vckbase.com/document/viewdoc/?id=674