美文网首页
图片压缩优化实战

图片压缩优化实战

作者: 阡陌昏晨 | 来源:发表于2021-11-02 16:06 被阅读0次

    一、前言:

    Android中图片有四种属性,分别是:
    ALPHA_8:每个像素占用1byte内存
    ARGB_4444:每个像素占用2byte内存
    ARGB_8888:每个像素占用4byte内存 (默认)
    RGB_565:每个像素占用2byte内存
    Android默认的颜色模式为ARGB_8888,这个颜色模式色彩最细腻,显示质量最高。但同样的,占用的内存也最大。 所以在对图片效果不是特别高的情况下使用RGB_565(565没有透明度属性)

    Android目前常用的图片格式有png,jpeg和webp,
    png:无损压缩图片格式,支持Alpha通道,Android切图素材多采用此格式
    jpeg:有损压缩图片格式,不支持背景透明,适用于照片等色彩丰富的大图压缩,不适合logo
    webp:是一种同时提供了有损压缩和无损压缩的图片格式,派生自视频编码格式VP8,从谷歌官网来看,无损webp平均比png小26%,有损的webp平均比jpeg小25%~34%,无损webp支持Alpha通道,有损webp在一定的条件下同样支持,有损webp在Android4.0(API 14)之后支持,无损和透明在Android4.3(API18)之后支持

    二、图片压缩优化实战

    第一步、修改采样率

    涉及BitmapFactory.Options
    BitmapFactort.Options这个是什么鬼呢, 很重要!bitmap加载的配置类,想要做图片内存优化是少不了跟它打“打交道”,如下其内部属性

    这里我们大概只说跟图片优化相关的几个重要属性

    insampleSize :采样率,默认1表示无缩放,等于2表示宽高缩放2倍,总大小缩小4倍;

    inBitmap :被复用的bitmap;

    inJustDecodeBound :如果设置为true,不获取图片,不分配内存,但会返回图片的高度宽度信息;

    inMutable :是否图片内容可变,如果Bitmap复用的话,需要设置为true;

    inDensity :加载bitmap时候对应的像素密度(后面会讲到);

    inTargetDensity :bitmap位图被真实渲染出的像素密度,对应终端设备的屏幕像素密度(后面会讲到);

    第二步、修改bitmap的宽高

    现在手机像素越来越高 所以控制一个bitmap的目标宽高值很重要,我的项目中的 newWidth、newHeight设置都是1280、1280自己可以依据项目来设置这个值

     /**
         * 等比缩放bitmap宽高
         * @param bm
         * @param newWidth
         * @param newHeight
         * @return
         */
        public Bitmap setImgSize(Bitmap bm, int newWidth, int newHeight) {
            // 获得图片的宽高.
            int width = bm.getWidth();
            int height = bm.getHeight();
            //如果之前图片的宽高 小于目标值就不缩放了
            if (width < newWidth || height < newHeight) {
                return bm;
            }
            // 计算缩放比例.
            float scaleWidth = ((float) newWidth) / width;
            float scaleHeight = ((float) newHeight) / height;
            float maxScale = Math.max(scaleWidth, scaleHeight);
            // 取得想要缩放的matrix参数.
            Matrix matrix = new Matrix();
            matrix.postScale(maxScale, maxScale);
            // 得到新的图片.
            Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
            return newbm;
        }
    

    第三步、修改压缩质量

    public boolean compress(CompressFormat format, int quality, OutputStream stream) 
    

    quality 为0-100 一般项目中设置为80 -100 我的项目中设置为90 图片质量效果也挺好

    三、优化实战代码

    整个三个步骤的流程代码我发出来可以借鉴一下

     File compress() throws IOException {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inSampleSize = computeSize();
            Bitmap tagBitmap = BitmapFactory.decodeStream(srcImg.open(), null, options);
            tagBitmap = setImgSize(tagBitmap, 1280, 1280);
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            if (isAutoRotating) {
                if (Checker.SINGLE.isJPG(srcImg.getMedia().getMimeType())) {
                    boolean isCut = srcImg.getMedia().isCut() && !TextUtils.isEmpty(srcImg.getMedia().getCutPath());
                    String url = isCut ? srcImg.getMedia().getCutPath() : srcImg.getMedia().getPath();
                    int degree = PictureMimeType.isContent(url) ? BitmapUtils.readPictureDegree(srcImg.open()) : BitmapUtils.readPictureDegree(context, url);
                    if (degree > 0) {
                        tagBitmap = BitmapUtils.rotatingImage(tagBitmap, degree);
                    }
                }
            }
            if (tagBitmap != null) {
                compressQuality = compressQuality <= 0 || compressQuality > 100 ? DEFAULT_QUALITY : compressQuality;
                tagBitmap.compress(focusAlpha || tagBitmap.hasAlpha() ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG, compressQuality, stream);
                tagBitmap.recycle();
                FileOutputStream fos = new FileOutputStream(tagImg);
                fos.write(stream.toByteArray());
                fos.flush();
                fos.close();
                stream.close();
                return tagImg;
            }
            return null;
        }
    
        /**
         * 等比缩放bitmap宽高
         * @param bm
         * @param newWidth
         * @param newHeight
         * @return
         */
        public Bitmap setImgSize(Bitmap bm, int newWidth, int newHeight) {
            // 获得图片的宽高.
            int width = bm.getWidth();
            int height = bm.getHeight();
            //如果之前图片的宽高 小于目标值就不缩放了
            if (width < newWidth || height < newHeight) {
                return bm;
            }
            // 计算缩放比例.
            float scaleWidth = ((float) newWidth) / width;
            float scaleHeight = ((float) newHeight) / height;
            float maxScale = Math.max(scaleWidth, scaleHeight);
            // 取得想要缩放的matrix参数.
            Matrix matrix = new Matrix();
            matrix.postScale(maxScale, maxScale);
            // 得到新的图片.
            Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
            return newbm;
        }
    

    srcImg.open() 是输入流InputStream 就是拍照 图片选择后的图片都转成了InputStream

    总结

    通过以上三步基本能把几M 的图片都压缩到200-300kb左右满足自己项目的业务需求
    当然还有额外的2个压缩方式
    Bitmap中有两个重要的内部类 CompressFormat 以及 Config
    下面分别介绍一下这两个类
    CompressFormat
    CompressFormat 是用来设置压缩方式的,是个枚举类,内部提供了三种图片压缩方式类型,

    JPEG :表示Bitmap采用JPEG压缩算法进行压缩,压缩后的格式可以是.jpg或者.png,是一种有损压缩方式。

    PNG :表示Bitmap采用PNG压缩算法进行压缩,压缩后的格式可以是.png,是一种无损压缩方式。

    WEBP :表示以WebP压缩算法进行图像压缩,压缩后的格式可以是".webp",是一种有损压缩,质量相同的情况下,WebP格式图像的体积要比JPEG格式图像小40%,美中不足的是,WebP格式图像的编码时间“比JPEG格式图像长8倍”, 而且还需要注意,在官方文档中有这样的描述:As of Build.VERSION_CODES.Q, a value of 100 results in a file in the lossless WEBP format. Otherwise the file will be in the lossy WEBP format. 意为Android10之后如果quality值(压缩质量)为100的话,bitmap压缩采用无损压缩格式,其他都为有损压缩;

    Config
    表示位图像素的存储格式,什么意思呢? 就是bitmap在屏幕上显示的每一像素在内存中存储的格式,会影响Bitmap真实图片的透明度以及图片质量;

    Bitmap.Config.ALPHA_8:颜色信息只由透明度组成,占8位;

    Bitmap.Config.ARGB_4444:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占4位,总共占16位;

    Bitmap.Config.ARGB_8888:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占8位,总共占32位,是Bitmap 默认的颜色存储格式,也是最占空间的一种配置;

    Bitmap.Config.RGB_565:颜色信息由R(Red),G(Green),B(Blue)三部分组成,R占5位,G占6位,B占5位,总共占16位;

    上面说了 android 系统默认存储位图方式是 ARGB_8888, 4个通道组成,每个通道8位,分表代表透明度和RGB颜色值, 也就是说一个位图像素占用了4个字节(1个byte8个bit位),

    同理:采用 Bitmap.Config.RGB_565 存储,单像素占用内存大小仅有2byte,换句话说一张图片采用ARGB_565格式相对于默认的ARGB_8888内存将减少一半,所以通过改变bitmap像素存储方式也是图片内存优化的重要渠道

    相关文章

      网友评论

          本文标题:图片压缩优化实战

          本文链接:https://www.haomeiwen.com/subject/zozxzltx.html