Bitmap的那些事

Bitmap占用内存大小的计算方式:

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

Bitmap编码

Bitmap.config


image.png

其中,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将节省一半的内存开销。

采样率

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。

图片压缩

前面我们讲了bitmap的大小:
Bitmap所占用的内存 = 图片长度 x 图片宽度 x 一个像素点占用的字节数

方式一 改变bitmap.config

根据我们前面的知识:Bitmap.config可以决定一个像素点占用的字节数,我们举一个实际例子:

    BitmapFactory.Options options = new BitmapFactory.Options();
    //不获取图片,不加载到内存中,只返回图片属性
    options.inJustDecodeBounds = false;
    BitmapFactory.decodeResource(getResources(),R.mipmap.ic_profile_cover, options);
    //图片的宽高
    int outHeight = options.outHeight;
    int outWidth = options.outWidth;
    Log.d("mmm", "图片宽=" + outWidth + "图片高=" + outHeight);
    //图片格式压缩
    options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    options.inJustDecodeBounds = false;
    options.inSampleSize=2;
    Bitmap bitmap =  BitmapFactory.decodeResource(getResources(),R.mipmap.ic_profile_cover, options);
    float bitmapsize = getBitmapSize(bitmap);
    Log.d("mmm","压缩后:图片占内存大小" + bitmapsize + "MB / 宽度=" + bitmap.getWidth() + "高度=" + bitmap.getHeight());

    BitmapFactory.Options options1 = new BitmapFactory.Options();
    options1.inPreferredConfig = Bitmap.Config.RGB_565;
    options1.inJustDecodeBounds = false;

    Bitmap bitmap1 =  BitmapFactory.decodeResource(getResources(),R.mipmap.ic_profile_cover, options1);
    float bitmapsize1 = getBitmapSize(bitmap1);
    Log.d("mmm","压缩后:图片占内存大小" + bitmapsize1 + "MB / 宽度=" + bitmap1.getWidth() + "高度=" + bitmap1.getHeight());

}

/**
 * 得到bitmap的大小
 */
public  int getBitmapSize(Bitmap bitmap) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {    //API 19
        return bitmap.getAllocationByteCount()/1024/1024;
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {//API 12
        return bitmap.getByteCount()/1024/1024;
    }
    // 在低版本中用一行的字节x高度
    return bitmap.getRowBytes() * bitmap.getHeight()/1024/1024;
}
image.png

我们观察两次打印的日志发现RGB_565所占用的内存只有ARGB_888的一半。
例如,如果图片中包含透明度,那么对该图片解码时使用的配置就需要支持透明度,默认会使用ARGB_8888来解码。

如果直接设置 RGB_565:

对于一张透明图片(png),内存、宽高不变,bitmap 也不会失去透明度。
对于一张非透明图片(png、jpg),宽高不变,内存减小。
注意:由于ARGB_4444的画质惨不忍睹,一般假如对图片没有透明度要求的话,可以改成RGB_565,相比ARGB_8888将节省一半的内存开销。

方式二 采样率压缩(改变bitmap的宽高)

我们知道通过改变采样率可以对bitmap宽高进行一定比例的压缩
下面我们举一个简单的例子:

    BitmapFactory.Options options = new BitmapFactory.Options();
    //不获取图片,不加载到内存中,只返回图片属性
    options.inJustDecodeBounds = false;
    BitmapFactory.decodeResource(getResources(),R.mipmap.ic_profile_cover, options);
    //图片的宽高
    int outHeight = options.outHeight;
    int outWidth = options.outWidth;
    Log.d("mmm", "图片宽=" + outWidth + "图片高=" + outHeight);
    //图片格式压缩
    options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    options.inJustDecodeBounds = false;
    Bitmap bitmap =  BitmapFactory.decodeResource(getResources(),R.mipmap.ic_profile_cover, options);
    float bitmapsize = getBitmapSize(bitmap);
    Log.d("mmm","压缩后:图片占内存大小" + bitmapsize + "MB / 宽度=" + bitmap.getWidth() + "高度=" + bitmap.getHeight());

    BitmapFactory.Options options1 = new BitmapFactory.Options();
    options1.inPreferredConfig = Bitmap.Config.ARGB_8888;
    options1.inJustDecodeBounds = false;
    options1.inSampleSize=2;
    Bitmap bitmap1 =  BitmapFactory.decodeResource(getResources(),R.mipmap.ic_profile_cover, options1);
    float bitmapsize1 = getBitmapSize(bitmap1);
    Log.d("mmm","压缩后:图片占内存大小" + bitmapsize1 + "MB / 宽度=" + bitmap1.getWidth() + "高度=" + bitmap1.getHeight());

}
image.png

