BMP點陣圖檔案結構及平滑縮放 (轉)

gugu99發表於2008-05-17
BMP點陣圖檔案結構及平滑縮放 (轉)[@more@]

用普通方法顯示BMP點陣圖,佔大,速度慢,在圖形縮小時,失真嚴重,在低顏色位數的裝置上顯示高顏色位數的圖形圖形時失真大。本文采用影片顯示BMP點陣圖,可以消除以上的缺點。

一、BMP結構

BMP檔案組成
BMP檔案由檔案頭、點陣圖資訊頭、顏色資訊和圖形資料四部分組成。
BMP檔案頭
BMP檔案頭資料結構含有BMP檔案的型別、檔案大小和點陣圖起始位置等資訊。
其結構定義如下:
typedef struct tagBITMAPFILEHEADER
{
WORfType; // 位件的型別,必須為BM
D bfSize; // 點陣圖檔案的大小,以位元組為單位
WORDbfReserved1; // 點陣圖檔案保留字,必須為0
WORDbfReserved2; // 點陣圖檔案保留字,必須為0
DWORD bfOffBits; // 點陣圖資料的起始位置,以相對於點陣圖
// 檔案頭的偏移量表示,以位元組為單位
} BITMAPFILEHEADER;


3. 點陣圖資訊頭


BMP點陣圖資訊頭資料用於說明點陣圖的尺寸等資訊。
typedef struct tagBITMNFOHEADER{
DWORD biSize; // 本結構所佔用位元組數
LONGbiWidth; // 點陣圖的寬度,以畫素為單位
LONGbiHeight; // 點陣圖的高度,以畫素為單位
WORD biPlanes; // 目標裝置的級別,必須為1
WORD biBitCount// 每個畫素所需的位數,必須是1(雙色),
// 4(16色),8(256色)或24(真彩色)之一
DWORD biCompression; // 點陣圖型別,必須是 0(不壓縮),
// 1(BI_RLE8壓縮型別)或2(BI_RLE4壓縮型別)之一
DWORD biSizeImage; // 點陣圖的大小,以位元組為單位
LONGbiXPelsPerMeter; // 點陣圖水平解析度,每米畫素數
LONGbiYPelsPerMeter; // 點陣圖垂直解析度,每米畫素數
DWORD biClrUsed;// 點陣圖實際使用的顏色表中的顏色數
DWORD biClrImportant;// 點陣圖顯示過程中重要的顏色數
} BITMAPINFOHEADER;


4. 顏色表

  顏色表用於說明點陣圖中的顏色,它有若干個表項,每一個表項是一個RGBQUAD型別的結構,定義一種顏色。RGBQUAD結構的定義如下:
typedef struct tagRGBQUAD {
BYTErgbBlue;// 藍色的亮度(值範圍為0-255)
BYTErgbGreen; // 綠色的亮度(值範圍為0-255)
BYTErgbRed; // 紅色的亮度(值範圍為0-255)
BYTErgbReserved;// 保留,必須為0
} RGBQUAD;
顏色表中RGBQUAD結構資料的個數有biBitCount來確定:
當biBitCount=1,4,8時,分別有2,16,256個表項;
當biBitCount=24時,沒有顏色表項。
點陣圖資訊頭和顏色表組成點陣圖資訊,BITMAPINFO結構定義如下:
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader; // 點陣圖資訊頭
RGBQUAD bmiColors[1]; // 顏色表
} BITMAPINFO;


5. 點陣圖資料
  點陣圖資料記錄了點陣圖的每一個畫素值,記錄順序是在掃描行內是從左到右,掃描行之間是從下到上。點陣圖的一個畫素值所佔的位元組數:
當biBitCount=1時,8個畫素佔1個位元組;
當biBitCount=4時,2個畫素佔1個位元組;
當biBitCount=8時,1個畫素佔1個位元組;
當biBitCount=24時,1個畫素佔3個位元組;
規定一個掃描行所佔的位元組數必須是
4的倍數(即以long為單位),不足的以0填充,
一個掃描行所佔的位元組數計算方法:
DataSizeine= (biWidth* biBitCount+31)/8;
// 一個掃描行所佔的位元組數
DataSizePerLine= DataSizePerLine/4*4; // 位元組數必須是4的倍數
點陣圖資料的大小(不壓縮情況下):
DataSize= DataSizePerLine* biHeight;


二、BMP點陣圖一般顯示方法

1. 申請記憶體空間用於存放點陣圖檔案
  GlobalAlloc(GHND,FileLength);

2. 點陣圖檔案讀入所申請記憶體空間中
   LoadFileToMemory( mpBitsSrc,mFileName);

3. 在OnPaint等函式中用建立顯示用點陣圖
  用CreateDIBitmap()建立顯示用點陣圖,用CreateCompatibleDC()建立相容DC,
  用Bitmap()選擇顯示點陣圖。

4. 用BitBlt或StretchBlt等函式顯示點陣圖

5. 用Delete()刪除所建立的點陣圖

  以上方法的缺點是: 1)顯示速度慢; 2) 記憶體佔用大; 3) 點陣圖在縮小顯示時圖形失真大,(可透過字型平滑來解決); 4) 在低顏色位數的裝置上(如256顯示)顯示高顏色位數的圖形(如真彩色)圖形失真嚴重。

三、BMP點陣圖縮放顯示
   用DrawDib影片函式來顯示點陣圖,記憶體佔用少,速度快,而且還可以對圖形進行淡化(Dithering)處理。淡化處理是一種圖形演算法,可以用來在一個支援比影像所用顏色要少的裝置上顯示彩色影像。BMP點陣圖顯示方法如下:

