算命生男生女:详解用VC实现bmp位图的打开 - fengqing888的日志 - 网易博客

来源:百度文库 编辑:中财网 时间:2024/04/19 16:44:28

详解用VC实现bmp位图的打开

技术_图片编程 2009-12-18 13:59:17 阅读262 评论0 字号:大中小

我最近在学VC++数字图像处理,作为一个初学者,万里长征的第一步当然是打开一幅图像,这几天一直在看怎么实现这一功能,虽说简单,但是如果这一步不能做到,那么下面也就无法进行了,所以我总结了一下这个过程,写出来供大家参考。也希望大家多多批评啊。

这里我就不想介绍关于位图的理论内容了,只是写一下实现的部分。

0.准备工作

创建一个SDI,工程名Test,“隐藏工具栏”和“打印和打印预览”取消了,不用那么复杂,简单点就行

1.创建菜单

创建两个菜单:

Caption: 打开       ID: ID_FILE_OPEN

Caption: 显示原图   ID: IDM_YUANTU

2.对打开菜单进行响应

右键打开菜单,建立类向导,在CTestDoc类中,进行COMMAND响应,生成OnFileOpen函数,代码如下:

void CTestDoc::OnFileOpen()

{

    //TODO: Add your command handler code here

    CFileDialogfileDlg(TRUE);//创建一个CfileDialog类对象fileDlg,第一个参数TRUE为打开对话框,若为FALSE,则为另存为

   fileDlg.m_ofn.lpstrTitle="图片打开对话框";//设置打开对话框的标题

    fileDlg.m_ofn.lpstrFilter="BMPFiles(*.bmp)\0*.bmp\0\0";//设置打开的文件类型

   if(IDOK==fileDlg.DoModal())//这个语句有两层意义,第一是dlg.DoModal()作用是弹出CPortDlg对话框,第二层是dlg.DoModal()==IDOK是你点击了对话框上的OK按钮就是说你同时做了上述两件事时就执行if语句后面的程序。

    filename.Format("%s",fileDlg.GetPathName());//将完整路径通过Format函数以字符串类型存入filename中

★CDib.LoadFile(filename);//注意这里CDib不是类,而是CDib类的对象,对象名称也为CDib,千万不要弄混,类不能直接调用成员函数,而类的对象则可以调用

}

其中,注意在CTestDoc类中,添加一个变量filename,Cstring类型,该变量作用是保存所指定的文件的文件完整路径。

注意最后一行★。这一句代码用来实现文件的读取(不含显示)。这里用到了CDib类的对象CDib,因此需要在CTestDoc.h中添加CDib类的头文件#include“Dib.h”,并且在CTestDoc类中添加这个对象,类型CDib,变量名称CDib。然后执行CDib类的成员函数LoadFile来读取文件。

3.CDib类的操作

这是一个处理DIB位图的专用类,封装了一些相关的函数与变量,基类选为CObject类。

在Dib.h中做如下声明:

class CDib : publicCObject

{

public:

    RGBQUAD* m_pRGB;

    BYTE* m_pData;

    UINT m_numberOfColors;

BOOLm_valid;

    BITMAPFILEHEADERbitmapFileHeader;//定义了一个文件头结构体的对象

   BITMAPINFOHEADER* m_pBitmapInfoHeader;//定义了一个指向信息头的结构体指针

    BITMAPINFO*m_pBitmapInfo;//定义了一个结构体指针,BITMAPINFO是一个包含有信息头,和调色板的结构体。

    BYTE* pDib;

DWORD size;

charm_fileName[256];//定义字符数组用来存放文件路径

public:

UINT GetNumberOfColors();

UINT GetHeight();

UINT GetWidth();

DWORD GetSize();

void LoadFile(const char* dibFileName);

CDib();

virtual~CDib();

};

CDib.cpp代码如下:

CDib::CDib()

{

   size=0;//构造函数初始化size

}

CDib::~CDib()

{

GlobalFreePtr(m_pBitmapInfo); //详见说明(1)

}

voidCDib::LoadFile(const char* dibFileName)

