1. 程式人生 > >Android朝花夕拾-介紹Bimap

Android朝花夕拾-介紹Bimap

這篇文章來介紹下安卓中操作圖片的 API: Bitmap

Bitmap的本質: 點陣圖,即用一些位儲存圖片資料的一種資料結構。 在這裡插入圖片描述 首先,我們用畫筆畫了一個48*48的圖片,儲存成bmp格式。

在這裡插入圖片描述 這裡windows畫筆只有四種點陣圖格式可選,我們選24位點陣圖。

在這裡插入圖片描述 檢視這個檔案的屬性,發現大小為 6966 byte。

讓我們猜測下Bitmap的資料結構。

根據儲存的格式:24位點陣圖 ,推測每一個畫素要使用24位(bit)來儲存,也就是3個位元組(3*8bit)。 點陣圖邏輯結構示意圖 那麼48x48大小的圖片需要的儲存空間為 48x48x3 = 6912位元組 與windows系統提供的大小差不多。 多出來的位元組數可能是檔案的其他資訊佔用的。

接下來,進入正題:Android的Bitmap API

官方對它沒有過多介紹。還是通過使用來認識。

Bitmap bitmap = Bitmap.createBitmap(96,96, Bitmap.Config.ARGB_8888);

Bitmap的createBitmap可以建立一個bitmap。這裡建立了一個96x96畫素大小的bitmap。 檢視一下新建立的bitmap所佔用的記憶體大小,使用getByteCount():

Toast.makeText(this,bitmap.getByteCount()+"",Toast.LENGTH_SHORT).show();

在這裡插入圖片描述 彈出的toast顯示,這個bitmap例項佔用36864 位元組。

createBitmap()方法使用寬,高,以及Config三個引數生成bitmap。Config表明了bitmap儲存空間大小:

ALPHA_8 :單色, RGB_565:每個畫素兩位元組,沒有透明度資訊, ARGB_4444:過時-不推薦使用, ARGB_8888:每個畫素4位元組,視覺效果拔群 RGBA_F16:每個畫素8位元組,用於顯示帶HDR效果的酷炫圖片。

之前使用ARGB_8888建立了一個96x96 畫素的bitmap例項,佔用記憶體大小:96x96x4位元組 = 36864 位元組。

Bitmap bitmap = Bitmap.createBitmap(96,96, Bitmap.Config.RGB_565);

如果改成RGB_565,則佔用記憶體為: 在這裡插入圖片描述 可見,比ARGB_8888減少了一半。但是沒有儲存透明度通道的資訊。

還記得文章開頭用畫筆建立的一個bmp檔案嗎,我們把它放到手機儲存卡里,載入成Bitmap。把這個檔案拷貝到了手機記憶體的這個根目錄。 在這裡插入圖片描述 然後使用BitmapFactory的decodeFile()來把這個檔案載入為Bitmap的一個例項。並顯示佔用記憶體大小。(單位為位元組);

        Bitmap fileBmp = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()
                +"/48px.bmp");
        Toast.makeText(this,fileBmp.getByteCount()+"",Toast.LENGTH_SHORT).show();

在這裡插入圖片描述 可以看到,這個48x48的圖片所佔記憶體為9216位元組,可以推算出,Bitmap的decodeFile()預設使用ARGB_8888這個模式將檔案載入為bitmap。與建立時這個檔案使用的儲存格式沒有關係(建立時使用windows畫筆,儲存成24位(每畫素3位元組,總共6912位元組))。

原始碼中decodeFile內容如下:

    public static Bitmap decodeFile(String pathName) {
        return decodeFile(pathName, null);
    }
    public static Bitmap decodeFile(String pathName, Options opts) {
        validate(opts);	                                                //1opts為空,不做任何操作
        Bitmap bm = null;
        InputStream stream = null;
        try {
            stream = new FileInputStream(pathName);
            bm = decodeStream(stream, null, opts);                      //2最終呼叫原生方法返回bitmap
        } catch (Exception e) {
            /*  do nothing.
                If the exception happened on open, bm will be null.
            */
            Log.e("BitmapFactory", "Unable to decode stream: " + e);
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                    // do nothing here
                }
            }
        }
        return bm;
    }

