Android Bitmap 压缩处理
00.Bitmap的压缩方式
- 常见压缩方法Api
- Bitmap.compress(),质量压缩,不会对内存产生影响;
- BitmapFactory.Options.inSampleSize,内存压缩;
- Bitmap.compress()质量压缩
- 质量压缩,不会对内存产生影响
- 它是在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的,不会减少图片的像素。进过它压缩的图片文件大小会变小,但是解码成bitmap后占得内存是不变的。
- BitmapFactory.Options.inSampleSize内存压缩
- 内存压缩
- 解码图片时,设置BitmapFactory.Options类的inJustDecodeBounds属性为true,可以在Bitmap不被加载到内存的前提下,获取Bitmap的原始宽高。而设置BitmapFactory.Options的inSampleSize属性可以真实的压缩Bitmap占用的内存,加载更小内存的Bitmap。
- 设置inSampleSize之后,Bitmap的宽、高都会缩小inSampleSize倍。例如:一张宽高为2048x1536的图片,设置inSampleSize为4之后,实际加载到内存中的图片宽高是512x384。占有的内存就是0.75M而不是12M,足足节省了15倍。
- 备注:inSampleSize值的大小不是随便设、或者越大越好,需要根据实际情况来设置。inSampleSize比1小的话会被当做1,任何inSampleSize的值会被取接近2的幂值。
01.Bitmap质量压缩
质量压缩方法:在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的,这样适合去传递二进制的图片数据,比如分享图片,要传入二进制数据过去,限制500kb之内。
- 1、bitmap图片的大小不会改变
- 2、bytes.length是随着quality变小而变小的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39/**
* 第一种:质量压缩法
* @param image 目标原图
* @param maxSize 最大的图片大小
* @return bitmap,注意可以测试以下压缩前后bitmap的大小值
*/
public static Bitmap compressImage(Bitmap image , long maxSize) {
int byteCount = image.getByteCount();
Log.i("yc压缩图片","压缩前大小"+byteCount);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 把ByteArrayInputStream数据生成图片
Bitmap bitmap = null;
// 质量压缩方法,options的值是0-100,这里100表示原来图片的质量,不压缩,把压缩后的数据存放到baos中
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 90;
// 循环判断如果压缩后图片是否大于maxSize,大于继续压缩
while (baos.toByteArray().length > maxSize) {
// 重置baos即清空baos
baos.reset();
// 这里压缩options%,把压缩后的数据存放到baos中
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
// 每次都减少10,当为1的时候停止,options<10的时候,递减1
if(options == 1){
break;
}else if (options <= 10) {
options -= 1;
} else {
options -= 10;
}
}
byte[] bytes = baos.toByteArray();
if (bytes.length != 0) {
// 把压缩后的数据baos存放到bytes中
bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
int byteCount1 = bitmap.getByteCount();
Log.i("yc压缩图片","压缩后大小"+byteCount1);
}
return bitmap;
}/**
- 第一种:质量压缩法
- @param src 源图片
- @param maxByteSize 允许最大值字节数
- @param recycle 是否回收
- @return 质量压缩压缩过的图片
*/
public static Bitmap compressByQuality(final Bitmap src, final long maxByteSize, final boolean recycle) {
if (src == null || src.getWidth() == 0 || src.getHeight() == 0 || maxByteSize <= 0) {
return null;
}
Log.i(“yc压缩图片”,”压缩前大小”+src.getByteCount());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
src.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] bytes;
if (baos.size() <= maxByteSize) {// 最好质量的不大于最大字节,则返回最佳质量
bytes = baos.toByteArray();
} else {
baos.reset();
src.compress(Bitmap.CompressFormat.JPEG, 0, baos);
if (baos.size() >= maxByteSize) { // 最差质量不小于最大字节,则返回最差质量
bytes = baos.toByteArray();
} else {
// 二分法寻找最佳质量
int st = 0;
int end = 100;
int mid = 0;
while (st < end) {
mid = (st + end) / 2;
baos.reset();
src.compress(Bitmap.CompressFormat.JPEG, mid, baos);
int len = baos.size();
if (len == maxByteSize) {
break;
} else if (len > maxByteSize) {
end = mid - 1;
} else {
st = mid + 1;
}
}
if (end == mid - 1) {
baos.reset();
src.compress(Bitmap.CompressFormat.JPEG, st, baos);
}
bytes = baos.toByteArray();
}
}
if (recycle && !src.isRecycled()){
src.recycle();
}
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
Log.i(“yc压缩图片”,”压缩后大小”+bitmap.getByteCount());
return bitmap;
}
/**
- 第一种:质量压缩法
- @param src 源图片
- @param quality 质量
- @param recycle 是否回收
- @return 质量压缩后的图片
*/
public static Bitmap compressByQuality(final Bitmap src, @IntRange(from = 0, to = 100) final int quality, final boolean recycle) {
if (src == null || src.getWidth() == 0 || src.getHeight() == 0) {
return null;
}
Log.i(“yc压缩图片”,”压缩前大小”+src.getByteCount());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
src.compress(Bitmap.CompressFormat.JPEG, quality, baos);
byte[] bytes = baos.toByteArray();
if (recycle && !src.isRecycled()) {
src.recycle();
}
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
Log.i(“yc压缩图片”,”压缩后大小”+bitmap.getByteCount());
return bitmap;
}/**1
2
3
4
5
6
7
8
9
### 02.Bitmap采样率压缩
- 什么是采样率压缩?
- 设置inSampleSize的值(int类型)后,假如设为n,则宽和高都为原来的1/n,宽高都减少,内存降低。上面的代码没用过options.inJustDecodeBounds = true;因为我是固定来取样的数据,
- 为什么这个压缩方法叫采样率压缩?是因为配合inJustDecodeBounds,先获取图片的宽、高(这个过程就是取样)。然后通过获取的宽高,动态的设置inSampleSize的值。当inJustDecodeBounds设置为true的时候, BitmapFactory通过decodeResource或者decodeFile解码图片时,将会返回空(null)的Bitmap对象,这样可以避免Bitmap的内存分配, 但是它可以返回Bitmap的宽度、高度以及MimeType。 - 第二种:按采样大小压缩
- @param src 源图片
- @param sampleSize 采样率大小
- @param recycle 是否回收
- @return 按采样率压缩后的图片
*/
public static Bitmap compressBySampleSize(final Bitmap src, final int sampleSize, final boolean recycle) {
if (src == null || src.getWidth() == 0 || src.getHeight() == 0) {
return null;
}
Log.i(“yc压缩图片”,”压缩前大小”+src.getByteCount());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
src.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] bytes = baos.toByteArray();
if (recycle && !src.isRecycled()) {
src.recycle();
}
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
Log.i(“yc压缩图片”,”压缩后大小”+bitmap.getByteCount());
return bitmap;
}
/**
- 第二种:按采样大小压缩
- @param src 源图片
- @param maxWidth 最大宽度
- @param maxHeight 最大高度
- @param recycle 是否回收
- @return 按采样率压缩后的图片
*/
public static Bitmap compressBySampleSize(final Bitmap src, final int maxWidth, final int maxHeight, final boolean recycle) {
if (src == null || src.getWidth() == 0 || src.getHeight() == 0) {
return null;
}
Log.i(“yc压缩图片”,”压缩前大小”+src.getByteCount());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
src.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] bytes = baos.toByteArray();
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
options.inJustDecodeBounds = false;
if (recycle && !src.isRecycled()) {
src.recycle();
}
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
Log.i(“yc压缩图片”,”压缩后大小”+bitmap.getByteCount());
return bitmap;
}
/**
- 计算获取缩放比例inSampleSize
*/
private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
final float totalPixels = width * height;
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
return inSampleSize;
}setTranslate(float dx,float dy):控制Matrix进行位移。1
2
3
4
5
6
7
8
### 03.Bitmap缩放法压缩
- Android中使用Matrix对图像进行缩放、旋转、平移、斜切等变换的。
- Matrix提供了一些方法来控制图片变换:Matrix调用一系列set,pre,post方法时,可视为将这些方法插入到一个队列。当然,按照队列中从头至尾的顺序调用执行。其中pre表示在队头插入一个方法,post表示在队尾插入一个方法。而set表示把当前队列清空,并且总是位于队列的最中间位置。当执行了一次set后:pre方法总是插入到set前部的队列的最前面,post方法总是插入到set后部的队列的最后面
setSkew(float kx,float ky):控制Matrix进行倾斜,kx、ky为X、Y方向上的比例。
setSkew(float kx,float ky,float px,float py):控制Matrix以px、py为轴心进行倾斜,kx、ky为X、Y方向上的倾斜比例。
setRotate(float degrees):控制Matrix进行depress角度的旋转,轴心为(0,0)。
setRotate(float degrees,float px,float py):控制Matrix进行depress角度的旋转,轴心为(px,py)。
setScale(float sx,float sy):设置Matrix进行缩放,sx、sy为X、Y方向上的缩放比例。
setScale(float sx,float sy,float px,float py):设置Matrix以(px,py)为轴心进行缩放,sx、sy为X、Y方向上的缩放比例。/**1
2
3
- 缩放法压缩工具类代码 - 第三种:按缩放压缩
- @param src 源图片
- @param newWidth 新宽度
- @param newHeight 新高度
- @param recycle 是否回收
- @return 缩放压缩后的图片
*/
public static Bitmap compressByScale(final Bitmap src, final int newWidth, final int newHeight, final boolean recycle) {
return scale(src, newWidth, newHeight, recycle);
}
public static Bitmap compressByScale(final Bitmap src, final float scaleWidth, final float scaleHeight, final boolean recycle) {
return scale(src, scaleWidth, scaleHeight, recycle);
}/**
- 缩放图片
- @param src 源图片
- @param scaleWidth 缩放宽度倍数
- @param scaleHeight 缩放高度倍数
- @param recycle 是否回收
- @return 缩放后的图片
*/
private static Bitmap scale(final Bitmap src, final float scaleWidth, final float scaleHeight, final boolean recycle) {
if (src == null || src.getWidth() == 0 || src.getHeight() == 0) {
return null;
}
Matrix matrix = new Matrix();
matrix.setScale(scaleWidth, scaleHeight);
Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
if (recycle && !src.isRecycled()) {
src.recycle();
}
return ret;
}