在VC中自建操作BMP位圖文件的類

有編程經驗的程序員都知道:要使應用程序的界面美觀不可避免的要使用大量位圖。現在流行的可視化編程工具對位圖的使用提供了很好的支持,被稱爲三大可視化開發工具的VB、VC、Delphi通過封裝位圖對象對位圖使用提供了很好的支持:VB提供了兩個功能很強的對象:PictureBox及Image,通過使用它們,裝載、顯示位圖變得非常容易。Delphi中也提供了一個位圖對象:TImage,它的功能與用法與VB中的Image類似。在VC中通過使用設備相關類CDC與GDI對象類CBitmap來完成位圖的操作。

然而在VC中使用CBitmap類必須將BMP位圖裝入資源中,然後通過類 CBitmap的成員函數使用它,在通過CDC類的成員函數操作它。這樣做有兩點缺陷:將位圖裝入資源導致可執行文件增大,不利於軟件發行;只能使用資源中有限的位圖,無法選取其它位圖。而且BMP位圖文件是以DIB(設備無關位圖)方式保存,BMP位圖裝入資源後被轉換爲DDB(設備相關位圖),類CBitmap就是對一系列DDB操作的API函數進行了封裝,使用起來有一定的侷限性,不如DIB可以獨立於平臺特性。

要彌補使用資源位圖的兩點不足,就必須直接使用BMP位圖文件。VC的示例中提供了一種方法讀取並顯示BMP位圖文件,但使用起來相當的麻煩。首先使用API函數GlobalAlloc分配內存並創建HDIB位圖句柄,所有操作只能直接讀寫內存,然後通過StrechDIBits及SetDIBsToDevice函數來顯示於屏幕上,操作起來費時費力。

因此筆者通過研究類CBitmap的封裝與DIB結構,使用Win32中提供的新函數,建立了一個專用於操作BMP文件的類,而且完全仿照類CBitmap的實現:從類CGdiObject派生,新類的所有接口與類CBitmap 的部分接口完全相同。這樣對於習慣使用CBitmap類接口用法的程序員來說兩者的接口在使用上沒有什麼分別。

首先我們先簡單介紹一下DIB的結構。DIB位圖既可以存在於內存,也可以以文件形式保存在磁盤上(BMP文件)。所有DIB都包含兩部分信息:位圖信息(BITMAPINFO),包括位圖信息頭和顏色表;位圖數據。對於內存中DIB的只要有上述兩部分就行,而對於DIB文件則還要加上位圖文件頭。兩種結構如圖所示:

DIB DIB文件

 

 

其次,Win32中提供了一個新函數CreateDIBSection,通過它可以創建一個存儲DIB位的內存區域,既可以執行相應的GDI操作,又可以直接通過指向DIB位區域的指針方位DIB位區域。這是一個非常有用的函數,通過它我們可以用DIB替代DDB。

在瞭解了相應的知識後,我們可以自己由類CGdiObject派生一個操作BMP文件的類:CBitmapFile。

在自己編寫類時有兩點值得注意:

在BitmapFile.h文件中定義類CBitmapFile,首先必須聲明類CBitmapFile是從類CGdiObject中公有派生。然後在類中首先使用宏DECLARE_DYNAMIC(CBitmapFile)表明新類的最高父類是類CObject,是符合MFC的類庫規範。緊接着宏DECLARE_DYNAMIC的是聲明靜態函數FromHandle,這兩個聲明必須放在類定義的最前面。
在BitmapFile.cpp文件中類的成員函數的實現前加上IMPLEMENT_DYNAMIC(CBitmapFile,CGdiObject);表明類CBitmapFile直接派生於類CGdiObject。
在類CBitmapFile的聲明中有三個函數與類Cbitmap中的定義稍有不同:

在類CbitmapFile中LoadBitmap函數的參數是LPCTSTR型,保存的是BMP文件的文件名。
在類CbitmapFile中CreateBitmap函數的參數中少了參數nPlanes,在函數內部默認爲1。
在類CbitmapFile中CreateBitmapIndirect函數的參數中多了參數lpBits,它指向指定位圖DIB位的內存區域。
在成員函數中最重要的是函數CreateBitmapIndirect和函數LoadBitmap:

