App下载 微信公众号

实现GIF压缩animated-gif-lib.jar+Java SDK【JavaEE】

技术 · 后端开发 · Java/ 作者【吾非言】/ 发布于2018-1-6/ 5.01k次浏览
2018 1/6 0:2
摘要: GIF的压缩,对大多数程序开发者来说是一大难点,要么无法压缩,要么压缩之后GIF失去动画效果。这是因为GIF不像其他格式图片,GIF是由一帧一帧的图片合成,所以在进行压缩的时候,就不能简简单单的按照其他图片压缩方式那样处理。

做一个努力的人好处在于,人人见了你都会想帮你。但如果你自己不先做出一点努力的样子,人家想拉你一把,都不知你的手在哪里。

伴职创作

GIF的压缩,对大多数程序开发者来说是一大难点,要么无法压缩,要么压缩之后GIF失去动画效果。这是因为GIF不像其他格式图片,GIF是由一帧一帧的图片合成,所以在进行压缩的时候,就不能简简单单的按照其他图片压缩方式那样处理。

那么究竟该如何处理GIF的压缩呢?

思路分析

GIF是由一帧一帧合成,那么压缩过程中可以先将GIF拆分成一帧一帧,然后在一帧一帧的压缩,最后将压缩结果合成新的GIF,这样就能起到压缩的效果。

标题中提到的animated-gif-lib.jar是用来拆分和合成GIF的帮助类,主要用到它提供的GifDecoder和AnimatedGifEncoder。

Java SDK是用来负责压缩每帧数据,只针对于质量压缩。

animated-gif-lib.jar介绍

animated-gif-lib.jar是专门针对GIF的工具类,可使用maven导入工程,也可去官网下载。

<dependency>
    <groupId>com.madgag</groupId>
    <artifactId>animated-gif-lib</artifactId>
    <version>1.2</version>
</dependency>

1、拆分GIF

拆分GIF需要借助GifDecoder:

GifDecoder decoder = new GifDecoder();
int status = decoder.read(imagePath);// imagePath源文件路径
if (status != GifDecoder.STATUS_OK) {
    throw new IOException("read image " + imagePath + " error!");
}

通过GifDecoder就可以获取每帧数据:

int frameCount = decoder.getFrameCount();// 获取GIF有多少个frame
for (int i = 0; i < frameCount; i++) {
    BufferedImage bufferedImage = decoder.getFrame(i);
    ......
}

2、合成GIF

合成Gif主要是AnimatedGifEncoder来实现:

AnimatedGifEncoder encoder = new AnimatedGifEncoder();
encoder.start(outputPath);// 设置合成位置
int frameCount = decoder.getFrameCount();// 获取GIF有多少个frame
for (int i = 0; i < frameCount; i++) {
    ......
    encoder.addFrame(zoomImage);// 合成
}
encoder.finish();

Java SDK压缩质量实现介绍

首先要获取图片的BufferedImage流,即读取图片内存数据。

File file = new File(imagePath);// imagePath源文件路径
BufferedImage bufferedImage = ImageIO.read(file);// 获取BufferedImage流

其次获取图片文件的ImageWriter,来实现图片文件的重构。

// 得到指定Format图片的writer
Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpeg");// 得到迭代器
ImageWriter writer = (ImageWriter) iter.next(); // 得到writer

接下来就是比较核心的内容,压缩图片。

// 得到指定writer的输出参数设置(ImageWriteParam)
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); // 设置可否压缩
iwp.setCompressionQuality(quality); // 设置压缩质量参数,0~1,1为最高质量
iwp.setProgressiveMode(ImageWriteParam.MODE_DISABLED);
ColorModel colorModel = ColorModel.getRGBdefault();
// 指定压缩时使用的色彩模式
iwp.setDestinationType(new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(16, 16)));
// 开始打包图片,写入byte[]
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 取得内存输出流
IIOImage iIamge = new IIOImage(bufferedImage, null, null);
// 此处因为ImageWriter中用来接收write信息的output要求必须是ImageOutput
// 通过ImageIo中的静态方法,得到byteArrayOutputStream的ImageOutput
writer.setOutput(ImageIO.createImageOutputStream(byteArrayOutputStream));
writer.write(null, iIamge, iwp);

最后获取压缩图片的ByteArrayOutputStream,转化成图片文件。

// 获取压缩后的btye
byte[] tempByte = byteArrayOutputStream.toByteArray();
// 创建输出文件,outputPath输出文件路径,imgStyle目标文件格式(png)
File outFile = new File(outputPath + "." + imgStyle);
FileOutputStream fos = new FileOutputStream(outFile);
fos.write(tempByte);
fos.close();

