登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> 程序员学前班[不再更新,只读] >> 主题: [wince/ppc]WinCE下加载流式JPG图片[zt]     [回主站]     [分站链接]
标题
[wince/ppc]WinCE下加载流式JPG图片[zt]
clq
浏览(0) + 2009-02-05 09:38:06 发表 编辑

关键字:

[wince/ppc]WinCE下加载流式JPG图片

http://www.cnblogs.com/ty--90/archive/2008/06/26/1230356.html

急!WinCE下加载流式JPG图片

最近两个星期一直在忙一个地图引擎的开发,难度很大,以致于周末跑同学那里求援,问题解决了一大半了,以为一切搞定的时候,致命的问题出现了,winCE不支持利用流加载图像OleLoadPicture函数!已经搞了三天了,还是没有解决。
MFC提供的CWnd只有默认加载BMP文件的接口,对JPG等图像是不支持。加载.JPG格式的图片,有两种方法,用流对象+IPicture接口加载;IImage接口加载。
对于这两种方法我写了两个VC的测试程序,可以,就性能上采用流对象加载要比IImage接口快,要命的我把代码移到PPC上就挂了,IImage接口可以实现显示但是必须提供图片的绝对路径和格式,也就是说图片必须以文件的方式存在,如果是内存的一段图片数据则没有办法,而我上星期刚把所有图片整合为一个二进制文件,拿到的全部是数据流,所有用IImage接口是没戏了;采用IPicture接口应该是最正确的办法,对于手持设备,CPU频率本来就低,直接将图片数据加载显示,性能上要提升很多,特别是对于我们这样频繁的LOAD图片。
郁闷的是OleLoadPictureAPI WinCE不支持!也想过用第三方的库Jpeglib来实现,昨天搞到晚上8点,还有8个错误解决不了,应该是Jpeglib版本不对,Jpeglib对于图象的压缩和转换有很好的支持,但是我们用不到,这个想法基本被否定了。

采用IPicture接口的实现
-----------------------------------------------------------------------
IPicture *m_picture;
OLE_XSIZE_HIMETRIC m_width;
OLE_YSIZE_HIMETRIC m_height;

CString m_filename("D:\\009.jpg");//文件名

CFile m_file(m_filename,CFile::modeRead );

//获取文件长度
DWORD m_filelen = m_file.GetLength();

//在堆上分配空间
HGLOBAL m_hglobal = GlobalAlloc(GMEM_MOVEABLE,m_filelen);

LPVOID pvdata = NULL;
//锁定堆空间,获取指向堆空间的指针
pvdata = GlobalLock(m_hglobal);

//将文件数据读区到堆中
m_file.ReadHuge(pvdata,m_filelen);

IStream* m_stream;

GlobalUnlock(m_hglobal);

//在堆中创建流对象
CreateStreamOnHGlobal(m_hglobal,TRUE,&m_stream);

//利用流加载图像
OleLoadPicture(m_stream,m_filelen,TRUE,IID_IPicture,(LPVOID*)&m_picture);

m_picture->get_Width(&m_width);
m_picture->get_Height(&m_height);

CDC* dc = GetDC();

m_IsShow = TRUE;
CRect rect;
GetClientRect(rect);
SetScrollRange(SB_VERT,0,(int)(m_height/26.45)-rect.Height());
SetScrollRange(SB_HORZ,0,(int)(m_width/26.45)-rect.Width());

m_picture->Render(*dc,1,50,(int)(m_width/26.45),(int)(m_height/26.45),0,m_height,m_width,-m_height,NULL);


采用IImage接口的实现
-------------------------------------------------------------------
HBITMAP CImageLoader::loadImageFile( CString &strFileName)
{
IImagingFactory *pImgFactory =NULL;
IImage *pImage =NULL;
CoInitializeEx(NULL,COINIT_MULTITHREADED);
HBITMAP hResult =0;
if (SUCCEEDED(CoCreateInstance(CLSID_ImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IImagingFactory,
(void **)&pImgFactory)))
{
ImageInfo imageInfo;
if(SUCCEEDED(pImgFactory->CreateImageFromFile(strFileName,&pImage))
&& SUCCEEDED(pImage->GetImageInfo(&imageInfo)))
{
CWindowDC dc(0);
CDC dcBitmap;
dcBitmap.CreateCompatibleDC(&dc);
hResult =CreateCompatibleBitmap(dc.GetSafeHdc(),imageInfo.Width,imageInfo.Height);
if(hResult){
HGDIOBJ hOldBitmap = dcBitmap.SelectObject(hResult);
pImage->Draw(dcBitmap.GetSafeHdc(),CRect(0,0,imageInfo.Width,imageInfo.Height),NULL);
dcBitmap.SelectObject(hOldBitmap);

}
pImage->Release();
}

pImgFactory->Release();
CoUninitialize();
return hResult;
}

}

