最近重新学习图片压缩,网上各种找资料,对比,总结,感觉有些理解了,总结一下。
在android中,图片的加载一般是把图片转化为Bitmap,设置到ImageView中显示出来。这里有个问题需要特别注意,也是我写下这篇记录的原因所在:当图片以Bitmap形式存在时,它占用的内存大小的计算方式:
memory=图片宽度(px)* 图片高度(px)* 单位像素占用字节数。
单位像素占用字节数就是图片的色彩模式,ARGB(透明度、红色、绿色、蓝色)常见的有:
ARGB_4444:单位像素所占字节数:2。argb各站4位,4*4/8=2(字节)。
ARGB_8888:单位像素所占字节数:4。argb各站8位,8*4/8=4(字节)。android默认格式。
RGB_565:单位像素所占字节数:2。
例:我手机上有张照片是:2320*4128,大小是3.1MB,要1:1显示出来的话,需要的内存是:2320*4128*4/1024/1024=36.53MB,好吧,我确定显示图片的时候必须要考虑压缩了。手机分辨率为1280*720,铺满全屏时,需要1280*720*4/1024/1024=3.52MB。很多时候,并不需要显示全屏的图片,仅仅只需要显示一张缩略图就够了。比如一排显示6张100*120的图片,需要的内存是100*120*4/1024*6=281.25KB,再添加一个缓存的功能,应用的流畅性大大提高。
接下来进入正题,压缩。
压缩主要分为2部分,尺寸压缩和质量压缩。
1.尺寸压缩
即压缩图片的像素大小。上代码:
/**
* 从数组得到图片
* @param b 图片
* @param width 指定的宽
* @param height 指定的高
* @return bitmap
*/
public static Bitmap compressBitmapFromByte(byte [] b,int width,int height){
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeByteArray(b, 0, b.length,options);
options.inSampleSize=getInSampleSize(options,width,height);
options.inJustDecodeBounds=false;
Bitmap bitmap=BitmapFactory.decodeByteArray(b,0,b.length,options);
return createScaledBitmap(bitmap,width,height);
}
/**
* 从内存中加载图片
* @param path 路径
* @param width 显示的宽
* @param height 显示的高
* @return bitmap
*/
public static Bitmap compressBitmapFromSD(String path,int width,int height){
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeFile(path,options);
options.inSampleSize=getInSampleSize(options,width,height);
options.inJustDecodeBounds=false;
Bitmap bitmap=BitmapFactory.decodeFile(path,options);
return createScaledBitmap(bitmap,width,height);
}
/**
* 得到压缩倍数 一般为2的N次幂倍
*/
private static int getInSampleSize(BitmapFactory.Options options,int width,int height){
int w=options.outWidth;
int h=options.outHeight;
int be=1;
if (w>width||h>height) {
int hh=w/2;
int ww=w/2;
while ((hh/be)>height&&(ww/be)>width){
be*=2;
}
}
return be;
}
/**
* 通过Bitmap.createScaledBitmap方法决定是否需要回收bm
*/
private static Bitmap createScaledBitmap(Bitmap bm,int w,int h){
// 如果放大图片,filter决定是否平滑,如果缩小图片,filter无影响
Bitmap bitmap=Bitmap.createScaledBitmap(bm,w,h,false);
if (bitmap!=bm){//如果没有缩放,不用回收
bm.recycle();
}
return bitmap;
}
其中需要注意的地方是options.inJustDecodeBounds=true;设置以后,程序可以读取到图片的宽高,而不需要吧图片完全读取近内存,能有效避免内存溢出的发生,options.inJustDecodeBounds=false设置后,正式将图片读入内存,进行压缩。bitmap很占内存,推荐在需要的时候主动回收。
2.质量压缩
现在的照片很多都有2M以上,我们上传的时候并不需要这么大,可以适当压缩大小后上传。
/**
* 压缩质量
* @param image Bitmap
* @param size 指定大小
* @return Bitmap
*/
public static Bitmap compressImage(Bitmap image,int size) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int options = 90;
while (baos.toByteArray().length / 1024 > size) { // 循环判断如果压缩后图片是否大于size,大于继续压缩
baos.reset();// 重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中
options -= 10;
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
return BitmapFactory.decodeStream(isBm, null, null);
}
这是我用过的压缩方法,把一张高清大图压缩成指定大小和尺寸的图片文件(不是精确值)
/**
* 得到压缩后的图片File
* @param path 需要压缩的图片路径
* @param filepath 压所得到的图片的储存路径
* @return File
*/
public static File getCompressFile(String path, String filepath,int width,int height,int size){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
options.inJustDecodeBounds = false;
options.inSampleSize = getInSampleSize(options,width,height);
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
return getCompressPic(bitmap,filepath,size);
}
/**
* 把Bitmap压缩到指定文件夹下
* @param bitmap bitmap
* @param filepath 压所得到的图片的储存路径
* @return
*/
public static File getCompressPic(Bitmap bitmap,String filepath,int size){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 90;
while (baos.toByteArray().length/ 1024 > size) {
baos.reset();
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);
options -= 10;
}
File file=new File(filepath);
if (!file.exists()) file.mkdirs();
String picName=System.currentTimeMillis()+".jpg";
File file2=new File(file,picName);
try {
FileOutputStream out=new FileOutputStream(file2);
out.write(baos.toByteArray());
out.flush();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
return file2;
} catch (IOException e) {
e.printStackTrace();
return file2;
}
return file2;
}
图片压缩差不多就这些,以后想到更好的在补充。