这里提及高效稍许有些夸张,仅为应景,因为本身就没有太多高科技,权且作为一种有效的实现。
首先是图片解码器的选择。一般来讲有几种选择:1、组装各种开源库,如libpng, libjpg, giflib等,支持什么格式就得添加对应的解码器;2、开源解码包,如freeimage,没用过但听说也很不错;3、GDI+,支持图片格式广泛,接口简单,性能一般。当然还有其它方式,大抵差不多。我选择的是GDI+,图简便好用,且目前微软支持的OS上都是自带的,无需发布?!。对QQ的程序集DLL进行分析,发现其中贯穿了各种解码技术,有直接采用开源库的,也有依赖GDI+的,不知道是历史遗留问题,还是各个部门之间技术偏好,抑或是另有玄机。扯到技术偏好,让我头疼的就是程序员在项目中随意的引入库,解决同一个问题,往往看到不同的人用不同的技术,甚至同一个人在不同的项目中造不同的轮子,有时候没法说服别人,只能罢了。
GDI+的初始化需要注意一件事情:不要在DLL入口处加载或卸载GDI+,否则会发生锁死现象,具体可查阅msdn。
考虑到高性能的界面中可能会采用多线程UI,因此我建立了ThreadState的TLS对象,所有的数据都存储在这个对象中,同步线程消息是通过惯用手法之隐藏窗口来保证。时至今日,或者早1、2年,我才深刻理解mfc库设计时的那些state管理结构体的用处。GDI+的初始化在ThreadState构造函数中调用,正好避免了加载冲突这个问题。TLS对象的销毁技巧来自谷歌的chromium源码中的base库里的tls实现,原始出处来自CodeProject:http://www.codeproject.com/threads/tls.asp。嗯,其实牛逼的程序员也是纵览乾坤,吸取精华。提到CodeProject,顺带提一下我的学习历程,早期疯狂的泡这个网站,几乎VC方面的东西都把玩过,形成了自己的点状知识积累,类似的有CodeGuru、vckbase(现在已经成为广告站了);之后是 sourceforge、codegoogle、codeplex 找一些小的项目研究,形成自己的线状知识结构;再后来就是大型的源码阅读,偶尔会去谷歌讨论组、微软新闻组看一些疑难杂症问题,构成了自己的面状知识体系。每个人都有自己的学习方法论,这里仅仅是分享我自己的。很多东西都已经看上去有些过时,现在的年轻程序员可能接触的是stackoverflow、github等。
GDI+中的Image是抽象接口,为了方便使用,增加了一层简易封装IMImage,主要接口如下:
static IMImage* FromFile(const std::wstring& uri);
long AddRef();
long Release();
const std::wstring& uri() const { return uri_; }
UINT frame_count() const { return frame_count_; }
bool IsAnimate() const { return frame_count_ > 1; }
long GetFrameDelay(UINT frame) const;
long GetWidth() const;
long GetHeight() const;
Gdiplus::Image* GetImage(UINT frame);
通过uri加载图片,GDI+对本地图片的加载非常简单,Image::FromFile可以直接返回;对于网络图片,需要先通过WinINet下载到本地,然后再加载。
由于使用了tls技术,这里的引用计数实现很简单,就是++、-- 操作。曾经有人告诉我引用技术是为了解决多线程中对象的生命周期问题,我欲与否认。引用计数只是为了解决对象的生命周期问题,而这种情况往往在多线程中出现,因此多线程中或多或少会用到引用计数。
IMImage类可以简单的返回一些GDI+的Image对象提供的图片信息,而GetImage需要多做一些事情。对于包含多帧图片的文件,在绘制的时候需要通过GDI+的Image::SelectActiveFrame方法选择当前帧,该操作非常耗时,因此在加载图片的时候,发现如果是多帧的,我们需要额外的decode_image_来解码存放每一帧图像,这样除第一次渲染比较耗时外,后面的获取都是非常快的。
GetFrameDelay函数是获取某一帧之间的时间间隔,通过GDI+的PropertyItem可以很容易的获取。需要指出的是PropertyItem类使用起来却不是类的方式,需要手动new出一块内存,所以这里用一个结构体我看更合适,而且还是C风格的。
再增加一个IMImageService,管理整个系统中用到的IMImage对象,接口很简单:
IMImage* GetImage(const std::wstring& uri);
void ReleaseImage(IMImage* image);
需要图像就找它要,用完之后记得释放即可。
这样就完成了图片的管理,目前来说足够用,也非常简单,不是么?
posted on 2012-06-17 09:48
万连文 阅读(2935)
评论(2) 编辑 收藏 引用 所属分类:
richedit