图片压缩优化实战

2021-11-02  本文已影响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像素存储方式也是图片内存优化的重要渠道

上一篇下一篇

猜你喜欢

热点阅读