Bitmap占用内存大小的计算方式:
Bitmap所占用的内存 = 图片长度 x 图片宽度 x 一个像素点占用的字节数
Bitmap编码
Bitmap.config
其中,A代表透明度;R代表红色;G代表绿色;B代表蓝色。
ALPHA_8
表示8位Alpha位图,即A=8,一个像素点占用1个字节,它没有颜色,只有透明度
ARGB_4444
表示16位ARGB位图,即A=4,R=4,G=4,B=4,一个像素点占4+4+4+4=16位,2个字节
ARGB_8888
表示32位ARGB位图,即A=8,R=8,G=8,B=8,一个像素点占8+8+8+8=32位,4个字节
RGB_565
表示16位RGB位图,即R=5,G=6,B=5,它没有透明度,一个像素点占5+6+5=16位,2个字节
Bitmap.Config主要作用是:以何种方式像素存储。不同的配置将会影响图像的画质(色彩深度),位数越高画质越高,显然在这里ARGB_8888是最占内存的。当然,画质越高也就越占内存了。
Tips:由于ARGB_4444的画质惨不忍睹,一般假如对图片没有透明度要求的话,可以改成RGB_565,相比ARGB_8888将节省一半的内存开销。
采样率
inSampleSize:这个值是一个int,当它小于1的时候,将会被当做1处理,如果大于1,那么就会按照比例(1 / inSampleSize)缩小bitmap的宽和高、降低分辨率,大于1时这个值将会被处置为2的倍数。例如,width=100,height=100,inSampleSize=2,那么就会将bitmap处理为,width=50,height=50,宽高降为1 / 2,像素数降为1 / 4。
图片压缩
前面我们讲了bitmap的大小:
Bitmap所占用的内存 = 图片长度 x 图片宽度 x 一个像素点占用的字节数
方式一 改变bitmap.config
根据我们前面的知识:Bitmap.config可以决定一个像素点占用的字节数,我们举一个实际例子:
BitmapFactory.Options options = new BitmapFactory.Options();
//不获取图片,不加载到内存中,只返回图片属性
options.inJustDecodeBounds = false;
BitmapFactory.decodeResource(getResources(),R.mipmap.ic_profile_cover, options);
//图片的宽高
int outHeight = options.outHeight;
int outWidth = options.outWidth;
Log.d("mmm", "图片宽=" + outWidth + "图片高=" + outHeight);
//图片格式压缩
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inJustDecodeBounds = false;
options.inSampleSize=2;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_profile_cover, options);
float bitmapsize = getBitmapSize(bitmap);
Log.d("mmm","压缩后:图片占内存大小" + bitmapsize + "MB / 宽度=" + bitmap.getWidth() + "高度=" + bitmap.getHeight());
BitmapFactory.Options options1 = new BitmapFactory.Options();
options1.inPreferredConfig = Bitmap.Config.RGB_565;
options1.inJustDecodeBounds = false;
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_profile_cover, options1);
float bitmapsize1 = getBitmapSize(bitmap1);
Log.d("mmm","压缩后:图片占内存大小" + bitmapsize1 + "MB / 宽度=" + bitmap1.getWidth() + "高度=" + bitmap1.getHeight());
}
/**
* 得到bitmap的大小
*/
public int getBitmapSize(Bitmap bitmap) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //API 19
return bitmap.getAllocationByteCount()/1024/1024;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {//API 12
return bitmap.getByteCount()/1024/1024;
}
// 在低版本中用一行的字节x高度
return bitmap.getRowBytes() * bitmap.getHeight()/1024/1024;
}
我们观察两次打印的日志发现RGB_565所占用的内存只有ARGB_888的一半。
例如,如果图片中包含透明度,那么对该图片解码时使用的配置就需要支持透明度,默认会使用ARGB_8888来解码。
如果直接设置 RGB_565:
对于一张透明图片(png),内存、宽高不变,bitmap 也不会失去透明度。
对于一张非透明图片(png、jpg),宽高不变,内存减小。
注意:由于ARGB_4444的画质惨不忍睹,一般假如对图片没有透明度要求的话,可以改成RGB_565,相比ARGB_8888将节省一半的内存开销。
方式二 采样率压缩(改变bitmap的宽高)
我们知道通过改变采样率可以对bitmap宽高进行一定比例的压缩
下面我们举一个简单的例子:
BitmapFactory.Options options = new BitmapFactory.Options();
//不获取图片,不加载到内存中,只返回图片属性
options.inJustDecodeBounds = false;
BitmapFactory.decodeResource(getResources(),R.mipmap.ic_profile_cover, options);
//图片的宽高
int outHeight = options.outHeight;
int outWidth = options.outWidth;
Log.d("mmm", "图片宽=" + outWidth + "图片高=" + outHeight);
//图片格式压缩
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_profile_cover, options);
float bitmapsize = getBitmapSize(bitmap);
Log.d("mmm","压缩后:图片占内存大小" + bitmapsize + "MB / 宽度=" + bitmap.getWidth() + "高度=" + bitmap.getHeight());
BitmapFactory.Options options1 = new BitmapFactory.Options();
options1.inPreferredConfig = Bitmap.Config.ARGB_8888;
options1.inJustDecodeBounds = false;
options1.inSampleSize=2;
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_profile_cover, options1);
float bitmapsize1 = getBitmapSize(bitmap1);
Log.d("mmm","压缩后:图片占内存大小" + bitmapsize1 + "MB / 宽度=" + bitmap1.getWidth() + "高度=" + bitmap1.getHeight());
}
通过上面的例子我们可以看到当我们把inSampleSize设置成2的时候,图片的宽高变为原来的1/2,图片大小变成原来的1/4。所以我们可以通过采样率对图片进行压缩。
方式三 质量压缩
Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(), R.mipmap.test);
iv_1.setImageBitmap(bitmap);
float bitmapsize = getBitmapSize(bitmap);
Log.d("mmm", "压缩后1:图片占内存大小" + bitmapsize + "MB / 宽度=" + bitmap.getWidth() + "高度=" + bitmap.getHeight());
//压缩图像后,显示
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 1, bos);
byte[] bytes = bos.toByteArray();
Bitmap bitmap1 = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
iv_2.setImageBitmap(bitmap1);
float bitmapsize1 = getBitmapSize(bitmap1);
Log.d("mmm", "压缩后2:图片占内存大小" + bitmapsize1 + "MB / 宽度=" + bitmap1.getWidth() + "高度=" + bitmap1.getHeight());
public Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
image.compress(Bitmap.CompressFormat.JPEG, 50, baos);
Log.d("mmm", "压缩比例"+"baos:"+baos.toByteArray().length);
int options = 100;
while ( baos.toByteArray().length / 1024>100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
baos.reset();//重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
options -= 10;//每次都减少10
Log.d("mmm", "压缩比例"+"options:"+options);
}
//把压缩后的数据baos存放到ByteArrayInputStream中
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null); //把ByteArrayInputStream数据生成图片
return bitmap;
}
我们可以看到压缩前和压缩后bitmap大小并没有改变,因为质量压缩不会减少图片的像素,它是在保持像素的前提下改变图片的位深及透明度,来达到压缩图片的目的,图片的长,宽,像素都不会改变,那么bitmap所占内存大小是不会变的。
我们可以看到有个参数:quality,可以调节你压缩的比例,但是还要注意一点就是,质量压缩堆png格式这种图片没有作用,因为png是无损压缩。
缩放压缩法
/***
* 缩放图片到指定宽和高
*
* @param bitmap
* @param newWidth 缩放后的宽度
* @param newHeight 缩放后的高度
* @param isCanBig 是否允许放大(否则只做缩小处理)
* @return
*/
public static Bitmap zoomImage(Bitmap bitmap, float newWidth, float newHeight, boolean isCanBig) {
// 获取这个图片的宽和高
float width = bitmap.getWidth();
float height = bitmap.getHeight();
// 如果原始尺寸比设定的小且不允许放大,则不用压缩
if (!isCanBig && width <= newWidth && height <= newHeight) {
return bitmap;
}
// 创建操作图片用的matrix对象
Matrix matrix = new Matrix();
// 计算宽高缩放率
float scaleWidth = newWidth / width;
float scaleHeight = newHeight / height;
// 缩放图片动作
matrix.postScale(scaleWidth, scaleHeight);
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, (int) width, (int) height, matrix, true);
return newBitmap;
}
缩放压缩使用的是通过矩阵对图片进行裁剪,也是通过缩放图片尺寸,来达到压缩图片的效果,和采样率的原理一样。