iOS開發(fā)圖片格式選擇

圖片是如何顯示的
在講解如何選擇圖片格式之前,我感覺有必要先了解下,圖片是如何展示的。如果我們要展示一張圖片,一般步驟是這樣的:
/// Assets.xcassets中的圖片,不需要后綴
let image = UIImage(named: "icon")
let imageView = UIImageView(frame: rect)
imageView.image = image
view.addSubview(imageView)
運(yùn)行程序,我們就可以在指定位置看到這個icon。看似簡單的代碼背后隱藏了很多細(xì)節(jié)工作。一張圖片的展示,從代碼執(zhí)行到展示出來大致經(jīng)歷了這些步驟:
1. 加載圖片
從磁盤中加載一張圖片;
然后將生成的
UIImage賦值給UIImageView;接著一個隱式的
CATransaction捕獲到了UIImageView圖層樹的變化;分配內(nèi)存緩沖區(qū)用于管理文件 IO 和解壓縮操作,將文件數(shù)據(jù)從磁盤讀到內(nèi)存中;
2. 圖片解碼(解壓)
將壓縮的圖片數(shù)據(jù)解碼成未壓縮的位圖形式,這是一個非常耗時的 CPU 操作,默認(rèn)在主線程進(jìn)行;
3. 圖片渲染
CoreAnimation中CALayer使用解壓(解碼)的位圖數(shù)據(jù)渲染UIImageView的圖層;CPU計(jì)算好圖片的Frame,對圖片解壓之后,就會交給GPU來做圖片渲染渲染流程;
GPU獲取獲取圖片的坐標(biāo),將坐標(biāo)交給頂點(diǎn)著色器(頂點(diǎn)計(jì)算),將圖片光柵化(獲取圖片對應(yīng)屏幕上的像素點(diǎn)),片元著色器計(jì)算(計(jì)算每個像素點(diǎn)的最終顯示的顏色值);
從幀緩存區(qū)中渲染到屏幕上;

這其中有個關(guān)鍵步驟是圖片解碼。那為什么要解碼呢,這是因?yàn)槲覀兤匠J褂玫膱D片一般為了節(jié)約空間都會經(jīng)過一些壓縮算法進(jìn)行封裝,而使用時屏幕要精確的渲染到每個像素點(diǎn),這就需要把壓縮的圖片解碼展開,便于系統(tǒng)處理。
名詞解釋
有損vs無損
有損壓縮:指在壓縮文件大小的過程中,損失了一部分圖片的信息,也即降低了圖片的質(zhì)量,并且這種損失是不可逆的,我們不可能從有一個有損壓縮過的圖片中恢復(fù)出全來的圖片。常見的有損壓縮手段,是按照一定的算法將臨近的像素點(diǎn)進(jìn)行合并。
無損壓縮:只在壓縮文件大小的過程中,圖片的質(zhì)量沒有任何損耗。我們?nèi)魏螘r候都可以從無損壓縮過的圖片中恢復(fù)出原來的信息。
索引色vs直接色
索引色:用一個數(shù)字來代表(索引)一種顏色,在存儲圖片的時候,存儲一個數(shù)字的組合,同時存儲數(shù)字到圖片顏色的映射。這種方式只能存儲有限種顏色,通常是256種顏色,對應(yīng)到計(jì)算機(jī)系統(tǒng)中,使用一個字節(jié)的數(shù)字來索引一種顏色。
直接色:使用四個數(shù)字來代表一種顏色,這四個數(shù)字分別代表這個顏色中紅色、綠色、藍(lán)色以及透明度。現(xiàn)在流行的顯示設(shè)備可以在這四個維度分別支持256種變化,所以直接色可以表示2的32次方種顏色。當(dāng)然并非所有的直接色都支持這么多種,為壓縮空間使用,有可能只有表達(dá)紅、綠、藍(lán)的三個數(shù)字,每個數(shù)字也可能不支持256種變化之多。
點(diǎn)陣圖vs矢量圖
點(diǎn)陣圖:也叫做位圖,像素圖。構(gòu)成點(diǎn)陣圖的最小單位是象素,位圖就是由象素陣列的排列來實(shí)現(xiàn)其顯示效果的,每個象素有自己的顏色信息,在對位圖圖像進(jìn)行編輯操作的時候,可操作的對象是每個象素,我們可以改變圖像的色相、飽和度、明度,從而改變圖像的顯示效果。點(diǎn)陣圖縮放會失真,用最近非常流行的沙畫來比喻最恰當(dāng)不過,當(dāng)你從遠(yuǎn)處看的時候,畫面細(xì)膩多彩,但是當(dāng)你靠的非常近的時候,你就能看到組成畫面的每粒沙子以及每個沙粒的顏色。
矢量圖:也叫做向量圖。矢量圖并不記錄畫面上每一點(diǎn)的信息,而是記錄了元素形狀及顏色的算法,當(dāng)你打開一張矢量圖的時候,軟件對圖形象對應(yīng)的函數(shù)進(jìn)行運(yùn)算,將運(yùn)算結(jié)果[圖形的形狀和顏色]顯示給你看。無論顯示畫面是大還是小,畫面上的對象對應(yīng)的算法是不變的,所以,即使對畫面進(jìn)行倍數(shù)相當(dāng)大的縮放,其顯示效果仍然相同(不失真)。
幾種格式的對比
一張圖片,如果我們將它的每一個像素及其對應(yīng)的顏色都存儲起來(BMP格式),是會很大的。為了減小圖片占用的存儲空間,派生出了各種不同壓縮算法所代表的圖片格式。常見的圖片格式有png、jpeg、heic、gif、webp,svg等。
PNG
PNG有兩種類型:PNG-8和PNG-24。
PNG-8是無損的、索引色、點(diǎn)陣圖。它支持透明度的調(diào)節(jié)。
PNG-24是無損的、直接色、點(diǎn)陣圖。因?yàn)槭褂弥苯由伾秶螅舱加酶蟮目臻g。他的目標(biāo)是替代JPEG,但一般而言,PNG-24的文件大小是JPEG的五倍之多,而顯示效果則通常只能獲得一點(diǎn)點(diǎn)提升。所以如果不是對圖片質(zhì)量要求特別高,不建議使用PNG-24
PNG是蘋果推薦的圖片格式,而且這些圖片會被一個叫pngcrush的開源工具優(yōu)化,這樣iOS設(shè)備就能在顯示時更快的解壓和渲染圖片。該工具位于目錄:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin
JPEG
JPEG是有損的、采用直接色的、點(diǎn)陣圖壓縮方式。JEPG目標(biāo)是在不影響人類可分辨的圖片質(zhì)量的前提下,盡可能的壓縮文件大小。一般都是用于相機(jī)圖片的格式。但因?yàn)橛袚p,會導(dǎo)致圖片失真。iOS可以通過以下方式壓縮圖片:
// 壓縮比范圍從0到1
func jpegData(compressionQuality: CGFloat) -> Data?
題外話一般來說,相同的圖片采用JPEG的壓縮方式會比png得到更小的尺寸,但也有例外。

