移动平台图像显示

IImage组件是在wince5.0新加入的,它可以调用公共接口来显示多种图片格式(jpg,png,gif,bmp),并且还可以扩展用以支持更多图片.但也许是因为新加入的原因,尚存在不少问题,其中编译的error link 2005就令人非常头痛.
   
   
首先让我们先来看看一个能够编译通过的最简单IImage的用法:

//////////////////////////////////////////////////////////////////////   
// TempApp.cpp : Defines the entry point for the application.

#include "stdafx.h"
#include "initguid.h"
#include "imaging.h"

#pragma comment (lib,"Ole32.lib")

IImage * m_pImage;
        IImagingFactory * m_pImagingFactory;
   
    HRESULT hr;

  //COM初始化
    if (FAILED(hr = CoInitializeEx(NULL, COINIT_MULTITHREADED)))
  { 
        goto END;
  }

//创建COM实例
    if(FAILED(hr = CoCreateInstance(CLSID_ImagingFactory,NULL,CLSCTX_INPROC_SERVER,IID_IImagingFactory,(void**) &m_pImagingFactory)))
    {
        goto END;
    }

  //从文件中创建图片
    if(FAILED(hr = m_pImagingFactory->CreateImageFromFile(TEXT("
测试.bmp"), &m_pImage)))
    {
        goto END;
    }
     
    //
绘制图片

    if(FAILED(hr = m_pImage->Draw(hdc,&rcWnd,NULL)))
    {
      goto END;
    }

 

 

END:
  //
释放资源

    if(m_pImage != NULL)
    {
        m_pImage->Release();
        m_pImage = NULL;
    }

    if(m_pImagingFactory != NULL)
    {
        m_pImagingFactory->Release();
        m_pImagingFactory = NULL;
    }

    CoUninitialize();

 

在这个文件中,我们需要注意两点
   
    1.
头文件的包含次序.
     #include "initguid.h"
一定要在#include "imaging.h"之前.如果不包含"initguid.h"或在#include "imaging.h"之后含,则编译时会出现错误
:
     TempApp.obj : error LNK2001: unresolved external symbol CLSID_ImagingFactory
     TempApp.obj : error LNK2001: unresolved external symbol IID_IImagingFactory  
   
    2."Ole32.lib"
需要
pragma
    
如果"Ole32.lib"没有pragma的话,那么编译的时候将会出现如下错误
:
     TempApp.obj : error LNK2019: unresolved external symbol __imp_CoUninitialize referenced in function "void __cdecl Open(void)" (
?Open@@YAXXZ
)
     TempApp.obj : error LNK2019: unresolved external symbol __imp_CoCreateInstance referenced in function "void __cdecl Open(void)" (
?Open@@YAXXZ
)
     TempApp.obj : error LNK2019: unresolved external symbol __imp_CoInitializeEx referenced in function "void __cdecl Open(void)" (
?Open@@YAXXZ
)
     MIPSIIDbg/TempApp.exe : fatal error LNK1120: 3 unresolved externals

   OK,就是这么简单,只要OS中只要包含了相应的解码器组件,以上代码显示大部分的图片应该是没问题的.好吧,既然如此,那么这篇豆腐块也就可以结束了~:-)
   
   
然而可惜的是,微软向来不会将事情做得很完满,要不然这篇豆腐块到这里确实是尽善尽美了.

 

这段代码之中,本应风平浪静,但偏偏有一个函数表现怪异,且很容易调用失败,它乃IImage::Draw()!
   
    1)
显示源文件特定区域
   
   
   
显示整副图片没啥问题,只要将srcRect参数赋值NULL即可.但如果是显示特定的区域呢?比如说,有一副800*600的图片,我只想显示一半,那该咋办?估计很多人(,也包括我),不会仔细看文档,而会是凭借经验直接这么写
:
      RECT rcSrc = {0,0,400,600};
    m_pImage->Draw(hdc,&rcWnd,&rcSrc);

    ,我的上帝,运行这段代码吧!你会看到什么?如果幸运的话,你会一个一个小方块!这时候,估计很多人会问候微软的父母,,等等,微软的东西是有不少bug,但这么简单的东西,还不至于让其如此糊涂的.
   
   
仔细看看文档关于该参数的说明:An optional pointer to a RECT that specifies, in 0.01mm units, the portion of the image to be drawn in dstRect.

 

看到最重要的一句了么:in 0.01mm units !! 0.01mm为单位,非像素点! 虽然微软的出发点是好的,让其显示的精度更为准确,但这个确实给我们带来不少的麻烦.但既然微软接口都如此定义了,与其在这里干跺脚,抱怨微软,还不如我们做点实际的,毕竟我们还是要完成任务,领取每个月的薪水养家糊口.:-(
   
   
天无绝人之路,OK,让我们看看单位的转换吧
:     m_pImage->GetImageInfo(&ImageInfo);
   
    double dDotPermmX = ImageInfo.Xdpi / 25.4;
        double dDotPermmY = ImageInfo.Ydpi / 25.4;
       
        //pSrcRect
指向以像素点为单位的区域

        RECT rcSrc = {(LONG)(pSrcRect->left / dDotPermmX / 0.01),
            (LONG)(pSrcRect->top / dDotPermmY / 0.01),
            (LONG)(pSrcRect->right / dDotPermmX / 0.01),
            (LONG)(pSrcRect->bottom / dDotPermmY / 0.01)};  
           
        m_pImage->Draw(hdc,&rcDraw,&rcSrc);
       
虽然这样能够基本正常显示指定的区域,但还是有一定的误差,不过接下来讨论的另一个问题,恰好可以解决.
        

2)IImage::Draw()耗时久
       
   
这是一个没有办法的问题,因为IImage::Draw()需要经历一些的解码才能绘制到目标DC,特别是在显示一些特别大的JPEG(如果能够显示的话:-))时更为明显.如果图片仅仅是显示一次,也许问题尚且不那么严重,但如果是需要多次调用,比方说拖动图片,那么这种速度绝对无法忍受的.当然咯,这个问题在将来是肯定不会出现的,并且这个将来也不会很久,不用等到人老珠黄或是为伊消得人憔悴,只要嵌入式设备的解码速度和目前主流的PC相差无几.这一天不会远了,只是对于解决今天的问题却是远水救不了近火,还是乖乖地用技巧解决吧! :-)
   
   
方法其实很简单,IImage::Draw()耗时久嘛,那我们只要第一次绘制时将图像保存到内存DC,然后再把内存DC的数据绘制到目标DC即可.这样一来,仅仅是第一次绘图时感到不愉快(如果你脾气暴躁,也许会感到烦躁),但之后的每次都是畅快之旅.以一次换来一劳永逸,还有比这更便宜的事嘛
?
  //
