前言
项目中有一个GIF图二次编辑的需求,这一功能我主要借助Glide库来实现,这里通过系列文章分享下实现过程和GIF相关的学习经验。
效果
首先看下效果
原图1:
原图2:
调整图2的大小和位置后与图1叠加,最终效果:
这里合并主要借助Glide实现。
GIF的二次,核心思路就是将输入的GIF图进行解码,对解码后的图像数据进行修改后,再进行合并,将合并后的结果再通过编码生成新的GIF图。项目中图片加载使用了 Glide,Android本身是不支持渲染GIF图的,Glide可以帮助我们加载GIF图到ImageView——其原理就是先解码GIF图,然后逐帧渲染到ImageView上——同时也提供了开箱即用的编解码器,所以这里编解码都借助Glide来实现。
解码
这里我自己用一个 GifImage
类存储解析后的所有数据,一个 GifFrame
类存储单个帧中的数据:
1 |
|
这里我在 GifFrame
中多加了一个字段来表示这一帧在整个播放序列中处于哪一时刻,这个在后面合并时会用到。
然后是用Glide解码的简单写法:
1 |
|
读取输入文件,创建一个解码器,先获取Header数据,然后遍历获取每一帧的数据,同时记录每一帧在播放序列中的时刻,这样一个简单的解码就完成了。
编码
编码就是解码的过程倒过来,Glide同样提供了解码器,在Glide仓库可以找到:gifencoder。把这三个类都拷贝到项目目录下即可使用:
然后一个编码的示例:
1 |
|
创建一个编码器,逐帧写入,编码器提供了很多public方法来设置一些属性,比如 setRepeat()
方法,用来设置GIF图是否可以循环播放/循环次数,如果不设置,默认生成的GIF图是不会自动循环播放的。
1 |
|
以及 setDelay()
设置当前帧显示之后到下一帧显示之前的等待时间:
1 |
|
合并
除了编码和解码之外,中间部分就是合并了。考虑最复杂的情况:输入的图片不止一张,其次输入的图片有的是GIF,有的是普通的静态图,且每张GIF图他的帧序列的时间间隔都是不一定不相等的,哪怕是同一张GIF其每两帧之间的间隔时间也不一定相同。因此合并的思路大体如下图:
输入的GIF图A B C,取所有帧的时刻做并集并去重得到总时间轴,合成后的每一帧都是由ABC的对应那一时刻的帧合并而成的静态图:
- R1(帧) = A1 C1 合并
- R2 = A1 B1 C1 合并
- R3 = A2 B1 C1 合并
- R4 = A1 B2 C1 合并
- R5 = A2 B2 C2 合并
- …
这样可以保证输出的结果播放速度/帧率保持不变,代码实现如下:
1 |
|
图片合并这里就直接用了Canvas来合并多张Bitmap,参数 duration
为总的播放时长,整体逻辑其实很简单。另外在实际项目中,每张GIF图都是经过手势变换改变了大小、旋转角度并裁剪后的,这部分跟静态图片是类似的处理逻辑,对每一帧都做同样的处理,就不放代码了,这里只展示了最终结果合并的过程。