学一学压缩GIF吗

219 阅读3分钟

1.背景 对于网络传输照片和文件大小肯定是越小越好 这点沙滩康师傅也是做了些后台判断 图片小于500k才提供上传 (亲测gif 将近3M也能传 应该是后台接口没限制gif后缀文件) 但是为了服务器压力 还是尽量压缩下图和动图吧

2.起因和查阅资料 因为本来想发gif动态图进行展示的 发现原先压缩图片的代码对于gif不生效 有查阅了一些资料 博客和掘金等网站都是好几年前的水wen 帮助不大 (可以说10篇9篇广告 剩下一篇还是图片压缩) 周日无聊拿着手机掘金APP一搜 就搜到了一篇文章 不过博主没有进行很详细的解析 贴出了github地址和使用方法

3.原博贴: Android压缩GIF格式图片

4.在我简化实操 最终生成以下代码 可以说每张gif可以减少最少一半的内存 (其实原理很简单 就是单纯抽帧 保留多少帧后将帧再写入文件内 就完成了)

github 引用 第三方库gifFile https://github.com/koral--/android-gif-drawable 一个实现类 https://github.com/nbadal/android-gif-encoder

首先需要能够获取gif的路径 这一步自行解决啦 代码如下

 /**
     * 先把GIF分解成bitmap,进行减帧操作,最后合成一张新的GIF
     * 1.根据文件指定文件长度进行压缩
     * @param gifFile      gif文件
     * @param targetLength 压缩的目标体积
     * @param savePath     压缩后的保存路径
     */
    public static String compressFrame(File gifFile, long targetLength, String savePath) {
            GifDrawable gifDrawable = null;
            try {
                long gifFileLength = gifFile.length()/1024;
                SLog.e("compressFrame file gifFileLength = "+gifFileLength);
                gifDrawable = new pl.droidsonroids.gif.GifDrawable(gifFile);
                if (gifFileLength > targetLength) {
                // 计算压缩比例 例如是2  每2张取1张 3 4 5 6 同理

                    int mold = (int) Math.ceil(gifFileLength * 1.0 / targetLength) +1;
                    SLog.e("compressFrame file mold = "+mold);
                    return compressFrameGif(gifDrawable , mold ,savePath);
                }
            } catch (Exception e) {
                SLog.e("compressFrameGif e  = "+e.toString());
            } finally {
                if (gifDrawable != null) {
                    gifDrawable.recycle();
                }
            }
            return savePath;
    }

  /**
     * 先把GIF分解成bitmap,进行减帧操作,最后合成一张新的GIF
     * 2.根据指定帧数进行压缩
     * @param gifFile   gif文件
     * @param maxFrames 最大帧数
     * @param savePath  压缩后的保存路径
     */
 public static String compressFrame(File gifFile, int maxFrames, String savePath) {
        GifDrawable gifDrawable = null;
        try {
            gifDrawable = new GifDrawable(gifFile);
            int gifFrames = gifDrawable.getNumberOfFrames();
            if ( gifFrames > maxFrames){
                // 计算压缩比例 例如是2  每2张取1张 3 4 5 6 同理

                int mold = (int) (Math.ceil(gifFrames * 1.0 / maxFrames)) + 1;
                return compressFrameGif(gifDrawable, mold, savePath);
            }
        } catch (Exception e){
            SLog.e("compressFrameGif e  = "+e.toString());
        } finally {
            if (gifDrawable != null){
                gifDrawable.recycle();
            }
        }
        return savePath;
    }
// 写入保存路径 抽离方法 
private static String compressFrameGif(GifDrawable gifDrawable ,int mold ,String savePath){
        ByteArrayOutputStream bos = null;
        FileOutputStream outStream = null;
        try {
            int gifFrames = gifDrawable.getNumberOfFrames();
            int gifDuration = gifDrawable.getDuration();
            bos = new ByteArrayOutputStream();
            AnimatedGifEncoder encoder = new AnimatedGifEncoder();
            encoder.setRepeat(0);
            encoder.start(bos);
            encoder.setDelay(gifDuration / gifFrames);
		 // 核心内容 抽帧 并添加帧进入文件类
            for (int i = 0; i < gifFrames; i++) {
                // 这里需要各自去定制 本质上抽出来的帧数的大小都会比原图帧的大 如何缩减达到减少内存的效果
                if (i % mold == 0) {
                    encoder.addFrame(gifDrawable.seekToFrameAndGet(i));
                }
            }
            encoder.finish();
            outStream = new FileOutputStream(savePath);
            outStream.write(bos.toByteArray());
        } catch (Exception e){
            SLog.e("compressFrameGif e  = "+e.toString());
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
                if (outStream != null) {
                    outStream.close();
                }
                return savePath;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return savePath;
    }

如何使用 涉及IO写入 开启线程 这里使用的是Rxjava

    public static final String SLASH = "/";

 /**
     * 压缩GIF
     */
    private void compressionGif(String path ) {
        //  转GIF 
        String directoryPrefix = context.getExternalFilesDir("").getAbsolutePath()+ImageCompressUtils.SLASH;
        String imagepath = directoryPrefix +"sugar"+ ImageCompressUtils.SLASH + "image"+ ImageCompressUtils.SLASH;
        File file = new File(imagepath);
        if (!file.exists()) {
            file.mkdirs();
        }
        Observable<String> observable = Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(@io.reactivex.annotations.NonNull ObservableEmitter<String> e) throws Exception {
                File compressImageFile = null;
                compressImageFile = File.createTempFile("image", ".gif", file);
                String s = ImageCompressUtils.compressFrame(new File(path), 24, compressImageFile.getAbsolutePath());
//                s = ImageCompressUtils.compressFrame(new File(path), 500l, compressImageFile.getAbsolutePath());
                e.onNext(s);
                e.onComplete();
            }
        });
        observable.subscribeOn(Schedulers.io());
        observable.observeOn(AndroidSchedulers.mainThread());
        observable.subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d){
            }

            @Override
            public void onNext(@io.reactivex.annotations.NonNull String compath) {
                if(!TextUtils.isEmpty(compath)){
                    pic_file = new File(compath);
			// 上传操作或者进行Glide显示都可
                }
            }

            @Override
            public void onError(@io.reactivex.annotations.NonNull Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });
    }


压缩图片的代码挺多的 这gif说是压缩 其实减少帧的数量达到大小减小 但是损坏了画质 或者应该说是流畅度 对于原图质量要求高的要谨慎使用 有问题沙滩联系啦 !! 内卷起来吧 滩友们! 欢迎大家来阳光沙滩摸鱼!