SDWebImage探究(六) —— 圖片類型判斷深入研究

版本記錄

版本號(hào) 時(shí)間
V1.0 2018.02.10

前言

我們做APP,文字和圖片是絕對(duì)不可缺少的元素坯癣,特別是圖片一般存儲(chǔ)在圖床里面,一般公司可以委托第三方保存,NB的公司也可以自己存儲(chǔ)圖片捧毛,ios有很多圖片加載的第三方框架,其中最優(yōu)秀的莫過(guò)于SDWebImage让网,它幾乎可以滿足你所有的需求呀忧,用了好幾年這個(gè)框架,今天想總結(jié)一下溃睹。感興趣的可以看其他幾篇而账。
1. SDWebImage探究(一)
2. SDWebImage探究(二)
3. SDWebImage探究(三)
4. SDWebImage探究(四)
5. SDWebImage探究(五)

圖片格式的判斷

這個(gè)其實(shí)在SDWebImage探究(四)中提到過(guò),但是只是給出了出處以及實(shí)現(xiàn)因篇,現(xiàn)在我們就繼續(xù)深入其中泞辐,探究一下為什么要這么做笔横。為了說(shuō)明方便我們還是先給出源碼,是一個(gè)NSData的分類 咐吼。

1. NSData+ImageContentType.h
#import <Foundation/Foundation.h>
#import "SDWebImageCompat.h"

typedef NS_ENUM(NSInteger, SDImageFormat) {
    SDImageFormatUndefined = -1,
    SDImageFormatJPEG = 0,
    SDImageFormatPNG,
    SDImageFormatGIF,
    SDImageFormatTIFF,
    SDImageFormatWebP
};

@interface NSData (ImageContentType)

/**
 *  Return image format
 *
 *  @param data the input image data
 *
 *  @return the image format as `SDImageFormat` (enum)
 */
+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data;

@end
2. NSData+ImageContentType.m
#import "NSData+ImageContentType.h"


@implementation NSData (ImageContentType)

+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data {
    if (!data) {
        return SDImageFormatUndefined;
    }
    
    uint8_t c;
    [data getBytes:&c length:1];
    switch (c) {
        case 0xFF:
            return SDImageFormatJPEG;
        case 0x89:
            return SDImageFormatPNG;
        case 0x47:
            return SDImageFormatGIF;
        case 0x49:
        case 0x4D:
            return SDImageFormatTIFF;
        case 0x52:
            // R as RIFF for WEBP
            if (data.length < 12) {
                return SDImageFormatUndefined;
            }
            
            NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
            if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
                return SDImageFormatWebP;
            }
    }
    return SDImageFormatUndefined;
}

@end

這個(gè)實(shí)現(xiàn)的代碼不多吹缔,就幾行,看著還是很清晰的锯茄。

首先我們需要知道怎么區(qū)分一張圖片的類型厢塘,每一張圖片是什么?其實(shí)就是一個(gè)矩陣撇吞,每一個(gè)元素都是十六進(jìn)制或者二進(jìn)制的表示俗冻,為什么不同的軟件可以針對(duì)不同的圖片做不同的處理,它是怎么識(shí)別圖片的類別呢牍颈?其實(shí)這就要看該圖像數(shù)據(jù)的第一個(gè)字節(jié)了迄薄。

從代碼中我們可以看到:

- JPEG 0xFF
- PNG 0x89
- GIF   0x47
- TIFF 0x49或0x4D
- R as RIFF for WEBP 0x52

這里每一種類型都有行業(yè)標(biāo)準(zhǔn)確定的表示字符進(jìn)行標(biāo)識(shí),其實(shí)就是文件頭標(biāo)識(shí)煮岁,長(zhǎng)度就是一個(gè)字節(jié)讥蔽。比較特殊的是0x52,在判斷是這個(gè)標(biāo)識(shí)符以后画机,作者還進(jìn)行了data長(zhǎng)度的判斷,長(zhǎng)度小于12就返回未定義格式SDImageFormatUndefined响禽,大于12就利用方法NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];進(jìn)行長(zhǎng)度的截取芋类,這里面大家可以看到作者用了不怎么常用的NSData的subdataWithRange:方法,進(jìn)行了長(zhǎng)度的截取并返回了一個(gè)NSString類型的字符串界阁,然后對(duì)這個(gè)字符串的首綴和尾綴進(jìn)行判斷侯繁,如果首綴是RIFF,尾綴是WEBP泡躯,就返回這是一個(gè)WEBP格式的圖片贮竟,否則還是返回未定義。具體的流程就是這樣的较剃。