Bitmap是一種無壓縮方式的影象儲存結構,來看下面的例子: 下面是一張網上下載的200x200的jpg格式圖片 檔名為200x200.jpg 在這裡插入圖片描述

大小為 在這裡插入圖片描述

我們同樣把這個檔案放入手機儲存卡中,使用載入為Bitmap例項。 並看載入後的bitmap佔多少記憶體。

        Bitmap fileBmp = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()
                +"/200x200.jpg");
        Toast.makeText(this,fileBmp.getByteCount()+"",Toast.LENGTH_SHORT).show();

在這裡插入圖片描述 結果發現,最終佔用了160k位元組,比原始檔案大了10倍。

我們的activity佈局檔案中,有一個imageview,大小為96x96,現在將這個160k的bitmap設定給這個imageview作為內容顯示出來。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <ImageView
        android:background="@color/colorAccent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:id="@+id/image"
        android:layout_width="96dp"
        android:layout_height="96dp" />
</android.support.constraint.ConstraintLayout>
        Bitmap fileBmp = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()
                +"/200x200.jpg");
        Toast.makeText(this,fileBmp.getByteCount()+"",Toast.LENGTH_SHORT).show();
        imageView.setImageBitmap(fileBmp);	//imageView是佈局中的imageView控制元件

在這裡插入圖片描述

我們的控制元件只有96的寬高,但載入了200x200的bitmap,浪費了記憶體,有沒有辦法優化呢?

答案是肯定的,BitmapFactory提供了過載方法用來decodeFile。 在這裡插入圖片描述 可以設定一個Options物件對影象載入進行配置。Options的inSampleSize欄位表示載入時要以幾倍的比率減少 bitmap的尺寸,如2,就會返回一個一半尺寸的bitmap物件。官方推薦這個值是2的倍數。

完整程式碼如下:

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;					           //通知載入時只加載影象資訊,不真正載入影象,以取得原始寬高
        BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()
                +"/200x200.jpg",options);
        options.inSampleSize = calculateInSampleSize(options,96,96);   //options裡存放了原始寬高,結合需要的寬高參數進行計算inSampleSize
        //並設定給options
        options.inJustDecodeBounds = false;                            //接下來真正載入圖片,將inJustDecodeBounds置為false
        Bitmap fileBmp = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()
                +"/200x200.jpg",options);                                      //用設定了inSampleSize值的option真正載入影象bitmap
        imageView.setImageBitmap(fileBmp);
        Toast.makeText(this,fileBmp.getByteCount()+"",Toast.LENGTH_SHORT).show();
	//根據原始bitmap引數與實際的尺寸計算縮小倍率
    public static int calculateInSampleSize(BitmapFactory.Options options,int requestWidth,int requestHeight){

        int inSampleSize = 1;			//縮小倍率初始值

        int width = options.outWidth;
        int height = options.outHeight;
        if(width>requestWidth||height>requestHeight){
            int halfWidth = width/2;
            int halfHeight = height/2;				//計算原始尺寸的一半,是為了保證下一次計算後,原始尺寸縮小inSimpleSize倍後
            										//計算結果仍然大於所需尺寸,也就是最終能根據inSampleSize計算出大於所需尺寸的最小尺寸。
            while(halfHeight/inSampleSize>requestHeight&&
                    halfWidth/inSampleSize>requestWidth){
                inSampleSize*=2;
            }
        }

        return inSampleSize;
    }

在這裡插入圖片描述

可以看到,現在顯示效果與之前完全尺寸bitmap載入效果用肉眼難以分辨,但記憶體佔用減少到了40k,省了3/4的記憶體佔用。

這也是大尺寸圖片載入的一個思路。

對於Bitmap,先介紹到這裡吧。後續會介紹影象載入相關的其他知識。 例如,如何正確選擇mipmap目錄(會影響記憶體佔用,影響效能);

例子中的200x200的jpg影象只有14k,但載入為Bitmap需要佔用十倍的記憶體,探究下能否直接使用壓縮圖片進行顯示?

現在很少直接使用bitmap這個API來載入圖片了,就結合流行的框架例如Glide來看框架是如何處理的;