Android 图片处理
一、图片压缩
图片有两类压缩:质量压缩和尺寸压缩。质量压缩一般用于上传大图,尺寸压缩一般用于生产缩略图。
图片有三种存在形式:file形式存在硬盘上(二进制形式),网络传输时是stream(二进制形式),内存中是stream或bitmap。
质量压缩只能影响file大小:把一个file转成bitmap再转成file,或者直接将一个bitmap转成file,降低的图片质量但图片宽高不变,最终file被压缩。因为bitmap在内存中的大小是按像素计算的,尺寸压缩时减小了图片的宽高修改了采样率,所以影响了bitmap,最终file也被压缩。
图片是有色相,明度,饱和度构成的
质量压缩时,BitmapFactory.decodeFile到内存中,变成Bitmap时,它的像素宽高不变,该方法会让图片重新构造, 但是有可能图像的位深(即色深)和每个像素的透明度会变化,以jpeg格式压缩后原图中的透明元素将消失,以png压缩后原图的透明元素不消失。
文件形式和流形式对图片大小没影响,如果图片是100k,那么通过流形式读到内存中要占100k内存,如果是bitmap形式,则要远大于100k。
读取三种形式大小的方法:
1.文件形式(即以二进制形式存在硬盘上),file.length()
2.流形式(二进制心事存内存),读到内存中的byte数:newFileInputStream(File).available()
3.Bitmap形式,bitmap.getByteCount()
尺寸压缩步骤:
1,获取原始图片的宽高
BitmapFactory.Options options = new BitmapFactory.Options();
Options.injustDecodeBounds = true;
BitmapFactory.decodeFile(filePath,options);
int width = options.outWidth;
int height = options.outHeight;
2.计算压缩比例
根据一定规则算法设定压缩比例,(inSampleSize=1表示宽高不缩放)
Options.inSampleSize = 3;
有一类比较快的压缩算法: 图片压缩倍数 = (宽度压缩倍数 + 高度压缩倍数)/ 2
即:ratio = (int)((srcWidth / targetWidth + srcHeight / targetHeight) / 2)
3.缩放图片并压缩
Options.inSampleSize=3;
Options.inJustDecodeBounds=false;
Bitmap bitmap=BitmapFactory.decodeFile(filePath,options);
ByteArrayOutputStream baos=new ByteArrayOutputStream();
Bitmap.compress(Bitmap.CompressFormat.JPEG,60,baos);
按质量压缩示例:
Private Bitmap compressImage(Bitmap bitmap){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//压缩函数,把压缩后的数据存放到baos中
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int quality=100;//图像质量
while(baos.toBytetoArray().length / l024 > 100){//判断是否大于100kb
baos.reset();//重置清空
qualiy -= 10;
bitmap.compress(Bitmap.CompressFormat.JPEG, quality ,baos);
//把压缩后的数据存入bais
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
//生成图片
Bitmap newBitmap = BitmapFactory.decodeStream(bais, null, null);
Return newBitmap;
}
}
尺寸压缩:示例一:
Private Bitmap compressImage(String srcPath){
BitmapFactory.Options newOpts = new BitmapFactory.Options();
//设置为只读去图片的边框
newOpts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);//此时返还bitmap为空
// 设置读取图片的内容
newOpts.inJustDecodeBounds = false;
//原宽高
int srcWidth = newOpts.outWidth;
int srcHeight = newOpts.outHeight;
// 目标宽高
Float targetWidth;
Float targetHeight;
Int ratio = 1;// 缩放值
// 缩放算法
If(srcWidth > srcHeight && srcWidth > targetWidth){
Ratio = (int) (srcWidth / targetWidth);
}else if(srcHeight > srcWidth && srcHeight > targetHeight){
Ratio = (int)(srcHeight / targetHeight);
}
If(ratio <=0 ){
Ratio = 1;
}
newOpts.inSampleSize = ratio;// 设置压缩比
bitmap = BitmapFactory.decodeFile(srcPath ,newOpts);// 进行尺寸压缩
return bitmap;
}
尺寸压缩:示例二:
Private Bitmap compress(Bitmap srcBitmap){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
srcBitmap.compress(Bitmap.CompressFormat.JPEG, 100 ,baos);
// 大于1MB,进行压缩,防止BitmapFactory.decodeStream溢出
if(baos.toByteArray().length / 1024 > 1024){
baos.reset();//重置清空
srcBitmap.compress(Bitmap.CompressFormat.JPEG, 50, baos);
}
BytaArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
BitmapFactory.Options newOpts = new BitmapFactory.Options();
//只读图片边框
newOpts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeStream(bais, null, newOpts);
newOpts.inJustDecodeBounds= false;
//原宽高
Int srcWidth = newOpts.outWidth;
Int srcHeight = newOpts.outHeight;
// 目标宽高
Float targetHeight;
Float targetWidth;
// 自定义缩放值算法
// 缩放值
Int ratio = 1;
newOpts.inSampleSize = ratio;// 缩放比例
// inJustDecodeBounds = false ,读取图片内容
//降低图片从ARGB888到RGB565,默认Config. ARGB_8888
newOpts.inPreferredConfig = Config.RGB_565;
bais = new ByteArrayInputStream(baos.toByteArray());
bitmap = BitmapFactory.decodeStream(bais , null ,newOpts);
return bitmap;
}
二、修改图片宽高
Android2.2提供了实现:Bitmap resizeBitmap =
ThumbnailUtils.extractThumbnail(bitmap,newWidth, newHeight);
考虑兼容2.2一下版本,可以如下实现:
// 获取原图片宽高
Int srcWidth = bitmap.getWidth();
Int srcHeight = bitmap.getHeight();
// 计算缩放比例
Float scaleWidth =((float)newWidth)/srcWidth;
Float scaleHeight =((float)newHeight)/srcHeight;
// 取得想要缩放的matrix参数
Matrix matrix = new Matrix();
Matrix.postScale(scaleWidth, scaleHeight);
// 创建新图片
BitmapnewBitmap=Bitmap.createBitmap(bitmap,0,0,srcWidth,srcHeight,matrix,true);
三、Bitmap的计算
Bitmap大小=图宽(px)* 图高(px) * 单位像素占用的字节数
单位像素占用的字节数由图片的色彩模式决定。
BitmapFactory.Options.inPreferredConfig配置图片的色彩模式。共4中:ARGB,透明度、红色、绿色、蓝色。
色彩模式 | 单位像素占用字节数 | 特点 |
ALPHA_8 | 1 | 只有透明度,没有颜色,类似遮罩,基本不用这个参数 |
ARGB_4444 | 2 | Argb各占4位,4*4/8=2,占2个字节 |
ARGB_8888 | 4 | Argb各占8位,高质量图片的格式,是android默认的格式 |
RGB_565 | 2 | 没有透明度,比如jpeg没有透明度时候是个不错的格式 |
通常使用完后调用Bitmap.recycle();或者等待GC进程自动回收。
压缩图片的原因:避免占用内存过多,图片太大浪费流量。Bitmap在内存中的大小只和图片的尺寸和色彩模式有关。改变图片尺寸,改变色彩模式,改变图片质量都可以压缩图片。
示例质量压缩:
Public static Bitmap compressQuality(Bitmapbitmap){
ByteArrayOutputStreambaos = new ByteArrayOutputStream();
Intratio = 100;
Bitmap.compress(Bitmap.CompressFormat.JPEG,100,baos);
While(baos.size()/1024/1024> 100){//是否大于100kb
baos.reset();
ratio -= 10;
bitmap.compress(Bitmap.CompressFormat.JPEG,ratio , baos);
}
ByteArrayInputStreambais = new ByteArrayInputStream(baos.toByteArray());
Bitmap newBitmap= BitmapFactory.decodeStream(bais);
If(newBitmap !=null){
Return newBitmap;
}else{
Return bitmap
}
}
尺寸压缩示例:
Public static Boolean getCacheImage(StringfilePath , String cachePath){
OutputStreamos = null;
BitmapFactory.Optionsoption = new BitmapFactory.Options();
Option.inJustDecodeBounds= true;// 只读尺寸,不读内容
Bitmapbitmap = BitmapFactory.decodeFile(filePath,option);
Option.inJustDecodeBounds= false;
IntsrcWidth = option.outWidth;
IntsrcHeight=option.outHeight;
InttargetWigth;
InttargetHeight;
//压缩比例
Intratio = 1;
//自定义
Option.inSampleSize=ratio;
Bitmap=BitmapFactory.decodeFile(filePath,option);
Try{
Os= new FileOutputStream(new File(cachePath));
}catch(IOExceptione){
e.printStackTrace();
}
Returnbitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
}
综合示例:
Public static File scal(Uri fileUri){
Stringpath = fileUri.getPath();
FileoutputFile = new File(path);
LongfileSize = outputFile.length();
Finallong fileMaxSize = 200 * 1024;// 200k
If(fileSize>= fileMaxSize){
BitmapFactory.Optionsopts = new BitmapFactory.Options();
Opts.inJustDecodeBounds= true;
BitmapFactory.decodeFile(path,opts);
//原宽高
IntsrcWidth = opts.outWidth;
IntsrcHeight = opts.outHeight;
Doublescale = Math.sqrt((float)fileSize / fileMaxSize);
Opts.outWidth=(int)(height/ scale);
Opts.outHeight= (int)(width / scale);
Opts.inSampleSize= (int)(scale+0.5);
Opts.inJustDecodeBounds= false;
Bitmapbitmap = BitmapFactory.decodeFile(path,opts);
outputFile= new File(PhotoUtil.createImageFile().getPath());
FileOutputStreamfos = null;
Try{
Fos = newFileOutputStream(outputFile);
Bitmap.compress(Bitmap.CompressFormat.JPEG, 50 , fos);
fos.close();
}catch(IOException e){
e.printStackTrace();
}
If(!bitmap.isRecycled()){
Bimap.recycle();
}else{
File tempFile =outputFile;
outputFile = new File(PhotoUtil.createImageFile().getPath());
PhotoUtil.copyFileUsingFileChannels(tempFile, outputFile);
}
}
ReturnoutputFile;
}
Public static Url createImageFile(){
//createan image file name
StringtimeStamp = new SimpleDateFormat(yyyyMMdd_HHmms).format(new Date());
StringimageFileName = JPEG_ + timestamp + _;
FilestorageDir = Environment.getExternalStoragePublicDirecoty(
Environment.DIRECTORY_PICTURES);
Fileimage = null;
Try{
Image= File.createTempFile(imageFileName , jpg , storageDir);
}catch(IOExceptione){
e.printStackTrace();
}
//savea file: path for use with ACTION_VIEW intents
ReturnUri.fromFile(image);
}
Public static void copyFileUsingFileChannels(FilescrFile , File destFile){
FileChannelinputChannel = null;
FileChanneloutputChannel = null;
Try{
Try{
inputChannel= new FileInputStream(srcFile).getChannel():
outputChannel= new FileOutputStream(destFile).getChannel();
outputChannel.transferFrom(inputChannel,0 ,inputChannel.size());
}catch(IOException e){
}
}finally{
Try{
inputChannel.close();
outputChannel.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
四、从资源中获取位图
可使用BitmapDrawable、BitmapFactory获取资源中的位图
Resource res = getResource();
使用BitmapDrawable获取位图
示例1:
InputStream is =res.openRawResource(R.drawable.icon);
BitmapDrawable bmDraw = new BitmapDrawable(is );
Bitmap bitmap = bmDraw.getBitmap();
示例2:
BitmapDrawable bmDraw =(BitmapDrawable)res.getDrawable(R.drawable.icon);
Bitmap bitmap = dmDraw.getBitmap();
使用BitmapFactory创建位图
Bitmap bm = BitmapFactory.decodeStream(InputStream is)
Bitmap bm = BitmapFactory.decodeResource(res , R.drawable.icon)
五、获取位图的信息
获取宽高,是否包含透明度,颜色格式等
ARGB颜色格式使用Bitmap.Config定义
Bitmap.compress()方法颜色图片,支持PNG,JPEG格式的压缩,其他格式需要android开发者自行实现。
六、显示位图
显示位图可以使用核心类Canvas,通过drawBitmap()显示位图,或者借助BitmapDrawable来将Bitmap绘制到Canvas。可通过BitmapDrawable将位图显示的View。
示例1BitmapDrawable显示位图:
//获取位图
Bitmap bmp = BitmapFactory.decodeResource(res, R.drawable.pic);
// 转换为BitmapDrawable对象
BitmapDrawable bmpDraw = new BitmapDrawable(bmp);
// 显示位图
ImageView iv = (ImageView)findViewById(R.id.ImageView1);
iv.setImageDrawable(bmpDraw);
示例2Canvas显示位图:
Class panel extends View{
Publicpanel(Context context){
Super(context);
}
Public voidonDraw(Canvas canvas){
Bitmap bmp =BitmapFactory.decodeResource(getResources(), R.drawable.pic);
Canvas.drawColor(Color.Black);
Canvas.drawBitmap(bmp , 10 ,10 , null);
}
}
七、位图缩放
1. 将一个位图按照需求重画一遍,画后的位图就是我们需要的了,与位图的显示几乎一样:drawBitmap(Bitmap bitmap, Rect src, Rect dst,Paint paint)。
2. 在原有位图的基础上,缩放原位图,创建一个新的位图:CreateBitmap(Bitmap source, int x, int y,int width, int height,Matrix m, boolean filter)
3. 借助Canvas的scale(floatsx, float sy),不过要注意此时整个画布都缩放了。
4. 借助Matrix:
Bitmap bmp =BitmapFactory.decodeResource(getResources(),R.drawable.pic);
Matrix matrix=new Matrix();
matrix.postScale(0.2f,0.2f);
Bitmap dstbmp=Bitmap.createBitmap(bmp,0,0,bmp.getWidth(),bmp.getHeight(),matrix,true);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(dstbmp,10, 10, null);
八、位图旋转
位图的旋转也可以借助Matrix或者Canvas来实现。Matrix在线性代数中都学习过,Android SDK提供了Matrix类,可以通过各种接口来设置矩阵。结合上面的例子程序,将位图缩放例子程序在显示位图的时候前,增加位图旋转功能,修改代码如下:
Matrix matrix = new Matrix();
//matrix.postScale(0.5f,0.5f);
matrix.setRotate(90,120,130);
canvas.drawBitmap(mbmpTest,matrix, mPaint);
除了这种方法之外,我们也可以在使用Bitmap提供的函数如下:
public staticBitmap createBitmap (Bitmapsource, int x, int y, int width, int height, Matrixm, boolean filter),在原有位图旋转的基础上,创建新位图。
九、回收图片内存
当用到大量图片而又没有做好图片内存回收,则容易造成OOM异常。可以在ActivityonDestroy中释放内存,将viewgroup(viewFipper、viewpager)中的view remove。
Private void recycleBitmap(){
If(viewGroup!= null){
Intcount = viewGroup.getChildCount;
For(int i=0; i < count ; i++){
View view =viewGroup.getChildAt(i);
ImageView img =(ImageView)view.findViewById(R.id.image1);
If(img ! = null){
Drawable drawable =img.getDrawable();
If(drawable != null){
If(drawable instanceof BitmapDrawable){
BitmapDrawablebitmapDrawable = (BitmapDrawable)drawable;
Bitmap bitmap =bitmapDrawable.getBitmap();
If(bitmap != null){
Bitmap.recycle();
}
}
}
}
}
}
}
Bitmap之大图片优化
Public Bitmap getBitmapFromNet(final Stringurl,final int width, final int height ){
BitmapFactory.Options opt = new BitmapFactory.Options();
Opt.inPurgeable= true;
Opt.inPutShareable=true;
Opt.inPreferredConfig= Bitmap.Config.RGB_565;
Opt.outWidth= width;
Opt.outHeight=height;
Opt.inJustDecodeBounds=true;//只读宽高
intyRatio = (int)Math.ceil(opt.outHeight/height);
int xRatio = (int)Math.ceil(opt.outWidth/width);
if (yRatio > 1 || xRatio > 1){
if (yRatio > xRatio) {
opt.inSampleSize = yRatio; //缩放值
} else {
opt.inSampleSize = xRatio;
}
}
opt.inJustDecodeBounds = false;//还原图片
Bitmap bitmap = BitmapFactory.decodeStream(newURL(url).openStream(),null,opt);
cache.put(url,new SoftReference<Bitmap>(bitmap));//保存缓存--------->缓存Map
return bitmap;
}