在IM中使用动画表情是一种非常有趣的方式,然而选择一种合适的方式来实现却并不容易。

一般来说,除了自己去实现一个富文本控件,目前主要的解决方案有3种:

1、使用浏览器做容器。

2、使用QT提供的Richtext做容器。

3、使用Richedit做容器。

使用浏览器做容器好处是使用简单,效率应该也不错(没有测试,只是感觉),缺点也很明显:内存占用太高,依赖于浏览器内核。基于IE内核虽然不需要带一个大的安装包,但IE内核问题不少,有了问题很难解决;webkit内核虽然开源,但是体积宠大。

而要使用QT的Richtext则要求程序是基于QT开发,如YY语音。对于一般的客户端程序来说,QT也还是太大了,而且QT中的Richtext能够实现的功能和windows中的richedit相比也还是略显单薄。

这里重点谈谈第三种方式,也是在各大IM中广泛使用的方式:基于Richedit的插入OLE对象的方式实现动画表情。

目前看来在QQ中实现的动画表情刷新效率应该是最高的,可以看到QQ从历史版本以来就一直坚持使用richedit来显示聊天内容。

使用Richedit的优点很多,和浏览器相比如体积小,内存占用小,效率在经过适当优化后也可以很高(现在的QQ在动画刷新上应该是首屈一指的);和QT的richtext相比,不需要依赖QT(这里只限于windows平台),功能丰富,而且MS也还在不断推出新版本。

虽然说在QQ中使用Richedit显示动画表情性能不错,然而不是随便哪个客户端开发人员简单实现一个OLE对象就可以实现的。到底能达到什么样的性能还要看最后优化的功力。

说了这么多,干货出场......

我们知道,Richedit是一个OLE对象的容器。通过自己实现一个新的OLE对象,就可以实现在Richedit中显示自绘的图象,当然也包含表情动画。

动画要动起来,不外乎定时去刷新这些OLE对象。

要刷新一个OLE对象,首先需要知道OLE对象的位置。

获得对象位置在我看来可以有两种方式:

1、每次都向richedit查询自己的位置(如本博客上一篇提供的方法类似)。

2、在OLE对象的OnDraw函数中缓存对象的绘制位置。

当容器中显示的表情数量相对较少时,如<100,这时第一种方式简单有效,但是当显示对象很多时(如满屏的小动画,>1000个),尽管采用两分法去查询坐标,CPU占用也不简单。

第二种方法简单直接,因为对象插入后是要显示的,一显示容器就要调用OLE的OnDraw方法来绘制这个OLE对象,其中的参数就有显示位置,只需要在这时把这个位置缓存起来即可,不需要每次都去查询。但是问题也来了,这种方法只知道谁要显示,不知道谁不要显示,那么那些不需要显示的表情的定时器如何处理呢?

解决这个问题的答案在于一个由Richedit在刷新时发出来的消息:EN_UPDATE。该消息在Richedit每次绘制前都会由Richedit Send到应用程序。有了这个消息,我们就可以在窗口刷新的时候清空那些已经隐藏了的表情的定时器。

到此我们的选择已经有了,那就是直接缓存表情坐标。

有了坐标如何定时刷新表情呢?

刷新对象有两种方式:

1、通过InvalidateRect来刷新Richedit中表情的位置。

2、直接在Richedit中表情显示位置绘制新的表情帧。

如果同时只显示少量的表情,采用哪种方法都没有问题。这里要解决的同时大量小表情刷新的问题(如>1000),这个时候脏矩形甚至都变得没有意义了,基本上都是整个窗口的刷新。

既然是整个窗口刷新,采用InvalidateRect和自己绘制会有什么不同吗?

在昨天以前我也一直不相信这样一个残酷的实现:Richedit在刷新满屏的小动画时效率非常低,低到让你无法忍受(单核CPU可以占到100%)。查了半天一直不明白瓶颈在哪。

经过不断排查,最后定位到Richedit的渲染问题上:当OLE对象很多时,Richedit在一个1920*1020的屏幕上刷新小表情动画一次要用掉200ms左右(CPU I5, 2.3G)。通过编译一份网上获得的Wince上使用的Richedit源码并运行,采用Intel的VTune分析性能瓶颈发现,在Richedit的绘制内容是一个字符串,每个Richedit对象在这个字符串中以0xFFFE代表,要绘制这个OLE对象,Richedit需要去一个OLE列表中查询哪一个OLE对象指向当前的0xFFFE所在的索引,尽管在查询时采用两分法去查询OLE对象的指针,当对象数量比较大时,效率依然非常低下(假定显示1024个对象,显示一个平均查询10次,那么就需要1024*10次查询)。

定位了瓶颈答案就显而易见了,直接通过Richedit的绘制来更新OLE显然对象这样的场景是不合适的,剩下的就只能是自己绘制表情了。