获取图片属性

    m_pImage->GetImageInfo(&ImageInfo);
  
    //
创建一个内存DC,用来存储图片数据

    hBitmap = CreateCompatibleBitmap(hdc,ImageInfo.Width,ImageInfo.Height);
    hdcMem = CreateCompatibleDC(hdc);
    hOldSel = SelectObject(hdcMem,hBitmap);

    ...

    //将图片数据存储到内存DC
    rcMemDC = {0,0,ImageInfo.Width,ImageInfo.Height};   
    m_pImage->Draw(hdcMem,&rcMemDC,NULL);

 

//将图片绘制到目标DC
    StretchBlt(hdc,
                            pDstRect->left,
                            pDstRect->top,
                            pDstRect->right - pDstRect->left,
                            pDstRect->bottom - pDstRect->top,
                            hdcMem,
                            pSrcRect->left,
                            pSrcRect->top,
                            pSrcRect->right - pSrcRect->left,
                            pSrcRect->bottom - pSrcRect->top,
                            SRCCOPY);

    只要第一次将图片数据保存到hdcMem,以后绘制图片时只要绘制hdcMem数据即可,这比还需要经历一次解码过程的IImage::Draw()快多了!
   

也许有细心的朋友可能会注意到StretchBlt()的形参.,在此对这些朋友表示我由衷的敬意.没错,是的,将图片数据保存到hdcMem,带来的额外一个好处就是,绘制图片某个区域时再也不用转换单位啦!
   
   
因为是将整张图片数据存储到hdcMem,所以参数只要设置为NULL;保存到hedMem,采用StretchBlt()函数绘制只需要以像素为单位.呵呵,是的,没错,无形中我们就避免了单位转换可能带来的损失.所谓的一箭双雕乎
?
   
   
    3)
绘制大图片失败

   
   
