iOS 關於 GIF 圖片那點事

Edward Wen發表於2016-11-04

前言

前幾天我們專案組的群裡提了這麼一件事情:在我們的應用中儲存動態的GIF圖到相簿,儲存的圖片變成了靜態圖片。而微博則能正確儲存,可見這並不是一個技術不可實現的。前不久剛好看了蘋果關於ImageIO框架的指南,藉著這個契機,我就去調研調研其中的原委。

使用UIImage讀取GIF圖片的不足

UIImage類在UIKit框架中,是我們最常使用的儲存圖片類。該類提供了可以使用圖片路徑或是圖片資料來例項化的類方法。UIImage類底層採用ImageIO框架來讀取圖片資料,下圖分別為+imageWithContentsOfFile:+imageWithData:呼叫的堆疊。

111495816-f35f8bb6e14eb42c

image堆疊.png

從堆疊中我們可以看到圖片讀取的大致流程如下:

  1. 根據檔案路徑或是資料生成CGImageSource
  2. 然後呼叫CGImageSourceCreateImageAtIndex方法獲取一幀的圖片,型別為CGImage;
  3. UIImage物件持有該CGImage

在流程的第一步生成的CGImageSource,仍然保留著GIF的全部資訊。而在流程的第二步中出了問題。動態的Gif圖與靜態格式圖片不同,它包含有多張的靜態圖片。CGImageSourceCreateImageAtIndex只能返回索引值的圖片,丟失了其他的圖片資訊。因此,我們只獲取到了其中的一幀圖片。出於好奇,我選擇了一張只有四幀完全不同的Gif圖,通過測試觀察,UIImage獲取的是第一幀的圖片。既然我們不能用UIImageCGImage來處理Gif圖,我們是否可以降級,採用ImageIO框架來處理呢。答案是肯定的。

使用 ImageIO 框架解析GIF圖片

我參考了YYImage框架的設計,定義了兩個類,分別為JWGifDecoderJWGifFrameJWGifDecoder類負責GIF圖片資料的解析,而JWGifFrame表示幀。兩者的標頭檔案如下:

JWGifDecoder內部使用CGImageSource來解析圖片資料。例項化時候,該類使用CGImageSourceCreateWithData ()方法(這裡的c語言方法忽略引數)一個CGImageSource,然後採用CGImageSourceGetCount ()獲得其內部的圖片個數也就是幀數。而在生成幀物件時候,採用CGImageSourceCopyPropertiesAtIndex ()方法獲得對應幀的屬性,採用CGImageSourceCreateImageAtIndex()方法得到圖片。

儲存GIF格式圖片至相簿

UIImage只會保留一幀的資訊,而圖片的資料則具有GIF的所有資料。因此,相簿讀取GIF格式圖片有個原則:在儲存圖片至相簿時,必須儲存圖片的資料而不是UIImage物件。
IOS8以下ALAssetsLibrary框架處理相簿,在IOS8以上,則是採用Photos框架。在這篇部落格中說在IOS8中使用Photos的方法會儲存不了,由於沒有IOS8的系統的手機,我也無法做相關測試。目前測試我手上的IOS10系統的iphone6s,可以成功儲存,儲存的GIF圖片可以在微信中成功傳送。儲存相簿的程式碼如下所示:

結語

ImageIO框架給了我們更加強大的圖片處理能力,它可以處理UIImage無法應付的Gif格式的圖片。對於那些較高階的API無法處理的事情,可以試一試用更低層的框架看看是否進行處理。發現很多內容在蘋果的 Guide裡都有說明,以後需要多多看看。
這篇文章中還有很多東西沒有講到,比如相簿讀取Gif圖片,又比如程式碼將圖片儲存成Gif圖片(這點在蘋果的Guide裡有提到)等,以後再補充吧。

參考

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

iOS 關於 GIF 圖片那點事 iOS 關於 GIF 圖片那點事

相關文章