Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9727263
  • 博文数量: 1227
  • 博客积分: 10026
  • 博客等级: 上将
  • 技术积分: 20273
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-16 12:40
文章分类

全部博文(1227)

文章存档

2010年(1)

2008年(1226)

我的朋友

分类: C/C++

2008-03-11 17:41:39

在VB中,我可以通过创建一个图像控件来显示一个JPG或GIF文件, 但是我如何在我的MFC应用程序中显示一个JGP文件呢?

许多读者
好问题!有时使用VB的程序员觉得这个很容易。只要往你的表中拖入一个图像控件,然后你就可以往下做了……然而C++程序员就不得不感到烦恼和头疼。那我们要做些什么呢,编写我们自己的JPG解压函数吗?
  当然不是这样的!事实上,C/C++程序员能够使用与VB程序员所使用的非常类似(可以说是差不多)的图像控件。我并没有开玩笑。VB图像控件是基于一个叫"IPicture"的系统COM类(如 Figure 1 所示)。IPicture管理一个图像对象和它的特性。图像对象为位图提供一个抽象化的东西。Windows提供了一个知道如何处理BMP,JPG和GIF位图的标准操作。你所要做的只是使IPicture实例化,并调用Render。你可以调用一个叫做"OleLoadPicture"的特殊函数,来替代通常所要调用的"CoCreateInstance"。
IStream* pstm = // 需要一个信息流
IPicture* pIPicture;
hr = OleLoadPicture(pstm, 0, FALSE,
IID_IPicture, (void**)&pIPicture);
OleLoadPicture从信息流里加载图像,并创建一个你能够用来显示图像的新的IPicture对象。
rc = // 要在其中显示的矩形
// 转换rc为HIMETRIC
spIPicture->Render(pDC, rc);
  IPicture包揽了所有的令人厌烦的用来推算图像是否是Windows位图,JPEG,或GIF文件的事, 它甚至还可以推算图像是否是图标和图元文件!自然,其中的细节是需要些技巧,所以我就写了一个将它们都包含其中的叫"ImgView"( 如 Figure 2 所示)的演示程序。


Figure 2 ImgView

  ImgView是一个典型的MFC文档/视图结构的应用程序,它使用了一个我以前写的叫"CPicture"的类(如 Figure 3 所示)来封装IPicture。CPicture将一些麻烦的COM类型的参数映射为那些更容易被MFC程序员接受的类型。例如,CPicture可以让你直接从一个文件名加载图像,如CFile或CArchive,而不是处理信息流;而且CPicture::Render完成了所有的令人厌烦的而又是IPicture所需要的HIMETRIC坐标转换,这样,你就没必要去做这些了。CPicture甚至还有一个可以从你的资源数据中加载图像的加载函数,所以要显示一个资源图像,你所要做的就是像下面那样写:
     CPicture pic(ID_MYPIC); // 加载pic
     CRect rc(0,0,0,0);      // 使用默认 rc
     pic.Render(pDC, rc);    // 显示它
  什么能够使工作变得更加容易呢?CPicture::Render能获得一个你想在其中显示图像的矩形。IPicture可以适当地拉伸图像。如果你传递了一个空的矩形,CPicture就使用图像本来的尺寸,并不对其进行拉伸。对于图像本身,CPicture要寻找一个名为"IMAGE"的资源类型,所以你必须对你的RC文件进行如下的编写:
IDR_MYPIC IMAGE MOVEABLE PURE "res\\MyPic.jpg"
  总的来说,CPicture相当没头脑。它有一个ATL CComQIPtr巧妙的指向IPicture界面的指针,其中不同的加载函数通过调用OleLoadPicture来初始化该界面。CPicture提供一般的封装函数来调用里面的IPicture。CPicture仅封装了我编写ImgView所需要的IPicture成员函数;这么做是因为我是这样的一个懒惰的程序员。如果你还需要调用IPicture::get_Handle或一些其它的较少用的IPicture成员函数,很抱歉,你就只好自己为其添加封装了。至少,这代码是件琐碎的事情。
  顺便说一下,在我写完CPicture后,我就觉得有件事要提一下,那就是我发现一个鲜为人知的叫做"CPictureHolder"的MFC类也做了绝大部分的类似的事情。你可以在afxctl.h中找到它。
  正如我先前提到的,ImgView是一个典型的MFC文档/视图结构的应用程序,其中CPictureDoc和CPictureView类分别对应于文档和视图结构。如 Figure 4 所示 中显示了该视图。CPictureDoc有些琐碎;它使用CPicture来保存图像--
