分类

  • 软件天地

  • 讨论在MFC程序中显示JPG/GIF图像‖
    像控件拖到Form中,分分钟
    么是不是要自己编写JPG解
    在MFC中显示JPG或者GIF图
    程的程序员,要在程序中显示JP
    即可搞掂。但是C++程序员要显
    压缩代码呢?当然不用那么复杂
    像。
    G或者GIF图像简直易如反掌,将图
    示同样的图形却没有那么轻松,那
    啦!本文将针对这个问题讨论如何


      用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的运行画面

      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创
    填成黑色。之所以要创建切边矩形
    绘制切边矩形内的区域,此乃

      

      
    图二 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+来旋转图像。
      

      
    图像旋转90度

      前面说过,原来程序中
    口。在本文的例子程序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                                                              

    上一页 下一页




    map