问题简介
最近在一些网站爬了一些搞笑动态图片,没想到保存好之后预览竟然是是这样:
用图片浏览器逐帧看了一下,原来每一张图片第一帧都是类似空白的画面,所以预览的缩略图也是第一张画面。
那么如果能用代码读取到GIF的每一帧,删除后在合并为一个新GIF那问题就解决了。
于是找了下Java 操作GIF图片的类库,最后在GitHub上找到了这个:
方法简介
整个工程主要就两个java文件,一个用于解码的GifDecoder
类,一个用于编码的AnimatedGifEncoder
,另外有两个辅助类复制进来即可。
GifDecoder类
主要方法有:
public int read(String name)
指定需要解码的GIF图片路径,还有两个重载方法,支持读取
InputStream
和BufferedInputStream
public int getDelay(int n)
获取指定帧的延迟时间。
public int getFrameCount()
获取GIF图片的帧数
public int getLoopCount()
获取GIF图片的播放次数,0表示无限循环播放
public BufferedImage getFrame(int n)
获取指定帧的图像数据
AnimatedGifEncoder类
另一个是用于编码的AnimatedGifEncoder
类,方法和解码基本相反:
public void setDelay(int ms)
设置每帧之间的延迟时间,
GifDecoder
能获取到任意两帧之间的延迟,而AnimatedGifEncoder
貌似没有提供两帧之间的延迟设定,按照同一延迟时间处理。public void setRepeat(int iter)
设置播放次数,0表示无限循环播放
public boolean addFrame(BufferedImage im)
添加帧
public void setFrameRate(float fps)
设置帧率,与delay作用类似,相对设置delay为Math.round(100f / fps)
public boolean finish()
添加帧并配置好之后调用这个方法关闭文件输出流等
public boolean start(String file)
设置输出的文件路径,另一个重载方法以流的形式输出
另外AnimatedGifEncoder
还提供一些其他方法,包括设置透明度、背景颜色、图片质量、尺寸等。
代码
知道用法之后就简单了,先解码将原始GIF的所有帧数据、帧延迟、播放次数读取出来,然后再编码回去,同时删除第一帧即可。
这里除了点小问题,我的环境中Eclipse突然无法读取绝对路径,所以使用File.separator代替斜杠。
private String baseOutPath = "D:" + File.separator + "p" + File.separator+ "2" + File.separator;
//存放解码出来的所有帧
private BufferedImage[] bi;
//延迟时间
private int delay;
public static void main(String[] args) throws IOException {
GIFHandler handler = new GIFHandler();
dt.start();
}
private void start() {
//"D:/p/1",要转换的GIF图片目录
File gifPath = new File("D:" + File.separator + "p" + File.separator+ "1");
File[] gifs = null;
//读取出所有GIF图片
if (gifPath.isDirectory()) {
gifs = gifPath.listFiles();
}
if (gifs != null && gifs.length != 0) {
for (File f : gifs) {
trans(f);
System.out.println(f.getName());
}
}
System.out.println("转换完成");
}
/**
* 转换
*/
private void trans(File transFile) {
decode(transFile);
encode(baseOutPath + transFile.getName());
}
private void encode(String outPath) {
AnimatedGifEncoder encoder = new AnimatedGifEncoder();
// 设置循环模式,0为无限循环 这里没有使用源文件的播放次数
encoder.setRepeat(0);
// 设置输出路径
encoder.start(outPath);
//这里从1开始,去掉第一帧
for (int i = 1; i < bi.length; i++) {
encoder.setDelay(delay);
encoder.addFrame(bi[i]);
}
encoder.finish();
}
private void decode(File f) {
GifDecoder decoder = new GifDecoder();
try {
decoder.read(new FileInputStream(f));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 获取到帧数
int frameCount = decoder.getFrameCount();
bi = new BufferedImage[frameCount];
// 获取到每一帧的数据保存到bi
for (int i = 0; i < frameCount; i++) {
bi[i] = decoder.getFrame(i);
}
// 获取到每帧之间的延迟时间,这里只取第一帧的
delay = decoder.getDelay(0);
// ImageIO.write(frame, "jpeg", out); 该方法用于输出分解得到的单个图片文件
}
使用这两个类可以进行一些简单的帧操作,比如添加水印、设置延迟等。另外源码也不复杂,可以自行定义。