通过上面的例子我们可以看到当我们把inSampleSize设置成2的时候,图片的宽高变为原来的1/2,图片大小变成原来的1/4。所以我们可以通过采样率对图片进行压缩。

方式三 质量压缩

    Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(), R.mipmap.test);
    iv_1.setImageBitmap(bitmap);
    float bitmapsize = getBitmapSize(bitmap);
    Log.d("mmm", "压缩后1:图片占内存大小" + bitmapsize + "MB / 宽度=" + bitmap.getWidth() + "高度=" + bitmap.getHeight());
    //压缩图像后,显示
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 1, bos);
    byte[] bytes = bos.toByteArray();
    Bitmap bitmap1 = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    iv_2.setImageBitmap(bitmap1);
    float bitmapsize1 = getBitmapSize(bitmap1);
    Log.d("mmm", "压缩后2:图片占内存大小" + bitmapsize1 + "MB / 宽度=" + bitmap1.getWidth() + "高度=" + bitmap1.getHeight());



public  Bitmap compressImage(Bitmap image) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    //质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
    image.compress(Bitmap.CompressFormat.JPEG, 50, baos);
    Log.d("mmm", "压缩比例"+"baos:"+baos.toByteArray().length);
    int options = 100;
    while ( baos.toByteArray().length / 1024>100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
        baos.reset();//重置baos即清空baos
        image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
        options -= 10;//每次都减少10
        Log.d("mmm", "压缩比例"+"options:"+options);
    }
    //把压缩后的数据baos存放到ByteArrayInputStream中
    ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
    Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null); //把ByteArrayInputStream数据生成图片
    return bitmap;
    
}
image.png

我们可以看到压缩前和压缩后bitmap大小并没有改变,因为质量压缩不会减少图片的像素,它是在保持像素的前提下改变图片的位深及透明度,来达到压缩图片的目的,图片的长,宽,像素都不会改变,那么bitmap所占内存大小是不会变的。
我们可以看到有个参数:quality,可以调节你压缩的比例,但是还要注意一点就是,质量压缩堆png格式这种图片没有作用,因为png是无损压缩。

缩放压缩法

/***
 * 缩放图片到指定宽和高
 *
 * @param bitmap
 * @param newWidth  缩放后的宽度
 * @param newHeight 缩放后的高度
 * @param isCanBig  是否允许放大(否则只做缩小处理)
 * @return
 */
public static Bitmap zoomImage(Bitmap bitmap, float newWidth, float newHeight, boolean isCanBig) {
    // 获取这个图片的宽和高
    float width = bitmap.getWidth();
    float height = bitmap.getHeight();
    // 如果原始尺寸比设定的小且不允许放大,则不用压缩
    if (!isCanBig && width <= newWidth && height <= newHeight) {
        return bitmap;
    }
    // 创建操作图片用的matrix对象
    Matrix matrix = new Matrix();
    // 计算宽高缩放率
    float scaleWidth = newWidth / width;
    float scaleHeight = newHeight / height;
    // 缩放图片动作
    matrix.postScale(scaleWidth, scaleHeight);
    Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, (int) width, (int) height, matrix, true);
    return newBitmap;
}

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

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

推荐阅读更多精彩内容