clq
2009-2-5 9:41:16 发表 编辑

IImage的图片显示收藏
新一篇: EVC编程技巧集合 | 旧一篇: SMS2Gps, 实现简单监控
//========================================================================
//TITLE:
// 详聊IImage的图片显示
//AUTHOR:
// norains
//DATE:
// Sunday 26-August-2007
//Environment:
// EVC4.0 + Windows CE 5.0 Standard SDK
//========================================================================
IImage是WinCE5.0之后才加入的COM组件,用处正如其名,更为方便显示图片.WinCE 5.0之后,我们就可以喜新厌旧,抛弃imgdecmp,转投IImage,呵呵~虽然个人觉得这两个解码,论速度不相伯仲,都是一样的----慢,特别是绘制大图片的时候还有限制,但毕竟有总好过无,所以我们就来看看今天的主角吧!

用IImage来显示图片真是异乎寻常的简单,无非就是调用几个函数而已:

1. IImage * m_pImage;
2. IImagingFactory * m_pImagingFactory;
3.
4. HRESULT hr;
5.
6. //COM初始化
7. if (FAILED(hr = CoInitializeEx(NULL, COINIT_MULTITHREADED)))
8. {
9. goto END;
10. }
11.
12. //创建COM实例
13. if(FAILED(hr = CoCreateInstance(CLSID_ImagingFactory,NULL,CLSCTX_INPROC_SERVER,IID_IImagingFactory,(void**) &m_pImagingFactory)))
14. {
15. goto END;
16. }
17.
18. //从文件中创建图片
19. if(FAILED(hr = m_pImagingFactory->CreateImageFromFile(TEXT("测试.bmp"), &m_pImage)))
20. {
21. goto END;
22. }
23.
24. //绘制图片
25. if(FAILED(hr = m_pImage->Draw(hdc,&rcWnd,NULL)))
26. {
27. goto END;
28. }
29.
30.
31. END:
32. //释放资源
33. if(m_pImage != NULL)
34. {
35. m_pImage->Release();
36. m_pImage = NULL;
37. }
38.
39. if(m_pImagingFactory != NULL)
40. {
41. m_pImagingFactory->Release();
42. m_pImagingFactory = NULL;
43. }
44.
45. CoUninitialize();

OK,就是这么简单,只要OS中只要包含了相应的解码器组件,以上代码显示大部分的图片应该是没问题的.好吧,既然如此,那么这篇豆腐块也就可以结束了~:-)

然而可惜的是,微软向来不会将事情做得很完满,要不然这篇豆腐块到这里确实是尽善尽美了.

这段代码之中,本应风平浪静,但偏偏有一个函数表现怪异,且很容易调用失败,它乃IImage::Draw()!

1)显示源文件特定区域

显示整副图片没啥问题,只要将srcRect参数赋值NULL即可.但如果是显示特定的区域呢?比如说,有一副800*600的图片,我只想显示一半,那该咋办?估计很多人(恩,也包括我),不会仔细看文档,而会是凭借经验直接这么写:


1. RECT rcSrc = {0,0,400,600};
2. 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,让我们看看单位的转换吧:



1. m_pImage->GetImageInfo(&ImageInfo);
2.
3. double dDotPermmX = ImageInfo.Xdpi / 25.4;
4. double dDotPermmY = ImageInfo.Ydpi / 25.4;
5.
6. //pSrcRect指向以像素点为单位的区域
7. RECT rcSrc = {(LONG)(pSrcRect->left / dDotPermmX / 0.01),
8. (LONG)(pSrcRect->top / dDotPermmY / 0.01),
9. (LONG)(pSrcRect->right / dDotPermmX / 0.01),
10. (LONG)(pSrcRect->bottom / dDotPermmY / 0.01)};
11.
12. m_pImage->Draw(hdc,&rcDraw,&rcSrc);

