一种快速刷新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语言表达式 ...
随机推荐
- python to be Windows Daemon
参考:http://assback.iteye.com/blog/1731565 安装 pywin32-.win32-py2..exe #32bit pywin32-.win-amd64-py2..e ...
- 方法重写和方法重载;this关键字和super关键字
1:方法重写和方法重载的区别?方法重载能改变返回值类型吗? 方法重写: 在子类中,出现和父类中一模一样的方法声明的现象. 方法重载: 同一个类中,出现的方法名相同,参数列表不同的现象. 方法重载能改变 ...
- centos6.5 tomcat开机启动
可参考:centos6.5 nginx开机启动 /etc/init.d/下添加tomcatd文件,内容如下: #!/bin/sh # # chkconfig: - # # Licensed to th ...
- ACM/ICPC 之 平面几何-两直线关系(POJ 1269)
题意:给定四点的坐标(x,y),分别确定两直线,求出其交点,若重合or平行则输出相应信息 用四个点的坐标算出直线通式(ax+by+c=0)中的a,b,c,然后利用a,b,c计算出交点坐标(其他公式不够 ...
- ffmpeg-20160701-git-bin.7z
ESC 退出 0 进度条开关 1 屏幕原始大小 2 屏幕1/2大小 3 屏幕1/3大小 4 屏幕1/4大小 S 下一帧 [ -2秒 ] +2秒 ; -1秒 ' +1秒 下一个帧 -> -5秒 f ...
- Java for LeetCode 234 Palindrome Linked List
解题思路: O(1)的空间复杂度,意味着不能通过开一个List来解决问题.我们可以把List分成前后两个部分,后半部分通过指针的相互赋值进行翻转即可. JAVA实现如下: public static ...
- iOS应用架构谈(三):View层的组织和调用方案(下)
iOS客户端应用架构看似简单,但实际上要考虑的事情不少.本文作者将以系列文章的形式来回答iOS应用架构中的种种问题,本文是其中的第二篇,主要讲View层的组织和调用方案.下篇主要讨论做View层架构的 ...
- mongochef如何链接有权限的mongodb3.x数据库
废话不多说,直接上图: 1.打开mongochef 2.打开的界面是这样的: 3.点击connect,上图红色框中的按钮,不要点下拉三角 4.点击New Connection按钮 5.1:上图标注1, ...
- Stanford大学机器学习公开课(三):局部加权回归、最小二乘的概率解释、逻辑回归、感知器算法
(一)局部加权回归 通常情况下的线性拟合不能很好地预测所有的值,因为它容易导致欠拟合(under fitting).如下图的左图.而多项式拟合能拟合所有数据,但是在预测新样本的时候又会变得很糟糕,因为 ...
- 经典.net试题
经典.net面试题目 1. 简述 private. protected. public. internal 修饰符的访问权限. 答 . private : 私有成员, 在类的内部才可以访问. pr ...