图片压缩优化实战
一、前言:
Android中图片有四种属性,分别是:
ALPHA_8:每个像素占用1byte内存
ARGB_4444:每个像素占用2byte内存
ARGB_8888:每个像素占用4byte内存 (默认)
RGB_565:每个像素占用2byte内存
Android默认的颜色模式为ARGB_8888,这个颜色模式色彩最细腻,显示质量最高。但同样的,占用的内存也最大。 所以在对图片效果不是特别高的情况下使用RGB_565(565没有透明度属性)
Android目前常用的图片格式有png,jpeg和webp,
png:无损压缩图片格式,支持Alpha通道,Android切图素材多采用此格式
jpeg:有损压缩图片格式,不支持背景透明,适用于照片等色彩丰富的大图压缩,不适合logo
webp:是一种同时提供了有损压缩和无损压缩的图片格式,派生自视频编码格式VP8,从谷歌官网来看,无损webp平均比png小26%,有损的webp平均比jpeg小25%~34%,无损webp支持Alpha通道,有损webp在一定的条件下同样支持,有损webp在Android4.0(API 14)之后支持,无损和透明在Android4.3(API18)之后支持
二、图片压缩优化实战
第一步、修改采样率
涉及BitmapFactory.Options类
BitmapFactort.Options这个是什么鬼呢, 很重要!bitmap加载的配置类,想要做图片内存优化是少不了跟它打“打交道”,如下其内部属性
这里我们大概只说跟图片优化相关的几个重要属性
insampleSize :采样率,默认1表示无缩放,等于2表示宽高缩放2倍,总大小缩小4倍;
inBitmap :被复用的bitmap;
inJustDecodeBound :如果设置为true,不获取图片,不分配内存,但会返回图片的高度宽度信息;
inMutable :是否图片内容可变,如果Bitmap复用的话,需要设置为true;
inDensity :加载bitmap时候对应的像素密度(后面会讲到);
inTargetDensity :bitmap位图被真实渲染出的像素密度,对应终端设备的屏幕像素密度(后面会讲到);
第二步、修改bitmap的宽高
现在手机像素越来越高 所以控制一个bitmap的目标宽高值很重要,我的项目中的 newWidth、newHeight设置都是1280、1280自己可以依据项目来设置这个值
/**
* 等比缩放bitmap宽高
* @param bm
* @param newWidth
* @param newHeight
* @return
*/
public Bitmap setImgSize(Bitmap bm, int newWidth, int newHeight) {
// 获得图片的宽高.
int width = bm.getWidth();
int height = bm.getHeight();
//如果之前图片的宽高 小于目标值就不缩放了
if (width < newWidth || height < newHeight) {
return bm;
}
// 计算缩放比例.
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
float maxScale = Math.max(scaleWidth, scaleHeight);
// 取得想要缩放的matrix参数.
Matrix matrix = new Matrix();
matrix.postScale(maxScale, maxScale);
// 得到新的图片.
Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
return newbm;
}
第三步、修改压缩质量
public boolean compress(CompressFormat format, int quality, OutputStream stream)
quality 为0-100 一般项目中设置为80 -100 我的项目中设置为90 图片质量效果也挺好
三、优化实战代码
整个三个步骤的流程代码我发出来可以借鉴一下
File compress() throws IOException {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = computeSize();
Bitmap tagBitmap = BitmapFactory.decodeStream(srcImg.open(), null, options);
tagBitmap = setImgSize(tagBitmap, 1280, 1280);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
if (isAutoRotating) {
if (Checker.SINGLE.isJPG(srcImg.getMedia().getMimeType())) {
boolean isCut = srcImg.getMedia().isCut() && !TextUtils.isEmpty(srcImg.getMedia().getCutPath());
String url = isCut ? srcImg.getMedia().getCutPath() : srcImg.getMedia().getPath();
int degree = PictureMimeType.isContent(url) ? BitmapUtils.readPictureDegree(srcImg.open()) : BitmapUtils.readPictureDegree(context, url);
if (degree > 0) {
tagBitmap = BitmapUtils.rotatingImage(tagBitmap, degree);
}
}
}
if (tagBitmap != null) {
compressQuality = compressQuality <= 0 || compressQuality > 100 ? DEFAULT_QUALITY : compressQuality;
tagBitmap.compress(focusAlpha || tagBitmap.hasAlpha() ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG, compressQuality, stream);
tagBitmap.recycle();
FileOutputStream fos = new FileOutputStream(tagImg);
fos.write(stream.toByteArray());
fos.flush();
fos.close();
stream.close();
return tagImg;
}
return null;
}
/**
* 等比缩放bitmap宽高
* @param bm
* @param newWidth
* @param newHeight
* @return
*/
public Bitmap setImgSize(Bitmap bm, int newWidth, int newHeight) {
// 获得图片的宽高.
int width = bm.getWidth();
int height = bm.getHeight();
//如果之前图片的宽高 小于目标值就不缩放了
if (width < newWidth || height < newHeight) {
return bm;
}
// 计算缩放比例.
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
float maxScale = Math.max(scaleWidth, scaleHeight);
// 取得想要缩放的matrix参数.
Matrix matrix = new Matrix();
matrix.postScale(maxScale, maxScale);
// 得到新的图片.
Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
return newbm;
}
srcImg.open() 是输入流InputStream 就是拍照 图片选择后的图片都转成了InputStream
总结
通过以上三步基本能把几M 的图片都压缩到200-300kb左右满足自己项目的业务需求
当然还有额外的2个压缩方式
Bitmap中有两个重要的内部类 CompressFormat 以及 Config;
下面分别介绍一下这两个类
CompressFormat
CompressFormat 是用来设置压缩方式的,是个枚举类,内部提供了三种图片压缩方式类型,
JPEG :表示Bitmap采用JPEG压缩算法进行压缩,压缩后的格式可以是.jpg或者.png,是一种有损压缩方式。
PNG :表示Bitmap采用PNG压缩算法进行压缩,压缩后的格式可以是.png,是一种无损压缩方式。
WEBP :表示以WebP压缩算法进行图像压缩,压缩后的格式可以是".webp",是一种有损压缩,质量相同的情况下,WebP格式图像的体积要比JPEG格式图像小40%,美中不足的是,WebP格式图像的编码时间“比JPEG格式图像长8倍”, 而且还需要注意,在官方文档中有这样的描述:As of Build.VERSION_CODES.Q, a value of 100 results in a file in the lossless WEBP format. Otherwise the file will be in the lossy WEBP format. 意为Android10之后如果quality值(压缩质量)为100的话,bitmap压缩采用无损压缩格式,其他都为有损压缩;
Config
表示位图像素的存储格式,什么意思呢? 就是bitmap在屏幕上显示的每一像素在内存中存储的格式,会影响Bitmap真实图片的透明度以及图片质量;
Bitmap.Config.ALPHA_8:颜色信息只由透明度组成,占8位;
Bitmap.Config.ARGB_4444:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占4位,总共占16位;
Bitmap.Config.ARGB_8888:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占8位,总共占32位,是Bitmap 默认的颜色存储格式,也是最占空间的一种配置;
Bitmap.Config.RGB_565:颜色信息由R(Red),G(Green),B(Blue)三部分组成,R占5位,G占6位,B占5位,总共占16位;
上面说了 android 系统默认存储位图方式是 ARGB_8888, 4个通道组成,每个通道8位,分表代表透明度和RGB颜色值, 也就是说一个位图像素占用了4个字节(1个byte8个bit位),
同理:采用 Bitmap.Config.RGB_565 存储,单像素占用内存大小仅有2byte,换句话说一张图片采用ARGB_565格式相对于默认的ARGB_8888内存将减少一半,所以通过改变bitmap像素存储方式也是图片内存优化的重要渠道