一种快速刷新richedit中内嵌动画的方法的实现
在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
一种快速刷新richedit中内嵌动画的方法的实现的更多相关文章
- 实现一种快速查找Richedit中可见区域内OLE对象的方法
Richedit是一个OLE容器,使用Richedit来显示IM聊天内容时,通常使用OLE对象来实现在Richedit中播放表情动画. 触发表情的绘制有两种途径: 1.来自Richedit的刷新消息. ...
- 【转】WebResource实现在自定义控件中内嵌JS文件
在类库中的资源 其他项目中要使用 需要嵌入才行 参考文献:WebResource实现在自定义控件中内嵌JS文件 1. WebResource简介 ASP.NET(1.0/1.1)给我们提供了一个开发 ...
- APP中内嵌H5页面为什么不能下载?
在APP中内嵌H5页面,若页面上存在下载链接,没有任何反应,为什么呢? 原因是app中内嵌的H5页面是WebView解析的,什么是WebView呢? 在Android手机中内置了一款高性能webkit ...
- Unity中内嵌网页插件UniWebView
一.常见Unity中内嵌网页实现方式: 1.UnityWebCore只支持windows 2.Unity-Webview支持Android,IOS 3.UniWebView支持mac os,Andro ...
- Linux内核--C语言中内嵌汇编 asm __volatile__
在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器,以及如何将计算结果写回C 变量,你只要告诉程序中C语言表达式与汇编指令操作数之间的对应关系即可 ...
- Linux内核系列—C语言中内嵌汇编 asm __volatile__,asm__volatile_【转】
转自:http://www.bkjia.com/Androidjc/1109412.html 在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器, ...
- Web网页中内嵌Activex的Activex插件开发 .
转载自: http://blog.csdn.net/tttyd/article/details/5258096 源代码下载 http://files.cnblogs.com/tttyd/Activex ...
- GCC在C语言中内嵌汇编 asm __volatile__ 【转】
转自:http://blog.csdn.net/pbymw8iwm/article/details/8227839 在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达 ...
- GCC在C语言中内嵌汇编 asm __volatile__
2012-11-26 22:20 17958人阅读 评论(2) 收藏 举报 分类: linux(59) 架构管理(24) C/C++(59) 目录(?)[+] 在内嵌汇编中,可以将C语言表达式 ...
随机推荐
- svn提交报错:svn: Aborting commit:XXXXXremains in conflict
Svn服务器上的对应内容,在上次Update后已被别人修改了,而我也做了修改,造成冲突.先备份自己的修改,从SVN上取一份别人的修改覆盖自己的文件,然后合并自己的修改进去,最后Commit.可以先更新 ...
- 【Storage】Ubuntu LVM 安装配置
参考资料: https://www.centos.bz/2012/02/installation-and-usage-of-lvm/ http://blog.chinaunix.net/uid-206 ...
- SSRS报表参数设置
一.日期时间类型的参数注意事项: 关于数据类型的选择:(只有数据类型设置为日期/时间格式,在查询的时候才会显示日期控件,提示信息一般改成汉字) 指定默认值:指定开始日期为前10天,
- ls常用选项总结
参考: http://billie66.github.io/TLCL/book/index.html ls - List directory contents 选项 长选项 描述 -a --all 列 ...
- iOS UIViewController 和 nib 相关的3个方法
iOS UIViewController 的 awakeFromNib 以及 - (id)initWithCoder:(NSCoder *)aDecoder 和 - (instancetype)ini ...
- 自动化运维工具ansible学习+使用ansible批量推送公钥到远程主机
目录: 一.ansible简介 1.1.ansible是什么 1.2.ansible如何工作 1.3.ansible优缺点 1.4.ansible安装方式 1.5.ansible文件简单介绍 1.6. ...
- Java for LeetCode 229 Majority Element II
Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times. The algorit ...
- ABAP ALV单个单元格状态编辑
*&---------------------------------------------------------------------* *& Report ZPPR0024 ...
- java 500/404错误总结
404是路径错误,简单的说就是你的页面找不到后台执行它的代码(未找到服务端代码)500最常见的就是你的编程语言语法错误导致的(服务端代码报错)
- jQuery与JS的区别,以及jQuery的基础语法
*在使用jQuery时,要在页面最上端加上 <script src="../jquery-1.11.2.min.js"></script> 看一下js与jQ ...