android中Bitmap缩放、压缩、占用内存计算等

本篇文章主要搞定的问题:

  • (1)Bitmap占用的手机内存怎样计算? 占用内存的大小和那些因素相关?

  • (2)质量压缩

  • (3)尺寸压缩

Bitmap vs 内存

图片占用内存大小的相关因素:

  • (1)图片的宽高

  • (2)图片单位像素占用的字节数

++占用的内存 = 图片的宽(像素) x 图片的高(像素) x 单位像素占用的字节数++

图片常用的压缩格式及单位像素分别占用的字节数

** 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个字节(Android默认的压缩格式)

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

  • RGBA_F16 : 8 个字节

质量压缩

质量压缩是保持像素的前提下改变图片的位深及透明度,来达到压缩图片的目的。
图片的长,宽,像素都不会改变,那么bitmap所占内存大小是不会变的。
质量压缩对png格式的图片没有作用, 因为png格式的图片是 无损压缩的。
质量压缩是耗时的操作,故一般不能放在主线程中进行操作,可能会导致ANR。


    /**

    * 将bitmap按照最大的file size保存到指定文件

    *

    * @param picFilePath 待保存的文件路径

    * @param bitmap      图片

    * @param maxSize    限制保存后的最大大小 单位B

    */

  public static void saveBitmapToFileWithCompress(String picFilePath, Bitmap bitmap,

                                                  final int maxSize) {

      File photoFile = new File(picFilePath);

      FileOutputStream fileOutputStream = null;

      ByteArrayOutputStream baos = new ByteArrayOutputStream();

      int scale = 100;

      try {

          fileOutputStream = new FileOutputStream(photoFile);

          if (bitmap != null) {

              if (bitmap.compress(Bitmap.CompressFormat.JPEG, scale, baos)) {

                  int baosSize = baos.toByteArray().length;

                  while (baosSize > maxSize && scale > 0) {

                      baos.reset();

                      bitmap.compress(Bitmap.CompressFormat.JPEG, scale, baos);

                      baosSize = baos.toByteArray().length;

                      scale -= 5;

                  }

                  // 缩放后的数据写入到文件中

                  baos.writeTo(fileOutputStream);

                  fileOutputStream.flush();

              }

          }

      } catch (FileNotFoundException e) {

          e.printStackTrace();

      } catch (IOException e) {

          photoFile.delete();

          e.printStackTrace();

      } finally {

          try {

              if (fileOutputStream != null)

                  fileOutputStream.close();

          } catch (IOException e) {

              e.printStackTrace();

          }

      }

  }

尺寸压缩

  • (1)通过修改inSimpleSize的值来压缩

  • (2)通过修改inPreferredConfig来压缩

  • (3)通过Bitmap的静态方法 createScaledBitmap(@NonNull Bitmap src, int dstWidth, int dstHeight,
    boolean filter) { ... }

修改inSimpleSize的值来压缩

看了很多文章, 主要有2种写法, 稍微有点区别。


  // 第一种,简单明了, 他是 四舍五入的

  // Math.round 四舍五入 : 原始数据的基础上 + 0.5 再向下取整。

  public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {

        final int width = options.outWidth;

        final int height = options.outHeight;

        int inSampleSize = 1;

        if (width > reqWidth || height > reqHeight) {

            if (width > height) {

                inSampleSize = Math.round((float) height / (float) reqHeight);

            } else {

                inSampleSize = Math.round((float) width / (float) reqWidth);

            }

        }

        return inSampleSize;

    }

    // 第二种, 相当于都 舍 而没有 入 的情况, 是直接向下取整。

  private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth,

                                      int reqHeight) {

        // 获取原始图片的宽高

        final int height = options.outHeight;

        final int width = options.outWidth;

        // inSimpleSize 为1的时候表示不压缩; 调节为2表示宽高都是原宽高的1/2,

        // 这样按照上边说的计算内存的公式 内存就变成之前的1/4。

        int inSampleSize = 1;

        if (width <= 0 || height <= 0) {

            return inSampleSize;

        }

        // 使用默认的方式取样

        if (reqWidth <= 0 || reqHeight <= 0) {

            // 高大于宽

            if (height >= width) {

                reqWidth = mPicMinLen;

                reqHeight = reqWidth * height / width;

            } else {

                reqHeight = mPicMinLen;

                reqWidth = reqHeight * width / height;

            }

        }

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;

            final int halfWidth = width / 2;

            while ((halfHeight / inSampleSize) >= reqHeight

                    && (halfWidth / inSampleSize) >= reqWidth) {

                inSampleSize *= 2;

            }

        }

        return inSampleSize;

    }