需要說(shuō)明的是:

if (data.length < 12) {
    return SDImageFormatUndefined;
}

像上面這種容錯(cuò)處理和提高性能的代碼是值得我們學(xué)習(xí)的咕别。

你可能覺(jué)得說(shuō)這些就完了嗎?下面我們繼續(xù)深入和擴(kuò)展写穴。


圖片格式判斷深入

下面我們就繼續(xù)深入顷级。

我們先看一下不同格式圖片的文件頭標(biāo)識(shí)

JPEG

  • 文件頭標(biāo)識(shí) (2 bytes): 0xff, 0xd8 (SOI) (JPEG 文件標(biāo)識(shí))
  • 文件結(jié)束標(biāo)識(shí) (2 bytes): 0xff, 0xd9 (EOI)

所以代碼中用oxff作為JPEG格式圖片的判斷。

PNG

  • 文件頭標(biāo)識(shí) (8 bytes) 89 50 4E 47 0D 0A 1A 0A

所以代碼中用ox89作為PNG格式圖片的判斷确垫。

GIF

  • 文件頭標(biāo)識(shí) (6 bytes) 47 49 46 38 39(37) 61 G I F 8 9 (7) a

所以代碼中用0x47作為GIF格式圖片的判斷弓颈。

BMP

  • – 文件頭標(biāo)識(shí) (2 bytes) 42 4D B M

代碼中未有識(shí)別該類型的圖片翔冀,如果是該類型一致返回的就是未定義。

TIFF

  • 文件頭標(biāo)識(shí) (2 bytes) 4D 4D 或 49 49

所以代碼中用0x49或者0x4D作為TIFF格式圖片的判斷披泪。

ICO

  • 文件頭標(biāo)識(shí) (8 bytes) 00 00 01 00 01 00 20 20

還有很多的圖片格式都有對(duì)應(yīng)的文件標(biāo)識(shí)符纤子。

上面列出來(lái)的是幾個(gè)常用的,下面我們就看一下更廣泛的范圍的文件頭款票。

// 常用文件的文件頭如下(16進(jìn)制):

JPEG (jpg)控硼,文件頭:FFD8FFE0或FFD8FFE1或FFD8FFE8

