在
VC
中自建操作
BMP
位圖文件的類
?
??????
作者:
???
賈暾
西安萬山軟件有限公司
??????
有編程經(jīng)驗(yàn)的程序員都知道:要使應(yīng)用程序的界面美觀不可避免的要使用大量位圖。現(xiàn)在流行的可視化編程工具對位圖的使用提供了很好的支持,被稱為三大可視化開發(fā)工具的
VB
、
VC
、
Delphi
通過封裝位圖對象對位圖使用提供了很好的支持:
VB
提供了兩個(gè)功能很強(qiáng)的對象:
PictureBox
及
Image
,通過使用它們,裝載、顯示位圖變得非常容易。
Delphi
中也提供了一個(gè)位圖對象:
TImage
,它的功能與用法與
VB
中的
Image
類似。在
VC
中通過使用設(shè)備相關(guān)類
CDC
與
GDI
對象類
CBitmap
來完成位圖的操作。
然而在
VC
中使用
CBitmap
類必須將
BMP
位圖裝入資源中,然后通過類
CBitmap
的成員函數(shù)使用它,在通過
CDC
類的成員函數(shù)操作它。這樣做有兩點(diǎn)缺陷:將位圖裝入資源導(dǎo)致可執(zhí)行文件增大,不利于軟件發(fā)行;只能使用資源中有限的位圖,無法選取其它位圖。而且
BMP
位圖文件是以
DIB
(設(shè)備無關(guān)位圖)方式保存,
BMP
位圖裝入資源后被轉(zhuǎn)換為
DDB
(設(shè)備相關(guān)位圖),類
CBitmap
就是對一系列
DDB
操作的
API
函數(shù)進(jìn)行了封裝,使用起來有一定的局限性,不如
DIB
可以獨(dú)立于平臺特性。
要彌補(bǔ)使用資源位圖的兩點(diǎn)不足,就必須直接使用
BMP
位圖文件。
VC
的示例中提供了一種方法讀取并顯示
BMP
位圖文件,但使用起來相當(dāng)?shù)穆闊J紫仁褂?/span>
API
函數(shù)
GlobalAlloc
分配內(nèi)存并創(chuàng)建
HDIB
位圖句柄,所有操作只能直接讀寫內(nèi)存,然后通過
StrechDIBits
及
SetDIBsToDevice
函數(shù)來顯示于屏幕上,操作起來費(fèi)時(shí)費(fèi)力。
因此筆者通過研究類
CBitmap
的封裝與
DIB
結(jié)構(gòu),使用
Win32
中提供的新函數(shù),建立了一個(gè)專用于操作
BMP
文件的類,而且完全仿照類
CBitmap
的實(shí)現(xiàn):從類
CGdiObject
派生,新類的所有接口與類
CBitmap
的部分接口完全相同。這樣對于習(xí)慣使用
CBitmap
類接口用法的程序員來說兩者的接口在使用上沒有什么分別。
首先我們先簡單介紹一下
DIB
的結(jié)構(gòu)。
DIB
位圖既可以存在于內(nèi)存,也可以以文件形式保存在磁盤上(
BMP
文件)。所有
DIB
都包含兩部分信息:位圖信息(
BITMAPINFO
),包括位圖信息頭和顏色表;位圖數(shù)據(jù)。對于內(nèi)存中
DIB
的只要有上述兩部分就行,而對于
DIB
文件則還要加上位圖文件頭。兩種結(jié)構(gòu)如圖所示:
?DIB?????????? ???????????????????????????????????????????????????????????????????????? ??DIB
文件
?
????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????? 位圖信息頭(BITMAPINFOHEADER)
位圖文件頭(
BITMAPFILEHEADER
)?????????
??位圖信息頭(BITMAPINFOHEADER)
?顏色表(RGBQUAD)?????????????????????????顏色表(RGBQUAD)
位圖數(shù)據(jù)??????????????????????????????????????????????????????????????????????? 位圖數(shù)據(jù)
?
?
?
其次,
Win32
中提供了一個(gè)新函數(shù)
CreateDIBSection
,通過它可以創(chuàng)建一個(gè)存儲
DIB
位的內(nèi)存區(qū)域,既可以執(zhí)行相應(yīng)的
GDI
操作,又可以直接通過指向
DIB
位區(qū)域的指針方位
DIB
位區(qū)域。這是一個(gè)非常有用的函數(shù),通過它我們可以用
DIB
替代
DDB
。
在了解了相應(yīng)的知識后,我們可以自己由類
CGdiObject
派生一個(gè)操作
BMP
文件的類:
CBitmapFile
。
在自己編寫類時(shí)有兩點(diǎn)值得注意:
1.???
在
BitmapFile.h
文件中定義類
CBitmapFile,
首先必須聲明類
CBitmapFile
是從類
CGdiObject
中公有派生。然后在類中首先使用宏
DECLARE_DYNAMIC
(
CBitmapFile
)表明新類的最高父類是類
CObject
,是符合
MFC
的類庫規(guī)范。緊接著宏
DECLARE_DYNAMIC
的是聲明靜態(tài)函數(shù)
FromHandle
,這兩個(gè)聲明必須放在類定義的最前面。
2.???
在
BitmapFile.cpp
文件中類的成員函數(shù)的實(shí)現(xiàn)前加上
IMPLEMENT_DYNAMIC(CBitmapFile,CGdiObject);
表明類
CBitmapFile
直接派生于類
CGdiObject
。
在類
CBitmapFile
的聲明中有三個(gè)函數(shù)與類
Cbitmap
中的定義稍有不同:
1.
在類
CbitmapFile
中
LoadBitmap
函數(shù)的參數(shù)是
LPCTSTR
型,保存的是
BMP
文件的文件名。
2.
在類
CbitmapFile
中
CreateBitmap
函數(shù)的參數(shù)中少了參數(shù)
nPlanes
,在函數(shù)內(nèi)部默認(rèn)為
1
。
3.
在類
CbitmapFile
中
CreateBitmapIndirect
函數(shù)的參數(shù)中多了參數(shù)
lpBits
,它指向指定位圖
DIB
位的內(nèi)存區(qū)域。
在成員函數(shù)中最重要的是函數(shù)
CreateBitmapIndirect
和函數(shù)
LoadBitmap
:
1.
在函數(shù)
CreateBitmapIndirect
中使用函數(shù)
CreateDIBSection
創(chuàng)建了一個(gè)以兼容
DC
為基礎(chǔ)的
HBITMAP
句柄,并用繼承自類
CGdiObject
的函數(shù)
Attach
把它與類
CGdiObject
的句柄
m_hObject
關(guān)聯(lián)起來。然后將指定位圖的
DIB
位圖數(shù)據(jù)拷貝到由函數(shù)
CreateDIBSection
創(chuàng)建的
DIB
位的內(nèi)存區(qū)域。
2.
在函數(shù)
LoadBitmap
中首先從指定文件名的文件中讀取以結(jié)構(gòu)
BITMAPFILEHEADER
為大小的數(shù)據(jù)塊,然后由文件頭標(biāo)志判斷文件是否為
BMP
位圖文件,然后由
BITMAPFILEHEADER
中
bfSize
保存的文件大小與文件的真實(shí)大小比較文件是否有損壞,再由
BITMAPFILEHEADER
中
bfOffBits
與
BITMAPFILEHEADER
結(jié)構(gòu)大小相減計(jì)算出位圖信息頭和顏色表一共的大小,動態(tài)申請一塊空間保存位圖信息頭和顏色表信息,再由
BITMAPFILEHEADER
中
bfSize
與
bfOffBits
相減計(jì)算出
DIB
位圖數(shù)據(jù)的大小,動態(tài)申請一塊空間保存
DIB
位圖數(shù)據(jù),最后調(diào)用成員函數(shù)
CreateBitmapIndirect
來創(chuàng)建
DIB
位圖。
在應(yīng)用程序的
OnPaint
()事件中繪制
DIB
位圖的方法與使用類
CBitmap
時(shí)繪制位圖的方法完全相同,但有一點(diǎn)要注意的是由于
CDC
類沒有提供返回新類
CBitmapFile
指針類型的將
DIB
位圖選入內(nèi)存的
SelectObject
函數(shù),所以在使用
SelectObject
時(shí)要將返回類型強(qiáng)制轉(zhuǎn)換為
CbitmapFile *
類型。
?
附源文件
//???
文件描述:定義類
CBitmapFile
,此類是用于讀取
BMP
文件,涉及讀取、
//??????????? ??
建立及一系列常用的操作。
//???
文件名:
? BitmapFile.h
#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
內(nèi)成員函數(shù)的實(shí)現(xiàn)
//???
文件名:
? BitmapFile.cpp
//?????????????????????????????????????????????????????????????????
?
#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();
}
posted on 2007-03-09 16:32
SIMONE 閱讀(1734)
評論(0) 編輯 收藏 所屬分類:
C++