在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. js里面的 InttoStr 和 StrtoInt

    javascript 字符串 和 数字的转换,话说好灵活,感觉回不去pascal了 int转换string: 1,var str=String(int); 2,num.toString(param) ...

  2. DataTable得到某行某列的值

    DataTable dt=this.GetRepeatTableData("repeating1"); int count=dt.Rows.Count;for(int x=0;x& ...

  3. 解读Unity中的CG编写Shader系列十 (光滑的镜面反射(冯氏着色))

    前文完成了最基本的镜面反射着色器,单平行光源下的逐顶点着色(per-vertex lighting),又称为古罗着色(Gouraud shading).这篇文章作为后续讨论更光滑的镜面反射方式,逐像素 ...

  4. android课件和源代码

    ppt.rar 代码.rar 也可以自己从这里下载http://pan.baidu.com/share/link?shareid=1287391506&uk=2634355140

  5. Python自动化运维工具fabric的安装

    使用shell命令进行复杂的运维时,代码往往变得复杂难懂,而使用python脚本语言来编写运维程序,就相当于开发普通的应用一样,所以维护和扩展都比较简单,更重要的是python运维工具fabric能自 ...

  6. 2106 Problem F Shuffling Along 中石油-未提交-->已提交

    题目描述 Most of you have played card games (and if you haven’t, why not???) in which the deck of cards ...

  7. Divide and conquer:4 Values whose Sum is 0(POJ 2785)

    找四个数的和为0 题目大意:给定四个集合,要你每个集合选4个数字,组成和为0 这题是3977的简单版,只要和是0就可以了 #include <iostream> #include < ...

  8. vs2013显示行号

    随便打开一个项目,可以看到代码框内并没有显示行号 选择“工具”-“选项”,打开后界面如下 选择文本编辑器,找到下图中的“行号”并勾选 行号可以显示了 5 这样我们就完成了任务

  9. ACdream 1224 Robbers (贪心)

    一道贪心题,很久前做的,代码是我以前写的. 题意:有n个抢劫者抢劫了m块金子,然后第i个人平分xi/y块金子,但是会有除不尽的情况而金子不可再分,那么每个人都有一个不满意度fabs(xi / y - ...

  10. IOS-Uikit框架介绍

    •UIKit可识别三种类型的输入事件: –触摸事件 –运动(加速计)事件 –远程控制事件 IKit框架将触击信息封装为一个UIEvent对象,并派发给恰当的视图(有关UIKit如何将事件递送给您的视图 ...