Android中图片压缩方案详解.docx
《Android中图片压缩方案详解.docx》由会员分享,可在线阅读,更多相关《Android中图片压缩方案详解.docx(25页珍藏版)》请在冰豆网上搜索。
Android中图片压缩方案详解
Android中图片压缩方案详解
1、质量压缩法
设置bitmapoptions属性,降低图片的质量,像素不会减少
第一个参数为需要压缩的bitmap图片对象,第二个参数为压缩后图片保存的位置
设置options属性0-100,来实现压缩。
privateBitmapcompressImage(Bitmapimage){
ByteArrayOutputStreambaos=newByteArrayOutputStream();
press(Bitmap.CompressFormat.JPEG,100,baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
intoptions=100;
while(baos.toByteArray().length/1024>100){//循环判断如果压缩后图片是否大于100kb,大于继续压缩
baos.reset();//重置baos即清空baos
press(Bitmap.CompressFormat.JPEG,options,baos);//这里压缩options%,把压缩后的数据存放到baos中
options-=10;//每次都减少10
}
ByteArrayInputStreamisBm=newByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
Bitmapbitmap=BitmapFactory.decodeStream(isBm,null,null);//把ByteArrayInputStream数据生成图片
returnbitmap;
}
质量压缩不会减少图片的像素。
它是在保持像素不变的前提下改变图片的位深及透明度等,来达到压缩图片的目的。
进过它压缩的图片文件大小会有改变,但是导入成bitmap后占得内存是不变的。
因为要保持像素不变,所以它就无法无限压缩,到达一个值之后就不会继续变小了。
显然这个方法并不适用于缩略图,其实也不适用于想通过压缩图片减少内存的适用,仅仅适用于想在保证图片质量的同时减少文件大小的情况而已。
2、采样率压缩法
privateBitmapgetimage(StringsrcPath){
BitmapFactory.OptionsnewOpts=newBitmapFactory.Options();
//开始读入图片,此时把options.inJustDecodeBounds设回true了
newOpts.inJustDecodeBounds=true;
Bitmapbitmap=BitmapFactory.decodeFile(srcPath,newOpts);//此时返回bm为空
newOpts.inJustDecodeBounds=false;
intw=newOpts.outWidth;
inth=newOpts.outHeight;
//现在主流手机比较多是1280*720分辨率,所以高和宽我们设置为
floathh=1280f;//这里设置高度为1280f
floatww=720f;//这里设置宽度为720f
//缩放比。
由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
intbe=1;//be=1表示不缩放
if(w>h&&w>ww){//如果宽度大的话根据宽度固定大小缩放
be=(int)(newOpts.outWidth/ww);
}elseif(whh){//如果高度高的话根据宽度固定大小缩放
be=(int)(newOpts.outHeight/hh);
}
if(be<=0)
be=1;
newOpts.inSampleSize=be;//设置缩放比例
//重新读入图片,注意此时已经把options.inJustDecodeBounds设回false了
bitmap=BitmapFactory.decodeFile(srcPath,newOpts);
returncompressImage(bitmap);//压缩好比例大小后再进行质量压缩
}
这个方法的好处是大大的缩小了内存的使用,在读存储器上的图片时,如果不需要高清的效果,可以先只读取图片的边,通过宽和高设定好取样率后再加载图片,这样就不会过多的占用内存。
3、缩放法
通过缩放图片像素来减少图片占用内存大小。
+方式一
publicstaticvoidcompressBitmapToFile(Bitmapbmp,Filefile){
//尺寸压缩倍数,值越大,图片尺寸越小
intratio=2;
//压缩Bitmap到对应尺寸
Bitmapresult=Bitmap.createBitmap(bmp.getWidth()/ratio,bmp.getHeight()/ratio,Config.ARGB_8888);
Canvascanvas=newCanvas(result);
Rectrect=newRect(0,0,bmp.getWidth()/ratio,bmp.getHeight()/ratio);
canvas.drawBitmap(bmp,null,rect,null);
ByteArrayOutputStreambaos=newByteArrayOutputStream();
//把压缩后的数据存放到baos中
press(Bitmap.CompressFormat.JPEG,100,baos);
try{
FileOutputStreamfos=newFileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
}catch(Exceptione){
e.printStackTrace();
}
}
方式二
ByteArrayOutputStreamout=newByteArrayOutputStream();
press(Bitmap.CompressFormat.JPEG,85,out);
floatzoom=(float)Math.sqrt(size*1024/(float)out.toByteArray().length);
Matrixmatrix=newMatrix();
matrix.setScale(zoom,zoom);
Bitmapresult=Bitmap.createBitmap(image,0,0,image.getWidth(),image.getHeight(),matrix,true);
out.reset();
press(Bitmap.CompressFormat.JPEG,85,out);
while(out.toByteArray().length>size*1024){
System.out.println(out.toByteArray().length);
matrix.setScale(0.9f,0.9f);
result=Bitmap.createBitmap(result,0,0,result.getWidth(),result.getHeight(),matrix,true);
out.reset();
press(Bitmap.CompressFormat.JPEG,85,out);
}
缩放法其实很简单,设定好matrix,在createBitmap就可以了。
但是我们并不知道缩放比例,而是要求了图片的最终大小。
直接用大小的比例来做的话肯定是有问题的,用大小比例的开方来做会比较接近,但是还是有差距。
但是只要再做一下微调应该就可以了,微调的话就是修改过的图片大小比最终大小还大的话,就进行0.8的压缩再比较,循环直到大小合适。
这样就能得到合适大小的图片,而且也能比较保证质量。
4、JNI调用libjpeg库压缩
JNI静态调用bitherlibjni.c中的方法来实现压缩Java_net_bither_util_NativeUtil_compressBitmap
net_bither_util为包名,NativeUtil为类名,compressBitmap为native方法名,我们只需要调用saveBitmap()方法就可以,bmp需要压缩的Bitmap对象,quality压缩质量0-100,fileName压缩后要保存的文件地址,optimize是否采用哈弗曼表数据计算品质相差5-10倍。
jstringJava_net_bither_util_NativeUtil_compressBitmap(JNIEnv*env,
jobjectthiz,jobjectbitmapcolor,intw,inth,intquality,
jbyteArrayfileNameStr,jbooleanoptimize){
AndroidBitmapInfoinfocolor;
BYTE*pixelscolor;
intret;
BYTE*data;
BYTE*tmpdata;
char*fileName=jstrinTostring(env,fileNameStr);
if((ret=AndroidBitmap_getInfo(env,bitmapcolor,&infocolor))<0){
LOGE("AndroidBitmap_getInfo()failed!
error=%d",ret);
return(*env)->NewStringUTF(env,"0");;
}
if((ret=AndroidBitmap_lockPixels(env,bitmapcolor,&pixelscolor))<0){
LOGE("AndroidBitmap_lockPixels()failed!
error=%d",ret);
}
BYTEr,g,b;
data=NULL;
data=malloc(w*h*3);
tmpdata=data;
intj=0,i=0;
intcolor;
for(i=0;ifor(j=0;jcolor=*((int*)pixelscolor);
r=((color&0x00FF0000)>>16);
g=((color&0x0000FF00)>>8);
b=color&0x000000FF;
*data=b;
*(data+1)=g;
*(data+2)=r;
data=data+3;
pixelscolor+=4;
}
}
AndroidBitmap_unlockPixels(env,bitmapcolor);
intresultCode=generateJPEG(tmpdata,w,h,quality,fileName,optimize);
free(tmpdata);
if(resultCode==0){
jstringresult=(*env)->NewStringUTF(env,error);
error=NULL;
returnresult;
}
return(*env)->NewStringUTF(env,"1");//success
}
5、质量压缩+采样率压缩+JNI调用libjpeg库压缩结合使用
首先通过尺寸压缩,压缩到手机常用的一个分辨率(1280*960微信好像是压缩到这个分辨率),然后我们要把图片压缩到一定大小以内(比如说200k),然后通过循环进行质量压缩来计算options需要设置为多少,最后调用JNI压缩。
+计算缩放比
/**
*计算缩放比
*@parambitWidth当前图片宽度
*@parambitHeight当前图片高度
*@returnint缩放比
*/
publicstaticintgetRatioSize(intbitWidth,intbitHeight){
//图片最大分辨率
intimageHeight=1280;
intimageWidth=960;
//缩放比
intratio=1;
//缩放比,由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
if(bitWidth>bitHeight&&bitWidth>imageWidth){
//如果图片宽度比高度大,以宽度为基准
ratio=bitWidth/imageWidth;
}elseif(bitWidthimageHeight){
//如果图片高度比宽度大,以高度为基准
ratio=bitHeight/imageHeight;
}
//最小比率为1
if(ratio<=0)
ratio=1;
returnratio;
}
质量压缩+JNI压缩
/**
*@Description:
通过JNI图片压缩把Bitmap保存到指定目录
*@paramcurFilePath
*当前图片文件地址
*@paramtargetFilePath
*要保存的图片文件地址
*/
publicstaticvoidcompressBitmap(StringcurFilePath,StringtargetFilePath){
//最大图片大小500KB
intmaxSize=500;
//根据地址获取bitmap
Bitmapresult=getBitmapFromFile(curFilePath);
ByteArrayOutputStreambaos=newByteArrayOutputStream();
//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
intquality=100;
press(Bitmap.CompressFormat.JPEG,quality,baos);
//循环判断如果压缩后图片是否大于500kb,大于继续压缩
while(baos.toByteArray().length/1024>maxSize){
//重置baos即清空baos
baos.reset();
//每次都减少10
quality-=10;
//这里压缩quality,把压缩后的数据存放到baos中
press(Bitmap.CompressFormat.JPEG,quality,baos);
}
//JNI保存图片到SD卡这个关键
NativeUtil.saveBitmap(result,quality,targetFilePath,true);
//释放Bitmap
if(!
result.isRecycled()){
result.recycle();
}
}
JNI图片压缩工具类
packagenet.bither.util;
importandroid.graphics.Bitmap;
importandroid.graphics.Bitmap.Config;
importandroid.graphics.BitmapFactory;
importandroid.graphics.Canvas;
importandroid.graphics.Matrix;
importandroid.graphics.Rect;
importandroid.media.ExifInterface;
importjava.io.ByteArrayOutputStream;
importjava.io.File;
importjava.io.FileInputStream;
importjava.io.FileNotFoundException;
importjava.io.IOException;
/**
*JNI图片压缩工具类
*
*@DescriptionTODO
*@Packagenet.bither.util
*@ClassNativeUtil
*/
publicclassNativeUtil{
privatestaticintDEFAULT_QUALITY=95;
/**
*@Description:
JNI基本压缩
*@parambit
*bitmap对象
*@paramfileName
*指定保存目录名
*@paramoptimize
*是否采用哈弗曼表数据计算品质相差5-10倍
*/
publicstaticvoidcompressBitmap(Bitmapbit,StringfileName,booleanoptimize){
saveBitmap(bit,DEFAULT_QUALITY,fileName,optimize);
}
/**
*@Description:
通过JNI图片压缩把Bitmap保存到指定目录
*@paramimage
*bitmap对象
*@paramfilePath
*要保存的指定目录
*/
publicstaticvoidcompressBitmap(Bitmapimage,StringfilePath){
//最大图片大小150KB
intmaxSize=150;
//获取尺寸压缩倍数
intratio=NativeUtil.getRatioSize(image.getWidth(),image.getHeight());
//压缩Bitmap到对应尺寸
Bitmapresult=Bitmap.createBitmap(image.getWidth()/ratio,image.getHeight()/ratio,Config.ARGB_8888);
Canvascanvas=newCanvas(result);
Rectrect=newRect(0,0,image.getWidth()/ratio,image.getHeight()/ratio);
canvas.drawBitmap(image,null,rect,null);
ByteArrayOutputStreambaos=newByteArrayOutputStream();
//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
intoptions=100;
press(Bitmap.CompressFormat.JPEG,options,baos);
//循环判断如果压缩后图片是否大于100kb,大于继续压缩
while(baos.toByteArray().length/1024>maxSize){
//重置baos即清空baos
baos.reset();
//每次都减少10
options-=10;
//这里压缩options%,把压缩后的数据存放到baos中
press(Bitmap.CompressFormat.JPEG,options,baos);
}
//JNI保存图片到SD卡这个关键
NativeUtil.saveBitmap(result,options,filePath,true);
//释放Bitmap
if(!
result.isRecycled()){
result.recycle();
}
}
/**
*@Description:
通过JNI图片压缩把Bitmap保存到指定目录
*@paramcurFilePath
*当前图片文件地址
*@paramtargetFilePath
*要保存的图片文件地址
*/
publicstaticvoidcompressBitmap(StringcurFilePath,StringtargetFilePath){
//最大图片大小500KB
intmaxSize=500;
//根据地址获取bitmap
Bitmapresult=getBitmapFromFile(curFilePath);
ByteArrayOutputStreambaos=newByteArrayOutputStream();
//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
intquality=100;
press(Bitmap.CompressFormat.JPEG,quality,baos);
//循环判断如果压缩后图片是否大于500kb,大于继续压缩
while(baos.toByteArray().length/1024>maxSize){
//重置baos即清空baos
baos.reset();
//每次都减少10
quality-=10;
//这里压缩quality,把压缩后的数据存放到baos中
press(Bitmap.CompressFormat.JPEG,quality,baos);
}
//JNI保存图片到SD卡这个关键
NativeUtil.saveBitmap(result,quality,targetFilePath,true);