最后将上面的思路代码封装成zoomBufferedImageByQuality(BufferedImage bufferedImage, float quality)方法,以方便后面进行调用。
GIF压缩质量,尺寸不变

/**
 * @param imagePath 原图片路径地址,如:F:\\a.png
 * @param imgStyle 目标文件类型
 * @param quality 输出的图片质量,范围:0.0~1.0,1为最高质量。
 * @param outputPath 输出文件路径(不带后缀),如:F:\\b,默认与原图片路径相同,为空时将会替代原文件
 * @throws IOException
 */
public void zoomGifByQuality(String imagePath, String imgStyle, float quality, String outputPath) throws IOException {
    // 防止图片后缀与图片本身类型不一致的情况
    outputPath = outputPath + "." + imgStyle;
    // GIF需要特殊处理
    GifDecoder decoder = new GifDecoder();
    int status = decoder.read(imagePath);
    if (status != GifDecoder.STATUS_OK) {
        throw new IOException("read image " + imagePath + " error!");
    }
    // 拆分一帧一帧的压缩之后合成
    AnimatedGifEncoder encoder = new AnimatedGifEncoder();
    encoder.start(outputPath);// 设置合成位置
    encoder.setRepeat(decoder.getLoopCount());// 设置GIF重复次数
    int frameCount = decoder.getFrameCount();// 获取GIF有多少个frame
    for (int i = 0; i < frameCount; i++) {
        encoder.setDelay(decoder.getDelay(i));// 设置GIF延迟时间
        BufferedImage bufferedImage = decoder.getFrame(i);
        // 利用java SDK压缩BufferedImage
        byte[] tempByte = zoomBufferedImageByQuality(bufferedImage, quality);
        ByteArrayInputStream in = new ByteArrayInputStream(tempByte);
        BufferedImage zoomImage = ImageIO.read(in);
        encoder.addFrame(zoomImage);// 合成
    }
    encoder.finish();
    File outFile = new File(outputPath);
    BufferedImage image = ImageIO.read(outFile);
    ImageIO.write(image, outFile.getName(), outFile);
}

GIF压缩尺寸大小

/**
 * @param imagePath 原图片路径地址,如:F:\\a.png
 * @param imgStyle 目标文件类型
 * @param width 目标文件宽
 * @param height 目标文件高
 * @param outputPath 输出文件路径(不带后缀),如:F:\\b,默认与原图片路径相同,为空时将会替代原文件
 * @throws IOException
 */
public void zoomGifBySize(String imagePath, String imgStyle, int width, int height, String outputPath) throws IOException {
    // 防止图片后缀与图片本身类型不一致的情况
    outputPath = outputPath + "." + imgStyle;
    // GIF需要特殊处理
    GifDecoder decoder = new GifDecoder();
    int status = decoder.read(imagePath);
    if (status != GifDecoder.STATUS_OK) {
        throw new IOException("read image " + imagePath + " error!");
    }
    // 拆分一帧一帧的压缩之后合成
    AnimatedGifEncoder encoder = new AnimatedGifEncoder();
    encoder.start(outputPath);
    encoder.setRepeat(decoder.getLoopCount());
    for (int i = 0; i < decoder.getFrameCount(); i++) {
        encoder.setDelay(decoder.getDelay(i));// 设置播放延迟时间
        BufferedImage bufferedImage = decoder.getFrame(i);// 获取每帧BufferedImage流
        BufferedImage zoomImage = new BufferedImage(width, height, bufferedImage.getType());
        Image image = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
        Graphics gc = zoomImage.getGraphics();
        gc.setColor(Color.WHITE);
        gc.drawImage(image, 0, 0, null);
        encoder.addFrame(zoomImage);
    }
    encoder.finish();
    File outFile = new File(outputPath);
    BufferedImage image = ImageIO.read(outFile);
    ImageIO.write(image, outFile.getName(), outFile);
}

以上便是封装的GIF压缩质量,和GIF压缩尺寸的方法,当然网上也有一些针对GIF处理的一些框架或者算法,这里写的方法仅供参考,个人不是很推荐GIF进行压缩,因为GIF在合成的时候自身已经进行了一次压缩,而且GIF压缩过程比较耗性能。

微信公众号:伴职创作

感谢您使用伴职平台,如有侵权,请投诉删除!

全部评价

最新
查看更多评论 加载

猜你喜欢

换一批