霍比特人网盘资源:使用vc显示bmp位图

来源:百度文库 编辑:中财网 时间:2024/04/28 04:57:19
ShowDIB-200905052009-05-05 18:51
      
使用vc显示bmp位图一
2008-12-14 14:52

BMP 是一种与硬件设备无关的图像文件格式,使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的

图像深度可选lbit、4bit、8bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。

由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。
文件结构:

典型的BMP图像文件由四部分组成:
1:位图文件头数据结构,它包含BMP图像文件的类型、显示内容等信息;
2:位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;
3:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;
4:位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。

对应的数据结构:

1:BMP文件组成
BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。
2:BMP文件头
BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。
其结构定义如下:
typedef struct tagBITMAPFILEHEADER
{
WORDbfType; // 位图文件的类型,必须为BM
DWORD bfSize; // 位图文件的大小,以字节为单位
WORDbfReserved1; // 位图文件保留字,必须为0
WORDbfReserved2; // 位图文件保留字,必须为0
DWORD bfOffBits; // 位图数据的起始位置,以相对于位图
// 文件头的偏移量表示,以字节为单位
} BITMAPFILEHEADER;
3:位图信息头
BMP位图信息头数据用于说明位图的尺寸等信息。
typedef struct tagBITMAPINFOHEADER{
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个字节;
Windows规定一个扫描行所占的字节数必须是
4的倍数(即以long为单位),不足的以0填充,

biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;


具体数据举例:
如某BMP文件开头:
424D 4690 0000 0000 0000 4600 0000 2800 0000 8000 0000 9000 0000 0100*1000 0300 0000 0090 0000 A00F 0000 A00F 0000 0000 0000 0000 0000*00F8 0000 E007 0000

1F00 0000 0000 0000*02F1 84F1 04F1 84F1 84F1 06F2 84F1 06F2 04F2 86F2 06F2 86F2 86F2 .... ....

BMP文件可分为四个部分:位图文件头、位图信息头、彩色板、图像数据阵列,在上图中已用*分隔。

一、图像文件头
1)1:(这里的数字代表的是"字",即两个字节,下同)图像文件头。424Dh=’BM’,表示是Windows支持的BMP格式。
2)2-3:整个文件大小。4690 0000,为00009046h=36934。
3)4-5:保留,必须设置为0。
4)6-7:从文件开始到位图数据之间的偏移量。4600 0000,为00000046h=70,上面的文件头就是35字=70字节。
二、位图信息头
5)8-9:位图图信息头长度。
6)10-11:位图宽度,以像素为单位。8000 0000,为00000080h=128。
7)12-13:位图高度,以像素为单位。9000 0000,为00000090h=144。
8)14:位图的位面数,该值总是1。0100,为0001h=1。
9)15:每个像素的位数。有1(单色),4(16色),8(256色),16(64K色,高彩色),24(16M色,真彩色),32(4096M色,增强型真彩色)。1000为0010h=16。
10)16-17:压缩说明:有0(不压缩),1(RLE 8,8位RLE压缩),2(RLE 4,4位RLE压缩,3(Bitfields,位域存放)。RLE简单地说是采用像素数+像素值的方式进行压缩

。T408采用的是位域存放方式,用两个字节表示一个像素,位域分配为r5b6g5。图中0300 0000为00000003h=3。
11)18-19:用字节数表示的位图数据的大小,该数必须是4的倍数,数值上等于位图宽度×位图高度×每个像素位数。0090 0000为00009000h=80×90×2h=36864。
12)20-21:用象素/米表示的水平分辨率。A00F 0000为0000 0FA0h=4000。
13)22-23:用象素/米表示的垂直分辨率。A00F 0000为0000 0FA0h=4000。
14)24-25:位图使用的颜色索引数。设为0的话,则说明使用所有调色板项。
15)26-27:对图象显示有重要影响的颜色索引的数目。如果是0,表示都重要。

三、彩色板
16)28-35:彩色板规范。对于调色板中的每个表项,用下述方法来描述RGB的值:
1字节用于蓝色分量
1字节用于绿色分量
1字节用于红色分量
1字节用于填充符(设置为0)
对于24-位真彩色图像就不使用彩色板,因为位图中的RGB值就代表了每个象素的颜色。
如,彩色板为00F8 0000 E007 0000 1F00 0000 0000 0000,其中:
00FB 0000为FB00h=1111100000000000(二进制),是红色分量的掩码。
E007 0000为 07E0h=0000011111100000(二进制),是绿色分量的掩码。
1F00 0000为001Fh=0000000000011111(二进制),是红色分量的掩码。
0000 0000总设置为0。
将掩码跟像素值进行“与”运算再进行移位操作就可以得到各色分量值。看看掩码,就可以明白事实上在每个像素值的两个字节16位中,按从高到低取5、 6、5位分别就是r、

