天天看點

你不知道的Bitmap

Bitmap在Android中廣泛應用,尤其是圖檔處理時。看下今天Agenda:

位圖的基本概念

Bitmap與檔案格式

Bitmap記憶體占用優化

BitmapShader的應用

recycle的實踐

我們先抛開Bitmap在Android上的實作不談,先看看Bitmap在實體世界的具體含義

計算機圖形學上的概念

你不知道的Bitmap

根據位深度,可将位圖分為1、4、8、16、24及32位圖像等。每個像素使用的資訊位數越多,可用的顔色就越多,顔色表現就越逼真,相應的資料量越大。例如,位深度為1的像素位圖隻有兩個可能的值(黑色和白色),是以又稱為二值位圖。位深度為8的圖像有28(即256)個可能的值。位深度為8的灰階模式圖像有256個可能的灰色值。

移動端開發中矢量圖的應用很少,對于區塊資訊,因為很少遇到這種需要無限縮放的場景。對于少數有縮放需要的場景,shape以及Bitmap類提供的(九點圖)功能已經可以滿足絕大多數場景。

你不知道的Bitmap

每個像素使用的資訊位數越多,可用的顔色就越多,顔色表現就越逼真,相應的資料量越大,Android定義了這樣幾種圖檔格式。

你不知道的Bitmap

4444已經被廢棄,因為顯示品質不好。

8888是Bitmap預設的顔色配置資訊,也是最占空間的一種配置。

565 如果不需要 alpha 通道,特别是資源本身為 jpg 格式的情況下,用這個格式比較理想

你不知道的Bitmap

Skia是一個開源的二維圖形庫,提供各種常用的API,并可在多種軟硬體平台上運作。谷歌Chrome浏覽器、Chrome OS、安卓、火狐浏覽器、火狐作業系統以及其它許多産品都使用它作為圖形引擎。

已知Bitmap模式為ARGB_8888,假設這張圖最後加載成一個300 * 300 的Bitmap ,那麼 記憶體中的大小應該是?

你不知道的Bitmap
你不知道的Bitmap
你不知道的Bitmap

Bitmap.CompressFormat.JPEG:表示以JPEG壓縮算法進行圖像壓縮,壓縮後的格式可以是“.jpg”或者“.jpeg”,是一種有損壓縮。

JPEG是最普遍在網際網路用來儲存和傳輸照片的格式。

Bitmap.CompressFormat.PNG:表示以PNG壓縮算法進行圖像壓縮,壓縮後的格式可以是“.png”,是一種無損壓縮。這意味着在生成檔案時,可能會忽略掉 品質。

和剛才的Bitmap.Config相比,這個内部類隻會在壓縮檔案等時被用到

Bitmap.CompressFormat.WEBP 2010年谷歌推遲的圖檔格式,專門用來在web中使用; 第一個版本的webp圖檔格式是有損的, 新版本中webp圖檔是無損的。

它的壓縮率是三者中最高的。

WebP

lossless images are 26% smaller in size compared to PNGs. WebP lossy

images are 25-34% smaller than comparable JPEG images at equivalent SSIM

quality index.

Lossless WebP supports transparency (also known as

alpha channel) at a cost of just 22% additional bytes. For cases when

lossy RGB compression is acceptable, lossy WebP also supports

transparency, typically providing 3× smaller file sizes compared to PNG.

你不知道的Bitmap

讓我們首先了解一下Bitmap從檔案生成的流程。

首先說Bitmap是一個final類,是以不能被繼承。Bitmap隻有一個構造方法,且該構造方法是沒有任何通路權限修飾符修飾,也就是說該構造方法是friendly,但是谷歌稱Bitmap的構造方法是private(私有的),感覺有點不嚴謹。不管怎樣,一般情況下,我們不能通過構造方法直接建立一個Bitmap對象。從檔案建立Bitmap類就離不開BitmapFactory

BitmapFactory類提供了四類方法:decodeFile、decodeRe-source、decodeStream和decodeByteArray,分别用于支援從檔案系統、資源、輸入流以及位元組數組中加載出一個Bitmap對象,其中decodeFile和decodeResource又間接調用了decode-Stream方法,這四類方法最終是在Android的底層實作的,對應着BitmapFactory類的幾個native方法。

你不知道的Bitmap

其實核心思想也很簡單,那就是采用BitmapFactory.Options來加載所需尺寸的圖檔。通過BitmapFactory.Options來縮放圖檔,主要是用到了它的inSampleSize參數,即采樣率。當inSampleSize為1時,采樣後的圖檔大小為圖檔的原始大小;當inSampleSize大于1時,比如為2,那麼采樣後的圖檔其寬/高均為原圖大小的1/2,而像素數為原圖的1/4,其占有的記憶體大小也為原圖的1/4。

你不知道的Bitmap

擷取到占用空間盡可能小

縮放圖檔,主要是用到了它的inSampleSize參數,即采樣率。當inSampleSize為1時,采樣後的圖檔大小為圖檔的原始大小;當inSampleSize大于1時,比如為2,那麼采樣後的圖檔其寬/高均為原圖大小的1/2,而像素數為原圖的1/4,其占有的記憶體大小也為原圖的1/4。

你不知道的Bitmap

(1)将BitmapFactory.Options的inJustDecodeBounds參數設為true并加載圖檔。 這一步并不會讀取檔案的像素區塊。隻會去從

(2)從BitmapFactory.Options中取出圖檔的原始寬高資訊,它們對應于outWidth和outHeight參數。

(3)根據采樣率的規則并結合目标View的所需大小計算出采樣率inSampleSize。

(4)将BitmapFactory.Options的inJustDecodeBounds參數設為false,然後重新加載圖檔。

先縮小後處理,如需要對圖檔做高斯模糊

(我們現在有個需求,要求将一張圖檔進行模糊,然後作為

ImageView 的 src 呈現給使用者,而我們的原始圖檔大小為

1080*1920,如果我們直接拿來模糊的話,一方面模糊的過程費時費力,另一方面生成的圖檔又占用記憶體,實際上在模糊運算過程中可能會存在輸入和輸出并存的情況,此時記憶體将會有一個短暫的峰值。

對于Bitmap而言,從檔案中讀取出來,工作還遠未結束。Bitmap的應用在很多時候都是用來建構一個BitmapDrawable,然後再去設定給别人做背景。

你不知道的Bitmap

當然這隻是其中一種的gc觸發路徑。在别的很多情況下都有可能,但是recycler并不會觸發gc,或者說recycler方法并不能在性能上帶來提升。gc的事情還是去交給gc去做吧。