android图片压缩

非原创,只是整理,如果里面发现引用的内容没有标识出来,欢迎指出。

一、基本知识

(1)两种图片:

1)矢量图:

矢量图又叫向量图,是用一系列计算机指令来描述和记录一幅图,一幅图可以解为一系列由点、线、面等到组成的子图,它所记录的是对象的几何形状、线条粗细和色彩等。生成的矢量图文件存储量很小,无论放大和缩小多少倍,图形都有一样的平滑边缘和清晰的视觉效果。

例如,输入法的文字就是矢量图。

2)点阵图(位图):

就是图象由无数个点(像素)组成。每个像素里都由一个颜色表现,所以点阵图是有一个个有颜色的点(像素)排列而成。我们平时看到的文件格式有PSD、TIF、JPG、GIF等都是点阵图,数码相机拍摄的照片就是点阵图。

(2)像素(pixel简称px):

在位图中,图片是由很多点组成,那每个点就是1个像素。像素是屏幕中可以显示的最小元素单元,我们应用里任何可见的东西都是由一个个像素点组成的。单独一个像素点非常的微小,肉眼是无法看见的,可是当许许多多的像素点聚集到一起时,就可以拼接成五彩缤纷的图案。

(3)屏幕尺寸

屏幕尺寸指屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米

比如常见的屏幕尺寸有2.4、2.8、3.5、3.7、4.2、5.0、5.5、6.0等

(4)分辨率:

图像分辨率:图像分辨率是指每英寸图像内的像素点数。这里的分辨率其实是密度,是指1英寸里有多少像素。它以  像素/英尺(ppi)为单位,如一个图像的分辨率为72ppi,就表示该图像中每英尺包含72个像素。(但是现在通常将分辨率表示成每一个方向上的像素数量,比如 640X480 等。)

屏幕分辨率:屏幕分辨率是指在横纵向上的像素点数,单位是px,1px=1个像素点,每个屏幕有自己的分辨率。一般以纵向像素*横向像素,如1960*1080。屏幕分辨率越高,所呈现的色彩越多,清晰度越高。

(5)色彩模式

图像的大小和质量不仅仅由像素总数决定,还跟色彩模型有关。色彩模型有RGB模式,CMYK模式,位图模式,灰度模式等等。

(6)图片大小

通过一个例子来理解:

选择一个24位深度分辨率为225×225的位图,

由于24位位图是真彩色,没有颜色表,所以

其文件大小为:152154=14+40+(225×3+1)×225

注:因为1个像素用三个字节来表示,所以乘以3;因为位图存储时,Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充,225×3+1刚好是4的倍数;14和40是位图文件头和位图信息头的字节大小;

当利用绘图程序将文件保存为256色,即8位深度的位图后,文件大小变为52378字节:

52378 = 14 + 50 + 256 × 4 + (225 + 3)× 225

注:256色位图有颜色表,每个颜色表结构体是4字节,所以颜色表这一项占256*4 个字节;225+3 刚好凑成4的倍数

可见分辨率没变,因为位深从24变成了8。所以图像质量下降了,图像大小也变小了。

二、图片的存在形式:

1.文件形式(即以二进制形式存在于硬盘上)

获取大小(Byte):File.length()

2.流的形式(即以二进制形式存在于内存中)

获取大小(Byte):new FileInputStream(File).available()

3.Bitmap形式

获取大小(Byte):Bitmap.getByteCount()

这三种形式的区别:

文件形式和流的形式对图片体积大小并没有影响,也就是说,如果你手机SD卡上的如果是100K,那么通过流的形式读到内存中,也一定是占100K的内存,注意是流的形式,不是Bitmap的形式。当图片以Bitmap的形式存在时,其占用的内存会变大。

三、bitmap介绍:

(1) Bitmap.Config

一张图片Bitmap所占用的内存 =图片长度 x 图片宽度 x 一个像素点占用的字节数

而Bitmap.Config,正是指定单位像素占用的字节数的重要参数。

其中,A代表透明度;R代表红色;G代表绿色;B代表蓝色。

ALPHA_8

表示8位Alpha位图,即A=8,一个像素点占用1个字节,它没有颜色,只有透明度

ARGB_4444

表示16位ARGB位图,即A=4,R=4,G=4,B=4,一个像素点占4+4+4+4=16位,2个字节

ARGB_8888

表示32位ARGB位图,即A=8,R=8,G=8,B=8,一个像素点占8+8+8+8=32位,4个字节

RGB_565

表示16位RGB位图,即R=5,G=6,B=5,它没有透明度,一个像素点占5+6+5=16位,2个字节

Bitmap.Config主要作用是:

以何种方式像素存储。不同的配置将会影响图像的画质(色彩深度),位数越高画质越高,显然在这里ARGB_8888是最占内存的。当然,画质越高也就越占内存了。

Tips:由于ARGB_4444的画质惨不忍睹,一般假如对图片没有透明度要求的话,可以改成RGB_565,相比ARGB_8888将节省一半的内存开销。


配置不同Bitmap.Config在相同分辨率下的占用内存情况:

一张图片Bitmap所占用的内存 = 图片长度 x 图片宽度 x 一个像素点占用的字节数