{

   strcpy(m_fileName,dibFileName);//将路径名称拷贝到m_fileName之中

    CFile dibFile(m_fileName,CFile::modeRead);//创建CFile类对象,只读方式

   dibFile.Read((void*)&bitmapFileHeader,sizeof(BITMAPFILEHEADER));//读取文件头的内容

    if (bitmapFileHeader.bfType==0x4d42)//判断是否为bmp格式,单步调试你会发现,此时的bfType值并非0x4d42,而是19778.注意,这是十进制,只要转换成十六进制即为0x4d42

    {

        DWORD fileLength =dibFile.GetLength();//读取文件的大小,你可以试试跟踪此值来看看它是否和你要打开的图片大小一致

         size = fileLength-sizeof(BITMAPFILEHEADER);//文件大小-文件头结构体的大小,此时你会发现,文件头的大小的确是14字节

         pDib=(BYTE*)GlobalAllocPtr(GMEM_MOVEABLE, size);//详见说明(2)

        dibFile.Read((void*)pDib,size);//通过读取,把读出的数据存入刚才分配的内存之中

       dibFile.Close();//文件操作完成之后关闭文件

       m_pBitmapInfo = (BITMAPINFO*) pDib;//BITMAPINFO结构体指针指向该内存

        m_pBitmapInfoHeader =(BITMAPINFOHEADER*) pDib;//信息头指向该内存

       m_pRGB = (RGBQUAD*)(pDib +

   m_pBitmapInfoHeader->biSize);//调色板指针指向该内存的调色板部分。因为pDib原本指向信息头,偏移40字节(信息头结构体的大小)之后便到了调色板部分,因此用加法来实现指针的偏移

       int m_numberOfColors =GetNumberOfColors();//调用GetNumberOfColors函数来得到颜色数

        if (m_pBitmapInfoHeader->biClrUsed== 0)

           m_pBitmapInfoHeader->biClrUsed =

      m_numberOfColors;//把颜色数赋予biClrUsed之中

       DWORD colorTableSize = m_numberOfColors *

            sizeof(RGBQUAD);//用每个调色板结构体大小乘以颜色数量,得到调色板的大小

        m_pData = pDib +m_pBitmapInfoHeader->biSize

           + colorTableSize;//这时候代表把m_pData指针指向实际图像数据了

   if (m_pRGB == (RGBQUAD*)m_pData) //如果调色板指针位置和实际图像位置指针指向位置相同,那就代表没有调色板

   m_pRGB = NULL;//指针赋予空

       m_pBitmapInfoHeader->biSizeImage = GetSize();//赋予实际位图的大小

   m_valid = TRUE;

    }   

    else//如果不是bmp位图则失败

    {

       m_valid = FALSE;

       AfxMessageBox("This isn't a bitmap file!");

    }

}

DWORD CDib::GetSize()

{

    if(m_pBitmapInfoHeader->biSizeImage != 0)

        return m_pBitmapInfoHeader->biSizeImage;

else

    {

        DWORD height = (DWORD)GetHeight();

        DWORD width =(DWORD) GetWidth();

        returnheight * width;

    }

}

UINTCDib::GetWidth()

{

    return (UINT)m_pBitmapInfoHeader->biWidth;

}

UINT CDib::GetHeight()

{

    return(UINT) m_pBitmapInfoHeader->biHeight;

}

UINT CDib::GetNumberOfColors()

{

    intnumberOfColors;

    if((m_pBitmapInfoHeader->biClrUsed == 0) &&

          (m_pBitmapInfoHeader->biBitCount< 9))

//biClrUsed表示实际用到的颜色数,若0为2的biBitCount次中颜色

//biBitCount为用到的颜色的位数,小于9则表示最大为8位,那么之多为256色

{

   switch (m_pBitmapInfoHeader->biBitCount)

   {

     case 1: numberOfColors = 2; break;

     case 4: numberOfColors = 16; break;

     case 8: numberOfColors = 256;

   }

}

   else

   numberOfColors = (int)m_pBitmapInfoHeader->biClrUsed;//若不是上面的情况,则直接返回颜色数

    return numberOfColors;

}

注意,由于调用了函数GlobalFreePtr以及GlobalAllocPtr,则需要在头文件上添加相关的头文件:

#include "windowsx.h"

下面来分析一下这段源代码。其核心是LoadFile(),文件的读取,看一下该类成员函数的定义

UINT GetNumberOfColors();//返回位图颜色数目

UINT GetHeight();//返回位图高度

UINT GetWidth();//返回位图宽度

DWORD GetSize();//返回位图文件大小

void LoadFile(const char*dibFileName);//文件的读取操作

在LoadFile()函数中调用其他函数。

4.显示图像

显示图像当然要在View类中,因此图像的显示操作要在该类的OnDraw函数中进行

在该类中,我们要在菜单中点解“图像显示”按钮,然后才显示图像,所以我们要先响应菜单中“图像显示”命令。因此,右键“图像显示”,建立类向导,在 CTestView类中进行COMMAND响应,生成OnYuanTu函数,代码如下:

void CTestView::OnYuantu()

{

// TODO:Add your command handler code here

CTestDoc*pDoc = GetDocument();

ASSERT_VALID(pDoc);

   filename=pDoc->filename;//注意区分一下,虽然都是叫做filename但是不同,右边这个filename是CTestDoc类的成员变量filename,存有文件的完整路径,左边这个是View类的filename

   state1=1;

Invalidate();//窗口重绘。当你的窗口需要重画时,你需要用Invalidate()来使窗口无效,然后会调用OnDraw()就会重画该窗口

}

当然,要在该类添加两个变量state1和filename,类型为int和CString.分别用来做按下“图像显示”按钮的标示,和存储文件名。

接下来,在点击“图像显示”按钮之后,就要在OnDraw函数中显示图像了,代码如下:

