SVGA源码分析与优化

4,586 阅读3分钟

背景

       由于项目使用了SVGA这个开源库来做礼物特效的支持,我觉得很有必要对SVGA这个开源库进行阅读,以便后续开发和优化可以进行下去。至于这个库怎么用的,这里就不在做过多的解析了,svga.io/intro.html,有兴趣的同学可以自行到官网上去学习。官网贴了一个大致的流程图


本文将对svga格式,svga解析,和Android平台上的播放的原理进行梳理。

SVGA 格式预览

svga的parser 流程会把svga流解析成对应的结构体,对应代码的结构体就是MovieEntity,

public MovieEntity(String version, MovieParams params, Map<String, ByteString> images, List<SpriteEntity> sprites, List<com.opensource.svgaplayer.proto.AudioEntity> audios, ByteString unknownFields)
 {  super(ADAPTER, unknownFields); 
 this.version = version;  
this.params = params;  
this.images = Internal.immutableCopyOf("images", images);  
this.sprites = Internal.immutableCopyOf("sprites", sprites);  
this.audios = Internal.immutableCopyOf("audios", audios);
}

这里用到了google库 ProtocalBuffers,高效的结构化数据存储格式,这里不做过多的篇幅去介绍这个。

version表示svga的版本库

params参数里面主要的内容是,画布的宽高,每秒播放的帧率。和总的帧数。

images表示这个动画库所用到的所有的图片资源

sprites描述了的是每一个位图对应的动画参数,包括画布的约束大小,透明度,矩阵的变换,和一些遮罩的路径,Shape矢量背景的。

audios 这个是后续高版本加入的,支持了声音。

SVGA帧播放

svga如何按照既定的帧率去播放特效的,答案是 ValueAnimator

假设解析到的svga的帧率是 15fps,SVGAImageView 如何实现每秒播放15帧的。

1.先找到开始帧startFrame,和结束帧endFrame

2.算出动画所需要的时间

duration =(endFrame-startFrame)/ 15fps*1000 毫秒

3.ValueAnimator设置动画的差值器是线性差值器

4.播放动画设置动画的监听,每次回调调用invalidateSelf,从而触发draw

SVGA draw()的实现

通过继承Drawable重载draw()方法,代码对应的类是SVGADrawable,但是真正绘画的幕后功臣是SVGACanvasDrawer。

1.通过当前帧的索引index,拿到绘制的Sprite,图层的图片对应的key,和该帧的相关变化,alpla,layout,transform(矩阵变换),遮罩maskPath,和shape的变化

2.绘制Image,drawImage,包含了矩阵的变化,aplha,和遮罩相关等,

3.绘制相关的Shape变换

源码相关的话,就不贴了,有兴趣的自行去看看吧,把位置贴一下


对SVGA的优化

  1. 关于内存比较小的机器经常会出现OOM的问题?

       这个问题主要是出现在SVGAVideoEntity 这个类找那个的resetImage,就是当解析成功的时候,会出把所有的imagedecode到内存中去,项目中用的svga礼物特效解析下来基本都超过了100M,一旦触犯申请内存就会出现内存溢出的错误,我是如何解决这个问题的,就是做了预解析,先把需要的内存算出来,这里不真在decode,只要知道每一张图的长和宽,源码用的是RGB_565,所以可以算出需要多少内存,再比对一下应用进程能申请的空闲内存,如果超过了一个阈值,就不要去播放这个svga了,播放也是没有意思的,肯定会奔溃的。

 2.Svga动态跳帧播放

   所谓的动态跳帧,就是在不改变帧率的情况下,需要15秒播完的动画,如何在9秒播完?这里就是需要跳帧播放了,首先算出步长,step,step = 原来的帧数/9*fps,在每一次线性动画回到回来的帧index,再去做偏移,index+step,

   跳帧播放可以提升用户体验,节省内存。