CImage类的介绍与使用 【图像打开,另存为,转为灰度图像(利用cimage方法实现)】

CImage类的介绍与使用

程序代码下载处:http://download.csdn.net/source/2098910

下载处:http://hi.baidu.com/wangleitongxing/blog/item/9063b03e5e20f3c97c1e71c8.html

Visual C++的CBitmap类和静态图片控件的功能是比较弱的,它只能显示出在资源中的图标、位图、光标以及图元文件的内容,而不像VB中的Image控件可以显示出绝大多数的外部图像文件(BMP、GIF、JPEG等)。因此,想要在对话框或其他窗口中显示外部图像文件则只能借助于第三方提供的控件或代码。 现在,MFC和ATL共享的新类CImage为图像处理提供了许多相应的方法,这使得Visual C++在图像方面的缺憾一去不复返了。

CImage类概述

CImage是MFC和ATL共享的新类,它能从外部磁盘中调入一个JPEG、GIF、BMP和PNG格式的图像文件加以显示,而且这些文件格式可以相互转换。由于CImage在不同的Windows操作系统中其某些性能是不一样的,因此在使用时要特别注意。例如,CImage::PlgBlt和 CImage::MaskBlt只能在 Windows NT 4.0 或更高版本中使用,但不能运行在Windows 95/98 应用程序中。CImage::AlphaBlend和CImage::TransparentBlt也只能在 Windows 2000/98或其更高版本中使用。即使在Windows 2000运行程序还必须将stdafx.h文件中的WINVER和_WIN32_WINNT的预定义修改成0x0500才能正常使用。

CImage封装了DIB(设备无关位图)的功能,因而可以让我们能够处理每个位图像素。它具有下列最酷特性:

1、AlphaBlend支持像素级的颜色混合,从而实现透明和半透明的效果。

2、PlgBlt能使一个矩形区域的位图映射到一个平行四边形区域中,而且还可能使用位屏蔽操作。

3、TransparentBlt在目标区域中产生透明图像,SetTransparentColor用来设置某种颜色是透明色。

4、MaskBlt在目标区域中产生源位图与屏蔽位图合成的效果。

使用CImage的一般方法

使用CImage的一般方法是这样的过程:

(1) 打开应用程序的stdafx.h文件添加CImage类的包含文件:

#include<atlimage.h>

(2) 定义一个CImage类对象,然后调用CImage::Load方法装载一个外部图像文件。

(3) 调用CImage::Draw方法绘制图像。Draw方法具有如下定义:

程序代码:

BOOL Draw( HDC hDestDC, int xDest, int yDest,int nDestWidth, int nDestHeight, int xSrc, int ySrc,int nSrcWidth, int nSrcHeight );
BOOL Draw( HDC hDestDC, const RECT& rectDest, const RECT& rectSrc );
BOOL Draw( HDC hDestDC, int xDest, int yDest );
BOOL Draw( HDC hDestDC, const POINT& pointDest );
BOOL Draw( HDC hDestDC, int xDest, int yDest,int nDestWidth, int nDestHeight );
BOOL Draw( HDC hDestDC, const RECT& rectDest );


其中,hDestDC用来指定绘制的目标设备环境句柄,(xDest, yDest)和pointDest用来指定图像显示的位置,这个位置和源图像的左上角点相对应。

nDestWidth和nDestHeight分别指定图像要显示的高度和宽度,xSrc、ySrc、nSrcWidth和nSrcHeight用来指定要显示的源图像的某个部分所在的位置和大小。

rectDest和rectSrc分别用来指定目标设备环境上和源图像所要显示的某个部分的位置和大小。

需要说明的是,Draw方法综合了StretchBlt、TransparentBlt和AlphaBlend函数的功能。默认时,Draw的功能和 StretchBlt相同。但当图像含有透明色或Alpha通道时,它的功能又和TransparentBlt、AlphaBlend相同。因此,在一般情况下,我们都应该尽量调用CImage::Draw方法来绘制图像。

例如,下面的示例Ex_Image是实现这样的功能:当选择"文件"ò"打开"菜单命令后,弹出一个文件打开对话框。当选定一个图像文件后,就会在窗口客户区中显示该图像文件内容。这个示例的具体步骤如下:

(1) 创建一个默认的单文档程序项目Ex_Image。

(2) 打开stdafx.h文件中添加CImage类的包含文件atlimage.h。

(3) 在view类中添加成员变量CImage m_Image;

CEx_ImageView类添加ID_FILE_OPEN的COMMAND事件映射程序,并添加下列代码:

程序代码:

 

void CEx_ImageView::OnFileOpen() //Cyan:添加
{
     // TODO: 在此添加命令处理程序代码
     CString strFilter;
     CSimpleArray<GUID>aguidFileTypes;
     HRESULT hResult;   //获取CImage支持的图像文件的过滤字符串
     hResult=m_Image.GetExporterFilterString(strFilter,aguidFileTypes,_T("All Image Files"));
     if(FAILED(hResult))
     {
         MessageBox(_T("GetExporterFilter调用失败!"));
         return;
     }
     CFileDialog dlg(TRUE,NULL,NULL,OFN_FILEMUSTEXIST,strFilter);
     if(IDOK!=dlg.DoModal())
         return;
     m_Image.Destroy();
     //将外部图像文件装载到CImage对象中
     hResult=m_Image.Load(dlg.GetFileName());
     if(FAILED(hResult))
     {
         MessageBox(_T("调用图像文件失败!"));
         return;
     }
     //设置主窗口标题栏内容
     CString str;
     str.LoadString(AFX_IDS_APP_TITLE);
     AfxGetMainWnd()->SetWindowTextW(str+_T("-")+dlg.GetFileName());
     Invalidate();//强制调用OnDraw函数
}


