图像系列:Bitmap:硬核 Android 中 Bitmap压缩的几种方法

决定Bitmap图片大小或者解码之后内存取决于:图片透明度,位深,图片采样率(分辨率),编码格式,图片本身宽高,下面我们围绕这几个因素来研究下

1:质量压缩法(改变图片位深和透明度)

1.1 :理论说明

所谓质量压缩法:不减少图片本身的像素,它在保持像素的前提下该变图片的位深以及透明度,来达到压缩图片的目的,压缩后的文件大小会有所改变,但是导入成 bitmap后所占内存是不会变化的,由于要保持像素不变,所以它无法无限压缩,达到一个极限值后就不会继续变小了。

所以这个方法,不适合缩略图,也不适合想通过压缩图片来减少内存的占用。它仅仅适用在想保证图片质量的前提下,同时减少文件大小的情况而已。

1.2:Demo 代码

        ByteArrayOutputStream baos = new ByteArrayOutputStream();  
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        int options = 100;  
        while ( baos.toByteArray().length / 1024>32) {  
            baos.reset();
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);
            options -= 10;
        }  
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());  
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);

2:采样率压缩法(改变图片分辨率)

2.1 理论说明

        限制图片的采样率可以直接限制解码读取到图片的大小,即图片分辨率大小,但是采样率是整数,这就会造成我们不能根据图片实际宽高来精确的给出图片采样率。

        所以我们一般都过下面的方式来计算图片分辨率

1:首先获取一张图片的大小和内存

2:根据获取到图片的宽高,结合我们需要的宽高,来重新计算图片的采样率

3:得到采样率之后,重新解码图片,就可以根据需要的大小来获取对应分辨率的图片

2.2 Demo代码说明

1:首先获取一张图片的大小

BitmapFactory.Options options = new BitmapFactory.Options();

// itmapFactory.Opitions的inJustDecodeBounds属性为true,就可以在不分配内存的情况下,获取该图片的大小和类型。否则一旦图过大,在解码这个图片时,可能会出线内存泄漏

options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

2: 获取到高度和宽度之后,就可以按照我们的需要的高度和宽度,来计算图片的缩放比,得出图片的采样率

public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

 3:得到采样率之后,重新解码图片,就可以根据需要的大小来获取对应分辨率的图片

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

3:缩放法

3.1 理论说明

        缩放法其实就是,通过设置好的 matrix , 然后再 createBitmap ,相当于将图片按照一定比例缩放。至于按照多大的比例,你可以按照经验值 进行0.8倍缩放,也可以循环缩放直到合适的尺寸

3.2 代码说明

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 85, out);
        float zoom = (float)Math.sqrt(size * 1024 / (float)out.toByteArray().length);
 
        Matrix matrix = new Matrix();
        matrix.setScale(zoom, zoom);
 
        Bitmap result = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), matrix, true);
 
        out.reset();
        result.compress(Bitmap.CompressFormat.JPEG, 85, out);
        while(out.toByteArray().length > size * 1024){
            System.out.println(out.toByteArray().length);
            matrix.setScale(0.9f, 0.9f);
            result = Bitmap.createBitmap(result, 0, 0, result.getWidth(), result.getHeight(), matrix, true);
            out.reset();
            result.compress(Bitmap.CompressFormat.JPEG, 85, out);
        } 

4:RGB_565法

 4.1 理论分析

图片默认的编码格式 是 ARGB_8888 ,转换成 ARGB_565可以减少一半内存消耗,可以解决一些OOM问题,但是 RGB_565是没有透明度的,如果图片本身需要保留透明度,就只能使用ARGB_8888。

ARGB -----分别代表图片:Alpha 透明度和 red  green blue三原色

4.2 :常见的图片编码格式

在 Bitmap.Config中,图片列举了下面几种编码格式

ARGB_8888:分别用8位来记录4个值,所以每个像素会占用32位。
ARGB_4444:分别用4位来记录4个值,所以每个像素会占用16位。
RGB_565:分别用5位、6位和5位来记录RGB三色值,所以每个像素会占用16位。
ALPHA_8:根据注释应该是不保存颜色值,只保存透明度(8位),每个像素会占用8位。

如果我们以ARGB_8888作为基准来进行对比

  • ARGB_4444:内存占用减少一半,但是每个值图片失真度很严重,这个参数本身已经不推荐使用了。
  • RGB_565:内存占用减少一半,舍弃了透明度,同时三色值也有部分损失,但是图片失真度很小。
  • ALPHA_8:内存占用减少3/4,没有颜色,只有透明度,即黑白。

4.3:总结 

由于ARGB_4444已废除,而ALPHA_8需要在特殊条件下使用,一般用来做特殊需求的,所以我们大多数是用的还是ARGB_8888和RGB_565。

RGB_565能够在保证图片质量的情况下大大减少内存的开销,是解决oom的一种方法。但是一定要注意RGB_565是没有透明度的,如果图片本身需要保留透明度,那么就不能使用RGB_565。
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值