apng2gif各种问题的解决
事情起因
追溯起来已经是两年多以前的事情了,当时有个表情包在Line上火了起来,之后血洗国内各大平台,这个表情包就是Menhera酱。
谁会抗拒可爱的事物呢?所以当时我就立志要把她们的表情包都给要了,最开始就是通过网友们的搬运,在百度网盘等地方收集。
但是时间久了,Menhera酱的热度也下去了,没有多少人再收集了,有多少人的喜欢能像我一样始终如一呢?
这时候我强迫症就来了,收集不完整那多难受啊,所以我决定亲自上Line收集。
上Line是需要魔法的,当时用了点魔法,又在Chrome上找了个插件,成功地把所有她们的表情都下载了下来。
但是!
Line上的动图是PNG格式的,更准确地来说是APNG格式的,这样的动图在微信上是用不了的,放上去就是张不动的图片,甚至背景还是黑乎乎的。
微信只支持GIF的动图,这我可忍不了,必须得给安排了!
艰辛的转换历程
找工具阶段
最开始肯定是找别人做好的批量转换的工具,关键词搜apng2gif,当然有一堆博文和工具了。
尝试了一个据说很好用的工具isparta,下载了很久很久很久才下载好。转换时间很长,但结果转换出来很多都有问题,在电脑上可以正常播放,但发送到微信上一遍播放完了就会花屏。
感觉可能是版本bug,就有花了好久下载其他版本,结果都不好用,只能放弃了这个软件。
然后又找到了一个在线转换的网站,需要配置很多参数,生成了一张试试效果居然还不错。
但是这个网站毕竟在线的,自动化程度不高,网络连接也比较慢,批量处理肯定是个大工程,只好放弃了。
这是第一次尝试。
自己造轮子阶段
后来的某一天又想起了这件事,决定重新思考一下怎么弄。
APNG格式诞生这么长时间了,肯定有很多资料吧?
思路是这样的,APNG是动图,肯定是由很多帧组成的,把这些帧给提取出来,然后再合成GIF不就行了?
找了一些资料,写了一个python程序,导出了一张APNG的各帧。
结果发现问题很大,APNG的每一帧并不是完整的一张图片,而是和上一帧有变化的地方!
最后合成出来的东西就很鬼畜,参见下图:
心情就很不美丽了,费这么大劲你给我合成出了啥奇怪的东西?
又在Github上找到了一个湾湾同胞写的python程序,反复调试运行都没法得到正确结果。
后来也有构思,还是解析合成的思路,只要把每一帧放在相应的坐标就行了,但是想了想太复杂就放弃了。
这是第二次尝试
改造轮子阶段
这两天看到了七濑胡桃公众号推送的文章,其中有一张可爱的动图,就转发给了对象,结果她直呼好可爱!
虽然尝试了那么多次,也失败了那么多次,但是这次一定要成功。
发现新轮子
思路还是先看看有没有现成的轮子,当然是有的!
我尝试了pip中的apng2gif包,完全不好使,甚至正常播放都不行。于是去找到了源代码,结果……
这不就是我上次参考的那个Github上的源代码吗?
放弃!
但是,我在命令行中不小心输入了apng2gif,忘记了加后缀,结果提示我可以通过apt安装?
原来Linux上也是有现成轮子可以用的!赶紧apt了下来。
这个程序的使用很简单:
试了一张APNG动图,导出的GIF效果居然还挺好?
立刻发到微信上试试,结果还是播放一遍就花屏,果然还是不靠谱。
给轮子加一轮转换
但是这个播放一遍就花屏问题,似乎就是因为它只循环了一遍?那我只需要把GIF的循环设置为一直循环不就可以了?
可惜的是这个工具并没有这个选项。
不过现在又可以造轮子了呀,这次只需要解析GIF的每一帧,然后合成,保存的时候设置loop为0就行了!
遗憾的是这么做还是存在问题。
有一个保存选项是disposal,设置为3的时候不会出现重影,但是存在部分帧只显示一部分的情况。
disposal设置为1或0的时候虽然会显示完整,但是会有重影。
再然后就是关于duration的奇怪问题,即使设置新gif的duration(帧间时长)和旧的duration一样,最后导出的gif的duration会变成两倍,不知何故!设置为一半时反而可以一样。
总之还是有问题的。
换个方式给轮子加一轮转换
既然这个轮子和我的需求只相差一个loop的值的问题,那我直接修改那个值不就好了?
GIF文件中肯定有特定的位用来存放loop的值。去找了GIF文件格式,也不知怎么搞的,网上都是互相转载的,没有找到直接的资料,最后找到一份代码中倒是有文件的详细结构。
经过分析,对文件进行16进制搜索,找到“NETSCAPE2.0”,再往后数第三、四个字节就是了。
将荧光笔标记的01改成00,loop就从一次变成无限循环了(下面两张分别为循环一次和无数次)。
这个方案确实有效!
那么编程思路也很简单,以16进制的方式读取文件,然后匹配并找到“NETSCAPE2.0”,确定loop值对应的位置,然后将值直接修改为0保存即可。
修改轮子
但是我没有按照上一部分的思路去写程序,别问,问就是懒。
我们换个思路,既然apng2gif这个工具最后合成了GIF,必然也写入了loop位,我们直接修改该工具的源代码不就可以了?
费了好大劲从SourceForge上下载了源码,因为下载各种失败,然后切换镜像。
找到源代码之后就简单了,在代码中合成GIF之前的位置,加入一句代码即可:
1 | num_loops = 0; |
重新make生成可执行文件,再尝试转换发现达到了想要的效果。
make过程中提示“-lpng”选项的问题,通过apt安装libpng-dev
即可:
1 | sudo apt-get install libpng-dev |
编写批处理程序
那么接下来,把执行apng2gif的过程变成批处理即可,这一步用shell或python都行,我用的是python(因为这段时间写了个千余行的shell脚本,现在不想写shell了)。
程序很简单,扫描所有后缀为.png的文件,调用apng2gif程序转换到gif文件夹下。
1 | import os |
所以折腾了这么久,真正有效的代码量只有修改轮子1句c代码和批处理中的7句python代码(python代码甚至可以精简成三句!)?
其中解决问题的方式不一定难,但探索的过程一定艰难且有趣。
保持好奇是探索发现的最大动力源泉。