修改inPreferredConfig来压缩

单位像素占用内存大小从小到大排列: [ALPHA_8, RGB_56, ARGB_4444, ARGB_8888, RGBA_F16]

RGB_56: 通过改变内存占用更小的编码格式来达到压缩效果,但是它没有透明度!!!

ARGB_4444: 画质不咋的...

综合考虑使用RGB_56更能符合压缩的要求,相对ARGB_8888能减少一半的内存。


        ...

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

        options.inPreferredConfig = Bitmap.Config.RGB_565;

        bitmap = BitmapFactory.decodeFile(imagePath, options);

        ...

通过 Bitmap的静态方法 createScaledBitmap(...) 来压缩

源码是这样的:


/**

    * Creates a new bitmap, scaled from an existing bitmap, when possible. If the

    * specified width and height are the same as the current width and height of

    * the source bitmap, the source bitmap is returned and no new bitmap is

    * created.

    *

    * @param src      The source bitmap.

    * @param dstWidth  The new bitmap's desired width.

    * @param dstHeight The new bitmap's desired height.

    * @param filter    true if the source should be filtered.

    * @return The new scaled bitmap or the source bitmap if no scaling is required.

    * @throws IllegalArgumentException if width is <= 0, or height is <= 0

    */

    public static Bitmap createScaledBitmap(@NonNull Bitmap src, int dstWidth, int dstHeight,

            boolean filter) {

        Matrix m = new Matrix();

        final int width = src.getWidth();

        final int height = src.getHeight();

        if (width != dstWidth || height != dstHeight) {

            final float sx = dstWidth / (float) width;

            final float sy = dstHeight / (float) height;

            m.setScale(sx, sy);

        }

        return Bitmap.createBitmap(src, 0, 0, width, height, m, filter);

    }

从源码注释可以知道 如果期望的宽高和原Bitmap的宽高是一样的,那么就不会新创建一个bitmap。 有时候这个可能是个大坑,要注意!!!
如: newBitmap = Bitmap.createScaledBitmap (oldBitmap, width, height, true); 紧接着如果你做 oldBitmap.recycle();
此时如果你给的width,height 和oldBitmap一样, newBitmap 就是 oldBitmap!!! 调用recycle()之后,你的 newBitmap 也就被recycle()而不能用了。

如有错误, 请指出。

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

推荐阅读更多精彩内容

  • 目录介绍 01.如何计算Bitmap占用内存1.1 如何计算占用内存1.2 上面方法计算内存对吗1.3 一个像素占...
    杨充211阅读 4,218评论 1 9
  • 2021期待与你一起共事,点击查看岗位[https://www.jianshu.com/p/6f4d67fa406...
    闲庭阅读 16,638评论 0 75
  • 一直以来Bitmap都是开发中很棘手的问题,这个问题就是传说中的OOM(java.lang.OutofMemory...
    M悇芐冋忆阅读 4,793评论 0 11
  • 摘要:对android 上图片压缩,其实总结起来基本可以分为两类压缩:尺寸压缩和质量压缩, 尺寸压缩其实也可以理解...
    男爵是只猫丶阅读 8,788评论 2 14
  • 非原创,只是整理,如果里面发现引用的内容没有标识出来,欢迎指出。 一、基本知识 (1)两种图片: 1)矢量图: 矢...
    风再起时ME阅读 2,450评论 0 19