Bitmap压缩方法
1、图片内存:
getRowBytes():每一行所占的空间数。
getByteCount():BitMap的大小。
bitmap的内存空间 = 图片宽度 * 图片高度 * 单位像素所占字节数(Byte)
详见:https://www.jianshu.com/p/0fbcadfd4213?winzoom=1
如果不需要alpha通道,特别是资源本身为jpg格式的情况下,用RGB_565格式解码更加节省内存。
2、图片采样率
/**
* If set to a value > 1, requests the decoder to subsample the original
* image, returning a smaller image to save memory. The sample size is
* the number of pixels in either dimension that correspond to a single
* pixel in the decoded bitmap. For example, inSampleSize == 4 returns
* an image that is 1/4 the width/height of the original, and 1/16 the
* number of pixels. Any value <= 1 is treated the same as 1. Note: the
* decoder will try to fulfill this request, but the resulting bitmap
* may have different dimensions that precisely what has been requested.
* Also, powers of 2 are often faster/easier for the decoder to honor.
*/
public int inSampleSize;
inSampleSize的值必须大于1。若inSampleSize的值为2,表示目标图片的宽、高都为原图的 1 / 2,此时内存占用只有原图的 1 / 4。
最新的官方文档中指出,采样率inSampleSize 的值,应当总是2的指数,比如1,2,4,8,16等,若传递给系统的值不是2的指数,那么系统会向下取整并选择一个最接近2的指数来代替。因此,在大部分情况下设置图片的采样率,只能达到节省内存的目的,并不能精确地控制图片的尺寸。
采样率计算:
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int width = options.outWidth;
final int height = options.outHeight;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
//使用需要的宽高的最大值来计算比率
final int suitedValue = reqHeight > reqWidth ? reqHeight : reqWidth;
final int heightRatio = Math.round((float) height / (float) suitedValue);
final int widthRatio = Math.round((float) width / (float) suitedValue);
inSampleSize = heightRatio > widthRatio ? heightRatio : widthRatio;//用最大
}
return inSampleSize;
}
官方文档方法:
/**
* 计算压缩的比例
*
* @param options 解析图片所需的BitmapFactory.Options
* @param minSideLength 调整后图片最小的宽或高值,一般赋值为 -1
* @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;
}
/**
* 计算原始大小
*
* @param options 解析图片所需的BitmapFactory.Options
* @param minSideLength 调整后图片最小的宽或高值,一般赋值为 -1
* @param maxNumOfPixels 调整后图片的内存占用量上限
* @return
*/
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 the larger one when there is no overlapping zone.
return lowerBound;
}
if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
return 1;
} else if (minSideLength == -1) {
return lowerBound;
} else {
return upperBound;
}
}
3、图片质量
/**
* 质量压缩方法
*
* @param image
* @return
*/
public static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int options = 90;
while (baos.toByteArray().length / 1024 > 100) { // 循环判断如果压缩后图片是否大于100kb,大于继续压缩
baos.reset(); // 重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中
options -= 10;// 每次都减少10
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
return bitmap;
}
CompressFormat.JPEG,
CompressFormat.PNG, PNG 格式是无损的,它无法再进行质量压缩,quality 这个参数就没有作用了,会被忽略,所以最后图片保存成的文件大小不会有变化;
CompressFormat.WEBP ,这个格式是 google 推出的图片格式,它会比 JPEG 更加省空间,经过实测大概可以优化 30% 左右。
对比:
- 采样率压缩会改变图片的尺寸和内存;
- 质量压缩不会减少图片的像素,它是在保持像素的前提下改变图片的位深及透明度,来达到压缩图片的目的,图片的长,宽,像素都不会改变,那么bitmap所占内存大小是不会变的。
注意一点就是,质量压缩堆png格式这种图片没有作用,因为png是无损压缩。
日志详见:https://blog.csdn.net/harryweasley/article/details/51955467
4、综合使用(压缩至50k以下)
/**
* 压缩并保存图片
* <p>
* 图片小于50k
*
* @param localUrl 本地图片路径(例如:/storage/emulated/0/Pictures/multi_image_20180808_130928.jpg)
* @param path 目标文件路径(例如:/storage/emulated/0/zhcx/img/cgzf/)
* @param filename 文件名称(例如:33000019_0.jpg)
*/
public static void saveCgzfPic(String localUrl, String path, String filename) {
//获得图片的宽和高,但并不把图片加载到内存当中
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(localUrl, options);
options.inSampleSize = computeSampleSize(options, -1, (int) (0.5 * 1024 * 1024));
//使用获取到的inSampleSize再次解析图片
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(localUrl, options);
LogUtil.myD("inSampleSize:" + options.inSampleSize);
File rootFile = new File(path);
if (!rootFile.exists()) {
rootFile.mkdirs();
}
File file = new File(rootFile, filename);
try {
if (!file.exists()) {
file.createNewFile();
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
//质量压缩
int quality = 95;
while (baos.toByteArray().length / 1024 > 50) { // 循环判断如果压缩后图片是否大于50kb,大于继续压缩
LogUtil.d("caowj", "length:" + baos.toByteArray().length + ",,,quality:" + quality);
baos.reset(); // 重置baos即清空baos
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);// 这里压缩options%,把压缩后的数据存放到baos中
//每次减少5%质量
if (quality > 5) {//避免出现options<=0
quality -= 5;
} else {
break;
}
}
LogUtil.d("caowj", "2length:" + baos.toByteArray().length + ",,,2quality:" + quality);
//保存图片
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, fos);
//// TODO: 2018/8/8 问题:byte数组解析成bitmap后,再次解析成byte数组,变大了,为什么?
// ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
// L.d("caowj", "3length:" + baos.toByteArray().length);
//
// Bitmap bitmap2 = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
//
// bitmap2.compress(Bitmap.CompressFormat.JPEG, 100, baos);
// L.d("caowj", "sss:" + baos.toByteArray().length);
// bitmap2.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
if (bitmap.isRecycled()) {
bitmap.recycle();
}
} catch (Exception e) {
e.printStackTrace();
}
}
上一篇: openjudge 海贼王之伟大航路 状态压缩dp
下一篇: Vue2.0学习笔记