最近在一些网站爬了一些搞笑动态图片,没想到保存好之后预览竟然是是这样:
用图片浏览器逐帧看了一下,原来每一张图片第一帧都是类似空白的画面,所以预览的缩略图也是第一张画面。
那么如果能用代码读取到GIF的每一帧,删除后在合并为一个新GIF那问题就解决了。
于是找了下Java 操作GIF图片的类库,最后在GitHub上找到了这个:
animated-gif-lib-for-java
整个工程主要就两个java文件,一个用于解码的GifDecoder类,一个用于编码的AnimatedGifEncoder,另外有两个辅助类复制进来即可。
主要方法有:
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类,方法和解码基本相反:
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); 该方法用于输出分解得到的单个图片文件 }使用这两个类可以进行一些简单的帧操作,比如添加水印、设置延迟等。另外源码也不复杂,可以自行定义。