在函數CreateBitmapIndirect中使用函數CreateDIBSection創建了一個以兼容DC爲基礎的HBITMAP句柄,並用繼承自類CGdiObject 的函數Attach把它與類CGdiObject的句柄m_hObject關聯起來。然後將指定位圖的DIB位圖數據拷貝到由函數CreateDIBSection創建的DIB位的內存區域。
在函數LoadBitmap中首先從指定文件名的文件中讀取以結構BITMAPFILEHEADER爲大小的數據塊,然後由文件頭標誌判斷文件是否爲BMP位圖文件,然後由BITMAPFILEHEADER中bfSize保存的文件大小與文件的真實大小比較文件是否有損壞,再由BITMAPFILEHEADER中bfOffBits與BITMAPFILEHEADER結構大小相減計算出位圖信息頭和顏色表一共的大小,動態申請一塊空間保存位圖信息頭和顏色表信息,再由BITMAPFILEHEADER中bfSize與bfOffBits相減計算出DIB位圖數據的大小,動態申請一塊空間保存DIB位圖數據,最後調用成員函數CreateBitmapIndirect來創建DIB位圖。
在應用程序的OnPaint()事件中繪製DIB位圖的方法與使用類CBitmap時繪製位圖的方法完全相同,但有一點要注意的是由於CDC類沒有提供返回新類CBitmapFile指針類型的將DIB位圖選入內存的SelectObject函數,所以在使用SelectObject時要將返回類型強制轉換爲CbitmapFile *類型。

至此,關於新類CBitmapFile編寫中的一些要點和使用時一些要注意的問題就介紹這麼多了。

附源文件

//

// 文件描述:定義類CBitmapFile,此類是用於讀取BMP文件,涉及讀取、

// 建立及一系列常用的操作。

// 文件名: BitmapFile.h

// 時間: 1999-2-11

// 作者: 賈暾

//

#ifndef _CBITMAPFILE_H_

#define _CBITMAPFILE_H_

class CBitmapFile : public CGdiObject

{

DECLARE_DYNAMIC(CBitmapFile)

public:

static CBitmapFile* PASCAL FromHandle(HBITMAP hBitmap);

// Constructors

CBitmapFile();

BOOL LoadBitmap(LPCTSTR lpszFileName);

BOOL CreateBitmap(int nWidth, int nHeight, UINT nBitCount, const void* lpBits);

BOOL CreateBitmapIndirect(LPBITMAPINFO lpBitmapInfo, const void* lpBits);

// Attributes

operator HBITMAP() const;

int GetBitmap(BITMAP* pBitMap);

protected:

// Attributes

int GetColorNumber(WORD wBitCount);

public:

// Operations

DWORD SetBitmapBits(DWORD dwCount, const void* lpBits);

DWORD GetBitmapBits(DWORD dwCount, LPVOID lpBits);

// Implementation

public:

virtual ~CBitmapFile();

};

#endif

 

// 文件描述:類CBitmapFile內成員函數的實現

// 文件名: BitmapFile.cpp

// 時間: 1999-2-11

// 作者: 賈暾

//

#include "BitmapFile.h"

#include <memory.h>

IMPLEMENT_DYNAMIC(CBitmapFile,CGdiObject);

CBitmapFile* PASCAL CBitmapFile::FromHandle(HBITMAP hBitmap)

{

return (CBitmapFile*) CGdiObject::FromHandle(hBitmap);

}

CBitmapFile::CBitmapFile()

{

}

BOOL CBitmapFile::LoadBitmap(LPCTSTR lpszFileName)

{

CFile file;

if(!file.Open(lpszFileName,CFile::modeRead|CFile::shareDenyWrite))

{

MessageBox(NULL,"BMP file open error!","warning",MB_OK);

return FALSE;

}

BITMAPFILEHEADER bfhHeader;

file.Read(&bfhHeader,sizeof(BITMAPFILEHEADER));

if(bfhHeader.bfType!=((WORD) ('M'<<8)|'B'))

{

MessageBox(NULL,"The file is not a BMP file!","warning",MB_OK);

return FALSE;

}

if(bfhHeader.bfSize!=file.GetLength())

{

MessageBox(NULL,"The BMP file header error!","warning",MB_OK);

return FALSE;

}

UINT uBmpInfoLen=(UINT) bfhHeader.bfOffBits-sizeof(BITMAPFILEHEADER);

LPBITMAPINFO lpBitmap=(LPBITMAPINFO) new BYTE[uBmpInfoLen];

file.Read((LPVOID) lpBitmap,uBmpInfoLen);

if((* (LPDWORD)(lpBitmap))!=sizeof(BITMAPINFOHEADER))

{

MessageBox(NULL,"The BMP is not Windows 3.0 format!","warning",MB_OK);

return FALSE;

}

DWORD dwBitlen=bfhHeader.bfSize - bfhHeader.bfOffBits;

LPVOID lpBits=new BYTE[dwBitlen];

file.ReadHuge(lpBits,dwBitlen);

file.Close();

BOOL bSuccess=CreateBitmapIndirect(lpBitmap, lpBits);

delete lpBitmap;

delete lpBits;

if(!bSuccess)

return FALSE;

return TRUE;

}

