版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2018.02.10 |
前言
我們做APP,文字和圖片是絕對不可缺少的元素,特別是圖片一般存儲在圖床里面,一般公司可以委托第三方保存,NB的公司也可以自己存儲圖片,ios有很多圖片加載的第三方框架,其中最優(yōu)秀的莫過于SDWebImage,它幾乎可以滿足你所有的需求,用了好幾年這個框架,今天想總結(jié)一下。感興趣的可以看其他幾篇。
1. SDWebImage探究(一)
2. SDWebImage探究(二)
3. SDWebImage探究(三)
4. SDWebImage探究(四)
5. SDWebImage探究(五)
圖片格式的判斷
這個其實(shí)在SDWebImage探究(四)中提到過,但是只是給出了出處以及實(shí)現(xiàn),現(xiàn)在我們就繼續(xù)深入其中,探究一下為什么要這么做。為了說明方便我們還是先給出源碼,是一個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
這個實(shí)現(xiàn)的代碼不多,就幾行,看著還是很清晰的。
首先我們需要知道怎么區(qū)分一張圖片的類型,每一張圖片是什么?其實(shí)就是一個矩陣,每一個元素都是十六進(jìn)制或者二進(jìn)制的表示,為什么不同的軟件可以針對不同的圖片做不同的處理,它是怎么識別圖片的類別呢?其實(shí)這就要看該圖像數(shù)據(jù)的第一個字節(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í)就是文件頭標(biāo)識,長度就是一個字節(jié)。比較特殊的是0x52,在判斷是這個標(biāo)識符以后,作者還進(jìn)行了data長度的判斷,長度小于12就返回未定義格式SDImageFormatUndefined,大于12就利用方法NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
進(jìn)行長度的截取,這里面大家可以看到作者用了不怎么常用的NSData的subdataWithRange:
方法,進(jìn)行了長度的截取并返回了一個NSString類型的字符串,然后對這個字符串的首綴和尾綴進(jìn)行判斷,如果首綴是RIFF
,尾綴是WEBP
,就返回這是一個WEBP格式的圖片,否則還是返回未定義。具體的流程就是這樣的。
需要說明的是:
if (data.length < 12) {
return SDImageFormatUndefined;
}
像上面這種容錯處理和提高性能的代碼是值得我們學(xué)習(xí)的。
你可能覺得說這些就完了嗎?下面我們繼續(xù)深入和擴(kuò)展。
圖片格式判斷深入
下面我們就繼續(xù)深入。
我們先看一下不同格式圖片的文件頭標(biāo)識
JPEG
- 文件頭標(biāo)識 (2 bytes):
0xff, 0xd8 (SOI)
(JPEG 文件標(biāo)識) - 文件結(jié)束標(biāo)識 (2 bytes):
0xff, 0xd9 (EOI)
所以代碼中用oxff作為JPEG格式圖片的判斷。
PNG
- 文件頭標(biāo)識
(8 bytes) 89 50 4E 47 0D 0A 1A 0A
所以代碼中用ox89作為PNG格式圖片的判斷。
GIF
- 文件頭標(biāo)識 (6 bytes)
47 49 46 38 39(37) 61
G I F 8 9 (7) a
所以代碼中用0x47作為GIF格式圖片的判斷。
BMP
- – 文件頭標(biāo)識 (2 bytes)
42 4D
B M
代碼中未有識別該類型的圖片,如果是該類型一致返回的就是未定義。
TIFF
- 文件頭標(biāo)識 (2 bytes)
4D 4D 或 49 49
所以代碼中用0x49
或者0x4D
作為TIFF格式圖片的判斷。
ICO
- 文件頭標(biāo)識 (8 bytes)
00 00 01 00 01 00 20 20
還有很多的圖片格式都有對應(yīng)的文件標(biāo)識符。
上面列出來的是幾個常用的,下面我們就看一下更廣泛的范圍的文件頭。
// 常用文件的文件頭如下(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
看完這個,大家就會明白為什么WEBP格式的只判斷第一個字符0x52為什么不可以了,因?yàn)?code>0x52為文件頭的文件也可能會是rar
等類型,所以這里只是判斷文件頭明顯就不夠了,我們需要更多的判斷,來確定WEBP格式的唯一特征。
那么還要從WEBP格式文件的文件頭入手,因?yàn)槲覀冎荒軓睦锩娴臄?shù)據(jù)找到特征,確定其唯一性。
可以看見,這里是12個字節(jié),前四個字節(jié)是RIFF,后四個字節(jié)是WEBP,這就是作者代碼中要截取12個字節(jié)的原因,根據(jù)這個12個字節(jié)的前綴和后綴,可以確保其WEBP格式的唯一性不會出現(xiàn)誤判的操作。
那么可能有人還有問題,為什么作者只判斷了這5
個類型?其實(shí)這好理解,JPEG和PNG就不多說了吧,非常常用的類型,那么WEBP呢?它用在什么場合?其實(shí)WEBP可以理解為簡化版的GIF,很多需要動態(tài)封面的地方,只需要后臺給一個WEBP的地址,我們用SDWebImage最普通的下載方法進(jìn)行下載就可以,這并不是多么難的技術(shù),其實(shí)非常簡單,這里給大家一個例子。
參考文章
后記
本篇已結(jié)束,后面更精彩。