1. 程式人生 > >Android bitmap壓縮優化方案

Android bitmap壓縮優化方案

Android中圖片是以bitmap形式存在的,那麼bitmap所佔記憶體,直接影響到了應用所佔記憶體大小,首先要知道bitmap所佔記憶體大小計算方式:

圖片長度 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個位元組 
RGB_565 
表示16位RGB點陣圖,即R=5,G=6,B=5,它沒有透明度,一個畫素點佔5+6+5=16位,2個位元組

我是用的小米手機2s來測試的,從sd卡取出一個照片,如下所示:

bit = BitmapFactory.decodeFile(Environment
                .getExternalStorageDirectory().getAbsolutePath()
                + "/DCIM/Camera/test.jpg");

        Log.i("wechat", "壓縮前圖片的大小" + (bit.getByteCount() / 1024 / 1024)
                + "M寬度為" + bit.getWidth() + "高度為"
+ bit.getHeight());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

出來的log是: 
這裡寫圖片描述

將取得的bitmap進行壓縮,下面開始說,bitmap的幾種壓縮方式。

1.質量壓縮

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int quality = Integer.valueOf(editText.getText().toString());
            bit.compress(CompressFormat.JPEG, quality, baos);
            byte[] bytes = baos.toByteArray
(); bm = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); Log.i("wechat", "壓縮後圖片的大小" + (bm.getByteCount() / 1024 / 1024) + "M寬度為" + bm.getWidth() + "高度為" + bm.getHeight() + "bytes.length= " + (bytes.length / 1024) + "KB" + "quality=" + quality);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

其中quality是從edittext獲取的數字,可以從0–100改變,這裡出來的log是: 
這裡寫圖片描述

可以看到,圖片的大小是沒有變的,因為質量壓縮不會減少圖片的畫素,它是在保持畫素的前提下改變圖片的位深及透明度等,來達到壓縮圖片的目的,這也是為什麼該方法叫質量壓縮方法。那麼,圖片的長,寬,畫素都不變,那麼bitmap所佔記憶體大小是不會變的。

但是我們看到bytes.length是隨著quality變小而變小的。這樣適合去傳遞二進位制的圖片資料,比如微信分享圖片,要傳入二進位制資料過去,限制32kb之內。

這裡要說,如果是bit.compress(CompressFormat.PNG, quality, baos);這樣的png格式,quality就沒有作用了,bytes.length不會變化,因為png圖片是無損的,不能進行壓縮。

CompressFormat還有一個屬性是,CompressFormat.WEBP格式,該格式是google自己推出來一個圖片格式,更多資訊,文末會貼出地址。

2.取樣率壓縮

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

            bm = BitmapFactory.decodeFile(Environment
                    .getExternalStorageDirectory().getAbsolutePath()
                    + "/DCIM/Camera/test.jpg", options);
            Log.i("wechat", "壓縮後圖片的大小" + (bm.getByteCount() / 1024 / 1024)
                    + "M寬度為" + bm.getWidth() + "高度為" + bm.getHeight());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

出來的log是

這裡寫圖片描述

設定inSampleSize的值(int型別)後,假如設為2,則寬和高都為原來的1/2,寬高都減少了,自然記憶體也降低了。

我上面的程式碼沒用過options.inJustDecodeBounds = true; 因為我是固定來取樣的資料,為什麼這個壓縮方法叫取樣率壓縮,是因為配合inJustDecodeBounds,先獲取圖片的寬、高【這個過程就是取樣】,然後通過獲取的寬高,動態的設定inSampleSize的值。

當inJustDecodeBounds設定為true的時候,BitmapFactory通過decodeResource或者decodeFile解碼圖片時,將會返回空(null)的Bitmap物件,這樣可以避免Bitmap的記憶體分配,但是它可以返回Bitmap的寬度、高度以及MimeType。

3.縮放法壓縮(martix)

Matrix matrix = new Matrix();
            matrix.setScale(0.5f, 0.5f);
            bm = Bitmap.createBitmap(bit, 0, 0, bit.getWidth(),
                    bit.getHeight(), matrix, true);
            Log.i("wechat", "壓縮後圖片的大小" + (bm.getByteCount() / 1024 / 1024)
                    + "M寬度為" + bm.getWidth() + "高度為" + bm.getHeight());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

出來的log是 
這裡寫圖片描述

可以看出來,bitmap的長度和寬度分別縮小了一半,圖片大小縮小了四分之一。 
關於martix更多資訊,文末會有一個參考文章。

4.RGB_565法

BitmapFactory.Options options2 = new BitmapFactory.Options();
            options2.inPreferredConfig = Bitmap.Config.RGB_565;

            bm = BitmapFactory.decodeFile(Environment
                    .getExternalStorageDirectory().getAbsolutePath()
                    + "/DCIM/Camera/test.jpg", options2);
            Log.i("wechat", "壓縮後圖片的大小" + (bm.getByteCount() / 1024 / 1024)
                    + "M寬度為" + bm.getWidth() + "高度為" + bm.getHeight());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

出來的log是: 
這裡寫圖片描述

我們看到圖片大小直接縮小了一半,長度和寬度也沒有變,相比argb_8888減少了一半的記憶體。

注意:由於ARGB_4444的畫質慘不忍睹,一般假如對圖片沒有透明度要求的話,可以改成RGB_565,相比ARGB_8888將節省一半的記憶體開銷。

5.createScaledBitmap

bm = Bitmap.createScaledBitmap(bit, 150, 150, true);
            Log.i("wechat", "壓縮後圖片的大小" + (bm.getByteCount() / 1024) + "KB寬度為"
                    + bm.getWidth() + "高度為" + bm.getHeight());
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

出來的log是 
這裡寫圖片描述

這裡是將圖片壓縮成使用者所期望的長度和寬度,但是這裡要說,如果使用者期望的長度和寬度和原圖長度寬度相差太多的話,圖片會很不清晰。

總結

以上就是5種圖片壓縮的方法,這裡需要強調,他們的壓縮僅僅只是對android中的bitmap來說的。如果將這些壓縮後的bitmap另存為sd中,他們的記憶體大小並不一樣。

android手機中,圖片的所佔的記憶體大小和很多因素相關,計算起來也很麻煩。為了計算出一個圖片的記憶體大小,可以將圖片當做一個檔案來間接計算,用如下的方法:

 File file = new File(Environment.getExternalStorageDirectory()
         .getAbsolutePath() + "/DCIM/Camera/test.jpg");
         Log.i("wechat", "file.length()=" + file.length() / 1024);
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

或者

FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            Log.i("wechat", "fis.available()=" + fis.available() / 1024);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

上面兩個方法計算的結果是一樣的。

看完了這篇內容,其實說白了,Bitmap壓縮都是圍繞這個來做文章:Bitmap所佔用的記憶體 = 圖片長度 x 圖片寬度 x 一個畫素點佔用的位元組數。3個引數,任意減少一個的值,就達到了壓縮的效果。