如何使用Java代码调整animated GIF(动态GIF)文件的尺寸?
Java 调整动图GIF尺寸实现方案
核心实现思路
动图GIF是由多帧静态图片+帧控制元数据组成的,不能直接按照单张静态图的方式缩放,需要先拆分提取所有帧和对应元数据(帧时长、循环次数、透明属性、渲染策略等),逐帧缩放完成后再按照原参数重新拼接为动图即可。
基础实现(仅用JDK内置API)
不需要引入第三方依赖,直接用JDK自带的javax.imageio相关类实现:
import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.metadata.IIOMetadata; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageOutputStream; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class GifResizer { // 按目标宽高缩放GIF public void resizeGif(File sourceGif, File targetGif, int targetWidth, int targetHeight) throws IOException { // 读取原GIF的所有帧和元数据 List<IIOImage> frames = new ArrayList<>(); List<IIOMetadata> frameMetadatas = new ArrayList<>(); IIOMetadata streamMetadata = null; ImageReader gifReader = getGifReader(); try (ImageInputStream iis = ImageIO.createImageInputStream(sourceGif)) { gifReader.setInput(iis); int frameCount = gifReader.getNumImages(true); streamMetadata = gifReader.getStreamMetadata(); for (int i = 0; i < frameCount; i++) { IIOMetadata frameMeta = gifReader.getImageMetadata(i); BufferedImage originalFrame = gifReader.read(i); // 缩放单帧 BufferedImage resizedFrame = resizeSingleFrame(originalFrame, targetWidth, targetHeight); frames.add(new IIOImage(resizedFrame, null, frameMeta)); frameMetadatas.add(frameMeta); } } finally { gifReader.dispose(); } // 写入新的GIF ImageWriter gifWriter = getGifWriter(); try (ImageOutputStream ios = ImageIO.createImageOutputStream(targetGif)) { gifWriter.setOutput(ios); ImageWriteParam writeParam = gifWriter.getDefaultWriteParam(); gifWriter.write(streamMetadata, frames.iterator(), writeParam); } finally { gifWriter.dispose(); } } // 缩放单帧图片,保留透明通道 private BufferedImage resizeSingleFrame(BufferedImage original, int targetWidth, int targetHeight) { BufferedImage resized = new BufferedImage(targetWidth, targetHeight, original.getType()); Graphics2D g2d = resized.createGraphics(); // 双三次插值保证缩放清晰度 g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.drawImage(original, 0, 0, targetWidth, targetHeight, null); g2d.dispose(); return resized; } // 获取GIF读取器 private ImageReader getGifReader() { Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("gif"); if (readers.hasNext()) { return readers.next(); } throw new RuntimeException("No GIF reader found"); } // 获取GIF写入器 private ImageWriter getGifWriter() { Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("gif"); if (writers.hasNext()) { return writers.next(); } throw new RuntimeException("No GIF writer found"); } }
JetBrains插件适配优化
如果是在JetBrains平台插件中使用,可以直接调用平台内置工具类减少重复代码:
- 单帧缩放可以直接用
com.intellij.util.ImageUtil#scaleImage方法,不需要自己实现缩放逻辑 - 图片读取可以用
com.intellij.util.ImageLoader类,兼容平台内的各类资源加载场景
注意事项
- 如果需要修改GIF的播放速度、循环次数,可以在读取元数据的时候修改对应参数再写入
- 透明GIF缩放时不要修改
BufferedImage的类型,避免丢失透明通道出现异常背景色 - 对于大尺寸GIF逐帧处理会占用一定内存,建议根据实际场景增加内存限制或者异步处理逻辑
内容的提问来源于stack exchange,提问作者Nikita
相关产品推荐
相关产品推荐

