2010年8月9日 星期一

BMP檔案結構結構及平滑縮放

用普通方法顯示BMP點陣圖,占記憶體大,速度慢,在圖形縮小時,失真嚴重,在低顏色位元數的設備上顯示高顏色位元數的圖形圖形時失真大。本文採用視頻函數顯示BMP點陣圖,可以消除以上的缺點。


一、BMP檔結構


1. BMP檔組成


BMP檔由檔頭點陣圖資訊頭、顏色資訊圖形資料四部分組成。


2. BMP文件頭


BMP檔頭資料結構含有BMP檔的類型、檔大小和BMP起始位置等資訊。


其結構定義如下:
typedef struct tagBITMAPFILEHEADER
{
  WORDbfType;            //
bmp檔的類型,必須為BMP
  DWORD bfSize;          / /
bmp檔的大小,以位元組為單位
 WORDbfReserved1;  //
bmp檔保留字,必須為0
 WORDbfReserved2;  //
bmp檔保留字,必須為0
 DWORD bfOffBits;   // bmp
資料的起始位置,以相對於點陣圖 檔頭的偏移量表示,以byte為單位
} BITMAPFILEHEADER;



3.
點陣圖信息頭


BMP點陣圖資訊頭資料用於說明點陣圖的尺寸等資訊。
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; //
本結構所佔用位元組數
LONGbiWidth; //
點陣圖的寬度,以pixel為單位
LONGbiHeight; //
點陣圖的高度,以pixel為單位
WORD biPlanes; //
目標設備的級別,必須為1
WORD biBitCount//
每個pixel所需的位元數,必須是1(雙色),    // 4(16)8(256)24(真彩色)之一
DWORD biCompression; //
點陣圖壓縮類型,必須是 0(不壓縮),  // 1(BI_RLE8壓縮類型)2  


                                              //  (BI_RLE4壓縮類型)之一
DWORD biSizeImage;      //
點陣圖的大小,以位元組為單位
LONGbiXPelsPerMeter;   //
點陣圖水準解析度,每米pixel
LONGbiYPelsPerMeter;   //
點陣圖垂直解析度,每米pixel
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. 點陣圖數據
  點陣圖資料記錄了點陣圖的每一個pixel值,記錄順序是在掃描行內是從左到右,掃描行之間是從下到上。點陣圖的一個pixel所占的位元組數:
biBitCount=1時,8pixel1個位元組;
biBitCount=4時,2pixel1個位元組;
biBitCount=8時,1pixel1個位元組;
biBitCount=24,1pixel3個位元組;
Windows
規定一個掃描行所占的位元組數必須是4的倍數(即以long為單位),不足的以0填充,一個掃描行所占的位元組數計算方法:


DataSizePerLine= (biWidth* biBitCount+31)/8;   // 一個掃描行所占的位元組數
DataSizePerLine= DataSizePerLine/4*4;                //
位元組數必須是4的倍數
點陣圖資料的大小(不壓縮情況下):
DataSize= DataSizePerLine* biHeight;



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


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


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


3. OnPaint等函數中用創建顯示用點陣圖
  用CreateDIBitmap()創建顯示用點陣圖,用CreateCompatibleDC()創建相容DC,
  用SelectBitmap()選擇顯示點陣圖。


4. BitBltStretchBlt等函數顯示點陣圖


5. DeleteObject()刪除所創建的點陣圖


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


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


1. 打開視頻函數DrawDibOpen(),一般放在在構造函數中


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


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


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()
void CViewBitmap::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打開)點擊ResourceView,點擊Menu左側的+符號展開Menu條目,雙擊IDR_MAINFRAME條目,進入功能表資源編輯,在'“查看(V)”下拉式菜單(英文版為View下拉式菜單)的尾部添加“ViewBitmap”條目,其IDID_VIEW_BITMAP


2. Visual C++5.0中點擊下拉式菜單Project- >Add To project- >Files...,將Bitmap0.hBitmap0.cpp添加到工程檔中。


3. Visual C++6.0中按Ctrl_W進入MFC ClassWizard,選擇類名稱為CMainFrame,ObjectIDs: ID_VIEW_BITMAPMessages選擇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方向比例不相同,如要保持相同比例,可在顯示程式中加以適當調整即可


沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。