Android 图片压缩
在android 中进行图片压缩处理, 通常是为了减小图片大小来节省存储空间或者加快网络传输速度.
本文主要记录下android 中原生的压缩图片方法.
1: 使用Bitmap.compress()方法压缩图片
示例代码1:
通过Bitmap的compress()方法将图片压缩为JPEG/PNG格式.
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.image);
ByteArrayOutputStream os = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG,80,os);
byte[] compressImage=os.toByteArray();
- compress方法中Bitmap.CompressFormat.JPEG指定压缩格式,
- 参数80: 压缩质量,范围是0-100,数值越小压缩率越高,当然图片的质量也越差.
由此我们可以延伸出限定图片大小,来循环压缩,具体代码如下:
/**
* 压缩 Bitmap 到指定大小以内(KB)
*/
private static byte[] compressBitmap(Bitmap bitmap, int maxKb, Bitmap.CompressFormat format) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
int quality = 100;
bitmap.compress(format, quality, outputStream);
while (outputStream.toByteArray().length > maxKb * 1024 && quality > 10) {
outputStream.reset();
quality -= 5;
bitmap.compress(format, quality, outputStream);
}
return outputStream.toByteArray();
}
可以看到封装的方法指定maxKb, 初始化时质量为100,拿到图片的原始大小,从而根据大小来循环执行压缩方法.并逐渐降低压缩质量.
2: 缩放图片尺寸压缩
示例代码2:
Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, 1024, 768, true);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream);
byte[] compressedImage = outputStream.toByteArray();
示例代码中将图片指定缩放到1024x768,并压缩质量值80.
private static Bitmap scaleBitmap(Bitmap bitmap, int targetWidth, int targetHeight) {
return Bitmap.createScaledBitmap(bitmap, targetWidth, targetHeight, true);
}
3: 处理 EXIF 旋转
/**
* 从 Uri 解码图片,并自动处理 EXIF 旋转
*/
private static Bitmap decodeBitmapFromUri(Context context, Uri uri) throws IOException {
ContentResolver resolver = context.getContentResolver();
InputStream inputStream = resolver.openInputStream(uri);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
inputStream.close();
// 处理 EXIF 旋转
ExifInterface exif = new ExifInterface(resolver.openInputStream(uri));
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.postRotate(90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.postRotate(180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.postRotate(270);
break;
default:
break;
}
return Bitmap.createBitmap(bitmap, 0, 0, options.outWidth, options.outHeight, matrix, true);
}
4: 采样率压缩
采样率是BitmapFactory.Options的重要参数,用于控制图片解码时的缩放比例,合理的设置inSanmpleSize可以有效的减少内存占用,减少oom的出现.
采样率的计算主要通过实际大小以及要求大小来计算,具体的代码如下:
public static int calculateInSampleSize(int actualWidth, int actualHeight, int reqWidth, int reqHeight) {
int inSampleSize = 1;
if (actualHeight > reqHeight || actualWidth > reqWidth) {
int halfWidth = actualWidth / 2;
int halfHeight = actualHeight / 2;
while ((halfWidth / inSampleSize) >= reqWidth
&& (halfHeight / inSampleSize) >= reqHeight) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
获取图片的原始大小, 我们可以通过
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 只读取元数据,不解码像素
InputStream is = context.getContentResolver().openInputStream(uri);
BitmapFactory.decodeStream(is, null, options);
is.close();
原始图片的宽高,就可以从options.outWidth 和 options.outHeight 得到.
5: 附上源码
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Build;
import android.content.ContentResolver;
import android.content.Context;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class ImageCompressUtils {
/**
* 压缩图片到指定最大大小(单位 KB),并返回压缩后的字节数组
*
* @param context 上下文
* @param uri 图片 Uri
* @param maxKb 最大允许大小(KB)
* @param format 压缩格式(Bitmap.CompressFormat)
* @param inSampleSize 采样率(2 的幂次,例如 1, 2, 4, 8...)
* @return 压缩后的图片字节数组
*/
public static byte[] compressImage(Context context, Uri uri, int maxKb, Bitmap.CompressFormat format, int inSampleSize) throws IOException {
Bitmap bitmap = decodeBitmapFromUri(context, uri, inSampleSize);
return compressBitmap(bitmap, maxKb, format);
}
/**
* 缩放并压缩图片到指定宽高和大小
*
* @param context 上下文
* @param uri 图片 Uri
* @param targetWidth 目标宽度
* @param targetHeight 目标高度
* @param maxKb 最大允许大小(KB)
* @param format 压缩格式
* @param inSampleSize 采样率(2 的幂次)
* @return 压缩后的图片字节数组
*/
public static byte[] compressToSize(Context context, Uri uri, int targetWidth, int targetHeight, int maxKb, Bitmap.CompressFormat format, int inSampleSize) throws IOException {
Bitmap original = decodeBitmapFromUri(context, uri, inSampleSize);
Bitmap scaled = scaleBitmap(original, targetWidth, targetHeight);
return compressBitmap(scaled, maxKb, format);
}
/**
* 从 Uri 解码图片,支持指定采样率,并自动处理 EXIF 旋转
*/
private static Bitmap decodeBitmapFromUri(Context context, Uri uri, int inSampleSize) throws IOException {
ContentResolver resolver = context.getContentResolver();
InputStream inputStream = resolver.openInputStream(uri);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = inSampleSize; // 设置采样率
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
inputStream.close();
// 处理 EXIF 旋转
ExifInterface exif = new ExifInterface(resolver.openInputStream(uri));
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.postRotate(90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.postRotate(180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.postRotate(270);
break;
default:
break;
}
return Bitmap.createBitmap(bitmap, 0, 0, options.outWidth, options.outHeight, matrix, true);
}
/**
* 缩放图片到指定宽高
*/
private static Bitmap scaleBitmap(Bitmap bitmap, int targetWidth, int targetHeight) {
return Bitmap.createScaledBitmap(bitmap, targetWidth, targetHeight, true);
}
/**
* 压缩 Bitmap 到指定大小以内(KB)
*/
private static byte[] compressBitmap(Bitmap bitmap, int maxKb, Bitmap.CompressFormat format) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
int quality = 100;
bitmap.compress(format, quality, outputStream);
while (outputStream.toByteArray().length > maxKb * 1024 && quality > 10) {
outputStream.reset();
quality -= 5;
bitmap.compress(format, quality, outputStream);
}
return outputStream.toByteArray();
}
/**
* 自动计算合适的采样率(inSampleSize)
*
* @param actualWidth 实际宽度
* @param actualHeight 实际高度
* @param reqWidth 请求宽度
* @param reqHeight 请求高度
* @return 合适的采样率
*/
public static int calculateInSampleSize(int actualWidth, int actualHeight, int reqWidth, int reqHeight) {
int inSampleSize = 1;
if (actualHeight > reqHeight || actualWidth > reqWidth) {
int halfWidth = actualWidth / 2;
int halfHeight = actualHeight / 2;
while ((halfWidth / inSampleSize) >= reqWidth
&& (halfHeight / inSampleSize) >= reqHeight) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
}