zoukankan      html  css  js  c++  java
  • android图片压缩总结

    一、bitmap 图片格式介绍

    android中图片是以bitmap形式存在的,那么bitmap所占内存,直接影响到了应用所占内存大小,首先要知道bitmap所占内存大小计算方式:

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

    以下是图片的压缩格式:

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

    1. ALPHA_8         表示8位Alpha位图,即A=8,一个像素点占用1个字节,它没有颜色,只有透明度  
    2. ARGB_4444    表示16位ARGB位图,即A=4,R=4,G=4,B=4,一个像素点占4+4+4+4=16位,2个字节 
    3. ARGB_8888    表示32位ARGB位图,即A=8,R=8,G=8,B=8,一个像素点占8+8+8+8=32位,4个字节 
    4. RGB_565         表示16位RGB位图,即R=5,G=6,B=5,它没有透明度,一个像素点占5+6+5=16位,2个字节

    内部相关函数介绍:http://blog.csdn.net/xxxzhi/article/details/51607765

    显示bitmap: http://blog.csdn.net/ymangu666/article/details/37729109

    二、生成Bitmap对象:

    1. 从drawable资源文件中生成Bitmap对象:可以通过Option设置生成bitmap的属性

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Bitmap.Config.RGB_565;
    options.inScaled  = false;
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.kb455,options);

    注: 如果资源只放在drawable目录下,没有在hdpi等文件夹中,获取的bitmap文件会非常大,超过原始图片的大小,很可能出现OOM。

    一种办法是将资源文件放在各个 dpi目录下  二、可以设置为options.inscaled = false,生成的bitmap与图片大小一致。

      还可以通过其它渠道生成bitmap如inputstream,代码见“四”中的例子。

    这里需要注意的是 Option.inJustDecodeBounds 这个变量,官方的解释:

    If set to true, the decoder will return null (no bitmap), but the out… fields will still be set, allowing the caller to query the bitmap without having to allocate the memory for its pixels.

    当只是需要来获取图片的宽高而不创建一个 bitmap对象时需要设置为 true,这时系统不会分配 bitmap的内存。如下是官方的例子:

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
    int imageHeight = options.outHeight;
    int imageWidth = options.outWidth;
    String imageType = options.outMimeType;

    三、图片压缩(不改变图片宽高)

      下面代码通过while循环将图片压缩到topLimit(KB)之下,但是该种方法有个缺点,如果topLimit很小,但是原始的Bitmap

    又很大,当options减小到负值时任然不能达到目标大小之下。(http://www.jianshu.com/p/0ab8fde08bcf)

    public static byte[] compressQualityBitmap(Bitmap bitmap, int topLimit) {
            if(bitmap != null && topLimit > 0) {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                int options = 100;
                bitmap.compress(CompressFormat.JPEG, options, baos);
    
                while(baos.toByteArray().length > topLimit * 1024) {
                    baos.reset();
                    options -= 5;
                    bitmap.compress(CompressFormat.JPEG, options, baos);
                }
    
                byte[] bitmapArray = baos.toByteArray();
    
                try {
                    baos.close();
                    baos = null;
                } catch (IOException var14) {
                    ;
                } finally {
                    if(baos != null) {
                        try {
                            baos.close();
                            baos = null;
                        } catch (IOException var13) {
                            ;
                        }
                    }
    
                    return bitmapArray;
                }
            } else {
                return null;
            }
        }

    1、下面函数是在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的,这也是为什么该方法叫质量压缩方法。图片的长,宽,像素都不变,bitmap所占内存大小是不会变的,但是压缩到byte数组baos的长度会逐渐的变小。

         bitmap.compress(CompressFormat.JPEG, options, baos);

    2、需要如果是bit.compress(CompressFormat.PNG, quality, baos);这样的png格式,quality就没有作用了,bytes.length不会变化,因为png图片是无损的,不能进行压缩。

    3、该方法压缩并不能无限制的压缩,当压缩到一定程度后,baos的长度变不再变化。

    改变图片的格式: 

      第一章中列出了四种格式的Config,可以将ARGB_8888格式转换为 RGB_565存储空间可以减少为原来的一半,而不改变图片的宽高。

    四、图片缩放(改变图片宽高)

     1、bitmap采样率压缩:在BitmapFactory.decodexxxx() 设置 options.inSampleSize 大小来缩放图片例如: 为2时,缩放为原来宽高的一半。

    public static Bitmap compressBoundsBitmap(Context context, Uri uri, int targetWidth, int targetHeight) {
            InputStream input = null;
            Bitmap bitmap = null;
    
            try {
                input = context.getContentResolver().openInputStream(uri);
                Options options = new Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(input, (Rect)null, options);
                input.close();
                int originalWidth = options.outWidth;
                int originalHeight = options.outHeight;
                if(originalWidth != -1 && originalHeight != -1) {
                    boolean be1 = true;
                    int widthBe = 1;
                    if(originalWidth > targetWidth) {
                        widthBe = originalWidth / targetWidth;
                    }
    
                    int heightBe = 1;
                    if(originalHeight > targetHeight) {
                        heightBe = originalHeight / targetHeight;
                    }
    
                    int be2 = widthBe > heightBe?heightBe:widthBe;
                    if(be2 <= 0) {
                        be2 = 1;
                    }
    
                    options.inJustDecodeBounds = false;
                    options.inSampleSize = be2;
                    input = context.getContentResolver().openInputStream(uri);
                    bitmap = BitmapFactory.decodeStream(input, (Rect)null, options);
                    input.close();
                    input = null;
                } else {
                    Object be = null;
                }
            } catch (FileNotFoundException var23) {
                ;
            } catch (IOException var24) {
                ;
            } finally {
                if(input != null) {
                    try {
                        input.close();
                    } catch (IOException var22) {
                        ;
                    }
                }
    
                return bitmap;
            }
        }

    优点:效率较高,解析速度快

    缺点:采样率inSampleSize的取值只能是2的次方数(例如:inSampleSize=15,实际取值为8;inSampleSize=17,实际取值为16;实际取值会往2的次方结算),因此该方法不能精确的指定图片的大小

    2.通过Matrix缩放图片(不推荐)

    /**
     * 图片的缩放方法
     *
     * @param bitmap  :源图片资源
     * @param maxSize :图片允许最大空间  单位:KB
     * @return
     */
    public static Bitmap getZoomImage(Bitmap bitmap, double maxSize) {
        if (null == bitmap) {
            return null;
        }
        if (bitmap.isRecycled()) {
            return null;
        }
    
        // 单位:从 Byte 换算成 KB
        double currentSize = bitmapToByteArray(bitmap, false).length / 1024;
        // 判断bitmap占用空间是否大于允许最大空间,如果大于则压缩,小于则不压缩
        while (currentSize > maxSize) {
            // 计算bitmap的大小是maxSize的多少倍
            double multiple = currentSize / maxSize;
            // 开始压缩:将宽带和高度压缩掉对应的平方根倍
            // 1.保持新的宽度和高度,与bitmap原来的宽高比率一致
            // 2.压缩后达到了最大大小对应的新bitmap,显示效果最好
            bitmap = getZoomImage(bitmap, bitmap.getWidth() / Math.sqrt(multiple), bitmap.getHeight() / Math.sqrt(multiple));
            currentSize = bitmapToByteArray(bitmap, false).length / 1024;
        }
        return bitmap;
    }
    
    /**
     * 图片的缩放方法
     *
     * @param orgBitmap :源图片资源
     * @param newWidth  :缩放后宽度
     * @param newHeight :缩放后高度
     * @return
     */
    public static Bitmap getZoomImage(Bitmap orgBitmap, double newWidth, double newHeight) {
        if (null == orgBitmap) {
            return null;
        }
        if (orgBitmap.isRecycled()) {
            return null;
        }
        if (newWidth <= 0 || newHeight <= 0) {
            return null;
        }
    
        // 获取图片的宽和高
        float width = orgBitmap.getWidth();
        float height = orgBitmap.getHeight();
        // 创建操作图片的matrix对象
        Matrix matrix = new Matrix();
        // 计算宽高缩放率
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // 缩放图片动作
        matrix.postScale(scaleWidth, scaleHeight);
        Bitmap bitmap = Bitmap.createBitmap(orgBitmap, 0, 0, (int) width, (int) height, matrix, true);
        return bitmap;
    }
    
    /**
     * bitmap转换成byte数组
     *
     * @param bitmap
     * @param needRecycle
     * @return
     */
    public static byte[] bitmapToByteArray(Bitmap bitmap, boolean needRecycle) {
        if (null == bitmap) {
            return null;
        }
        if (bitmap.isRecycled()) {
            return null;
        }
    
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, output);
        if (needRecycle) {
            bitmap.recycle();
        }
    
        byte[] result = output.toByteArray();
        try {
            output.close();
        } catch (Exception e) {
            Log.e(TAG, e.toString());
        }
        return result;
    }
    View Code

    优点:可以精确地指定图片的缩放大小

    缺点:是在原bitmap的基础之上生成的,占内存,效率低.

    3/ThumbnailUtils 类进行

     将bitmap转换为byte方法:

    public static byte[] BitmapToByte(Bitmap bitmap) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap.compress(CompressFormat.PNG, 80, baos);//其中80参数表示要压缩的比例
        return baos.toByteArray();
    }

     参考:http://blog.csdn.net/harryweasley/article/details/51955467

     

  • 相关阅读:
    2013-10-31 《问题儿童居然一天两更!?》
    2013-10-31 《October 31st, 2013》
    2013-10-31 《三天里什么都没干……总之把目前为止的代码发了吧……》
    日怎么没人告诉我这博客可以改博文界面的显示宽度的
    俗话说打脸哦不打铁要趁热所以记录下替换图片的方法
    GUI好看码难写不是难写是难看我是说码难看不是GUI
    虽然保持了连续代码生产量但是仔细想想也没什么必要
    重写了电话本代码全面更新居然连续三天每天一个程序
    专注写字典三十年问你怕未又被编码卡了简直难以置信
    我就写个字典居然卡了两天重申一遍文字编码日你大爷
  • 原文地址:https://www.cnblogs.com/NeilZhang/p/7763206.html
Copyright © 2011-2022 走看看