posts - 495,comments - 227,trackbacks - 0

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 文件

 

                                                                                    

                                            位图信息头(BITMAPINFOHEADER
位图文件头(
BITMAPFILEHEADER )            位图信息头(BITMAPINFOHEADER

 颜色表(RGBQUAD)                         颜色表(RGBQUAD

位图数据                                                                        位图数据

 

 

 


其次, Win32 中提供了一个新函数 CreateDIBSection ,通过它可以创建一个存储 DIB 位的内存区域,既可以执行相应的 GDI 操作,又可以直接通过指向 DIB 位区域的指针方位 DIB 位区域。这是一个非常有用的函数,通过它我们可以用 DIB 替代 DDB

在了解了相应的知识后,我们可以自己由类 CGdiObject 派生一个操作 BMP 文件的类: CBitmapFile

在自己编写类时有两点值得注意:

1.    BitmapFile.h 文件中定义类 CBitmapFile, 首先必须声明类 CBitmapFile 是从类 CGdiObject 中公有派生。然后在类中首先使用宏 DECLARE_DYNAMIC CBitmapFile )表明新类的最高父类是类 CObject ,是符合 MFC 的类库规范。紧接着宏 DECLARE_DYNAMIC 的是声明静态函数 FromHandle ,这两个声明必须放在类定义的最前面。

2.    BitmapFile.cpp 文件中类的成员函数的实现前加上 IMPLEMENT_DYNAMIC(CBitmapFile,CGdiObject); 表明类 CBitmapFile 直接派生于类 CGdiObject

在类 CBitmapFile 的声明中有三个函数与类 Cbitmap 中的定义稍有不同:

1. 在类 CbitmapFile LoadBitmap 函数的参数是 LPCTSTR 型,保存的是 BMP 文件的文件名。

2. 在类 CbitmapFile CreateBitmap 函数的参数中少了参数 nPlanes ,在函数内部默认为 1

3. 在类 CbitmapFile CreateBitmapIndirect 函数的参数中多了参数 lpBits ,它指向指定位图 DIB 位的内存区域。

在成员函数中最重要的是函数 CreateBitmapIndirect 和函数 LoadBitmap

1. 在函数 CreateBitmapIndirect 中使用函数 CreateDIBSection 创建了一个以兼容 DC 为基础的 HBITMAP 句柄,并用继承自类 CGdiObject 的函数 Attach 把它与类 CGdiObject 的句柄 m_hObject 关联起来。然后将指定位图的 DIB 位图数据拷贝到由函数 CreateDIBSection 创建的 DIB 位的内存区域。

2. 在函数 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 ,此类是用于读取 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 内成员函数的实现

//    文件名:   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 阅读(1713) 评论(0)  编辑  收藏 所属分类: C++

只有注册用户登录后才能发表评论。


网站导航: