二、SDWebImage源码解析NSData + ImageContentType

● 我们先来看.h文件

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

一个枚举,列举出图片的类型

/**
 *  Return image format  返回图片类型
 *
 *  @param data the input image data 
 *
 *  @return the image format as `SDImageFormat` (enum)
 */
+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data;

/**
 Convert SDImageFormat to UTType 转成系统type

 @param format Format as SDImageFormat
 @return The UTType as CFStringRef
 */
+ (nonnull CFStringRef)sd_UTTypeFromSDImageFormat:(SDImageFormat)format;

在看.m文件之前,我们先说一些知识
● SDWebImage通过文件头来获取图片的类型,已png为例,png的前8个字节依次为:0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A,再比如jpg图片,jpg文件比较复杂,JPG文件数据,分很多很多的数据段, 并且每个数据段都会以 0xFF开头 ,所以我们就能明白以下代码的含义,

+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data {
    if (!data) {
        return SDImageFormatUndefined;
    }
    
    // File signatures table: http://www.garykessler.net/library/file_sigs.html
    uint8_t c;
    [data getBytes:&c length:1];
    switch (c) {
        case 0xFF:
            return SDImageFormatJPEG; //jpeg是 FF开头
        case 0x89:
            return SDImageFormatPNG; //png 是 89开头
        case 0x47:
            return SDImageFormatGIF; // gif 是 47开头
        case 0x49:
        case 0x4D:
            return SDImageFormatTIFF; // tiff 是 49 或者 4D开头
        case 0x52: {
            if (data.length >= 12) {
                //RIFF....WEBP
                NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
                if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
                    return SDImageFormatWebP;
                }
            }
            break;
        }
        case 0x00: {
            if (data.length >= 12) {
                //....ftypheic ....ftypheix ....ftyphevc ....ftyphevx
                NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(4, 8)] encoding:NSASCIIStringEncoding];
                if ([testString isEqualToString:@"ftypheic"]
                    || [testString isEqualToString:@"ftypheix"]
                    || [testString isEqualToString:@"ftyphevc"]
                    || [testString isEqualToString:@"ftyphevx"]) {
                    return SDImageFormatHEIC;
                }
            }
            break;
        }
    }
    return SDImageFormatUndefined;
}

● 比较麻烦的是webp格式,webp是以524946462A73010057454250开头的,占用12个字节,当第一个字节为52时,如果长度<12 我们就认定为不是图片。因此返回nil。我们通过数据截取后获得testString,如果testString头部包含RIFF且尾部也包含WEBP,那么就认定该图片格式为webp。

● 关于.heic格式的图片
1.什么是.heic格式图片?
之前叫“live”图片,打开下图红框中的按钮即可打开该模式,拍照后会截取拍照前后大概两秒的一个片段,与“Gif”图不同的是,该格式还包含了声音(目前只有),只有在 iOS11系统下且CPU为A10及其以上(最低也得是iPhone 7),其他情况下拍出来的都是普通“live”图,即在需要转换格式的时候会自动转换为“.jpg/.jpeg”格式
2.如何判断.heic格式图片?
第四到第八个字节,分别等于:ftypheic,ftypheix,ftyphevc,ftyphevx,就可以认定他是.heic格式的图片

● 关于kSDUTTypeWebP和kSDUTTypeHEIC

// Currently Image/IO does not support WebP
#define kSDUTTypeWebP ((__bridge CFStringRef)@"public.webp")
// AVFileTypeHEIC is defined in AVFoundation via iOS 11, we use this without import AVFoundation
#define kSDUTTypeHEIC ((__bridge CFStringRef)@"public.heic")

因为webpiOS系统没有对应的type,所以手动创建了一个,heic格式只在iOS11以后才支持,所以也手动创建。

+ (nonnull CFStringRef)sd_UTTypeFromSDImageFormat:(SDImageFormat)format {
    CFStringRef UTType;
    switch (format) {
        case SDImageFormatJPEG:
            UTType = kUTTypeJPEG;
            break;
        case SDImageFormatPNG:
            UTType = kUTTypePNG;
            break;
        case SDImageFormatGIF:
            UTType = kUTTypeGIF;
            break;
        case SDImageFormatTIFF:
            UTType = kUTTypeTIFF;
            break;
        case SDImageFormatWebP:
            UTType = kSDUTTypeWebP;
            break;
        case SDImageFormatHEIC:
            UTType = kSDUTTypeHEIC;
            break;
        default:
            // default is kUTTypePNG
            UTType = kUTTypePNG;
            break;
    }
    return UTType;
}

这个方法没什么可说的,就是讲枚举转化成系统type

你可能感兴趣的:(二、SDWebImage源码解析NSData + ImageContentType)