class CPictureDoc : public CDocument {
protected:
  CPicture m_pict; // 图像
};
,并且CPictureDoc::Serialize调用CPicture::Load从MFC所建立的存档中读取图像。
void CPictureDoc::Serialize(CArchive& ar)
{

  if (ar.IsLoading()) {
    m_pict.Load(ar);
  }
}
  仅仅是为了有趣,CPictureDoc::OnNewDocument从程序的资源数据中加载了一张漂亮的NASA图像。为了显示这图像,CPictureView::OnDraw调用了CPicture::Render。
void CPictureView::OnDraw(CDC* pDC)
{
  CPictureDoc* pDoc = GetDocument();
  CPicture* ppic = pDoc->GetPicture();
  CRect rc;
  GetImageRect(rc);
  ppic->Render(pDC,rc);
}
  GetImageRect是CPictureView的一个函数,它依靠当前ImgView缩放比例而返回一个适当的图像矩形。(ImgView可以通过25%,33%,50%,75%,100%或"自适应比例"这六种比例形式来显示图像)。GetImageRect调用CPicture::GetImageSize获得真实的图像尺寸,随后依据比例适当地缩放。
  现在,在CPictureView中剩下的就是典型的CScrollView部分,其中有用于视的初始化和滚动条尺寸与句柄命令的设置之类的代码。对于IPicture唯一有意思的是,正如之前我所提到的,IPicture::Render希望它的坐标是HIMETRIC单位的,然而一般的MFC应用程序使用的是默认的MM_TEXT映射模式。不要担心,CPicture::Render和CPicture::GetImageSize对其做了魔术般的转换,所以你就没必要为这样世俗的和令人厌烦的琐事而操过多的心了。
  CPictureView有一个消息处理函数值得注意,它就是OnEraseBkgnd。它被要求在图像比视的客户区小的情况时对空白的区域进行填充(如图5所示)。OnEraseBkgnd创建一个与图像大小一样的剪切的矩形,然后将客户矩形填充为黑色。当你变化窗口尺寸的时候,这样的剪切就避免了闪烁,其中的FillRect并没有往被剪切的矩形中填充。这是标准的Windows图形101。


图 5 OnEraseBkgnd填充被剪切的图像

  IPicture/CPicture真正使得显示图像变得容易了。它甚至可以完成调色板的实现和所有令人厌烦的事情。你可以丢掉原先所有的用来加载调色板,BitBlts和StretchBlts等的DIB的绘图代码了,IPicture是个很好的办法。如果你还没有使用IPicture来显示图像,那么现在就开始用它吧!
  所有的事情都是这么的简单,这让我想写另一个类来试试。当你想写一个图像浏览器时,CPictureView是很好用的,但要是你想将一个图像加到对话框或其它的一些窗口上,那该怎么办呢?为了实现这,我写了另一个类,CPictureCtrl(如 Figure 6 所示)。CPictureCtrl可以让你将一个图像作为一个子控件放在任何的对话框或窗口上。例如:
class CAboutDialog : public CDialog {
protected:
  CPictureCtrl m_wndPict;
  virtual BOOL OnInitDialog();
};
BOOL CAboutDialog::OnInitDialog()
{
  m_wndPict.SubclassDlgItem(IDC_MYIMAGE,this);
  return CDialog::OnInitDialog();
}
  这里假设在你的对话框中有一个静态控件,它的ID是IDC_IMAGE,同时还有一个具有相同ID的IMAGE资源。我从我那很常用的CStaticLink中派生了CPictureCtrl,这样,如果你想的话就可以声明一个URL超链接了(或仅仅创建一个与控件和图像具有相同ID的字符串资源)。如果你声明了一个URL,在这图像上点击鼠标将启动你的浏览器并实现这个链接。令人惊奇的是,CPicture保存了一个CPicture对象,并通过重载WM_PAINT来调用CPicture::Render,而不是通过一般的静态控件。要想了解更多的细节,可从本文开始处的链接下载源文件,使用它吧,我祝福你!

使用 发送你的问题和评论给 Paul
 
作者简介
  Paul DiLascia 是一个自由作家,顾问和 Web/UI 方面资深的设计师。他是 Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992)一书的作者。你可以在 网站和 Paul 联系上。
 
本文出自 的 期刊,可通过当地 报摊获得,或者最好是
阅读(1426) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~