图片压缩,二次采样

图片压缩就是为了避免我们内存溢出,所有要对一系列进行压缩二次采样等

1.什么是OOM?为什么会引起OOM?

out of Memory(内存溢出)我们都知道Android系统会被每个App分配一个独立的空间,或者说分配一个Dalvik虚拟机(Dalvik是Google公司自己设计用于Android平台的虚拟机。Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一。它可以支持已转换为 .dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且[1]每一个Dalvik应用作为一个独立的Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。)这样每个APP都可以独立运行而不相互影响!而Android对于每个 Dalvik虚拟机都会有一个最大内存限制,如果当前占用的内存加上我们申请的内存资源超过了这个限制 ,系统就会抛出OOM错误!另外,这里别和RAM混淆了,即时当前RAM中剩余的内存有1G多,但是OOM还是会发生!别把RAM(物理内存)和OOM扯到一起!另外RAM不足的话,就是杀应用了,而不是仅仅是OOM了! 而这个Dalvik中的最大内存标准,不同的机型是不一样的


这里可以获取系统分给你App多少内存

ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE); Log.e("HEHE","最大内存:" + activityManager.getMemoryClass());


一、Bitmap:

Bitmap是Android系统中的图像处理的最重要类之一。用它可以获取图像文件信息,进行图像剪切、旋转、缩放等操作,并可以指定格式保存图像文件。

常用方法:

public void recycle()  // 回收位图占用的内存空间,把位图标记为Dead

public final boolean isRecycled()  //判断位图内存是否已释放

public final int getWidth() //获取位图的宽度

public final int getHeight() //获取位图的高度

public final boolean isMutable() //图片是否可修改

public int getScaledWidth(Canvas canvas) //获取指定密度转换后的图像的度

public int getScaledHeight(Canvas canvas) //获取指定密度转换后的图像的度

public boolean compress(CompressFormat format, int quality, OutputStream stream) //按指定的图片格式以及画质,将图片转换为输出流。

format:压缩图像的格式,如Bitmap.CompressFormat.PNG或 ·Bitmap.CompressFormat.JPEG

quality:画质,0-100.0表示最低画质压缩,100以最高画质压缩。对于PNG等无损格式的图片,会忽略此项设置。

stream: OutputStream中写入压缩数据。

return: 是否成功压缩到指定的流。

public static Bitmap createBitmap(Bitmap src)  //以src为原图生成不可变得新图像

public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) //以src为原图,创建新的图像,指定新图像的高宽以及是否可变。

public static Bitmap createBitmap(int width, int height, Config config) //创建指定格式、大小的位图

public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) //以source为原图,创建新的图片,指定起始坐标以及新图像的高宽。

BitmapFactory.Option可设置参数

Option 参数类:

public boolean inJustDecodeBounds //如果设置为true,不获取图片,不分配 内存,但会返回图片的高度宽度信息。

如果将这个值置为true,那么在解码的时候将不会返回bitmap,只会返回这个bitmap 的尺寸。这个属性的目的是,如果你只想知道一个bitmap的尺寸,但又不想将其加载到内存时。这是一个非常有用的属性。

public int inSampleSize //图片缩放的倍数

这个值是一个int,当它小于1的时候,将会被当做1处理,如果大于1,那么就会按照比例(1 / inSampleSize)缩小bitmap的宽和高、降低分辨率,大于1时这个值将会被处置为2的倍数。例如,width=100,height=100,inSampleSize=2,那么就会将bitmap处理为,width=50,height=50,宽高降为1 / 2,像素数降为1 / 4。

public int outWidth //获取图片的宽度值

public int outHeight //获取图片的高度值

表示这个Bitmap的宽和高,一般和inJustDecodeBounds一起使用来获得Bitmap的宽高,但是不加载到内存。

public int inDensity //用于位图的像素压缩比

public int inTargetDensity //用于目标位图的像素压缩比(要生成的位图)

public byte[] inTempStorage  //创建临时文件,将图片存储

public boolean inScaled //设置为true时进行图片压缩,从inDensity到inTargetDensity

public boolean inDither  //如果为true,解码器尝试抖动解码

public Bitmap.Config inPreferredConfig  //设置解码器

这个值是设置色彩模式,默认值是ARGB_8888,在这个模式下,一个像素点占用4bytes空间,一般对透明度不做要求的话,一般采用RGB_565模式,这个模式下一个像素点占用2bytes。

public String outMimeType  //设置解码图像

public boolean inPurgeable //当存储Pixel的内存空间在系统内存不足时是否可以被回收

public boolean inInputShareable  //inPurgeable为true情况下才生效,是否可以共享一个InputStream

public boolean inPreferQualityOverSpeed  //为true则优先保证Bitmap质量其次是解码速度

public boolean inMutable  //配置Bitmap是否可以更改,比如:在Bitmap上隔几个像素加一条线段

public int inScreenDensity  //当前屏幕的像素密度

从资源中获取位图的方式有两种:通过BitmapDrawable或者BitmapFactory

你可以创建一个构造一个BitmapDrawable对象,比如通过流构建BitmapDrawable:

BitmapDrawable bmpMeizi = new BitmapDrawable(getAssets().open("pic_meizi.jpg")); Bitmap mBitmap = bmpMeizi.getBitmap(); img_bg.setImageBitmap(mBitmap);


工厂方法:

public static Bitmap decodeFile(String pathName, Options opts)  //从文件读取图片

