1. 程式人生 > >談談 iOS 中圖片的解壓縮

談談 iOS 中圖片的解壓縮

對於大多數 iOS 應用來說,圖片往往是最佔用手機記憶體的資源之一,同時也是不可或缺的組成部分。將一張圖片從磁碟中加載出來,並最終顯示到螢幕上,中間其實經過了一系列複雜的處理過程,其中就包括了對圖片的解壓縮。

圖片載入的工作流

概括來說,從磁碟中載入一張圖片,並將它顯示到螢幕上,中間的主要工作流如下:

  1. 假設我們使用 +imageWithContentsOfFile: 方法從磁碟中載入一張圖片,這個時候的圖片並沒有解壓縮;
  2. 然後將生成的 UIImage 賦值給 UIImageView ;
  3. 接著一個隱式的 CATransaction 捕獲到了 UIImageView 圖層樹的變化;
  4. 在主執行緒的下一個 run loop 到來時,Core Animation 提交了這個隱式的 transaction ,這個過程可能會對圖片進行 copy 操作,而受圖片是否位元組對齊等因素的影響,這個 copy 操作可能會涉及以下部分或全部步驟:
    1. 分配記憶體緩衝區用於管理檔案 IO 和解壓縮操作;
    2. 將檔案資料從磁碟讀到記憶體中;
    3. 將壓縮的圖片資料解碼成未壓縮的點陣圖形式,這是一個非常耗時的 CPU 操作;
    4. 最後 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
UIImage *image = [UIImage imageNamed:@"check_green"];
CFDataRef rawData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));

就可以獲取到這個圖片的原始畫素資料,大小為 3600B :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
01020102 032c023c 0567048c 078d06bf 08a006d9 09b307f3 09b307f3 08a006d9 078d06bf
0567048c 032c023c 01020102 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 01060108 05570476 09ab07e9 09bb07ff 09bb07ff 09bb07ff 09bb07ff 09bb07ff
09bb07ff 09bb07ff 09bb07ff 09bb07ff 09bb07ff 09ab07e9 05570476 01060108 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 033d0353 08a607e2 09bb07ff 09bb07ff 09bb07ff 09bb07ff
...
09bb07ff 09bb07ff 09bb07ff 09bb07ff 08a607e2 033d0353 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 01060108 05570476 09ab07e9 09bb07ff 09bb07ff 09bb07ff 09bb07ff 09bb07ff
09bb07ff 09bb07ff 09bb07ff 09bb07ff 09bb07ff 09ab07e9 05570476 01060108 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 01020102 032c023c 0567048c
078d06bf 08a006d9 09b307f3 09b307f3 08a006d9 078d06bf 0567048c 032c023c 01020102
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

也就是說,這張檔案大小為 843B 的 PNG 圖片解壓縮後的大小是 3600B ,是原始檔案大小的 4.27 倍。那麼這個 3600B 是怎麼得來的呢?與圖片的檔案大小或者畫素有什麼必然的聯絡嗎?事實上,解壓縮後的圖片大小與原始檔案大小之間沒有任何關係,而只與圖片的畫素有關:

1
解壓縮後的圖片大小 = 圖片的畫素寬 30 * 圖片的畫素高 30 * 每個畫素所佔的位元組數 4

至於這個公式是怎麼得來的,我們後面會有詳細的說明,現在只需要知道即可。

至此,我們已經知道了什麼是點陣圖,並且直觀地看到了它的原始畫素資料,那麼它與我們經常提到的圖片的二進位制資料有什麼聯絡嗎?是同一個東西嗎?事實上,這二者是完全獨立的兩個東西,它們之間沒有必然的聯絡。為了加深理解,我把這個圖片拖進 Sublime Text 2 中,得到了這個圖片的二進位制資料,大小與原始檔案大小一致,為 843B :

1
2
3
4
5
6
7
8
9
10
11
12