关于bitmap一些常见问题大小压缩等

虽说从事android开发多年,但是一直处于不求甚解的过程,只是积累了工作年限,但实际的工作经验并未见长,这不在日常开发中会经常遇到的bitmap的相关问题,每次都得百度一番惭愧惭愧呀!

首先在实际应用中,会遇到各种概念,有时总是傻傻的分不清楚,索性又百度了一番结合源码来个小小的总结吧。

与大小相关的概念
  • 图片实际大小
  • 图片占用内存空间大小
原始图片

在tinyPNG网站上压缩之后的图片
占用内存空间显示

一张图片占用内存空间大小是多少?

我们可以看到2个图片的分辩率都是1500*2560,一个是2.73 MB,一个是583 KB,在经过BitmapFactory.decodeResource (禁止缩放)之后得到bitmap,再通过Bitmap.getAllocationByteCount获取占用内存空间大小竟然是一样的,让人疑惑?

Bitmap类中有2个方法getAllocationByteCountgetByteCount:

getAllocationByteCount 根据文档的意思即占用内存空间大小,在没有其他操作的情况下默认与getByteCount取值一样,那么也就是说默认这个值一样,大小为getRowBytes() * getHeight()再深入一些,会发现getRowBytes涉及到了C++代码,暂且不说,可以参考其他文档

image-20200910173344850

getRowBytes代码没找到,C++不懂,不过这个取值实际就是= 宽 * 4 * 高,这个是在没有任何缩放的情况下计算的 。

  • 作一个说明如果图片来源是File、URL 或者 Assets目录,通过BitmapFactory得到的图片则是没有缩放的,其占用内存空间大小就是等于 宽 * 4 * 高;

  • 如果图片来源资源目录xxhdpi(480dpi) 、xxxhdpi(640dpi)等其他目录 ,则在经过·BitmapFactory解析得到bitmap的时候 会经过缩放,缩放比为设备的DPI与资源目录对应的DPI进行对比,也就是我们经常遇过的现象 如手机设备的density为480,

    • 这个时候若把图片放在xxhdpi里面,解析得到的bitmap然后取其宽高,这个取值跟实际的一样,

    • 这个时候若将图片放到xhdpi里面,则得到的bitmap的宽高都会变大,图片放大了

    • 这个时候若将图片放到xxhdpi里面,刚得到bitmap的宽高都会变小,图片缩小了

    在种场景之下,获取的的bitmap所占用的内存空间则为 宽 x scale x 4 x 高 x scale

与图片压缩相关
  • 质量压缩 compress
  • 邻近采样压缩 options.inSampleSize=2
  • 双线性采样 matrix.setScale(0.5f, 0.5f);
  • 还有一些其他策略但涉及到底层算法之类的,暂时还搞不懂

具体可以参考上篇文章里面推荐的链接

Android中图片压缩分析(下)

LuBan 仿微信朋友圈压缩策略