虽然这样能够基本正常显示指定的区域,但还是有一定的误差,不过接下来讨论的另一个问题,恰好可以解决.


2)IImage::Draw()耗时久

这是一个没有办法的问题,因为IImage::Draw()需要经历一些的解码才能绘制到目标DC,特别是在显示一些特别大的JPEG(如果能够显示的话:-))时更为明显.如果图片仅仅是显示一次,也许问题尚且不那么严重,但如果是需要多次调用,比方说拖动图片,那么这种速度绝对无法忍受的.当然咯, 这个问题在将来是肯定不会出现的,并且这个将来也不会很久,不用等到人老珠黄或是为伊消得人憔悴,只要嵌入式设备的解码速度和目前主流的PC相差无几.这一天不会远了,只是对于解决今天的问题却是远水救不了近火,还是乖乖地用技巧解决吧! :-)

方法其实很简单,IImage::Draw()耗时久嘛,那我们只要第一次绘制时将图像保存到内存DC中,然后再把内存DC的数据绘制到目标DC即可.这样一来,仅仅是第一次绘图时感到不愉快(如果你脾气暴躁,也许会感到烦躁),但之后的每次都是畅快之旅.以一次换来一劳永逸,还有比这更便宜的事嘛?


1. //获取图片属性
2. m_pImage->GetImageInfo(&ImageInfo);
3.
4. //创建一个内存DC,用来存储图片数据
5. hBitmap = CreateCompatibleBitmap(hdc,ImageInfo.Width,ImageInfo.Height);
6. hdcMem = CreateCompatibleDC(hdc);
7. hOldSel = SelectObject(hdcMem,hBitmap);
8.
9. ...
10.
11. //将图片数据存储到内存DC中
12. rcMemDC = {0,0,ImageInfo.Width,ImageInfo.Height};
13. m_pImage->Draw(hdcMem,&rcMemDC,NULL);
14.
15. ...
16.
17. //将图片绘制到目标DC
18. StretchBlt(hdc,
19. pDstRect->left,
20. pDstRect->top,
21. pDstRect->right - pDstRect->left,
22. pDstRect->bottom - pDstRect->top,
23. hdcMem,
24. pSrcRect->left,
25. pSrcRect->top,
26. pSrcRect->right - pSrcRect->left,
27. pSrcRect->bottom - pSrcRect->top,
28. SRCCOPY);



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

也许有细心的朋友可能会注意到StretchBlt()的形参.恩,在此对这些朋友表示我由衷的敬意.没错,是的,将图片数据保存到hdcMem后,带来的额外一个好处就是,绘制图片某个区域时再也不用转换单位啦!

因为是将整张图片数据存储到hdcMem,所以参数只要设置为NULL;保存到hedMem后,采用StretchBlt()函数绘制只需要以像素为单位.呵呵,是的,没错,无形中我们就避免了单位转换可能带来的损失.所谓的一箭双雕乎?


3)绘制大图片失败

在调用IImage::Draw()绘制大图片时,很有可能导致的后果是:绘制失败.而提示的错误,更多是:E_OUTOFMEMORY,内存不足.这是一个很无奈的现实.虽然我的设备内存已经很明显大于图片所需的,但由于受限制于WinCE5.0 32M虚拟内存,即使是采用旁门左道分配更多的虚拟内存,其结果无非也是:失败!没办法,恩,至少我是没找到解决的方法.

当然咯,万事没绝对,如果对图片质量要求不高,倒还是有法子让其显示的.很简单,只要获取缩略图实例,然后再绘制即可.



1. m_pImage->GetThumbnail(WIDTH,HEIGHT,&m_pThum);
2. m_pThum->Draw(hdc,&rcDC,NULL);



这次IImage::Draw()应该不会失败了.当然,如果还是那么倒霉,那唯一的方法就是将WIDTH和HEIGHT减小,直到能正常显示为止.不过,随着数值的减小,图片的质量也会随之降低.

clq
2009-2-5 9:43:59 发表 编辑

在您的C++程序中显示GIF、JPEG以及其它格式的图像文件
作者:microsoft 来源:microsoft 时间:2003年2月14日 13:32 阅读1958次



