Android 图片压缩,Bitmap优化

本文介绍了Bitmap导致内存溢出的问题及解决方案,包括对图片质量与尺寸的压缩方法。详细讲解了通过调整颜色模式和inSampleSize来减少Bitmap占用的空间,提供了具体的代码实现。

Bitmap是导致OOM的一个最重要的问题。通常我们会对图片进行压缩来避免OOM。下面总结了几种对图片进行压缩的方法。

对图片进行压缩,无外乎两种方法:对图片质量进行压缩、对图片尺寸进行压缩。我在网上看了许多博客,那些所谓的高质量压缩无非就是用了这两种方法。我们要根据情况,合理的选择方法,才能达到最好的效果。

对图片质量的压缩,是对图片file大小的压缩,这可以保证图片占空间大小改变,但是这种方法压缩并不能使我们避免OOM。

图片的存在形式有三种:file、stream、bitmap、drawable。而影响OOM的是Bitmap。我们对file进行压缩,并不会影响到图片的像素,而Bitmap是根据像素计算的。

对Bitmap操作时,常修改的是颜色模式以及options.inSampleSize。

原因如下:

颜色模式:

ALPHA_8 会将每一个像素存储为单个的透明原色,只用八位存储了透明度 
ARGB_4444 过时的,因为严重影响了图片质量,所以被弃用,用ARGB_888代替 
ARGB_8888 每个像素点被存储为4个字节 
RGB_565 每个像素点被存储为2个字节,只存储 RGB 三原色,5位存储红原色(32种编码),5位存储蓝原色(三十二种编码),6位存储绿原色(64种编码)

不同的颜色模式意味着透明度、以及每个像素点所占字节的大小,因此对透明度没有需求的情况下我们使用RGB565,这样每个像素所占空间就会减小。

inSampleSize:

这个属性是采样率的意思, inSampleSize 这个属性只认2的整数倍为有效,比如你将 inSampleSize 赋值为2,那就是每隔2行采1行,每隔2列采一列,那你解析出的图片就是原图大小的1/4。

下面是对图片压缩的一些总结:

package com.xiaoqi.bitmapcompressutils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

/**
 * Created by xiaoqi on 2016/8/15.
 */
public class CompressUtils {
	/**
	 * 按质量压缩
	 * @param bitmap
	 * @return
	 */
		public static Bitmap compressImage(Bitmap bitmap){
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
			bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
			int options = 100;
			//循环判断如果压缩后图片是否大于100kb,大于继续压缩
			while ( baos.toByteArray().length / 1024>100) {
				//清空baos
				baos.reset();
				bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);
				options -= 10;//每次都减少10
			}
			//把压缩后的数据baos存放到ByteArrayInputStream中
			ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
			//把ByteArrayInputStream数据生成图片
			Bitmap newBitmap = BitmapFactory.decodeStream(isBm, null, null);
			return newBitmap;
		}

	/**
	 * 按图片尺寸压缩 参数为路径
	 * @param imgPath 图片路径
	 * @param pixelW 目标图片宽度
	 * @param pixelH 目标图片高度
	 * @return
	 */
	public static Bitmap compressImageFromPath(String imgPath, int pixelW, int pixelH) {
		BitmapFactory.Options options = new BitmapFactory.Options();
		// 开始读入图片,此时把options.inJustDecodeBounds 设回true,即只读边不读内容
		options.inJustDecodeBounds = true;
		options.inPreferredConfig = Bitmap.Config.RGB_565;
		BitmapFactory.decodeFile(imgPath,options);
		options.inJustDecodeBounds = false;
		options.inSampleSize = computeSampleSize(options , pixelH > pixelW ? pixelH : pixelW ,pixelW * pixelH );
		Bitmap bitmap = BitmapFactory.decodeFile(imgPath, options);
		return bitmap;
	}

	/**
	 * 按图片尺寸压缩 参数是bitmap
	 * @param bitmap
	 * @param pixelW
	 * @param pixelH
	 * @return
	 */
	public static Bitmap compressImageFromBitmap(Bitmap bitmap, int pixelW, int pixelH) {
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
		if( os.toByteArray().length / 1024>1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
			os.reset();
			bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);//这里压缩50%,把压缩后的数据存放到baos中
		}
		ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		options.inPreferredConfig = Bitmap.Config.RGB_565;
		BitmapFactory.decodeStream(is, null, options);
		options.inJustDecodeBounds = false;
		options.inSampleSize = computeSampleSize(options , pixelH > pixelW ? pixelH : pixelW ,pixelW * pixelH );
		is = new ByteArrayInputStream(os.toByteArray());
		Bitmap newBitmap = BitmapFactory.decodeStream(is, null, options);
		return newBitmap;
	}

	/**
	 * 对图片进行缩放指定大小
	 * @param bitmap
	 * @param width
	 * @param height
	 * @return
	 */
	public static Bitmap scaleTo(Bitmap bitmap, int width, int height){
		int originalWidth = bitmap.getWidth();
		int originalHeight = bitmap.getHeight();
		float xScale = (float)width / originalWidth;
		float yScale = (float)height / originalHeight;
		Matrix matrix = new Matrix();
		matrix.setScale(xScale,yScale);
		return Bitmap.createBitmap(bitmap, 0, 0, originalWidth, originalHeight, matrix, true);
	}

	/**
	 * 以最省内存的方式读取本地资源的图片
	 * @param context
	 * @param resId
	 * @return
	 */
	public static Bitmap readBitmap(Context context, int resId) {
		BitmapFactory.Options opt = new BitmapFactory.Options();
		opt.inPreferredConfig = Bitmap.Config.RGB_565;
		opt.inPurgeable = true;
		opt.inInputShareable = true;
		// 获取资源图片
		InputStream is = context.getResources().openRawResource(resId);
		return BitmapFactory.decodeStream(is, null, opt);
	}

	/**
	 * android源码提供给我们的动态计算出图片的inSampleSize方法
	 * @param options
	 * @param minSideLength
	 * @param maxNumOfPixels
	 * @return
	 */
	public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
		int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
		int roundedSize;
		if (initialSize <= 8) {
			roundedSize = 1;
			while (roundedSize < initialSize) {
				roundedSize <<= 1;
			}
		} else {
			roundedSize = (initialSize + 7) / 8 * 8;
		}
		return roundedSize;
	}

	private static int computeInitialSampleSize(BitmapFactory.Options options,int minSideLength, int maxNumOfPixels) {
		double w = options.outWidth;
		double h = options.outHeight;
		int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
		int upperBound = (minSideLength == -1) ? 128 :(int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
		if (upperBound < lowerBound) {
			return lowerBound;
		}
		if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
			return 1;
		} else if (minSideLength == -1) {
			return lowerBound;
		} else {
			return upperBound;
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值