绘制表情就需要涉及到表情的选择状态,背景,及其它一些不需要更新的内容的剪裁等细节问题,只需要做好适当处理即可。

下面帖出在SOUI中插入大量小表情和在QQ中插入大量小表情CPU占用比较:

1920×1080:soui占用3%, QQ占用19%。(身边没有机器,没有截图)

1366*768:soui占用0%, QQ占用8%(见下图)。

SOUI.DEMO

QQ

一种快速刷新richedit中内嵌动画的方法的实现的更多相关文章

  1. 实现一种快速查找Richedit中可见区域内OLE对象的方法

    Richedit是一个OLE容器,使用Richedit来显示IM聊天内容时,通常使用OLE对象来实现在Richedit中播放表情动画. 触发表情的绘制有两种途径: 1.来自Richedit的刷新消息. ...

  2. 【转】WebResource实现在自定义控件中内嵌JS文件

    在类库中的资源  其他项目中要使用 需要嵌入才行 参考文献:WebResource实现在自定义控件中内嵌JS文件 1. WebResource简介 ASP.NET(1.0/1.1)给我们提供了一个开发 ...

  3. APP中内嵌H5页面为什么不能下载?

    在APP中内嵌H5页面,若页面上存在下载链接,没有任何反应,为什么呢? 原因是app中内嵌的H5页面是WebView解析的,什么是WebView呢? 在Android手机中内置了一款高性能webkit ...

  4. Unity中内嵌网页插件UniWebView

    一.常见Unity中内嵌网页实现方式: 1.UnityWebCore只支持windows 2.Unity-Webview支持Android,IOS 3.UniWebView支持mac os,Andro ...

  5. Linux内核--C语言中内嵌汇编 asm __volatile__

    在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器,以及如何将计算结果写回C 变量,你只要告诉程序中C语言表达式与汇编指令操作数之间的对应关系即可 ...

  6. Linux内核系列—C语言中内嵌汇编 asm __volatile__,asm__volatile_【转】

    转自:http://www.bkjia.com/Androidjc/1109412.html 在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器, ...

  7. Web网页中内嵌Activex的Activex插件开发 .

    转载自: http://blog.csdn.net/tttyd/article/details/5258096 源代码下载 http://files.cnblogs.com/tttyd/Activex ...

  8. GCC在C语言中内嵌汇编 asm __volatile__ 【转】

    转自:http://blog.csdn.net/pbymw8iwm/article/details/8227839 在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达 ...

  9. GCC在C语言中内嵌汇编 asm __volatile__

    2012-11-26 22:20 17958人阅读 评论(2) 收藏 举报  分类: linux(59)  架构管理(24)  C/C++(59)  目录(?)[+] 在内嵌汇编中,可以将C语言表达式 ...

随机推荐

  1. JAVA上百实例源码以及开源项目

    简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级.中级.高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情.执着,对IT的憧憬. ...

  2. Leetcode Unique Word Abbreviation

    An abbreviation of a word follows the form <first letter><number><last letter>. Be ...

  3. [Linux]yum开启rpm包缓存

    在CentOS下用yum安装,回发现在/var/cache/yum/下的base.extrs和updates下的packages下都没有发现下载的RPM 原来在/etc/yum.conf下没有设置下载 ...

  4. ACM/ICPC 之 最短路径-dijkstra范例(ZOJ2750-POJ1135(ZOJ1298))

    最短路经典算法-dijkstra范例(两道),第一道是裸的dijkstra,第二道需要枚举所有边已找到可能的情况. ZOJ2750-Idiomatic Phrases Game 题意:见Code 题解 ...

  5. ACM/ICPC 之 四道MST-Prim解法(POJ1258-POJ1751-POJ2349-POJ3026)

    四道MST,适合Prim解法,也可以作为MST练习题. 题意包括在代码中. POJ1258-Agri Net 水题 //Prim-没什么好说的 //接受一个邻接矩阵,求MST //Time:0Ms M ...

  6. Python字符编码

    http://www.runoob.com/python/python-strings.html ASCII Unicode UTF-8 # -*- coding: utf-8 -*- 格式化 %运算 ...

  7. nyoj123_士兵杀敌(四)_树状数组_插线求点

    士兵杀敌(四) 时间限制:2000 ms  |  内存限制:65535 KB 难度:5   描述 南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战 ...

  8. oracle触发器设置uuid变量

    create or replace trigger tri_org_department after insert or update on bjlt.sys_org for each row dec ...

  9. linux下修改tomcat内存大小

    转载自:http://blog.sina.com.cn/s/blog_7fb5109d0101o49a.html 1.  出现此问题的原因:       在tomcat中发布了多个webapp引用,每 ...

  10. 如何:使用PicturBox实现类似淘宝网站图片的局部放大功能

    转载至http://xuzhihong1987.blog.163.com/blog/static/267315872011822113131823/ 概要: 本文将讲述如何使用PictureBox控件 ...