1. 開啟影片函式DrawDibOpen(),一般放在在建構函式中

2. 申請記憶體空間用於存放點陣圖檔案
  GlobalAlloc(GHND,FileLength);

3. 點陣圖檔案讀入所申請記憶體空間中----
  LoadFileToMemory( mpBitsSrc,mFileName);

4. 在OnPaint等函式中用DrawDibRealize(),DrawDibDraw()顯示點陣圖

5. 關閉影片函式DrawDibClose(),一般放在在解構函式中

  以上方法的優點是: 1)顯示速度快; 2) 記憶體佔用少; 3) 縮放顯示時圖形失真小,4) 在低顏色位數的裝置上顯示高顏色位數的圖形圖形時失真小; 5) 透過直接處理點陣圖資料,可以製作簡單動畫。

四、CViewBimap類要點

1. 在CViewBimap類中新增影片函式等成員

HDRAWDIB m_hDrawDib; // 影片函式
HANDLEmhBitsSrc; // 點陣圖檔案控制程式碼(記憶體)
LPSTR mpBitsSrc; // 點陣圖檔案地址(記憶體)
BITMAPINFOHEADER *mpBitmapInfo; // 點陣圖資訊頭

2. 在CViewBimap類建構函式中新增開啟影片函式
  m_hDrawDib= DrawDibOpen();

3. 在CViewBimap類解構函式中新增關閉影片函式

if( m_hDrawDib != NULL)
{
DrawDibClose( m_hDrawDib);
m_hDrawDib = NULL;
}

4. 在CViewBimap類圖形顯示函式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)
// 準備顯示真彩點陣圖
pBitmapFileHeader= (BITMAPFILEHEADER *) mpBitsSrc;
bfoffBits= pBitmapFileHeader->bfOffBits;

// 使用普通函式顯示點陣圖

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);// 建立記憶體
HBITMAP hBitmapOld= SelectBitmap(hMemDC, hBitmap); // 選擇
// 成員CRect mDispR用於指示圖形顯示區域的大小.
// 成員CPoint mPos用於指示圖形顯示起始位置座標.
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);
}
// 結束顯示真彩點陣圖
::DeleteObject(SelectObject(hMemDC,hBitmapOld));
// 刪 除 位 圖
} else {

// 使用影片函式顯示點陣圖

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 顯示時, 不能大於圖形大小
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中新建一個名稱為mymap工程檔案,型別為MFC AppWizard[exe]。在編譯執行透過後,在WorkSpace(如被關閉,用Alt_0開啟)點選ReView,點選Menu左側的+符號展開Menu條目,雙擊IDR_MAINFRAME條目,進入選單資源編輯,在'“檢視(V)”下拉式選單(英文版為View下拉式選單)的尾部新增“ViewBitmap”條目,其ID為ID_VIEW_BITMAP。

2. 在Visual C++5.0中點選下拉式選單Project- >Add To project- >Files...,將Bitmap0.h和Bitmap0.cpp新增到工程檔案中。

3. 在Visual C++5.0中按Ctrl_W進入MFC ClassWizard,選擇類名稱為CMainFrame,ObjectIDs: ID_VIEW_BITMAP,Messages選擇Command,然後點選Add Fucction按鈕,然後輸入函式名為OnViewBimap。在新增OnViewBimap後,在Member functions: 中點選OnViewBimap條目,點選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",然後編譯執行。

4. 找一個大一點的真彩色的BMP點陣圖,將它複製到BITMAP.BMP中。

5. 執行時,點選下拉式選單“檢視(V)- >ViewBitmap”(英文版為View- > ViewBitmap)即可顯示BITMAP.BMP點陣圖。

六、CViewBimap類功能說明

1. 在客戶區中帶有水平和垂直捲軸。在點陣圖大小大於顯示客戶區時,可以使用捲軸;在點陣圖大小小於顯示客戶區或全屏顯示時,捲軸無效。

2. 在客戶區中底部帶有狀態條。狀態條中的第一格為點陣圖資訊,第二格為點陣圖顯示方法,可以是使用普通函式或使用影片函式。在第二格區域內點選滑鼠,可在兩者之間接換。第三格為點陣圖顯示比例,可以是1;1顯示或全屏顯示。在第三格區域內點選滑鼠,可在兩者之間接換。在全屏顯示時,如果點陣圖比客戶區小,則對點陣圖放大; 如果點陣圖比客戶區大,則對點陣圖縮小。

3. 支援檔案拖放功能。可以從中拖動一個點陣圖檔案到客戶區,就可以顯示該點陣圖。

  程式透過後,可以找一個較大的真彩色點陣圖或調整客戶區比點陣圖小,在全屏顯示方式下,比較使用普通函式與使用影片函式的差別。可以看出,點陣圖放大時兩者差別不大,但在點陣圖縮小時,兩者差別明顯; 使用影片函式時點陣圖失真小,顯示速度快。
  還可以從控制皮膚中將螢幕顯示方式從真彩色顯示模式切換到256色顯示模式,再比較使用普通函式與使用影片函式顯示同一個真彩色點陣圖的差別。現在可以體會到使用影片函式的優越性了吧。
  在全屏顯示時,點陣圖的xy方向比例不相同,如要保持相同比例,可在顯示程式中加以適當調整即可,讀者可自行完成。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-1004158/,如需轉載,請註明出處,否則將追究法律責任。

相關文章