YYImage之YYImageDecoder

YYImageDecoder 这个类是 YYImage 底层对图片处理的解码库,它会对图片解析成 YYImageFrame 对象,如果是 GIF 等动态图片,会解码成多张 YYImageFrame。其核心包括了图片格式探测,图片解码,提供对 APNG 的解码(iOS 8 系统提供),WebP 的解码。

普通图片的解码

YYImageDecoder 通过暴露的

- (BOOL)updateData:(nullable NSData *)data final:(BOOL)final;

来解析图片数据

在内部有一个对应的

- (BOOL)_updateData:(NSData *)data final:(BOOL)final

在做幕后的工作。
在通过 data 的数据探测到图片格式后,如果是 WebP 会使用 WebP 解析方法

- (void)_updateSourceWebP;

如果是 PNG 则会使用

- (void)_updateSourceWebP;

其他图片则会使用

- (void)_updateSourceImageIO;

让我们先来看普通的图片的处理,这里的普通指的是格式是 Gif,jpg,icon 等的图片。

定位到_updateSourceImageIO方法,其实这一层大部分是 CGImage 层的操作,代码比较长,这里整理成一个简短的流程,把关键点标注出来:

  1. 使用CGImageSourceCreateWithData来通过 data 创建 CGImage 对象,如果是增量的 data 则使用CGImageSourceCreateIncrementalCGImageSourceUpdateData来生成 CGImage
  2. 通过CGImageSourceGetCount来获取图片的帧数
  3. 使用CGImageSourceCopyProperties获取图片原信息,如果多个帧的图片可以通过CGImageSourceCopyPropertiesAtIndex来获取每一帧的图片信息,这里有几个 key 可以注意一下。
kCGImagePropertyPixelWidth:宽的像素
kCGImagePropertyPixelHeight:高的像素
kCGImagePropertyGIFDictionary:GIF相关的属性
kCGImagePropertyGIFUnclampedDelayTime:Gif的duration
kCGImagePropertyOrientation:图片的方向
  1. 把前3步收集的信息封装成_YYImageDecoderFrame对象。并封装到内部 frame 集合中

APNG的解码

WebP的解码

其他

后台线程解码图片

主要思路是在获取 image 对象之后,CGBitmapContextCreate生成 bitmap 上下文,CGContextDrawImage把图片内容画入上下文中,CGBitmapContextCreateImage获取 bitmap 图片完成解码

实例代码:

 CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef) & kCGBitmapAlphaInfoMask;
        BOOL hasAlpha = NO;
        if (alphaInfo == kCGImageAlphaPremultipliedLast ||
            alphaInfo == kCGImageAlphaPremultipliedFirst ||
            alphaInfo == kCGImageAlphaLast ||
            alphaInfo == kCGImageAlphaFirst) {
            hasAlpha = YES;
        }
        // BGRA8888 (premultiplied) or BGRX8888
        // same as UIGraphicsBeginImageContext() and -[UIView drawRect:]
        CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
        bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
        CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, YYCGColorSpaceGetDeviceRGB(), bitmapInfo);
        if (!context) return NULL;
        CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); // decode
        CGImageRef newImage = CGBitmapContextCreateImage(context);
        CFRelease(context);
        return newImage;

侦测图片的格式

老生常谈的话题,可以根据文件头几个字节来获取相应的图片格式
如 png 头4个字节是0x89, 'P', 'N', 'G',gif 头3个字节是G', 'I', 'F'

实例代码

YYImageType YYImageDetectType(CFDataRef data) {
    if (!data) return YYImageTypeUnknown;
    uint64_t length = CFDataGetLength(data);
    if (length < 16) return YYImageTypeUnknown;
    
    const char *bytes = (char *)CFDataGetBytePtr(data);
    
    uint32_t magic4 = *((uint32_t *)bytes);
    switch (magic4) {
        case YY_FOUR_CC(0x4D, 0x4D, 0x00, 0x2A): { // big endian TIFF
            return YYImageTypeTIFF;
        } break;
            
        case YY_FOUR_CC(0x49, 0x49, 0x2A, 0x00): { // little endian TIFF
            return YYImageTypeTIFF;
        } break;
            
        case YY_FOUR_CC(0x00, 0x00, 0x01, 0x00): { // ICO
            return YYImageTypeICO;
        } break;
            
        case YY_FOUR_CC(0x00, 0x00, 0x02, 0x00): { // CUR
            return YYImageTypeICO;
        } break;
            
        case YY_FOUR_CC('i', 'c', 'n', 's'): { // ICNS
            return YYImageTypeICNS;
        } break;
            
        case YY_FOUR_CC('G', 'I', 'F', '8'): { // GIF
            return YYImageTypeGIF;
        } break;
            
        case YY_FOUR_CC(0x89, 'P', 'N', 'G'): {  // PNG
            uint32_t tmp = *((uint32_t *)(bytes + 4));
            if (tmp == YY_FOUR_CC('\r', '\n', 0x1A, '\n')) {
                return YYImageTypePNG;
            }
        } break;
            
        case YY_FOUR_CC('R', 'I', 'F', 'F'): { // WebP
            uint32_t tmp = *((uint32_t *)(bytes + 8));
            if (tmp == YY_FOUR_CC('W', 'E', 'B', 'P')) {
                return YYImageTypeWebP;
            }
        } break;
        /*
        case YY_FOUR_CC('B', 'P', 'G', 0xFB): { // BPG
            return YYImageTypeBPG;
        } break;
        */
    }
    
    uint16_t magic2 = *((uint16_t *)bytes);
    switch (magic2) {
        case YY_TWO_CC('B', 'A'):
        case YY_TWO_CC('B', 'M'):
        case YY_TWO_CC('I', 'C'):
        case YY_TWO_CC('P', 'I'):
        case YY_TWO_CC('C', 'I'):
        case YY_TWO_CC('C', 'P'): { // BMP
            return YYImageTypeBMP;
        }
        case YY_TWO_CC(0xFF, 0x4F): { // JPEG2000
            return YYImageTypeJPEG2000;
        }
    }
    
    // JPG             FF D8 FF
    if (memcmp(bytes,"\377\330\377",3) == 0) return YYImageTypeJPEG;
    
    // JP2
    if (memcmp(bytes + 4, "\152\120\040\040\015", 5) == 0) return YYImageTypeJPEG2000;
    
    return YYImageTypeUnknown;
}

你可能感兴趣的:(YYImage之YYImageDecoder)