談談 iOS 中圖片的解壓縮
對於大多數 iOS 應用來說,圖片往往是最佔用手機記憶體的資源之一,同時也是不可或缺的組成部分。將一張圖片從磁碟中加載出來,並最終顯示到螢幕上,中間其實經過了一系列複雜的處理過程,其中就包括了對圖片的解壓縮。
圖片載入的工作流
概括來說,從磁碟中載入一張圖片,並將它顯示到螢幕上,中間的主要工作流如下:
-
假設我們使用
+imageWithContentsOfFile:
方法從磁碟中載入一張圖片,這個時候的圖片並沒有解壓縮; -
然後將生成的
UIImage
賦值給UIImageView
; -
接著一個隱式的
CATransaction
捕獲到了UIImageView
圖層樹的變化; -
在主執行緒的下一個 run loop 到來時,Core Animation 提交了這個隱式的 transaction ,這個過程可能會對圖片進行 copy 操作,而受圖片是否位元組對齊等因素的影響,這個 copy 操作可能會涉及以下部分或全部步驟:
- 分配記憶體緩衝區用於管理檔案 IO 和解壓縮操作;
- 將檔案資料從磁碟讀到記憶體中;
- 將壓縮的圖片資料解碼成未壓縮的點陣圖形式,這是一個非常耗時的 CPU 操作;
-
最後 Core Animation 使用未壓縮的點陣圖資料渲染
UIImageView
的圖層。
在上面的步驟中,我們提到了圖片的解壓縮是一個非常耗時的 CPU 操作,並且它預設是在主執行緒中執行的。那麼當需要載入的圖片比較多時,就會對我們應用的響應性造成嚴重的影響,尤其是在快速滑動的列表上,這個問題會表現得更加突出。
為什麼需要解壓縮
既然圖片的解壓縮需要消耗大量的 CPU 時間,那麼我們為什麼還要對圖片進行解壓縮呢?是否可以不經過解壓縮,而直接將圖片顯示到螢幕上呢?答案是否定的。要想弄明白這個問題,我們首先需要知道什麼是點陣圖:
A bitmap image (or sampled image) is an array of pixels (or samples). Each pixel represents a single point in the image. JPEG, TIFF, and PNG graphics files are examples of bitmap images.
其實,點陣圖就是一個畫素陣列,陣列中的每個畫素就代表著圖片中的一個點。我們在應用中經常用到的 JPEG 和 PNG 圖片就是點陣圖。下面,我們來看一個具體的例子,這是一張 PNG 圖片,畫素為 30 × 30 ,檔案大小為 843B :
我們使用下面的程式碼:
1 2 |
|
就可以獲取到這個圖片的原始畫素資料,大小為 3600B :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
也就是說,這張檔案大小為 843B 的 PNG 圖片解壓縮後的大小是 3600B ,是原始檔案大小的 4.27 倍。那麼這個 3600B 是怎麼得來的呢?與圖片的檔案大小或者畫素有什麼必然的聯絡嗎?事實上,解壓縮後的圖片大小與原始檔案大小之間沒有任何關係,而只與圖片的畫素有關:
1
|
|
至於這個公式是怎麼得來的,我們後面會有詳細的說明,現在只需要知道即可。
至此,我們已經知道了什麼是點陣圖,並且直觀地看到了它的原始畫素資料,那麼它與我們經常提到的圖片的二進位制資料有什麼聯絡嗎?是同一個東西嗎?事實上,這二者是完全獨立的兩個東西,它們之間沒有必然的聯絡。為了加深理解,我把這個圖片拖進 Sublime Text 2 中,得到了這個圖片的二進位制資料,大小與原始檔案大小一致,為 843B :
1 2 3 4 5 6 7 8 9 10 11 12 |