在Android里面可以通过下面的代码来设置解码率

(2) Bitmap.CompressFormat

从字面上理解,它的含义是:Bitmap压缩格式

其实这个参数很简单,就是指定Bitmap是以JPEG、PNG还是WEBP格式来压缩

(3) Bitmap.compress()方法

重磅方法来了,通过这个方法,可以实现图片的压缩。使用该方法需要传三个参数进去:

CompressFormat

指定Bitmap的压缩格式,可选择JPEG、PNG、WEBP

int类型的quality

指定Bitmap的压缩品质,范围是0 ~ 100;该值越高,画质越高。0表示画质最差,100画质最高。

OutputStream

指定Bitmap的字节输出流。一般使用:

ByteArrayOutputStream stream = new ByteArrayOutputStream();

这个方法不改变图片分辨率,只通过改变编码格式来改变每个像素大小,从而改变图片大小和质量。

四、BitmapFactory

BitmapFactory是获取Bitmap和压缩Bitmap的重要类,下面开始介绍BitmapFactory几个重要的成员变量和方法:

(1)通过BitmapFactory解码(获取)Bitmap的几种方式

1)decodeFile()//从SD卡文件读取

Bitmap  bm=BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/photo.jpg");

2)decodeResource()//从资源文件res读取

Bitmap bm=BitmapFactory.decodeResource(this.getResources(), R.mipmap.test_pic);

3)decodeStream()//从输入流读取

Bitmap bm=BitmapFactory.decodeStream(inputStream);

4)decodeByteArray()//从字节数组读取

Bitmap bm=BitmapFactory.decodeByteArray(bytes,0,bytes.length);


(2)BitmapFactory.Options

BitmapFactory在使用方法decodeFile()、decodeResource()解码图片时,可以指定它的BitmapFactory.Options。

这个参数作用非常大,它可以设置Bitmap的采样率,通过改变图片的宽度、高度、缩放比例等,以达到降低图片的像素的目的,这样可以做到图片压缩,减少Bitmap的内存

下面列出BitmapFactory.Options的部分成员变量:


in开头的代表的就是设置某参数;out开头的代表的就是获取某参数。比如,inSampleSize就是设置Bitmap的缩放比例、outWidth就是获取Bitmap的高度。

1) inJustDecodeBounds 设置只去读图片的附加信息(宽高),不去解析真实的Bitmap

从字面上理解,它的含义是:”设置仅解码Bitmap的边界”。那它真正的作用是啥呢?

当inJustDecodeBounds设置为true的时候,BitmapFactory通过decodeResource或者decodeFile解码图片时,将会返回空(null)的Bitmap对象,这样可以避免Bitmap的内存分配,但是它可以返回Bitmap的宽度、高度以及MimeType。


这样做的意义就在于,可以先不用产生Bitmap内存,从而获得图片的宽高信息,尽可能的做到节约内存。

2)inSampleSize 设置图片的缩放比例(宽和高)

这里注意,google推荐用2的倍数:

在这里着重讲一下这个inSampleSize。从字面上理解,它的含义是:”设置取样大小“。

它的作用是:设置inSampleSize的值(int类型)后,假如设为4,则宽和高都为原来的1/4,宽高都减少了,自然内存也降低了。

如图所示:

在这里参考Google官方文档来解释:

http://developer.android.com/intl/zh-cn/training/displaying-bitmaps/load-bitmap.html#load-bitmap

如何理解”设置取样大小“呢?如果你认真看了上面的内容话,聪明的你一定知道,肯定需要配合inJustDecodeBounds,先获取图片的宽、高【这个过程就是取样】,然后通过获取的宽高,动态的设置inSampleSize的值。

【当然,你也可以不动态,可以写死inSampleSize的值。比如设置inSampleSize = 4的话,一张分辨率为2048x1536px的图像将使用inSampleSize值为4的设置来解码,产生的Bitmap大小约为512*384px。相较于完整图片占用12M的内存,这种方式只需0.75M内存(假设Bitmap配置为ARGB_8888)。】

四、两种压缩方式:

(1)质量压缩:

将图片保存到本地时进行压缩, 即将图片从Bitmap形式变为File形式时进行压缩

该方法是利用compress方法。压缩图片的质量, 它不会减少图片的像素。

比方说, 你的图片是300K的, 1280*700像素的, 经过该方法压缩后, File形式的图片是在100k以下, 但是你BitmapFactory.decodeFile到内存中,变成Bitmap时,它的像素仍然是1280*700。计算图片像素的方法是 bitmap.getWidth()和bitmap.getHeight()。

(2)取样压缩

通过调整inSampleSize的值,在图片从本地读到内存时,进行压缩 ,即图片从File形式变为Bitmap形式。这种方式通过改变图像的尺寸(像素总数)来压缩,每个像素所占用的大小不变。

(3)常用的图片压缩方法是将两者结合。


参考资料:

http://blog.sina.com.cn/s/blog_9e1e8c1301015xat.html

http://www.tuicool.com/articles/eue6z2

http://anany.me/2015/10/15/bitmap1/

https://developer.android.com/training/displaying-bitmaps/load-bitmap.html

http://www.bubuko.com/infodetail-1041128.html

推荐阅读更多精彩内容