在调用IImage::Draw()绘制大图片时,很有可能导致的后果是:绘制失败.而提示的错误,更多是:E_OUTOFMEMORY,内存不足.这是一个很无奈的现实.虽然我的设备内存已经很明显大于图片所需的,但由于受限制于WinCE5.0 32M虚拟内存,即使是采用旁门左道分配更多的虚拟内存,其结果无非也是:失败!没办法,,至少我是没找到解决的方法.
   
   
当然咯,万事没绝对,如果对图片质量要求不高,倒还是有法子让其显示的.很简单,只要获取缩略图实例,然后再绘制即可
.     m_pImage->GetThumbnail(WIDTH,HEIGHT,&m_pThum);
    m_pThum->Draw(hdc,&rcDC,NULL);
这次IImage::Draw()应该不会失败了.当然,如果还是那么倒霉,那唯一的方法就是将WIDTHHEIGHT减小,直到能正常显示为止.不过,随着数值的减小,图片的质量也会随之降低.

 

下面是自个根据上面写的捣腾出来的  开发环境 VS2005  + WINCE6.0  中文SDK 模拟器

void CWinCE_TestDlg::ShowPic(const TCHAR *fileName)
{

 IImage * m_pImage, *m_pThum;
 IImagingFactory * m_pImagingFactory;

 HRESULT hr;

 HBITMAP  hBitmap = 0;

 CWindowDC hdc(0); 
 CDC hdcMem;
 HGDIOBJ  hOldSel;
 ImageInfo imageInfo;

 //COM初始化
 if (FAILED(hr = CoInitializeEx(NULL, COINIT_MULTITHREADED)))
 { 
  goto END;
 }

 //创建COM实例
 if(FAILED(hr = CoCreateInstance(CLSID_ImagingFactory,NULL,CLSCTX_INPROC_SERVER,IID_IImagingFactory,(void**) &m_pImagingFactory)))
 {
  goto END;
 }

 //从文件中创建图片
 if(FAILED(hr = m_pImagingFactory->CreateImageFromFile(fileName, &m_pImage)))
 {
  goto END;
 }

 

//绘制图片
//  if(FAILED(hr = m_pImage->Draw(hdc,&rcWnd,NULL)))
//  {
//   goto END;
//  }
 //
获取图片属性


 m_pImage->GetImageInfo(&imageInfo);

 //double dDotPermmX = imageInfo.Xdpi / 25.4;
 //double dDotPermmY = imageInfo.Ydpi / 25.4;

 ////pSrcRect指向以像素点为单位的区域
 //RECT rcSrc = {(LONG)(pSrcRect->left / dDotPermmX / 0.01),
 // (LONG)(pSrcRect->top / dDotPermmY / 0.01),
 // (LONG)(pSrcRect->right / dDotPermmX / 0.01),
 // (LONG)(pSrcRect->bottom / dDotPermmY / 0.01)};  

 //创建一个内存DC,用来存储图片数据
 hdcMem.CreateCompatibleDC(&hdc);
 hBitmap = CreateCompatibleBitmap(hdc.GetSafeHdc(),imageInfo.Width,imageInfo.Height);
 hOldSel = hdcMem.SelectObject(hBitmap);


 //
将图片数据存储到内存DC
 RECT  rcMemDC = {0,0,imageInfo.Width,imageInfo.Height};

 //获取缩略图实例
 m_pImage->GetThumbnail(imageInfo.Width,imageInfo.Height,&m_pThum);

 m_pThum->Draw(hdcMem.GetSafeHdc(),&rcMemDC,NULL);

 //hdcMem.SelectObject(hOldSel);

 RECT *pDstRect = &rcMemDC;
 RECT *pSrcRect = &rcMemDC;

 

 

//将图片绘制到目标DC
   StretchBlt(hdc,
//    pDstRect->left,
//    pDstRect->top,
//    pDstRect->right - pDstRect->left,
//    pDstRect->bottom - pDstRect->top,
 30,
 50,
 128*2,
 120*2,
   hdcMem,
    pSrcRect->left,
    pSrcRect->top,
    pSrcRect->right - pSrcRect->left,
    pSrcRect->bottom - pSrcRect->top,
   SRCCOPY);


END:
 //
释放资源
 if(m_pImage != NULL)
 {
  m_pImage->Release();
  m_pImage = NULL;
 }

 if(m_pImagingFactory != NULL)
 {
  m_pImagingFactory->Release();
  m_pImagingFactory = NULL;
 }
 
 CoUninitialize();
}
能够正常显示·

你可能感兴趣的:(WinCE)