在網(wǎng)上查了資料說是JEPG更適合處理帶有很多雜色的風(fēng)景圖,而對于使用數(shù)位板等電子繪制的純色卡通系風(fēng)格圖片,JEPG的壓縮方式會適得其反,導(dǎo)致體積更大。
HEIC
HEIC是HEIF(High Efficiency Image Format 高效率圖像文件格式)的一種,在iOS 11更新后,iPhone 7及其后硬件,在拍攝照片時的默認(rèn)圖像存儲格式。與JPG相比,它占用的空間更小,畫質(zhì)更加無損。HEIC的目的就是作為JPEG的繼任者,以后或許會成為一種趨勢。目前可以想到的在開發(fā)中的應(yīng)用是,對于一些需要下載的大圖可以轉(zhuǎn)成HEIC格式,供客戶端使用。但是當(dāng)前卻很少應(yīng)用,大概率是考慮到圖片兼容問題吧。
題外話
一個有趣的現(xiàn)象,我用相機(jī)(iPhoneXR)拍攝一張照片,通過AirDrop傳到電腦,顯示為HEIC格式。當(dāng)我在拍照時選擇系統(tǒng)自帶的任意一種濾鏡,圖片格式就變成了JPEG。這是為什么?
pdf圖片通常是矢量的,它的導(dǎo)入方式有些特殊。我們需要在 Assets.xcassets文件,創(chuàng)建一個 NewImageSet,然后將該文件的 Scales設(shè)置為 SingleScale,拖入1x尺寸的pdf文件即可:
使用時我們可以把它當(dāng)做普通圖片對待:
let image = UIImage(named: "sunset")
在運(yùn)行期間Xcode會根據(jù)屏幕的比例因子生成對應(yīng)尺寸的png圖像。比如導(dǎo)入一張100x100的pdf圖片,在2x和3x的機(jī)型里面會生成對應(yīng)的200x200,300x300的png(可以在Assets.car中找到)。所以pdf只不過是Xcode處理圖片的中間狀態(tài),下載到手機(jī)的應(yīng)用包里面是沒有這張pdf的。
這種處理方式有一個好處就是,當(dāng)蘋果以后發(fā)布一款4x屏幕的手機(jī)時,使用pdf處理的圖片會自適應(yīng)生成對應(yīng)的4x資源,不需要再手動導(dǎo)入。但相比優(yōu)點(diǎn),pdf作為圖片資源的缺點(diǎn)更多。
首先是尺寸上,因?yàn)槭亲詣由蓪?yīng)的png,并沒有任何優(yōu)化和壓縮,而且我們也并不能在這中間做什么。對比相同尺寸經(jīng)過ImageOptim壓縮過的png,在大小上后者會是前者的1/2,甚至1/4。
另外pdf對陰影和漸變的處理會存在失真的情況:

