分类
讨论在MFC程序中显示JPG/GIF图像‖
像控件拖到Form中,分分钟
么是不是要自己编写JPG解
在MFC中显示JPG或者GIF图
即可搞掂。但是C++程序员要显
压缩代码呢?当然不用那么复杂
像。
示同样的图形却没有那么轻松,那
啦!本文将针对这个问题讨论如何
| 用VB写图像显示程序之 做的事情都一一搞掂。而C+ 也能使用那些VB程序员所用 一个系统级COM类——IPict | 所以如此轻松,完全是利用了琳 +程序员为了实现相同的功能必 的(或者说几乎一样的)图像控 ure。下面是有关 IPicture 的 | 琅满目的图像处理控件,把你想要 须忙乎半天。其实,C/C++程序员 件。VB用的图像控件实际上都基于 方法描述: |
![]() |
| 从上面这个表可以看出 ,而Windows负责BMP、JPG 调用其Render函数。与通常 函数,而是用一个专门的函 | ,IPicture操纵着图像对象及其 和GIF位图的标准实现。程序员 使用接口的方式不同,这里实例 数OleLoadPicture。 | 属性。图像对象提供对位图的抽象 要做的只是实例化IPicture,然后 的创建我们不用CoCreateInstance |
| IStream* pstm = // 需要一个流(stream) |
| IPicture* pIPicture; |
| hr = OleLoadPicture(pstm, 0, FAL | SE, IID_IPicture, (void**)&pIPicture); |
| OleLoadPicture从流中加载图像并创 // 显示图像的矩形 | 建一个可用来显示图像的新IPicture对象。 rc = |
| // 将rc 转换为 HIMETRIC |
| spIPicture->Render(pDC, rc); |
| IPicture 负责处理所 件——甚至是图标和元文件 此我写了一个Demo程序Myim | 有琐事,以便确定图形之格式, (metafiles)。当然啦,所有 gapp(如图二)来示范这些IPictu | 如 Windows 位图、JPEG或者GIF文 这些的实现细节是需要技巧的,为 re的使用方法。 |
![]() |
| |
| Myimgapp是个典型的MFC文档/视图程 接口进行封装,之所以要这么做,主要是 行编程,另外将IPicture的主要功能封装 这个C++类名字叫做CPicture。它的定义 | 序,在编写这个程序之前,我首先对 IPicture COM 考虑到并不是每一个程序员都能熟练运用COM接口进 在C++类中可以使我们的问题更容易解决,我封装的 和实现细节请参考本文提供的源代码。 |
| 我在这个类中将复杂而 CPicture可以让你直接从文 CPicture::Render替你完成 。CPicture甚至具备了一个 码就可以显示资源中的图像 | 陌生的COM风格的参数映射成MFC 件名加载一幅图像,CFile或者C 了IPicture中所有令人讨厌的但 Load函数,它可以从资源数据中 : CPicture pic(ID_MYPIC); / | 程序员更为熟悉的类型。例如, Archive,而不用去处理流, 又是必须的HIMETRIC平滑转换工作 加载图像,所以你只要用下面的代 / 加载图像 |
| CRect rc(0,0,0,0); // 使用缺省的rc |
| pic.Render(pDC, rc); // 显示图像 |
| CPicture::Render提供 一个空矩形,则CPicture用 查找"IMAGE"类型的资源, MOVEABLE PURE "res\MyPi | 一个显示图片的矩形。IPicture 图像本身的大小--唤醒由齑?br>所以在资源文件中你必须要加入 c.jpg" | 对图像进行延伸处理。如果传递 理。对于图像本身而言,CPicture 下面的代码: IDR_MYPIC IMAGE |
| CPicture是个很棒的傻 通过调用OleLoadPicture来 层的IPicture。CPicture只 IPicture::get_Handle或其 代码。 另外,在编写完CPi 个类的功能几乎与CPicture Demo例子是个典型的MFC文 CPictureDoc 和CPictureVi | 瓜类,它具备一个 ATL 智能指 初始化不同的Load函数。CPictu 封装了那些在Demo例子程序中要 它一些很少用到的IPicture方法 cture之后,我发现了一个现成 完全一样,你可以在afxctl.h文 档/视图应用程序,因此它肯定 ew: | 针CComQIPtr指向IPicture接口, re提供了常用的打包函数来调用底 用到的方法。如果你需要调用 ,你可以自己尝试编写相应的打包 的MFC类——CPictureHolder,这 件中找到它的定义。 前面说过, 少不擞胛牡岛褪油祭嘞喽杂Φ?br> |
| CPictureDoc类没有什么特别的处理 CPictureDoc : public CDocument { | 代码,它用CPicture对象存储图像: class |
| protected: |
| CPicture m_pict; // the picture |
| }; |
| 并且CPictureDoc::Serialize 调用C void CPictureDoc::Serialize(CArchive | Picture::Load 从MFC存档的数据中读取图像。 & ar) |
| { |
| if (ar.IsLoading()) { |
| m_pict.Load(ar); |
| } |
| } |
| 为了使Myimgapp程序更 像。为了显示这幅图像,CP 会显示一幅默认的图像。 v | 实用,CPictureDoc::OnNewDocu ictureView::OnDraw要调用CPic oid CPictureView::OnDraw(CDC | ment从程序资源数据加载了一幅图 ture::Render。这样程序一启动便 * pDC) |
| { |
| CPictureDoc* pDoc = GetDocument(); |
| CPicture* ppic = pDoc->GetPicture(); |
| CRect rc; |
| GetImageRect(rc); |
| ppic->Render(pDC,rc); |
| } |
| GetImageRect是CPictu 可用25%、33%、50%、75%、 CPicture::GetImageSize来 部分完全和CScrollView的 人操心的是IPicture::Rend 映射模型。不用担心,CPic 以你不必为这些事情伤神。 ,当要显示的图像比客户区 建一个与图像大小相等的切 ,主要是避免当改变窗口大 Windows图形处理的常识。 | reView类的一个成员函数,作用 100%或自适应方式)获取图像矩 获得真正的图像大小,然后根据 做法差不多,初始化视图并设置 er中HIMETRIC的处理问题,因为 ture::Render和CPicture::GetI CPictureView有一个消息处理 小的时候,这个函数必须绘制空 边(clip)矩形,然后将客户区 小时出现的抖动——FillRect不 | 是根据当前Myimgapp的缩放比率( 形。GetImageRect调用 比率显示。 CPictureView其余的 滚动大小,处理命令等等。唯一让 标准的MFC应用程序都使用MM_TEXT mageSize会将这一切转换过来,所 器值得一提:它就是OnEraseBkgnd 白区域,如图二,OnEraseBkgnd创 填成黑色。之所以要创建切边矩形 绘制切边矩形内的区域,此乃 |
![]() |
| |
| IPicture/CPicture简化了图像的显 你完全可以抛开老式DIB 图像绘制方法, 切IPicture全都可以搞掂。如果你未曾用 CPictureView完成图像浏览的任务看来不 话框或者其它的什么窗口中怎么办呢?为 | 示。它甚至可以实现调色板的识别这样复杂的处理。 如加载调色板、BitBlts、StretchBlts等等——这一 IPicture显示过图像,那么现在试试吧。 是什么难事了。但是如果要把一幅图像添加到一个对 此我创建了另外一个类——CPictureCtrl。 |
| CPictureCtrl 使你可 CAboutDialog : public CD | 以在任何对话框或窗口中把图像 ialog { | 作为子窗口显示。例如: class |
| protected: |
| CPictureCtrl m_wndPict; |
| virtual BOOL OnInitDialog(); |
| }; |
| BOOL CAboutDialog::OnInitDialog() |
| { |
| m_wndPict.SubclassDl | gItem(IDC_MYIMAGE,this); |
| return CDialog::OnInitDialog(); |
| } |
| 假设你的对话框中有一个静态控制, 相同。则从CStaticLink派生出的CPictur 此控制或图像的ID相同的串资源)。如果 浏览器访问URL。真是酷呆了。CPicture 调用CPicture::Render代替通常的静态控 序的“关于”对话框就知道了。 | 它的ID=IDC_IMAGE,并且有一幅IMAGE资源的ID与之 eCtrl还可以指定一个URL超链接(或者创建一个ID与 你指定了一个URL,则在图像上单击鼠标将启动默认 控制着CPicture对象并改写WM_PAINT消息处理例程, 制处理例程。处理细节请参见代码。打开Myimgapp程 |
| 使用GDI+进行图像处理 |
| 编译/NorthTibet下载源代码 |
| 前段时间VCKBASE发布 后不断有人问我如何对图像 怎么办?反正不会来找我.. | 了一篇有关图像处理的文章“在 进行旋转处理,也就是让用户歪 .... | MFC程序中显示JPG/GIF图像”,之 着脖子看图像,用户的脖子拧断了 |
| 其实这个问题的一种解决方法是利用 中时所学的三角知识或者大学中的线性代 么这个点的旋转坐标可以通过 | 二维(x,y坐标中)矩阵转换实现图像旋转。使用高 数知识就可以解决。其原理是已知一个点的坐标,那 |
| (x*cos(A) + y*sin | (A),- x*sin(A) + y*cos | (A?br> |
| 求得,这里A是以弧度 ,然后将它选入设备上下文 素,便可以实现图像的旋转 弦、余弦的值无外乎+/-1或 ,我当然不会叫你用这种方 现图像旋转处理。我想了解 细节,请参考MSDN的有关文 | 为单位的角度(2P弧度=360度) ,接着调用GetPixel和SetPixel 效果。对于90、180、-90度的旋 者0......话还没等我说完,一 法做事情啦!有更好的方法呢。 GDI+的人不是很多,因为它是Wi 章。 | 。因此,只要将图像加载到内存中 ,象上面所说的那样映射所有的像 转,这是一个不错的方法,因为正 块砖头就朝我头上飞过来了,啊唷 下面就是本文的正题:使用GDI+实 ndows中的新东西。要想了解它的 |
| GDI+是GDI图形库的一个增强版本,C Microsoft .NET,而对于Windows 98、Wi 本。GDI+是一个C++ API。它用C++类和C+ 所描述的这些。为了使用GDI+,你必须包 库,这两个文件包含在最新的Windows SD 的例子代码Myimgapp进行了重写,并改名 ,因为原来的CPicture主要针对IPicture Myimgapp2运行画面,如图一所示。这个 | ++可以使用这个库。它内建于Windows XP 和 ndows NT和Windows 2000,则有一个可重新发布的版 +方法。GDI+所包含的内容非常多,远远不止我在此 含(#include)文件,并将工程链接到gdiplus.lib K中。我对“在MFC程序中显示JPG/GIF图像”一文中 为Myimgapp2,其代码对CPicture类进行了重大改动 进行封装,而这一次主要是封装GDI+。下面是 程序示范了如何用GDI+来旋转图像。 |
![]() |
| |
| 前面说过,原来程序中 口。在本文的例子程序Myim Image)。对所有的细节都 而是用Image取而代之,所 。但有两个障碍要解决:第 说它是个障碍,还不如说它 在程序的 InitInstance 和 | 的那个C++类CPicture是基于IPi gapp2中,我重写了CPicture的 进行了封装。新的CPicture类不 有其它的类如CPictureView 和 一个是你必须对GDI库进行初始 是GDI+本身的需要,在哪里进行 ExitInstance函数中: | cture接口的,它处理图像的COM接 代码,使用了GDI+里的图像类( 再使用 IPicture 这个COM接口, CPictureCtrl都能和从前一样工作 化以及最后的终止释放操作,与其 这两个操作呢?最佳的地方莫过于 |
| //初始化 GDI |
| class CMyApp : public CWinApp { |
| protected: |
| GdiplusStartupInput m_gdiplusSta | rtupInput; |
| ULONG_PTR m_gdiplusToken; |
| ……. |
| }; |
| //释放GDI |
| BOOL CMyApp::InitInstance() |
| { |
| VERIFY(GdiplusStartup(&m_gdiplusToken, |
| &m_gdiplusStartupInput, NULL)==Ok); |
| ……. |
| } |
| int CMyApp::ExitInstance() |
| { |
| GdiplusShutdown(m_gdiplusToken); |
| return CWinApp::ExitInstance(); |
| } |
| CMyApp::m_gdiplusToken 是一个很 GdiplusShutdown。m_gdiplusStartupInp 构造函数建立一个智能的缺省值,它又一 它了。原来的CPicture类有一个指向IPic 的指针。同样,它也有可重载的Load函数 CPicture如何从某个路径名中加载图像文 | 神奇的东东,它来自GdiplusStartup 并被传递到 ut 是一个结构,它包含某些GDI+的启动参数。缺省 次证明了C++比C更好。一旦你启动GDI+,就可以使用 ture的指针,而新版的CPicture类有一个指向Image 来从不同的地方加载图象。例如,下面便是新版的 件。 |
| BOOL CPicture::Load( | LPCTSTR pszPathName) |
| { |
| Free(); |
| USES_CONVERSION; |
| m_pImage = Image::Fr | omFile(A2W(pszPathName), |
| m_bUseEmbeddedColorManagement); |
| return m_pImage->GetLastStatus()==Ok; |
| } |
| 在此代码段中,重点是GDI+要用宽字 的CPicture用Load函数从某个CFile、CAr 都走到从流中加载图像的例程:CPicture ,并用GDI+函数从流中加载数据时,它不 CArchiveStream类,这个类在CArchive类 所有的IStream方法,Image::FromStream 是它返回一切OK,但实际上当你试图显示 写了CPicture::Load(UINT nID),其中用 用,它在一个全局内存块中创建一个流: | 符串,所以你要用USES_CONVERSION 和 A2W.。原来 chive、资源ID或流中加载图像。所有Load函数最终 ::Load(IStream*)。当我开始用Image代替IPicture 工作。情况真是很糟,令人沮丧。问题出在MFC的 之上实现流化。可能是CArchiveStream没姓肥迪?br>无法正确处理基于CArchiveStream的流操作,更糟的 或者删除Image时会失败。 为了解决这个问题,我重 到了CreateStreamOnHGlobal函数。这个API函数很好 |
| // 分配全局内存并在其中创建流 |
| HGLOBAL m_hMem = GlobalAlloc(GME | M_FIXED, len); |
| BYTE* pmem = (BYTE*)GlobalLock(m_hMem); |
| memcpy(pmem,lpRsrc,len); |
| IStream* pstm; |
| CreateStreamOnHGloba | l(m_hMem,FALSE,&pstm); |
| 这里lpRsrc已经指向内 内存中,在内存上创建一个 子源代码。当对象被销毁?br>CFile或者CArchive加载的 下面看看如何显示图像,你 CDC)类似,它也有一个方 | 存中的图形资源。所以基本思路 流,然后用Image::FromStream 某人加载另外的图像时,CPictu 情况——其实,根本不需要考虑 必须使用GDI+类Graphics——与 法DrawImage: | 是加载图像资源,将它拷贝到全局 创建一个图象,有关细节请参见例 re自动释放全局内存。对于从 这种情况。 以上是加载的方法, 原来GDI中的设备上下文(HDC或 |
| BOOL CPicture::Rende | r(CDC* pDC, CRect rc) const |
| { |
| ……. |
| Graphics graphics(pDC->m_hDC); |
| graphics.DrawImage(m_pImage, |
| rc.left, rc.top, rc. | Width(), rc.Height()); |
| } |
| 有关细节请参考源代码 Image::GetWidth 和Image: IPicture中HIMETRIC单位的 | 。用Image代替IPicture来表现 :GetHeight分别获得象素的宽度 东东。一般来说,用GDI+很容易 | 图像要简单一些。 和高度,这正是你所需要代替 编程,例如下面示范了如何旋转: |
| void CPicture::Rotate(RotateFlip | Type rft) |
| { |
| if (m_pImage) { |
| m_pImage->RotateFlip(rft); |
| } |
| } |
| 这个代码将图像顺时针 RotateFlipType 选项 | 旋转90度,下面列出了RotateFl | ipType所有可能的情觯? |
| // 用于Image::RotateFlip的枚举类型 |
| enum RotateFlipType |
| { |
| RotateNoneFlipNone = 0, |
| Rotate90FlipNone = 1, |
| Rotate180FlipNone = 2, |
| Rotate270FlipNone = 3, |
| RotateNoneFlipX = 4, |
| Rotate90FlipX = 5, |
| Rotate180FlipX = 6, |
| Rotate270FlipX = 7, |
| RotateNoneFlipY = Rotate180FlipX, |
| Rotate90FlipY = Rotate270FlipX, |
| Rotate180FlipY = RotateNoneFlipX, |
| Rotate270FlipY = Rotate90FlipX, |
| RotateNoneFlipXY = Rotate180FlipNone, |
| Rotate90FlipXY = Rotate270FlipNone, |
| Rotate180FlipXY = RotateNoneFlipNone, |
| Rotate270FlipXY = Rotate90FlipNone |
| }; |
| GDI+ 的其它函数用于 用来解决缩略图问题,有兴 员因为它速度慢而不喜欢它 个类叫CachedBitmap,这个 敏感的应用程序你可能不会 来说,GDI+不失为一种比GD | 展开和修剪图像,它甚至还有一 趣的话不妨自己试一下。 我鼓 ,但是有很多文档中提供了一些 类以优化设备的格式保存位图。 用GDI+来编程(一般都用Direct I更好的选择。 | 个函数Image::GetThumbnailImage 励大家去研究一下GDI+。有些程序 技巧来改善它的性能。例如,有一 对于象视频或高端图像编辑等图形 X),但对于日常的一般图形应用 |
| 总结一下 VC6 下如何使用GDI+ :) |
| 1.下载解压GDI+开发包: |
| http://www.codeguru.com/gdi/GDIP | lus.zip |
| 2.正确设置include & lib 目录 |
| 3.在 stdafx.h 添加: |
| #ifndef ULONG_PTR |
| #define ULONG_PTR unsigned long* |
| #endif |
| #include |



