作者:马健
邮箱:stronghorse@tom.com主页:http://stronghorse.yeah.net
一、为什么要使用IImgCtx进行图像解码?
在Windows下开发图像显示、图像处理软件的时候,第一步要面对的就是图像解码。目前免费的图像解码代码不少,单独的包括:
Independent JPEG Group(独立JPEG小组)发布的专门解JPEG格式的源代码
libpng组织提供的专门解png格式的源代码
libtiff组织提供的专门解TIFF格式的源代码
giflib组织提供的专门解gif格式的源代码
如果觉得一个、一个去找这些源代码太麻烦,也可以使用Davide Pizzolato开发的CxImage,其中集成了对大多数常见图像格式的解码源代码。
但是在我用过这些源代码后,也发现一些问题:
用起来太麻烦了,不仅在编译的时候需要对编译参数进行设置,而且解码后的图像用起来也麻烦。
可靠性令人怀疑,而且在出错后,跟踪、调试太难了,那一大堆C代码不是好玩的。
所有的解码代码都需要嵌入编译后生成的EXE文件,或DLL中,增加了最终发行包的尺寸。当然如果最终用户希望到手的是一个“大型软件”,则另当别论。
所以我和其它一些“懒”程序员一样,也希望能够找到一个像Windows
API那样,用起来方便,生成的代码又小的通用图像解码器。
根据我在网上看到的资料,我最先考虑使用Windows本身提供的IPicture接口。这个接口在MSDN中有非常详细的说明,在codeguru、codeproject上也有详细的例子可供参考,用起来很方便。可惜的是,这个接口只能解码BMP、WMF、ICO、GIF、JPG格式(公开文档只承认可以解BMP、WMF、ICO)的图像,对PNG、TIFF格式无效。
后来在安装Windows系统的时候,我发现缺省情况下,Windows下GIF、PNG、JPG文件都被关联到了IE浏览器,再仔细想想,这些图像都经常在网页中出现,所以IE支持他们也没有什么好奇怪的。那么IE内核有没有提供什么接口,就好像可以用IHTMLDocument解析HTML源代码一样,可以供我们进行图像解码呢?
答案是:有的,就是IImgCtx接口。这个接口可以解码所有能够在IE中显示的图像格式,包括JPG、PNG、GIF、BMP、WMF、ICO,有时也能解TIFF(后面我会解释为什么说“有时”)。
二、如何使用IImgCtx进行图像解码?
在我手头的IE 5.5 SDK包中,有对IImgCtx接口的完整定义(在IImgCtx.h文件中),在VC++
6的安装目录中,也有对这个接口的定义,文件名一样,文件内容也只差了一行:IE
5.5 SDK包中多定义了一个常量DWN_MIRRORIMAGE,估计是IE 5.5比IE 4多出来的东西,可以对解码出来的图像进行镜像翻转。
但是不要说在MSDN中找,就算是用google搜,也找不到对这个接口的任何说明。好在这个接口比较简单,从接口定义还能大致猜测到它的用法:
DECLARE_INTERFACE_(IImgCtx, IUnknown)
{
#ifndef NO_BASEINTERFACE_FUNCS
/* IUnknown methods */
STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
#endif
/* IImgCtx methods */
/* Initialization/Download methods */
STDMETHOD(Load)(THIS_ LPCWSTR pszUrl, DWORD dwFlags) PURE;
STDMETHOD(SelectChanges)(THIS_ ULONG ulChgOn, ULONG ulChgOff, BOOL fSignal) PURE;
STDMETHOD(SetCallback)(THIS_ PFNIMGCTXCALLBACK pfn, void * pvPrivateData) PURE;
STDMETHOD(Disconnect)(THIS) PURE;
/* Query methods */
STDMETHOD(GetUpdateRects)(THIS_ struct tagRECT FAR* prc, struct tagRECT FAR* prcImg, long FAR* pcrc) PURE;
STDMETHOD(GetStateInfo)(THIS_ ULONG FAR* pulState, struct tagSIZE FAR* psize, BOOL fClearChanges) PURE;
STDMETHOD(GetPalette)(THIS_ HPALETTE FAR* phpal) PURE;
/* Rendering methods */
STDMETHOD(Draw)(THIS_ HDC hdc, struct tagRECT FAR* prcBounds) PURE;
STDMETHOD(Tile)(THIS_ HDC hdc, struct tagPOINT FAR* pptBackOrg, struct tagRECT FAR* prcClip, struct tagSIZE FAR* psize) PURE;
STDMETHOD(StretchBlt)(THIS_ HDC hdc, int dstX, int dstY, int dstXE, int dstYE, int srcX, int srcY, int srcXE, int srcYE, DWORD dwROP) PURE;
};
从接口上看,在创建IImgCtx接口对象后,可以用Load方法调入图像文件,解码完成后即可用Draw方法显示了,不过:
Load方法中的参数指明是一个URL(估计是IE内核的要求),因此需要将本地文件名转换成file://开头的URL,并且必须使用Unicode编码。
与IE中的其它接口一样,IImgCtx工作过程也是异步的,所以才需要SetCallback方法设置回调函数,SelectChanges方法对状态进行监视,不过由于实在找不到什么资料,所以我也不知道这两个接口怎么用。我只知道对于这种异步处理过程,必须创建消息泵,对解码过程中产生的消息进行分发,否则解码过程会被吊住。
Draw方法允许传入一个HDC作为目标DC,如果这个HDC是一个内存DC,即可不显示,直接在内存中对图像进行解码。解码出来的图像是DDB,再转换成DIB后,即可进行常规图像处理。
用IImgCtx进行解码的详细过程见实例文件中的WndImgCtx.cpp。
三、如何将实例源代码集成到VC++工程中?
本文提供的实例包含在ImgViewer.zip中,解开后是一个完整的VC++
6工程。其中除了包含用IImgCtx对图像进行解码的代码外,还包含一个对目录中的文件进行搜索、排序,实现顺序浏览目录中所有图像的实例代码。
如果只需要使用图像解码部分,将WndImgCtx.h、WndImgCtx.cpp包含到你的工程中,在需要用到解码功能的CPP文件开头加入:
#include "WndImgCtx.h"
然后就可以调用
HBITMAP GetBitmapFromFile(LPCTSTR pszFilename);
函数进行解码。从函数原型上即可看出,这个函数传入一个文件路径,返回一个DDB句柄。
如果需要包含顺序显示目录下所有图像的功能,将SrchDir.h、SrchDir.cpp、DirMng.h、DirMng.cpp包含到你的工程中,具体使用方法见MainFrm.cpp中的相关代码。
从使用的效果来看,在Windows 98/Me下,IImgCtx解码TIFF文件的时候有问题,在MSDN上也有几篇文章讨论在98/Me下IE显示不了TIFF的文章,不过照文章上说的进行设置,好像还是不行。在Windows
2000/XP下则没有什么问题。
四、为什么TIFF文件是一个例外?
IE几乎支持所有常见图像格式,包括JPEG、PNG、GIF等,但是在IE的“打开文件”对话框中,唯独不见TIFF。我用libtiff组织提供的TIFF图像例子,测试本文提供的图像浏览器实例,结果发现在98/Me/XP下,一个TIFF文件也打不开;在Windows
2000下,能够打开差不多一半,另一半打不开。
在Windows 2000下,在VC 6中按Debug模式编译、运行ImageViewer,打开TIFF文件,退出,在Output窗口中可以看到下列行:
Loaded 'C:\WINNT\system32\imgutil.dll', no matching symbolic information found.
Loaded 'C:\WINNT\system32\tifflt.dll', no matching symbolic information found.
Loaded 'C:\WINNT\system32\oieng400.dll', no matching symbolic information found.
Loaded 'C:\WINNT\system32\mscms.dll', no matching symbolic information found.
Loaded 'C:\WINNT\system32\winspool.drv', no matching symbolic information found.
而在Windows XP下,只能看到第一行,没有后续的行。
在Windows 2000中,运行regedit,然后搜索tifflt.dll,可以看到它提供“TIFFilter Class”,CLSID为
{EBD0F6B6-4AED-11D1-9CDB-00805F0C62F5} 。再继续在regedit32中搜索这个CLSID,可以在
HKEY_CLASSES_ROOT\MIME\Database\Content Type\image/tiff 项中,看到 Image
Filter CLSID 为这个值。在这个项的前面,还有 image/png、image/jpeg
等项,它们都有 Image Filter CLSID
这个键。从MSDN提供的文档可以知道,HKEY_CLASSES_ROOT\MIME\Database\Content
Type 项下列出的是IE能够识别的所有MIME type,MIME
type一般从web server返回的HTML包头提取。
从 Image Filter CLSID 这个键的名称来看,这个CLSID记录的应该就是图像解码器的CLSID。在Windows
98/Me/XP下运行regedit32,查找到HKEY_CLASSES_ROOT\MIME\Database\Content Type\image/tiff项,在它下面并没有
Image Filter CLSID 这个键。而在 image/png、image/jpeg
等项下面,都有这个键。这个大概就是为什么在Windows 98/Me/XP下,IE
无法解码TIFF文件的原因:没有对应的解码器。
我曾经尝试过将tifflt.dll移至到Windows XP下,但是因为DLL冲突,没有成功。看来要想在2000以外的Windows平台下显示TIFF文件,只有等待微软推出对应的TIFF解码器了。
五、进一步的练习
实例代码虽然演示了一个图像浏览器的最原始功能,但是要想成为一个真正的图像浏览器,还有很多需要改进的地方,有兴趣的可以试一下,就当做是练习好了:
支持在线浏览。其实IImgCtx本身就支持对网络图片进行解码,而且可以边解码边显示,就象在IE中一样。
支持从内存中显示图片,而不是读文件。这个需要一定的技巧,可以有两种简单点的办法:一种是架势web服务器,解码器按照http协议获取图片。在codeguru和codeproject上,有很多现成的web server代码,直接拿来用就好,自己只要考虑怎么填写返回内容即可。VC 6自带的MSDN光盘上,也带了一个名为HTTPSVR的例子,说明如何用MFC和WinSock创建web server。另一种是使用Asynchronous Pluggable Protocols(协议插件),到MSDN、codeguru和codeproject上搜索这几个关键字,从理论到源代码都能找出一堆,在这里我就不罗嗦了。
图像居中显示。这个相对容易一些。
对于大图像,支持用鼠标拖拽。这个应该是有现成例子可供参考,找一下吧。
支持图像处理。这个可以参考CxImage,它支持将DDB转换成DIB,然后对DIB进行各种处理。不过如果不使用其它图像编码代码的话,编辑完的图像大概只能存储成BMP文件。
支持浏览子目录下的图片。SrchDir.h、SrchDir.cpp、DirMng.h、DirMng.cpp都是我从ComicsViewer源代码中拷贝过来的,不过做了很多简化,有兴趣的可以再把我简化掉的东西试着补回去。