GIF ([gif](https://baike.baidu.com/item/gif/217778)),文件頭:47494638PNG ([png](https://baike.baidu.com/item/png/174154))卡乾,文件頭:89504E47

TIFF (tif)幔妨,文件頭:49492A00

Windows Bitmap ([bmp](https://baike.baidu.com/item/bmp)),文件頭:424DC001

CAD ([dwg](https://baike.baidu.com/item/dwg/5953048))谍椅,文件頭:41433130

Adobe Photoshop (psd)误堡,文件頭:38425053

Rich Text Format ([rtf](https://baike.baidu.com/item/rtf/13211185)),文件頭:7B5C727466

XML (xml)雏吭,文件頭:3C3F786D6C

HTML (html)锁施,文件頭:68746D6C3E

Email [thorough only] (eml),文件頭:44656C69766572792D646174653A

Outlook Express (dbx)杖们,文件頭:CFAD12FEC5FD746F

Outlook (pst)悉抵,文件頭:2142444E

MS Word/Excel (xls.or.doc),文件頭:D0CF11E0

MS Access ([mdb](https://baike.baidu.com/item/mdb/9688610))胀莹,文件頭:5374616E64617264204A

WordPerfect (wpd)基跑,文件頭:FF575043

Adobe Acrobat ([pdf](https://baike.baidu.com/item/pdf/317608)),文件頭:255044462D312E

Quicken (qdf)描焰,文件頭:AC9EBD8F

Windows Password (pwl)媳否,文件頭:E3828596

ZIP Archive ([zip](https://baike.baidu.com/item/zip/15417954)),文件頭:504B0304

RAR Archive ([rar](https://baike.baidu.com/item/rar/2502036))荆秦,文件頭:52617221

Wave ([wav](https://baike.baidu.com/item/wav/218914))篱竭,文件頭:57415645

AVI ([avi](https://baike.baidu.com/item/avi/213655)),文件頭:41564920

Real Audio (ram)步绸,文件頭:2E7261FD

Real Media (rm)掺逼,文件頭:2E524D46

MPEG ([mpg](https://baike.baidu.com/item/mpg/213809)),文件頭:000001BA

MPEG (mpg)瓤介,文件頭:000001B3

Quicktime (mov)吕喘,文件頭:6D6F6F76

Windows Media ([asf](https://baike.baidu.com/item/asf/3918))赘那,文件頭:3026B2758E66CF11

MIDI (mid),文件頭:4D546864

看完這個(gè)氯质,大家就會(huì)明白為什么WEBP格式的只判斷第一個(gè)字符0x52為什么不可以了募舟,因?yàn)?code>0x52為文件頭的文件也可能會(huì)是rar等類型,所以這里只是判斷文件頭明顯就不夠了闻察,我們需要更多的判斷拱礁,來(lái)確定WEBP格式的唯一特征。

那么還要從WEBP格式文件的文件頭入手辕漂,因?yàn)槲覀冎荒軓睦锩娴臄?shù)據(jù)找到特征呢灶,確定其唯一性。

可以看見(jiàn)钉嘹,這里是12個(gè)字節(jié)鸯乃,前四個(gè)字節(jié)是RIFF,后四個(gè)字節(jié)是WEBP隧期,這就是作者代碼中要截取12個(gè)字節(jié)的原因飒责,根據(jù)這個(gè)12個(gè)字節(jié)的前綴和后綴,可以確保其WEBP格式的唯一性不會(huì)出現(xiàn)誤判的操作仆潮。

那么可能有人還有問(wèn)題宏蛉,為什么作者只判斷了這5個(gè)類型?其實(shí)這好理解性置,JPEG和PNG就不多說(shuō)了吧拾并,非常常用的類型,那么WEBP呢鹏浅?它用在什么場(chǎng)合?其實(shí)WEBP可以理解為簡(jiǎn)化版的GIF嗅义,很多需要?jiǎng)討B(tài)封面的地方,只需要后臺(tái)給一個(gè)WEBP的地址隐砸,我們用SDWebImage最普通的下載方法進(jìn)行下載就可以之碗,這并不是多么難的技術(shù),其實(shí)非常簡(jiǎn)單季希,這里給大家一個(gè)例子褪那。

參考文章

1. 文件頭

后記

本篇已結(jié)束,后面更精彩式塌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末博敬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子峰尝,更是在濱河造成了極大的恐慌偏窝,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異祭往,居然都是意外死亡伦意,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門链沼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)默赂,“玉大人,你說(shuō)我怎么就攤上這事括勺。” “怎么了曲掰?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵疾捍,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我栏妖,道長(zhǎng)乱豆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任吊趾,我火速辦了婚禮,結(jié)果婚禮上论泛,老公的妹妹穿的比我還像新娘屁奏。我一直安慰自己,他們只是感情好勇边,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布粒褒。 她就那樣靜靜地躺著奕坟,像睡著了一般怕享。 火紅的嫁衣襯著肌膚如雪函筋。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天首懈,我揣著相機(jī)與錄音究履,去河邊找鬼。 笑死最仑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的欲芹。 我是一名探鬼主播吟吝,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼浙宜!你這毒婦竟也來(lái)了蛹磺?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎清酥,沒(méi)想到半個(gè)月后蕴侣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體昆雀,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狞膘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年已球,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片智亮。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阔蛉,死狀恐怖弃舒,靈堂內(nèi)的尸體忽然破棺而出状原,到底是詐尸還是另有隱情聋呢,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布坝冕,位于F島的核電站,受9級(jí)特大地震影響瓦呼,放射性物質(zhì)發(fā)生泄漏测暗。R本人自食惡果不足惜央串,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碗啄。 院中可真熱鬧质和,春花似錦、人聲如沸稚字。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)昌讲。三九已至国夜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間醋闭,已是汗流浹背窄驹。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人饮戳。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓豪治,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親扯罐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子负拟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容