BMP位圖文件結(jié)構(gòu)及平滑縮放
發(fā)表時(shí)間:2024-06-18 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]西安交通大學(xué)流體機(jī)械研究所張義云 用普通方法顯示BMP位圖,占內(nèi)存大,速度慢,在圖形縮小時(shí),失真嚴(yán)重,在低顏色位數(shù)的設(shè)備上顯示高顏色位數(shù)的圖形圖形時(shí)失真大。本文采用視頻函數(shù)顯示BMP位圖,可以消除以上的缺點(diǎn)。一、BMP文件結(jié)構(gòu)BMP文件組成BMP文件由文件頭、位圖信息頭、顏色信息和圖形數(shù)據(jù)四部...
西安交通大學(xué)流體機(jī)械研究所
張義云
用普通方法顯示BMP位圖,占內(nèi)存大,速度慢,在圖形縮小時(shí),失真嚴(yán)重,在低顏色位數(shù)的設(shè)備上顯示高顏色位數(shù)的圖形圖形時(shí)失真大。本文采用視頻函數(shù)顯示BMP位圖,可以消除以上的缺點(diǎn)。
一、BMP文件結(jié)構(gòu)
BMP文件組成
BMP文件由文件頭、位圖信息頭、顏色信息和圖形數(shù)據(jù)四部分組成。
BMP文件頭
BMP文件頭數(shù)據(jù)結(jié)構(gòu)含有BMP文件的類型、文件大小和位圖起始位置等信息。
其結(jié)構(gòu)定義如下:
typedef struct tagBITMAPFILEHEADER
{
WORDbfType; // 位圖文件的類型,必須為BM
DWORD bfSize; // 位圖文件的大小,以字節(jié)為單位
WORDbfReserved1; // 位圖文件保留字,必須為0
WORDbfReserved2; // 位圖文件保留字,必須為0
DWORD bfOffBits; // 位圖數(shù)據(jù)的起始位置,以相對(duì)于位圖
// 文件頭的偏移量表示,以字節(jié)為單位
} BITMAPFILEHEADER;
3. 位圖信息頭
BMP位圖信息頭數(shù)據(jù)用于說明位圖的尺寸等信息。
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // 本結(jié)構(gòu)所占用字節(jié)數(shù)
LONGbiWidth; // 位圖的寬度,以像素為單位
LONGbiHeight; // 位圖的高度,以像素為單位
WORD biPlanes; // 目標(biāo)設(shè)備的級(jí)別,必須為1
WORD biBitCount// 每個(gè)像素所需的位數(shù),必須是1(雙色),
// 4(16色),8(256色)或24(真彩色)之一
DWORD biCompression; // 位圖壓縮類型,必須是 0(不壓縮),
// 1(BI_RLE8壓縮類型)或2(BI_RLE4壓縮類型)之一
DWORD biSizeImage; // 位圖的大小,以字節(jié)為單位
LONGbiXPelsPerMeter; // 位圖水平分辨率,每米像素?cái)?shù)
LONGbiYPelsPerMeter; // 位圖垂直分辨率,每米像素?cái)?shù)
DWORD biClrUsed;// 位圖實(shí)際使用的顏色表中的顏色數(shù)
DWORD biClrImportant;// 位圖顯示過程中重要的顏色數(shù)
} BITMAPINFOHEADER;
4. 顏色表
顏色表用于說明位圖中的顏色,它有若干個(gè)表項(xiàng),每一個(gè)表項(xiàng)是一個(gè)RGBQUAD類型的結(jié)構(gòu),定義一種顏色。RGBQUAD結(jié)構(gòu)的定義如下:
typedef struct tagRGBQUAD {
BYTErgbBlue;// 藍(lán)色的亮度(值范圍為0-255)
BYTErgbGreen; // 綠色的亮度(值范圍為0-255)
BYTErgbRed; // 紅色的亮度(值范圍為0-255)
BYTErgbReserved;// 保留,必須為0
} RGBQUAD;
顏色表中RGBQUAD結(jié)構(gòu)數(shù)據(jù)的個(gè)數(shù)有biBitCount來確定:
當(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]; // 顏色表
} 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é);
Windows規(guī)定一個(gè)掃描行所占的字節(jié)數(shù)必須是
4的倍數(shù)(即以long為單位),不足的以0填充,
一個(gè)掃描行所占的字節(jié)數(shù)計(jì)算方法:
DataSizePerLine= (biWidth* biBitCount+31)/8;
// 一個(gè)掃描行所占的字節(jié)數(shù)
DataSizePerLine= DataSizePerLine/4*4; // 字節(jié)數(shù)必須是4的倍數(shù)
位圖數(shù)據(jù)的大小(不壓縮情況下):
DataSize= DataSizePerLine* biHeight;
二、BMP位圖一般顯示方法
1. 申請(qǐng)內(nèi)存空間用于存放位圖文件
GlobalAlloc(GHND,F(xiàn)ileLength);
2. 位圖文件讀入所申請(qǐng)內(nèi)存空間中
LoadFileToMemory( mpBitsSrc,mFileName);
3. 在OnPaint等函數(shù)中用創(chuàng)建顯示用位圖
用CreateDIBitmap()創(chuàng)建顯示用位圖,用CreateCompatibleDC()創(chuàng)建兼容DC,
用SelectBitmap()選擇顯示位圖。
4. 用BitBlt或StretchBlt等函數(shù)顯示位圖
5. 用DeleteObject()刪除所創(chuàng)建的位圖
以上方法的缺點(diǎn)是: 1)顯示速度慢; 2) 內(nèi)存占用大; 3) 位圖在縮小顯示時(shí)圖形失真大,(可通過安裝字體平滑軟件來解決); 4) 在低顏色位數(shù)的設(shè)備上(如256顯示模式)顯示高顏色位數(shù)的圖形(如真彩色)圖形失真嚴(yán)重。
三、BMP位圖縮放顯示
用DrawDib視頻函數(shù)來顯示位圖,內(nèi)存占用少,速度快,而且還可以對(duì)圖形進(jìn)行淡化(Dithering)處理。淡化處理是一種圖形算法,可以用來在一個(gè)支持比圖像所用顏色要少的設(shè)備上顯示彩色圖像。BMP位圖顯示方法如下:
1. 打開視頻函數(shù)DrawDibOpen(),一般放在在構(gòu)造函數(shù)中
2. 申請(qǐng)內(nèi)存空間用于存放位圖文件
GlobalAlloc(GHND,F(xiàn)ileLength);
3. 位圖文件讀入所申請(qǐng)內(nèi)存空間中----
LoadFileToMemory( mpBitsSrc,mFileName);
4. 在OnPaint等函數(shù)中用DrawDibRealize(),DrawDibDraw()顯示位圖
5. 關(guān)閉視頻函數(shù)DrawDibClose(),一般放在在析構(gòu)函數(shù)中
以上方法的優(yōu)點(diǎn)是: 1)顯示速度快; 2) 內(nèi)存占用少; 3) 縮放顯示時(shí)圖形失真小,4) 在低顏色位數(shù)的設(shè)備上顯示高顏色位數(shù)的圖形圖形時(shí)失真小; 5) 通過直接處理位圖數(shù)據(jù),可以制作簡(jiǎn)單動(dòng)畫。
四、CViewBimap類編程要點(diǎn)
1. 在CViewBimap類中添加視頻函數(shù)等成員
HDRAWDIB m_hDrawDib; // 視頻函數(shù)
HANDLEmhBitsSrc; // 位圖文件句柄(內(nèi)存)
LPSTR mpBitsSrc; // 位圖文件地址(內(nèi)存)
BITMAPINFOHEADER *mpBitmapInfo; // 位圖信息頭
2. 在CViewBimap類構(gòu)造函數(shù)中添加打開視頻函數(shù)
m_hDrawDib= DrawDibOpen();
3. 在CViewBimap類析構(gòu)函數(shù)中添加關(guān)閉視頻函數(shù)
if( m_hDrawDib != NULL)
{
DrawDibClose( m_hDrawDib);
m_hDrawDib = NULL;
}
4. 在CViewBimap類圖形顯示函數(shù)OnPaint中添加GraphicDraw()
voidCViewBitmap::OnPaint()
{
CPaintDC dc(this); // device context for painting
GraphicDraw( );
}
voidCViewBitmap::GraphicDraw( void )
{
CClientDC dc(this); // device context for painting
BITMAPFILEHEADER *pBitmapFileHeader;
ULONG bfoffBits= 0;
CPoint Wid;
// 圖形文件名有效 (=0 BMP)
if( mBitmapFileType < ID_BITMAP_BMP ) return;
// 圖形文件名有效 (=0 BMP)
// 準(zhǔn)備顯示真彩位圖
pBitmapFileHeader= (BITMAPFILEHEADER *) mpBitsSrc;
bfoffBits= pBitmapFileHeader->bfOffBits;
// 使用普通函數(shù)顯示位圖
if( m_hDrawDib == NULL mDispMethod == 0)
{
HBITMAP hBitmap=::CreateDIBitmap(dc.m_hDC,
mpBitmapInfo, CBM_INIT, mpBitsSrc+bfoffBits,
(LPBITMAPINFO) mpBitmapInfo,DIB_RGB_COLORS);
// 建立位圖
HDC hMemDC=::CreateCompatibleDC(dc.m_hDC);// 建立內(nèi)存
HBITMAP hBitmapOld= SelectBitmap(hMemDC, hBitmap); // 選擇對(duì)象
// 成員CRect mDispR用于指示圖形顯示區(qū)域的大小.
// 成員CPoint mPos用于指示圖形顯示起始位置坐標(biāo).
if( mPos.x > (mpBitmapInfo- >biWidth - mDispR.Width() ))
mPos.x= mpBitmapInfo->biWidth - mDispR.Width() ;
if( mPos.y > (mpBitmapInfo- >biHeight- mDispR.Height()))
mPos.y= mpBitmapInfo- >biHeight- mDispR.Height();
if( mPos.x < 0 ) mPos.x= 0;
if( mPos.y < 0 ) mPos.y= 0;
if( mFullViewTog == 0)
{
// 顯示真彩位圖
::BitBlt(dc.m_hDC,0,0, mDispR.Width(), mDispR.Height(),
hMemDC,mPos.x,mPos.y, SRCCOPY);
} else {
::StretchBlt(dc.m_hDC,0,0, mDispR.Width(), mDispR.Height(),
hMemDC,0,0, mpBitmapInfo- >biWidth, mpBitmapInfo-
>biHeight, SRCCOPY);
}
// 結(jié)束顯示真彩位圖
::DeleteObject(SelectObject(hMemDC,hBitmapOld));
// 刪 除 位 圖
} else {
// 使用視頻函數(shù)顯示位圖
if( mPos.x > (mpBitmapInfo- >biWidth - mDispR.Width() ))
mPos.x= mpBitmapInfo- >biWidth - mDispR.Width() ;
if( mPos.y > (mpBitmapInfo- >biHeight- mDispR.Height()))
mPos.y= mpBitmapInfo- >biHeight- mDispR.Height();
if( mPos.x < 0 ) mPos.x= 0;
if( mPos.y < 0 ) mPos.y= 0;
// 顯示真彩位圖
DrawDibRealize( m_hDrawDib, dc.GetSafeHdc(), TRUE);
if( mFullViewTog == 0)
{
Wid.x= mDispR.Width();
Wid.y= mDispR.Height();
// 1:1 顯示時(shí), 不能大于圖形大小
if( Wid.x > mpBitmapInfo- >biWidth )
Wid.x = mpBitmapInfo- >biWidth;
if( Wid.y > mpBitmapInfo- >biHeight)
Wid.y = mpBitmapInfo- >biHeight;
DrawDibDraw( m_hDrawDib, dc.GetSafeHdc()
, 0, 0, Wid.x, Wid.y,
mpBitmapInfo, (LPVOID) (mpBitsSrc+bfoffBits),
mPos.x, mPos.y, Wid.x, Wid.y, DDF_BACKGROUNDPAL);
} else {
DrawDibDraw( m_hDrawDib, dc.GetSafeHdc(),
0, 0, mDispR.Width(), mDispR.Height(),
mpBitmapInfo, (LPVOID) (mpBitsSrc+bfoffBits),
0, 0, mpBitmapInfo- >biWidth, mpBitmapInfo- >biHeight,
DDF_BACKGROUNDPAL);
}
}
return;
}
五、使用CViewBimap類顯示BMP位圖
1. 在Visual C++5.0中新建一個(gè)名稱為mymap工程文件,類型為MFC AppWizard[exe]。在編譯運(yùn)行通過后,在WorkSpace(如被關(guān)閉,用Alt_0打開)點(diǎn)擊ResourceView,點(diǎn)擊Menu左側(cè)的+符號(hào)展開Menu條目,雙擊IDR_MAINFRAME條目,進(jìn)入菜單資源編輯,在'“查看(V)”下拉式菜單(英文版為View下拉式菜單)的尾部添加“ViewBitmap”條目,其ID為ID_VIEW_BITMAP。
2. 在Visual C++5.0中點(diǎn)擊下拉式菜單Project- >Add To project- >Files...,將Bitmap0.h和Bitmap0.cpp添加到工程文件中。
3. 在Visual C++5.0中按Ctrl_W進(jìn)入MFC ClassWizard,選擇類名稱為CMainFrame,ObjectIDs: ID_VIEW_BITMAP,Messages選擇Command,然后點(diǎn)擊Add Fucction按鈕,然后輸入函數(shù)名為OnViewBimap。在添加OnViewBimap后,在Member functions: 中點(diǎn)擊OnViewBimap條目,點(diǎn)擊Edit Code按鈕編輯程序代碼。代碼如下:
void CMainFrame::OnViewBitmap()
{
// TODO: Add your command handler code here
CViewBitmap *pViewBitmap= NULL;
pViewBitmap= new CViewBitmap( "BITMAP.BMP", this);
pViewBitmap- >ShowWindow( TRUE);
}
并在該程序的頭部添加#include "bitmap0.h",然后編譯運(yùn)行。
4. 找一個(gè)大一點(diǎn)的真彩色的BMP位圖,將它拷貝到BITMAP.BMP中。
5. 運(yùn)行時(shí),點(diǎn)擊下拉式菜單“查看(V)- >ViewBitmap”(英文版為View- > ViewBitmap)即可顯示BITMAP.BMP位圖。
六、CViewBimap類功能說明
1. 在客戶區(qū)中帶有水平和垂直滾動(dòng)條。在位圖大小大于顯示客戶區(qū)時(shí),可以使用滾動(dòng)條;在位圖大小小于顯示客戶區(qū)或全屏顯示時(shí),滾動(dòng)條無效。
2. 在客戶區(qū)中底部帶有狀態(tài)條。狀態(tài)條中的第一格為位圖信息,第二格為位圖顯示方法,可以是使用普通函數(shù)或使用視頻函數(shù)。在第二格區(qū)域內(nèi)點(diǎn)擊鼠標(biāo),可在兩者之間接換。第三格為位圖顯示比例,可以是1;1顯示或全屏顯示。在第三格區(qū)域內(nèi)點(diǎn)擊鼠標(biāo),可在兩者之間接換。在全屏顯示時(shí),如果位圖比客戶區(qū)小,則對(duì)位圖放大; 如果位圖比客戶區(qū)大,則對(duì)位圖縮小。
3. 支持文件拖放功能?梢詮馁Y源管理器中拖動(dòng)一個(gè)位圖文件到客戶區(qū),就可以顯示該位圖。
程序調(diào)試通過后,可以找一個(gè)較大的真彩色位圖或調(diào)整客戶區(qū)比位圖小,在全屏顯示方式下,比較使用普通函數(shù)與使用視頻函數(shù)的差別?梢钥闯,位圖放大時(shí)兩者差別不大,但在位圖縮小時(shí),兩者差別明顯; 使用視頻函數(shù)時(shí)位圖失真小,顯示速度快。
還可以從控制面板中將屏幕顯示方式從真彩色顯示模式切換到256色顯示模式,再比較使用普通函數(shù)與使用視頻函數(shù)顯示同一個(gè)真彩色位圖的差別。現(xiàn)在可以體會(huì)到使用視頻函數(shù)的優(yōu)越性了吧。
在全屏顯示時(shí),位圖的xy方向比例不相同,如要保持相同比例,可在顯示程序中加以適當(dāng)調(diào)整即可,讀者可自行完成。