Android 图片处理

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;

}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值