zoukankan      html  css  js  c++  java
  • 一种快速刷新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

    QQ

  • 相关阅读:
    python学习笔记3:基础(元组、字符串、列表、字典)
    python学习笔记2:基础(邮件发送)
    pycharm版本控制
    Pycharm版本控制之本地Git用法
    GitHub使用
    qtp简单说明
    查询Activity方便一点的方法
    appium基础框架
    loadrunner基本流程
    python下的unittest框架
  • 原文地址:https://www.cnblogs.com/setoutsoft/p/4242907.html
Copyright © 2011-2022 走看看