鲁班压缩,这个工具类库代码不多,1.1.8版本压缩核心方法其实也就是先采样再质量压缩 ,这样可以适用于一般的场景之下, 由于这个类库长期未进行更新,遗留许多问题需要改进,但我们可以借鉴其原理,根据实际情况加一改进。

 File compress() throws IOException {

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = computeSize();//计算采样率
        Bitmap tagBitmap = BitmapFactory.decodeStream(srcImg.open(), null, options);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        if (Checker.SINGLE.isJPG(srcImg.open())) {
            //旋转图片
            tagBitmap = rotatingImage(tagBitmap,Checker.SINGLE.getOrientation(srcImg.open()));
        }
        //是否保留透明通道 保留PNG无损压缩 JPEG质量60%
     tagBitmap.compress(focusAlpha ? Bitmap.CompressFormat.PNG :     Bitmap.CompressFormat.JPEG, 60, stream);

        tagBitmap.recycle();

        FileOutputStream fos = new FileOutputStream(tagImg);
        fos.write(stream.toByteArray());
        fos.flush();
        fos.close();
        stream.close();
        return tagImg;
 }
  private int computeSize() {
        //%2==1的话 加1  是为了方便计算取整吧
        srcWidth = srcWidth % 2 == 1 ? srcWidth + 1 : srcWidth;
        srcHeight = srcHeight % 2 == 1 ? srcHeight + 1 : srcHeight;

        int longSide = Math.max(srcWidth, srcHeight);
        int shortSide = Math.min(srcWidth, srcHeight);
        //先计算一下宽高比   拍照宽高比 16:9=1:0.5625
        float scale = ((float) shortSide / longSide);
      //这些取值不知道作者是根据什么推断出来的,但现在从微信聊天记录中保存的图片大小似乎是1080*1440
        if (scale <= 1 && scale > 0.5625) {
            if (longSide < 1664) {
                return 1;
            } else if (longSide < 4990) { //1024*4 = 4096
                return 2;
            } else if (longSide > 4990 && longSide < 10240) {
                return 4;
            } else {
                return longSide / 1280 == 0 ? 1 : longSide / 1280;
            }
        } else if (scale <= 0.5625 && scale > 0.5) {
            return longSide / 1280 == 0 ? 1 : longSide / 1280;
        } else {
            return (int) Math.ceil(longSide / (1280.0 / scale));
        }
    }
保存图片大小的问题

在对bitmap压缩之后,有时我们会将图片保存到本地,这个时候有时会发现实际保存的图片大小 与压缩之后计算的预计保存的图片大小不一致的问题?

InputStream inputStream1 = getResources().openRawResource(R.mipmap.china_map_xxhdpi);
//这个大小 基本与硬盘中的显示大小一样,如果略有差别应该是kb按照1000或者1024计算的原因
KLog.d(TAG, "==china map 原始大小" + inputStream1.available() / default_size + "kb");

Drawable drawable = getResources().getDrawable(R.mipmap.china_map_xxhdpi);
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
KLog.d(TAG, "占用内存大小 getAllocationByteCount===" + bitmap.getAllocationByteCount() / default_size + " kb");

ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
KLog.d(TAG, "==before=baos大小===最高质量压缩的大小" + baos.toByteArray().length / default_size + " kb");

int quality = 95;
boolean flag = true;
while (baos.toByteArray().length / default_size > 100 && flag) {
    baos.reset();   
    bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
    quality -= 5;
    if (quality <= 0) {
        flag = false;
    }

}
KLog.d(TAG, "==after=baos大小===控制100以内-----" + baos.toByteArray().length / default_size + " kb");
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(baos.toByteArray());
KLog.d(TAG, "byteArrayInputStream==1024===" + byteArrayInputStream.available() / default_size + " kb");
KLog.d(TAG, "byteArrayInputStream==1000===" + byteArrayInputStream.available() / 1000 + " kb");
    ...省略
//图片大小 与计算大小致
String filepath = dirpath + File.separator + name + ".jpg";
KLog.d(filepath);
FileOutputStream fos = new FileOutputStream(new File(filepath));
fos.write(baos.toByteArray());
fos.flush();
fos.close();

ByteArrayOutputStream baosResult = new ByteArrayOutputStream();
resultBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baosResult);
KLog.d(TAG, "===baosResult==resultBitmap==" + baosResult.toByteArray().length / default_size + " kb");

//经测试保存的图片确实 变大 了
String filepath2 = dirpath + File.separator + name + "_result_" + baosResult.toByteArray().length / default_size + ".jpg";
KLog.d(filepath2);
FileOutputStream fos2 = new FileOutputStream(new File(filepath2));
fos2.write(baosResult.toByteArray());
fos2.flush();
fos2.close();

image-20200910190503664

image-20200910191137723

所以说当压缩图片到目标大小后,不能再经过其他操作,直接使用流保存到本地这个时候大小就与计算大小一样,如果再经过转换大小就可能出现变化。

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