g、b分量值。取出分量值后把r、g、b值分别乘以8、4、8就可以补齐第个分量为一个字节,再把这三个字节按rgb组合,放入存储器(同样要反序),就可以转换为24位标准BMP格

式了。

四、图像数据阵列
17)17-...:每两个字节表示一个像素。阵列中的第一个字节表示位图左下角的象素,而最后一个字节表示位图右上角的象素。

五、存储算法
BMP文件通常是不压缩的,所以它们通常比同一幅图像的压缩图像文件格式要大很多。例如,一个800×600的24位几乎占据1.4MB空间。因此它们通常不适合在因特网或者其它低速

或者有容量限制的媒介上进行传输。根据颜色深度的不同,图像上的一个像素可以用一个或者多个字节表示,它由n/8所确定(n是位深度,1字节包含8个数据位)。图片浏览器等

基于字节的 ASCII值计算像素的颜色,然后从调色板中读出相应的值。更为详细的信息请参阅下面关于位图文件的部分。 n位2n种颜色的位图近似字节数可以用下面的公式计算:

BMP文件大小约等于 54+4*2的n次方+(w*h*n)/8
,其中高度和宽度都是像素数。需要注意的是上面公式中的54是位图文件的文件头,是彩色调色板的大小。另外需要注意的是这是一个近似值,对于n位的位图图像来说,尽管可能

有最多2n中颜色,一个特定的图像可能并不会使用这些所有的颜色。由于彩色调色板仅仅定义了图像所用的颜色,所以实际的彩色调色板将小于。如果想知道这些值是如何得到的

,请参考下面文件格式的部分。由于存储算法本身决定的因素,根据几个图像参数的不同计算出的大小与实际的文件大小将会有一些细小的差别。

显示所有种类bmp位图的程序ShowDIB过程详解