void CTestView::OnDraw(CDC* pDC)

{

//CTestDoc*pDoc = GetDocument();

//ASSERT_VALID(pDoc);

// TODO: add draw code for native datahere

if(state1==1)

{

   CBitmapm_bitmap;

     HBITMAPhBitmap=(HBITMAP)LoadImage(NULL,_T(filename),IMAGE_BITMAP,

0,0,LR_CREATEDIBSECTION|LR_DEFAULTSIZE|LR_LOADFROMFILE);

//LoadImage函数载入图像,成功返回一个图像句柄,第一个参数是实例的句柄,这里不需要,_T(filename)文件名,第三个参数指读取的是位图(还可以有光标,图标),第四,第五个参数为0加上最后一个参数的|LR_DEFAULTSIZE,那么图像的大小将由资源本身决定,LR_CREATEDIBSECTION:当第三个参数指定为IMAGE_BITMAP时,使得函数返回一个DIB部分位图,而不是一个兼容的位图。这个标志在装载一个位图,而不是映射它的颜色到显示设备时非常有用。

        m_bitmap.Attach (hBitmap);

//Attach基本就是把一个句柄附加到一个mfc的对象上,比如你通过loadimage创建了一个handle,现在想用cbitmap类的成员函数,你就可以声明一个CBitmap对象,通过Attach将他们关联在一起,以后就可以使用CBitmap的成员函数来操作hBitmap了

     CDC dcImage;

     if(!dcImage.CreateCompatibleDC (pDC))

      return;

    BITMAP bm;

     m_bitmap.GetBitmap(&bm);//得到CBitmap对象的信息(BITMAP结构体),并存入那个参数(bm)中

        dcImage.SelectObject (&m_bitmap);

     pDC->BitBlt (0,0,bm.bmWidth,bm.bmHeight ,&dcImage,0,0,SRCCOPY);

}

}

5.运行程序

好了,运行程序吧,打开一幅位图看看吧。呵呵

说明:

(1).GlobalFreePtr(m_pBitmapInfo);

GlobalFreePtr函数从定义上看:

#define    GlobalFreePtr(lp)                \

           (GlobalUnlockPtr(lp), (BOOL)GlobalFree(GlobalPtrHandle(lp)))

定义为GlobalUnlockPtr与GlobalFree函数,

首先,其中GlobalFree函数为API函数,作用是释放指定内存空间并使句柄无效。其参数为一个句柄,因此看GlobalPtrHandle(lp),这个函数被定义为

#define    GlobalPtrHandle(lp)         \

               ((HGLOBAL)GlobalHandle(lp))

那么看GlobalHandle函数,作用是得到指针所指向内存的相关的句柄,函数前的(HGLOBAL)意思也是强制转换为句柄类型,注意看HGLOBAL的定义:

typedef HANDLE             HGLOBAL;

就是一个HANDLE而已。

接着看GlobalUnlockPtr函数,定义为

#define     GlobalUnlockPtr(lp)      \

               GlobalUnlock(GlobalPtrHandle(lp))

GlobalUnlock作用是解除被锁定的全局内存对象,参数为一个句柄,用之前GlobalPtrHandle得到句柄。

综上所述,可以看到GlobalFreePtr函数的作用就是解锁内存对象,释放内存并且使指针无效,通过多次定义,把几个API函数整合到一个新的函数中,简化操作而已。在这里,用途就是把m_pBitmapInfo指向的图像的内存与句柄释放并无效,意思就是清空m_pBitmapInfo的内容

(2)GlobalAllocPtr(GMEM_MOVEABLE,size)

通过查看定义发现,与上面的函数类似

#define     GlobalAllocPtr(flags, cb)        \

               (GlobalLock(GlobalAlloc((flags), (cb))))

GlobalLock函数锁定一个全局的内存对象,返回指向该对象的第一个字节的指针,其参数为一个句柄,那么接着由GlobalAlloc来获得句柄,GlobalAlloc函数是API函数,作用是分配一个全局内存块,返回一个句柄,起参数flags,如本例为GMEM_MOVEABLE则为分配一个可移动内存块。第二个参数是分配的字符数size

综上所述,该函数的功能就是先分配内存,接着锁定内存块,然后返回指针,可以用该指针来操作该内存

小结:这几天的学习,总算明白了图像打开以及显示的大体思路以及操作方法,但在某些方面还是不太清楚,比如显示方面,有关设备上下文的操作不是很清楚,还有加强学习,呵呵。整体的框架思路来源于《VC++图像处理程序设计》这本书,特别感谢。希望大家指正。

参考资料:

《数字图像处理编程入门》 清华大学出版社 吕凤军编著

《VC++图像处理程序设计》清华大学出版社 杨淑莹编著

《VC++深入详解》 电子工业出版社 孙鑫编著

《新编Windows API参考大全》 电子工业出版社

《数字图像处理基础》 科学出版社 朱虹编著