(4) 定位到CEx_ImageView::OnDraw函数处,添加下列代码:

程序代码:

 

void CEx_ImageView::OnDraw(CDC* pDC)
{
     CEx_ImageDoc* pDoc = GetDocument();
     ASSERT_VALID(pDoc);
     //if (!pDoc)
         //return;
     // TODO: 在此处为本机数据添加绘制代码
     // Cyan:添加
     if(!m_Image.IsNull())
     {
         m_Image.Draw(pDC->m_hDC,0,0);
     }
}


(5) 打开Ex_ImageView.h文件,添加一个公共的成员数据m_Image:

程序代码:

public:    CImage m_Image;(Cyan:我觉得设置成私有成员比较好)

 (6) 编译并运行。单击"打开"工具按钮,在弹出的对话框中指定一个图像文件后,单击"打开"按钮。

将图片用其它格式保存

CImage::Save方法能将一个图像文件按另一种格式来保存,它的原型如下:

HRESULT Save( LPCTSTR pszFileName, REFGUID guidFileType= GUID_NULL);

其中,pszFileName用来指定一个文件名,guidFileType用来指定要保存的图像文件格式,当为GUID_NULL时,其文件格式由文件的扩展名来决定,这也是该函数的默认值。它还可以是GUID_BMPFile(BMP文件格式)、GUID_PNGFile(PNG文件格式)、 GUID_JPEGFile(JPEG文件格式)和GUID_GIFFile(GIF文件格式)。

例如,下面的过程是在Ex_Image示例基础上进行的,我们在CEx_ImageView类添加ID_FILE_SAVE_AS的COMMAND事件映射程序,并添加下列代码:

程序代码:

 

void CEx_ImageView::OnFileSaveAs()   //Cyan:添加
{
     // TODO: 在此添加命令处理程序代码
     if(m_Image.IsNull())
     {
         MessageBox(_T("请打开要另存的图像!"));
         return;
     }
     CString strFilter;
     strFilter="bmp位图文件|*.bmp|JPEG图像文件|*.jpg|GIF图像文件|*.gif|PNG图像文件|*.png||";
     CFileDialog dlg(FALSE,NULL,NULL,NULL,strFilter);
     if(IDOK!=dlg.DoModal())
         return;
     //如果没有指定文件扩展名,则为其添加一个
     CString strFileName,strExtension;
     strFileName=dlg.m_ofn.lpstrFile;
     if(dlg.m_ofn.nFileExtension==0)
     {
         switch(dlg.m_ofn.nFilterIndex)
         {
         case 1:
              strExtension="bmp";
              break;
         case 2:
              strExtension="jpg";
              break;
         case 3:
              strExtension="gif";
              break;
         case 4:
              strExtension="png";
              break;
         default:
              break;
         }
         strFileName=strFileName+_T(".")+strExtension;
     }
     //图像保存
     HRESULT hResult=m_Image.Save(strFileName);
     if(FAILED(hResult))
     {
         MessageBox(_T("保存图像文件失败!"));
     }
}


彩色图像转换成灰度图像

由于许多图像文件使用颜色表来发挥显示设备的色彩显示能力,因而将一张彩色图片变成黑色图片时需要调用CImage::IsIndexed来判断是否使用颜色表,若是则修改颜色表,否则直接将像素进行颜色设置。例如下面的代码:

程序代码:

 

void CEx_ImageView::OnToGray()//Cyan:添加
{
     // TODO: 在此添加命令处理程序代码
     if(m_Image.IsNull())
         return;
     if(!m_Image.IsIndexed())
     {
         //直接修改像素颜色
         COLORREF pixel;
         int maxY=m_Image.GetHeight();
         int maxX=m_Image.GetWidth();
         byte r,g,b,avg;
         for(int x=0;x<maxX;x++)
         {
              for(int y=0;y<maxY;y++)
              {
                   pixel=m_Image.GetPixel(x,y);
                   r=GetRValue(pixel);
                   g=GetGValue(pixel);
                   b=GetBValue(pixel);
                   avg=(int)(((int)r+g+b)/3);
                   m_Image.SetPixelRGB(x,y,avg,avg,avg);
              }       
         }
     }
     else
     {
         //获取并修改颜色表
         int maxColors=m_Image.GetMaxColorTableEntries();
         RGBQUAD* lpColorTable;
         lpColorTable=new RGBQUAD[maxColors];
         m_Image.GetColorTable(0,maxColors,lpColorTable);
         for(int i=0;i<maxColors;i++)
         {
              int avg=(lpColorTable[i].rgbRed+lpColorTable[i].rgbGreen+lpColorTable[i].rgbBlue)/3;
              lpColorTable[i].rgbRed=avg;
              lpColorTable[i].rgbGreen=avg;
              lpColorTable[i].rgbBlue=avg;
         }
         m_Image.SetColorTable(0,maxColors,lpColorTable);
         delete(lpColorTable);
     }
     Invalidate();//强制调用OnDraw
}  

这个代码对像素点的访问效率很低,高效率访问像素点数据参见我博客的转帖:http://blog.csdn.net/ifinver/article/details/8049036


文章转自 http://blog.csdn.net/huamin1990/article/details/5349947

本博客所有博文,若无专门说明皆为原创,转载请注明作者和出处!
原文地址:https://www.cnblogs.com/ifinver/p/2828696.html