在当前很多直播应用中,拥有给主播送礼物的功能,当用户点击赠送礼物后,视频界面上会出现比较炫酷的礼物特效。这些特效,有的是用粒子效果做成的,但是更多的时用播放逐帧动画实现的,本篇博客将会讲解在Android下如何利用OpenGLES流畅的播放逐帧动画。在本篇博客中的动画素材,是从花椒直播中“借”出来的。
逐帧动画的实现方案分析
有些朋友看到逐帧动画可能会想,逐帧动画还不容易吗?Android中的动画本来就支持逐帧动画啊,不是分分钟就能实现么?没错,用Android的Animation的确很容易就实现了逐帧动画。但是用Android的Animation实现动画,当图片要求较高时,播放会比较卡。为什么呢?
Png图片并不能在被直接用来播放动画,它需要先被解码成Bitmap,才能被绘制到屏幕上。而这个解码是一个比较耗时的工作。而且解码时间与手机、CPU工作状态、Png图片内容都有很大的关系。当图片较小时,播放出来的逐帧动画效果还不错,但是当图片较大时,比如720720,解码时间就往往需要100多ms,甚至会达到200ms以上。这个时间让我们很难以接受。
那么怎么办呢?限制动画的是PNG解码时间,而不是渲染时间,用OpenGL做渲染又有什么用呢?是的,用OpenGL来播放PNG逐帧动画,虽然比用Animation会有一些改善,但是并不能解决动画播放卡顿的问题。(当初天真的以为Animation播放动画是因为Animation用CPU绘制导致卡顿,然后改成用GPU来做,发现然并卵,这才把视线放到PNG解码上了。)
既然是PNG解码占用时间,那么能不能直接用BMP格式存储图片,来做动画呢?这样解码的时间就基本可以忽略了。那么问题又来了,BMP是不进过压缩的,一张720720的PNG图片大小转成BMP就为7207204/1024=2025kb,那么一秒25帧动画,就要二十四五兆了。显然是难以让人接受的。那么怎么办呢?以下为Android下OpenGLES实现逐帧动画的方案比较:
待选方案
1. 直接2D纹理逐帧加载PNG
2. 使用ETC压缩纹理替代PNG
3. 使用ETC2压缩纹理替代PNG
4. 使用PVRTC压缩纹理替代PNG
5. 使用S3TC压缩纹理替代PNG
文件大小对比
1. PNG图片大小与其内容有关,透明区域越多,大小越小。
2. ETC1图片每个像素占0.5byte,720*720png变为ETC后大小为720*720*2*0.5+16(alpha通道导致文件高度增加一倍,16个字节为文件头部信息),约507KBytes。
3. ETC2大小与设置相关,不包含A通道,大小与ETC1不保留A通道相同,包含A通道的,与ETC1保留A通道相同。
4. S3TC 相对于24位原图,DXT1压缩比例为6:1,DXT2-DXT5压缩比例为4:1。
5. PVRTC4 压缩比为6:1,PVRTC2压缩比为12:1(PVRTC图片宽高为2的幂数)
文件支持对比
1. PNG通用
2. ETC1是OpenGL2.0支持标准,基本上所有支持OpenGLES2.0,版本不低于2.2的Android设备都能使用。
3. ETC2是OpenGL3.0支持标准,基本上所有支持OpenGLES3.0,版本不低于4.3的Android设备都能使用。
4. S3TC广泛用于Windows平台上,DirectX中使用较多。在Android上支持率很低,主要是NVIDIA Tegra2芯片的手机。
5. PVRTC只有PowerVR的显卡支持。在苹果系中使用广泛。
方案选择
根据上述分析,在Android中使用OpenGLES加载动画:
方案4和方案5由于支持问题,直接排除了。
方案1可以使用
当前Android市场Android2.2以下设备基本不没有了,Android2.2及以上到Android4.3下,占比15%左右。所以方案2与方案3之中,取方案2。
选择方案1与方案2进行对比。
方案1和方案2数据
针对测试用的60张png烟花图片动画进行量化分析(图片大小为720*720,手机360F4):
PNG图片总大小为4.88M,ETC总大小29.6M。
PNG IO+解码耗时为15-