背景
由于项目使用了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
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的优化
- 关于内存比较小的机器经常会出现OOM的问题?
这个问题主要是出现在SVGAVideoEntity 这个类找那个的resetImage,就是当解析成功的时候,会出把所有的imagedecode到内存中去,项目中用的svga礼物特效解析下来基本都超过了100M,一旦触犯申请内存就会出现内存溢出的错误,我是如何解决这个问题的,就是做了预解析,先把需要的内存算出来,这里不真在decode,只要知道每一张图的长和宽,源码用的是RGB_565,所以可以算出需要多少内存,再比对一下应用进程能申请的空闲内存,如果超过了一个阈值,就不要去播放这个svga了,播放也是没有意思的,肯定会奔溃的。
2.Svga动态跳帧播放
所谓的动态跳帧,就是在不改变帧率的情况下,需要15秒播完的动画,如何在9秒播完?这里就是需要跳帧播放了,首先算出步长,step,step = 原来的帧数/9*fps,在每一次线性动画回到回来的帧index,再去做偏移,index+step,
跳帧播放可以提升用户体验,节省内存。