1.新建一个基于多文档的项目ShowDIB,CShowDIBView类的基类是CscrollView类。
2.将事先写好的DIBAPI.H和DIBAPI.CPP两个文件增加到项目中。这两个文件声明和定义了DIB处理函数。
3.String Table中的字符串资源IDR_DISPLATYPE修改为:(为了能打开.bmp格式的文件)
\nDib\nDib\nDib Files (*.bmp; *.dib)\n.bmp\nShowDIB.Document\nShowDIB Document
/*本人理解为修改打开时的文件类型,如建立的是一单文档的话就把IIDR_MAINFRAME做以上修改×/
4.在ShowDIBDoc.h文件中增加如下语句:(此类中增加成员变量表示DIB数据块内存句柄)
public:
HANDLE m_hDIB;
一定要在构造函数中对它初始化:m_hDIB=NULL;否则就让你死都不知道怎么死的
5.在ShowDIBDoc.h中增加如下语句:
public:
        virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
virtual BOOL OnSaveDocument(LPCTSTR lpszPathName);
在ShowDIBDoc.cpp中添加如下语句
#include "dibapi.h"
BOOL CShowDIBDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
if (!CDocument::OnOpenDocument(lpszPathName))
   return FALSE;

m_hDIB = LoadDIB(lpszPathName);
if (m_hDIB == NULL)
{
   // may not be DIB format
   return FALSE;
}
SetPathName(lpszPathName);
SetModifiedFlag(FALSE);     // start off with unmodified

return TRUE;
}

BOOL CShowDIBDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
return SaveDIB(m_hDIB, lpszPathName);
}
以上语句的作用是重载了成员函数OnOpenDocument和OnSaveDocument,分别调用了我们定义的LoadDIB和SaveDIB来读写DIB文件,所以还要包含头文件"dibapi.h"。
6.在ShowDIBView.h中添加:
public:
CRect m_rcDIB;即为此类添加一个成员变量
将CShowDIBView类中的函数OnInitialUpdate()修改为:
void CShowDIBView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();

CShowDIBDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);

CSize sizeTotal;
if (pDoc->m_hDIB != NULL)
{
   LPBITMAPINFOHEADER lpDIB = (LPBITMAPINFOHEADER)GlobalLock(pDoc->m_hDIB);
   m_rcDIB.left = 0;
   m_rcDIB.top = 0;
   sizeTotal.cx = m_rcDIB.right = ((LPBITMAPINFOHEADER)lpDIB)->biWidth;
   sizeTotal.cy = m_rcDIB.bottom = ((LPBITMAPINFOHEADER)lpDIB)->biHeight;
   GlobalUnlock(pDoc->m_hDIB);
}
else
{
   m_rcDIB.SetRectEmpty();
   sizeTotal.cx = sizeTotal.cy = 100;
}

SetScrollSizes(MM_TEXT, sizeTotal);
}
装入DIB文件时,OnInitialUpdate函数会首先调用
7.ShowDIBView.cpp文件中添加语句
#include "dibapi.h"
View类的OnDraw函数修改为:
void CShowDIBView::OnDraw(CDC* pDC)
{
CShowDIBDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);

if (pDoc->m_hDIB == NULL)
   return;

PaintDIB(pDC->GetSafeHdc(),
       m_rcDIB,
    pDoc->m_hDIB,
    m_rcDIB,
    NULL,
    SRCCOPY);
}

8.MainFrm.h中定义:“#define WM_REALIZEPAL     (WM_USER + 0x100)”。当系统调色板已被修改时,主框窗口将向视发送WM_REALIZEPAL消息。在CShowDIBView类
中增加响应该消息的函数OnRealizePal和相应的消息映射,代码如下
LRESULT CShowDIBView::OnRealizePal(WPARAM wParam, LPARAM lParam)
{
ASSERT(wParam != NULL);
CShowDIBDoc* pDoc = GetDocument();

if (pDoc->m_hDIB == NULL)
   return 0L; // must be a new document

LPBYTE lpbi = (LPBYTE)GlobalLock(pDoc->m_hDIB);
CPalette* pPal = CPalette::FromHandle(CreateDIBPalette(lpbi));
GlobalUnlock(pDoc->m_hDIB);
if (pPal != NULL)
{
   CWnd* pAppFrame = AfxGetApp()->m_pMainWnd;

   CClientDC appDC(pAppFrame);
   // All views but one should be a background palette.
   // wParam contains a handle to the active view, so the SelectPalette
   // bForceBackground flag is FALSE only if wParam == m_hWnd (this view)
   CPalette* oldPalette = appDC.SelectPalette(pPal, ((HWND)wParam) != m_hWnd);

   if (oldPalette != NULL)
   {
    UINT nColorsChanged = appDC.RealizePalette();
    if (nColorsChanged > 0)
     GetDocument()->UpdateAllViews(NULL);
    appDC.SelectPalette(oldPalette, TRUE);
   }
   else
   {
    TRACE0("\tSelectPalette failed!\n");
   }
}

return 0L;
}
在 ShowDIBView.h添加如下代码
protected:
   afx_msg LRESULT OnRealizePal(WPARAM wParam, LPARAM lParam);

当系统调色板被修改时,主框收到系统的WM_PALETECHANGED和WM_QUERYNEWPALETTE消息,主框窗口通过下面的函数将向视发送WM_REALIZEPAL消息,OnRealizePal函数响应

WM_REALIZEPAL消息。
9.添加响应WM_PALETECHANGED和WM_QUERYNEWPALETTE消息的函数,当系统察觉调色板发生变化时,会向程序的主框窗口发送这两个消息,然后由主框窗口通知各视窗。
在MainFrm.cpp中添加如下代码:
void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd)
{
CMDIFrameWnd::OnPaletteChanged(pFocusWnd);

// always realize the palette for the active view
CMDIChildWnd* pMDIChildWnd = MDIGetActive();
if (pMDIChildWnd == NULL)
   return; // no active MDI child frame
CView* pView = pMDIChildWnd->GetActiveView();
ASSERT(pView != NULL);

// notify all child windows that the palette has changed
SendMessageToDescendants(WM_REALIZEPAL, (WPARAM)pView->m_hWnd);
}

BOOL CMainFrame::OnQueryNewPalette()
{
// always realize the palette for the active view
CMDIChildWnd* pMDIChildWnd = MDIGetActive();
if (pMDIChildWnd == NULL)
   return FALSE; // no active MDI child frame (no new palette)
CView* pView = pMDIChildWnd->GetActiveView();
ASSERT(pView != NULL);

// just notify the target view
pView->SendMessage(WM_REALIZEPAL, (WPARAM)pView->m_hWnd);
return TRUE;
}
在MainFrm.h中添加如下代码
protected:
afx_msg void OnPaletteChanged(CWnd* pFocusWnd);
afx_msg BOOL OnQueryNewPalette();

打开三种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文件则还要加上位图文件头。

其次,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

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();

}