BOOL CBitmapFile::CreateBitmap(int nWidth, int nHeight, UINT nBitCount,

const void* lpSrcBits)

{

ASSERT(nBitCount==1||nBitCount==4||nBitCount==8

||nBitCount==16||nBitCount==24||nBitCount==32);

LPBITMAPINFO lpBitmap;

lpBitmap=(BITMAPINFO*) new BYTE[sizeof(BITMAPINFOHEADER) +

GetColorNumber(nBitCount) * sizeof(RGBQUAD)];

lpBitmap->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);

lpBitmap->bmiHeader.biWidth=nWidth;

lpBitmap->bmiHeader.biHeight=nHeight;

lpBitmap->bmiHeader.biBitCount=nBitCount;

lpBitmap->bmiHeader.biPlanes=1;

lpBitmap->bmiHeader.biCompression=BI_RGB;

lpBitmap->bmiHeader.biSizeImage=0;

lpBitmap->bmiHeader.biClrUsed=0;

BOOL bSuccess=CreateBitmapIndirect(lpBitmap, lpSrcBits);

delete lpBitmap;

if(!bSuccess)

return FALSE;

return TRUE;

}

BOOL CBitmapFile::CreateBitmapIndirect(LPBITMAPINFO lpBitmapInfo, const void* lpSrcBits)

{

DeleteObject();

LPVOID lpBits;

CDC *dc=new CDC;

dc->CreateCompatibleDC(NULL);

HBITMAP hBitmap=::CreateDIBSection(dc->m_hDC,lpBitmapInfo,DIB_RGB_COLORS,

&lpBits,NULL,0);

ASSERT(hBitmap!=NULL);

delete dc;

Attach(hBitmap);

BITMAP bmp;

GetBitmap(&bmp);

DWORD dwCount=(DWORD) bmp.bmWidthBytes * bmp.bmHeight;

if(SetBitmapBits(dwCount,lpSrcBits)!=dwCount)

{

MessageBox(NULL,"DIB build error!","warning",MB_OK);

return FALSE;

}

return TRUE;

}

CBitmapFile::operator HBITMAP() const

{

return (HBITMAP)(this == NULL ? NULL : m_hObject);

}

int CBitmapFile::GetBitmap(BITMAP* pBitMap)

{

ASSERT(m_hObject != NULL);

return ::GetObject(m_hObject, sizeof(BITMAP), pBitMap);

}

int CBitmapFile::GetColorNumber(WORD wBitCount)

{

ASSERT(wBitCount==1||wBitCount==4||wBitCount==8

||wBitCount==16||wBitCount==24||wBitCount==32);

switch(wBitCount)

{

case 1:

return 2;

case 4:

return 16;

case 8:

return 256;

default:

return 0;

}

}

DWORD CBitmapFile::SetBitmapBits(DWORD dwCount, const void* lpBits)

{

if(lpBits!=NULL)

{

BITMAP bmp;

GetBitmap(&bmp);

memcpy(bmp.bmBits,lpBits,dwCount);

return dwCount;

}

else

return 0;

}

DWORD CBitmapFile::GetBitmapBits(DWORD dwCount, LPVOID lpBits)

{

if(lpBits!=NULL)

{

BITMAP bmp;

GetBitmap(&bmp);

memcpy(lpBits,bmp.bmBits,dwCount);

return dwCount;

}

else

return 0;

}

CBitmapFile::~CBitmapFile()

{

CGdiObject::DeleteObject();

}

//
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章