本文是使用Microsoft eMbedded Visual C++; (EVC++)为Pocket PC开发C++应用程序系列文章的第二篇。本文介绍了如何通过免费的CVOImage类库使用Pocket PC的IMGDECMP.dll图像处理程序。

需要具备

* Microsoft eMbedded Visual C++ V3.0或更高版本,该软件是免费的(您需要支付运费和手续费)。
* VOImage类库,可从Virtual Office Systems公司的站点免费下载。
* IMGDECMP.dll的头文件,可以从Conduits Technologies公司免费下载。您还可以通过VOImage页面下载该头文件。

支持的语言
英语

IMGDECMP.dll的使用方法
Microsoft在中使用IMGDECMP DLL显示JPEG、GIF、BMP、PNG以及其它标准格式的图像文件。出于几方面的原因,我们需要在我们自己的应用程序中使用IMGDECMP DLL解码图像文件:

* 通过使用类似IMGDECMP DLL这样的共用组件,您可以缩小应用程序的内存体积。
* 您不用随同您的产品一起发售解码代码,从而无需购买专利代码的许可证,例如GIF格式,该格式由CompuServe所拥有,而且一般情况下需要支付许可费用。
* 可以获得优良的执行性能。
* 这些代码都经过了完全彻底的测试,而且非常稳定。

如果您愿意,您可以直接对IMGDECMP DLL编写代码。Conduits Technologies公司为此专门开辟了一个优秀的Web站点,所以我就不再就如何直接对IMGDECMP.dll编写代码详加赘述了。简单步骤如下:

1. 创建一些回调函数对输入的数据流进行处理。
2. 定义并且打开输入数据流。
3. 填充“DecompressImageInfo”结构。
4. 调用IMGDECMP DLL 中的DecompressImageIndirect()函数,该函数会重复调用流输入函数接收每个数据包,直到输入源末尾。

直接对IMGDECMP DLL编写代码相当复杂。为了简化这个强大资源的使用过程,您可以使用Virtual Office Systems公司提供的免费CVOImage类。

使用CVOImage
为了简化使用IMGDECMP DLL所需编写的代码量,Virtual Office Systems公司开发了CVOImage C++类,该C++类将回调函数、加载操作以及结果位图的绘制函数封装在一起。它的源代码对于个人用途和商业用途来说均是免费的。

使用CVOImage的第一种方法是从磁盘(Pocket PC上的存储内存)上加载图像文件并显示它们:

1. 构造一个CVOImage对象。
2. 使用GetDC() Win32 API获得窗口设备上下文环境的一个句柄。您还可以将这个句柄传递给一个内存设备上下文环境。
3. 调用CVOImage::Load()方法,传递设备上下文环境句柄和欲加载文件的文件名(.gif、.jpg等)。
4. 调用CVOImage::Draw()方法,传递设备上下文环境句柄并且对准图像的左上角,此外,如果希望以不同于原始尺寸的大小绘制图像,你还可以设置想要的图像宽度和高度。

使用CVOImage的第二个方法是将图像文件保存为应用程序的.exe或.dll文件中的一个资源:

1. 使用EVC++的资源编辑器,从菜单中选择“插入资源…”(Insert…Resource)。
2. 单击“插入资源”对话框中的“导入…”按钮。
3. 选择想要导入的图像文件。
4. 系统要求您输入一个类名。您可以指定任意一个不重复的类名。然后,该图像文件将成为您应用程序或者DLL编译资源的一部分。
5. 构造一个CVOImage对象。
6. 使用GetDC() Win32 API获得窗口设备上下文环境的一个句柄。您还可以将这个句柄传递给一个内存设备上下文环境。
7. 调用CVOImage::Load()方法,传递设备上下文环境句柄和欲加载文件的文件名(.gif、.jpg等)。
8. 调用CVOImage::Draw()方法,传递设备上下文环境句柄并且对准图像的左上角,此外,如果希望以不同于原始尺寸的大小绘制图像,你还可以设置想要的图像宽度和高度。

小结
您可以在应用程序中显示BMP格式以外的其它图像文件。通过使用CVOImage类和IMGDECMP DLL,您无需添加太多代码,即可在应用程序中显示GIF、JPEG、XBM以及其它图像格式。


总数:2 页次:1/1 首页 尾页  
总数:2 页次:1/1 首页 尾页  


所在合集/目录



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1