左邊是png,右邊是pdf。在一些漸變和光影的圖像部分可以看出明顯的失真。
更多關(guān)于pdf和png的差別,可以看這篇:Why I don't use PDFs for iOS assets: https://bjango.com/articles/idontusepdfs/
SVG/SF Symbol
SVG是一種無損的矢量圖,是眾多矢量圖中的一種,它的特點(diǎn)是使用XML來描述圖片。使用XML的優(yōu)點(diǎn)是,任何時候你都可以把它當(dāng)做一個文本文件來對待,也就是說,你可以非常方便的修改SVG圖片,你所需要的只需要一個文本編輯器。
在iOS13之前應(yīng)用中直接使用SVG的場景非常少,但從iOS13開始,蘋果推出了SF Symbol,一種svg格式的矢量符號集。而且蘋果還提供了多于1500多種icon模板,我們可以在這里下載查看。

我們可以從中選擇適合自己的icon,選中之后,從File > Export Custom Symbol Templete中導(dǎo)出svg格式圖片集,然后拖到Xcode的 Assets.xcassets。

SF Symbol有9種粗細(xì)的調(diào)節(jié)——從ultralight到black——每一種都相當(dāng)于 SanFrancisco系統(tǒng)字體的重量(weight)。(SF Symbol中的SF是 SanFrancisco(舊金山)的縮寫)。這種對應(yīng)使您能夠在符號和相鄰文本之間實(shí)現(xiàn)精確的權(quán)重匹配,同時支持不同大小和上下文的靈活性。
當(dāng)然如果這些圖標(biāo)都不能滿足需求,我們還可以自定義SF圖標(biāo),然后通過SF Symbol App進(jìn)行驗(yàn)證和導(dǎo)出。操作細(xì)節(jié)可以看這里:Creating Custom Symbol Images for Your App。
SF Symbol使用起來也很簡單:
let configuration = UIImage.SymbolConfiguration.init(scale: .small)
imageView.image = UIImage(systemName: "alarm", withConfiguration: configuration)
SF Symbol可以一次性解決相同icon,不同尺寸,不同粗細(xì)的問題,它讓我們處理圖片像處理字體一樣方便。可以想象這就是應(yīng)用圖標(biāo)的未來。
當(dāng)看到SF Symbol僅支持iOS13+,watchOS6+,我又不得不退回到現(xiàn)實(shí),png也挺好的。
題外話
我在測試SF Symbol圖標(biāo)時,從生成的應(yīng)用包中查看圖片,會得到這樣的結(jié)果:

代碼中的我將圖片設(shè)置為100x100,僅有這一處地方使用。跟pdf類似我們找不到svg源文件,這好理解,svg只是中間狀態(tài),我們最終使用的還是png,但為什么會有多個小尺寸的png圖像呢?
如何選擇圖片格式
我們平常開發(fā)時,使用最多的就是png了,甚至可能是不加考慮的全部使用png。其實(shí)這樣是不好的,我們應(yīng)該充分發(fā)揮不同格式圖片的優(yōu)點(diǎn),從兼容性、空間占用、展示效果三方面考量選取最佳格式。
關(guān)于圖片格式的選擇,蘋果的Human Interface Guidelines有以下說法:
一般情況下,使用PNG圖片,因?yàn)镻NG支持透明性,而且是無損的,壓縮工件不會模糊重要的細(xì)節(jié)或改變顏色。對于需要陰影、紋理和高光效果的復(fù)雜藝術(shù)品來說,這是一個不錯的選擇。使用8位的PNG圖形,不需要完全24位的顏色。8位PNG可以在不降低圖像質(zhì)量的情況下減小文件大小。精致的應(yīng)用圖標(biāo)最好使用png。
對于照片應(yīng)該使用JPEG格式,它的壓縮算法通常比無損格式產(chǎn)生更小的尺寸,而且很難在照片中辨別出來。應(yīng)該嘗試優(yōu)化JPEG文件,在大小和質(zhì)量之間找到平衡。大多數(shù)JPEG文件可以被壓縮而不會導(dǎo)致圖像明顯的退化。即使是少量的壓縮也可以節(jié)省大量的磁盤空間。
使用PDF處理字形和其他需要高分辨率縮放的平面矢量圖形。
最終可以做以下總結(jié)。
| 圖片格式 | 適用范圍 | 注意事項(xiàng) |
|---|---|---|
| png | 應(yīng)用icon,界面icon,卡通風(fēng)格的背景圖 | 導(dǎo)入項(xiàng)目前可以使用ImageOptim進(jìn)行壓縮 |
| jpeg | 尺寸較大的風(fēng)景圖,照片 | 不支持透明度;因?yàn)榭梢哉{(diào)節(jié)壓縮比,可以在大小和質(zhì)量之間尋找最佳平衡。 |
| 字形,高分辨率的矢量圖 | 存在展開尺寸較大,光效失真的情況 | |
| svg(sf symbol) | 指示性icon | 僅支持iOS13及以上,系統(tǒng)sf符號是有版權(quán)的,使用時要注意應(yīng)用范圍和蘋果要求 |
引用
談?wù)?iOS 中圖片的解壓縮
圖片格式那么多,哪種更適合你
iOS圖片格式選擇
Why I don't use PDFs for iOS assets
SF Symbols: The benefits and how to use them guide