public static Bitmap decodeFile(String pathName)

public static Bitmap decodeStream(InputStream is)  //从输入流读取图片

public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)

public static Bitmap decodeResource(Resources res, int id)  //从资源文件读取图片

public static Bitmap decodeResource(Resources res, int id, Options opts)

public static Bitmap decodeByteArray(byte[] data, int offset, int length)  //从数组读取图片

public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)

public static Bitmap decodeFileDescriptor(FileDescriptor fd) //从文件读取文件 与decodeFile不同的是这个直接调用JNI函数进行读取 效率比较高

public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)

首先了解一下关于Bitmap的Config的理解

A:透明度 R:红色 G:绿 B:蓝

Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位

Bitmap.Config ARGB_8888:每个像素占四位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位

Bitmap.Config RGB_565:每个像素占四位,即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位

Bitmap.Config ALPHA_8:每个像素占四位,只有透明度,没有颜色。


bitmap内存大小 = 图片长度 x 图片宽度 x 单位像素占用的字节数

起决定因素就是最后那个参数了,Bitmap'常见有2种编码方式:ARGB_8888和RGB_565,ARGB_8888每个像素点4个byte,RGB_565是2个byte,一般都采用ARGB_8888这种。那么常见的1080*1920的图片内存占用就是:

1920 x 1080 x 4 = 7.9M


常用的压缩方法:

1.质量压缩

private void compressQuality() { Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.test); mSrcSize = bm.getByteCount() + "byte"; ByteArrayOutputStream bos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.JPEG, 100, bos); byte[] bytes = bos.toByteArray(); mSrcBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); }

质量压缩不会减少图片的像素,它是在保持像素的前提下改变图片的位深及透明度,来达到压缩图片的目的,图片的长,宽,像素都不会改变,那么bitmap所占内存大小是不会变的。

我们可以看到有个参数:quality,可以调节你压缩的比例,但是还要注意一点就是,质量压缩堆png格式这种图片没有作用,因为png是无损压缩。

2.采样率压缩

private void compressSampling() { BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test, options); }

采样率压缩其原理其实也是缩放bitamp的尺寸,通过调节其inSampleSize参数,比如调节为2,宽高会为原来的1/2,内存变回原来的1/4.

3.放缩法压缩

private void compressMatrix() { Matrix matrix = new Matrix(); matrix.setScale(0.5f, 0.5f); Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.test); mSrcBitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); bm = null; }

放缩法压缩使用的是通过矩阵对图片进行裁剪,也是通过缩放图片尺寸,来达到压缩图片的效果,和采样率的原理一样

4.RGB_565压缩

private void compressRGB565() { BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test, options); }

这是通过压缩像素占用的内存来达到压缩的效果,一般不建议使用ARGB_4444,因为画质实在是辣鸡,如果对透明度没有要求,建议可以改成RGB_565,相比ARGB_8888将节省一半的内存开销。

5.createScaledBitmap

private void compressScaleBitmap() { Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.test); mSrcBitmap = Bitmap.createScaledBitmap(bm, 600, 900, true); bm = null; }

将图片的大小压缩成用户的期望大小,来减少占用内存。


图片二次采样

private Bitmap getCompressBm(int id, int maxw, int maxh) {

Bitmap bm = null;

int iSamplesize = 1;

//第一次采样

BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();

//该属性设置为true只会加载图片的边框进来,并不会加载图片具体的像素点

bitmapFactoryOptions.inJustDecodeBounds = true;

bm = BitmapFactory.decodeResource(getResources(), id, bitmapFactoryOptions);

Log.e("Tag", "onActivityResult: 压缩之前图片的宽:" + bitmapFactoryOptions.outWidth + "--压缩之前图片的高:"

+ bitmapFactoryOptions.outHeight + "--压缩之前图片大小:" + bitmapFactoryOptions.outWidth * bitmapFactoryOptions.outHeight * 4 / 1024 + "kb");

int iWidth = bitmapFactoryOptions.outWidth;

int iHeight = bitmapFactoryOptions.outHeight;

//对缩放比例进行调整,直到宽和高符合我们要求为止

while (iWidth > maxw|| iHeight > maxh){

//如果宽高的任意一方的缩放比例没有达到要求,都继续增大缩放比例

//sampleSize应该为2的n次幂,如果给sampleSize设置的数字不是2的n次幂,那么系统会就近取

iSamplesize = iSamplesize*2;//宽高均为原图的宽高的1/2 内存约为原来的1/4

iWidth = iWidth/iSamplesize;

iHeight = iHeight/iSamplesize;

}

//二次采样开始

//二次采样时我需要将图片加载出来显示,不能只加载图片的框架,因此inJustDecodeBounds属性要设置为false

bitmapFactoryOptions.inJustDecodeBounds = false;

bitmapFactoryOptions.inSampleSize = iSamplesize;

// 设置像素颜色信息

// bitmapFactoryOptions.inPreferredConfig = Bitmap.Config.RGB_565;

bm = BitmapFactory.decodeResource(getResources(),id, bitmapFactoryOptions);

//默认的图片格式是Bitmap.Config.ARGB_8888

Log.e("Tag", "onActivityResult: 图片的宽:" + bm.getWidth() + "--图片的高:"

+ bm.getHeight() + "--图片大小:" + bm.getWidth() * bm.getHeight() * 4 / 1024 + "kb");

return bm;//返回压缩后的照片

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容