Android 图片压缩,Bitmap优化

时间:2021-01-31 20:56:31

Bitmap是导致OOM的一个最重要的问题。通常我们会对图片进行压缩来避免OOM。下面总结了几种对图片进行压缩的方法。

对图片进行压缩,无外乎两种方法:对图片质量进行压缩、对图片尺寸进行压缩。我在网上看了许多博客,那些所谓的高质量压缩无非就是用了这两种方法。我们要根据情况,合理的选择方法,才能达到最好的效果。

对图片质量的压缩,是对图片file大小的压缩,这可以保证图片占空间大小改变,但是这种方法压缩并不能使我们避免OOM。

图片的存在形式有三种:file、stream、bitmap、drawable。而影响OOM的是Bitmap。我们对file进行压缩,并不会影响到图片的像素,而Bitmap是根据像素计算的。

对Bitmap操作时,常修改的是颜色模式以及options.inSampleSize。

原因如下:

颜色模式:

ALPHA_8 会将每一个像素存储为单个的透明原色,只用八位存储了透明度 
ARGB_4444 过时的,因为严重影响了图片质量,所以被弃用,用ARGB_888代替 
ARGB_8888 每个像素点被存储为4个字节 
RGB_565 每个像素点被存储为2个字节,只存储 RGB 三原色,5位存储红原色(32种编码),5位存储蓝原色(三十二种编码),6位存储绿原色(64种编码)

不同的颜色模式意味着透明度、以及每个像素点所占字节的大小,因此对透明度没有需求的情况下我们使用RGB565,这样每个像素所占空间就会减小。

inSampleSize:

这个属性是采样率的意思, inSampleSize 这个属性只认2的整数倍为有效,比如你将 inSampleSize 赋值为2,那就是每隔2行采1行,每隔2列采一列,那你解析出的图片就是原图大小的1/4。

下面是对图片压缩的一些总结:

package com.xiaoqi.bitmapcompressutils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

/**
* Created by xiaoqi on 2016/8/15.
*/
public class CompressUtils {
/**
* 按质量压缩
* @param bitmap
* @return
*/
public static Bitmap compressImage(Bitmap bitmap){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 100;
//循环判断如果压缩后图片是否大于100kb,大于继续压缩
while ( baos.toByteArray().length / 1024>100) {
//清空baos
baos.reset();
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);
options -= 10;//每次都减少10
}
//把压缩后的数据baos存放到ByteArrayInputStream中
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
//把ByteArrayInputStream数据生成图片
Bitmap newBitmap = BitmapFactory.decodeStream(isBm, null, null);
return newBitmap;
}

/**
* 按图片尺寸压缩 参数为路径
* @param imgPath 图片路径
* @param pixelW 目标图片宽度
* @param pixelH 目标图片高度
* @return
*/
public static Bitmap compressImageFromPath(String imgPath, int pixelW, int pixelH) {
BitmapFactory.Options options = new BitmapFactory.Options();
// 开始读入图片,此时把options.inJustDecodeBounds 设回true,即只读边不读内容
options.inJustDecodeBounds = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
BitmapFactory.decodeFile(imgPath,options);
options.inJustDecodeBounds = false;
options.inSampleSize = computeSampleSize(options , pixelH > pixelW ? pixelH : pixelW ,pixelW * pixelH );
Bitmap bitmap = BitmapFactory.decodeFile(imgPath, options);
return bitmap;
}

/**
* 按图片尺寸压缩 参数是bitmap
* @param bitmap
* @param pixelW
* @param pixelH
* @return
*/
public static Bitmap compressImageFromBitmap(Bitmap bitmap, int pixelW, int pixelH) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
if( os.toByteArray().length / 1024>1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
os.reset();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);//这里压缩50%,把压缩后的数据存放到baos中
}
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
BitmapFactory.decodeStream(is, null, options);
options.inJustDecodeBounds = false;
options.inSampleSize = computeSampleSize(options , pixelH > pixelW ? pixelH : pixelW ,pixelW * pixelH );
is = new ByteArrayInputStream(os.toByteArray());
Bitmap newBitmap = BitmapFactory.decodeStream(is, null, options);
return newBitmap;
}

/**
* 对图片进行缩放指定大小
* @param bitmap
* @param width
* @param height
* @return
*/
public static Bitmap scaleTo(Bitmap bitmap, int width, int height){
int originalWidth = bitmap.getWidth();
int originalHeight = bitmap.getHeight();
float xScale = (float)width / originalWidth;
float yScale = (float)height / originalHeight;
Matrix matrix = new Matrix();
matrix.setScale(xScale,yScale);
return Bitmap.createBitmap(bitmap, 0, 0, originalWidth, originalHeight, matrix, true);
}

/**
* 以最省内存的方式读取本地资源的图片
* @param context
* @param resId
* @return
*/
public static Bitmap readBitmap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
// 获取资源图片
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}

/**
* android源码提供给我们的动态计算出图片的inSampleSize方法
* @param options
* @param minSideLength
* @param maxNumOfPixels
* @return
*/
public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
int roundedSize;
if (initialSize <= 8) {
roundedSize = 1;
while (roundedSize < initialSize) {
roundedSize <<= 1;
}
} else {
roundedSize = (initialSize + 7) / 8 * 8;
}
return roundedSize;
}

private static int computeInitialSampleSize(BitmapFactory.Options options,int minSideLength, int maxNumOfPixels) {
double w = options.outWidth;
double h = options.outHeight;
int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
int upperBound = (minSideLength == -1) ? 128 :(int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
if (upperBound < lowerBound) {
return lowerBound;
}
if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
return 1;
} else if (minSideLength == -1) {
return lowerBound;
} else {
return upperBound;
}
}
}