读 SDWebImage 六(编码器五:SDWebImageGIFCoder)
编码
判断图片格式是否支持编码,该类仅针对动图的操作,所以仅判断图片的格式是否为GIF格式,如果是返回YES,否则返回NO
1 2 3
| - (BOOL)canEncodeToFormat:(SDImageFormat)format { return (format == SDImageFormatGIF); }
|
根据给定的图片格式将图片进行编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| - (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format { //如果image不存在,返回nil if (!image) { return nil; } //该类只支持动图的编解码,如果image的格式不是GIF,返回nil if (format != SDImageFormatGIF) { return nil; } //临时变量imageData,用来保存图片数据 NSMutableData *imageData = [NSMutableData data]; // 获取GIF图像格式的CFStringRef格式字符串 CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:SDImageFormatGIF]; // 生成图片对象的SDWebImageFrame类型元素的数组 NSArray<SDWebImageFrame *> *frames = [SDWebImageCoderHelper framesFromAnimatedImage:image]; // 创建图像目标。 GIF不支持EXIF图像方向 CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, frames.count, NULL); //如果创建失败,返回nil if (!imageDestination) { // Handle failure. return nil; } if (frames.count == 0) { // 用于静态单个GIF图片(如果是单帧的动图就直接将图片添加到imageDestination中) CGImageDestinationAddImage(imageDestination, image.CGImage, nil); } else { // 用于动画的GIF图片 //// 获取到动图的循环次数 NSUInteger loopCount = image.sd_imageLoopCount; // 创建一个动图属性字典保存循环次数 NSDictionary *gifProperties = @{(__bridge NSString *)kCGImagePropertyGIFDictionary: @{(__bridge NSString *)kCGImagePropertyGIFLoopCount : @(loopCount)}}; // 为图像目标设置属性 CGImageDestinationSetProperties(imageDestination, (__bridge CFDictionaryRef)gifProperties); //循环每一帧的图拼啊 for (size_t i = 0; i < frames.count; i++) { // 获取SDWebImageFrame对象 SDWebImageFrame *frame = frames[i]; //获取没一帧的显示时间 float frameDuration = frame.duration; //获取每一帧取位图图片 CGImageRef frameImageRef = frame.image.CGImage; // 创建一个临时变量字典保存每一帧的展示时间 NSDictionary *frameProperties = @{(__bridge NSString *)kCGImagePropertyGIFDictionary : @{(__bridge NSString *)kCGImagePropertyGIFDelayTime : @(frameDuration)}}; // 将位图和其对应的属性添加到imageDestination中 CGImageDestinationAddImage(imageDestination, frameImageRef, (__bridge CFDictionaryRef)frameProperties); } } // 如果编码失败就返回nil if (CGImageDestinationFinalize(imageDestination) == NO) { imageData = nil; } // 释放imageDestination对象 CFRelease(imageDestination); 返回图片数据 return [imageData copy]; }
|
解码
判断是否支持图片数据的解码
该类仅针对动图的操作,所以仅判断图片的格式是否为GIF格式,如果是支持该图片数据解码,否则返回NO
1 2 3
| - (BOOL)canDecodeFromData:(nullable NSData *)data { return ([NSData sd_imageFormatForImageData:data] == SDImageFormatGIF); }
|
将图片数据解码为图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| - (UIImage *)decodedImageWithData:(NSData *)data { //如果数据为空,返回nil if (!data) { return nil; } #if SD_MAC SDAnimatedImageRep *imageRep = [[SDAnimatedImageRep alloc] initWithData:data]; NSImage *animatedImage = [[NSImage alloc] initWithSize:imageRep.size]; [animatedImage addRepresentation:imageRep]; return animatedImage; #else // 生成图片源 CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); //如果图片源不存在,返回nil if (!source) { return nil; } // 获取子图片数量 size_t count = CGImageSourceGetCount(source); // 创建临时变量,用来保存动图对象 UIImage *animatedImage; //如果子图片个数不大于2,直接将数据转化成图片 if (count <= 1) { animatedImage = [[UIImage alloc] initWithData:data]; } else { // 创建可变数组保存SDWebImageFrame对象 NSMutableArray<SDWebImageFrame *> *frames = [NSMutableArray array]; // 遍历子图片对象,并将其包装成SDWebImageFrame对象 for (size_t i = 0; i < count; i++) { // 获取指定帧数的相位图 CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, i, NULL); // 如果没获取到就跳过进入下次循环 if (!imageRef) { continue; } // 获取指定帧数的持续时间 float duration = [self sd_frameDurationAtIndex:i source:source]; // 根据相位图生成图片对象 UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; // 释放相位图 CGImageRelease(imageRef); // 将一帧的信息封装成SDWebImageFrame对象 SDWebImageFrame *frame = [SDWebImageFrame frameWithImage:image duration:duration]; // 将封装好的SDWebImageFrame对象添加到数组中保存 [frames addObject:frame]; } // 创建临时变量,用来保存循环次数 NSUInteger loopCount = 1; // 获取到图片属性 NSDictionary *imageProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(source, nil); // 获取到GIF相关的图像属性 NSDictionary *gifProperties = [imageProperties valueForKey:(__bridge NSString *)kCGImagePropertyGIFDictionary]; 如果gifProperties存在 if (gifProperties) { //获取GIF循环次数 NSNumber *gifLoopCount = [gifProperties valueForKey:(__bridge NSString *)kCGImagePropertyGIFLoopCount]; //如果可以转换成NSNumber类型,就将获取到的GIF循环次数赋值给loopCount if (gifLoopCount != nil) { loopCount = gifLoopCount.unsignedIntegerValue; } } // 利用封装好的SDWebImageFrame对象数组生成动图对象 animatedImage = [SDWebImageCoderHelper animatedImageWithFrames:frames]; // 设置动图对象的循环次数 animatedImage.sd_imageLoopCount = loopCount; //设置动图的图片格式 animatedImage.sd_imageFormat = SDImageFormatGIF; } //释放图片源source CFRelease(source); //返回动图 return animatedImage; #endif }
|
如果执行动图的解压操作,就直接返回该图片,动图不支持解压
1 2 3 4 5 6
| - (UIImage *)decompressedImageWithImage:(UIImage *)image data:(NSData *__autoreleasing _Nullable *)data options:(nullable NSDictionary<NSString*, NSObject*>*)optionsDict { // GIF do not decompress return image; }
|
私有方法
获取动图每一帧的显示时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| - (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source { //临时变量每一帧的显示时间,默认为0.1f float frameDuration = 0.1f; // 获取图片源中指定位置的图片属性 CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil); //如果获取失败,就返回默认每一帧的显示时间 if (!cfFrameProperties) { return frameDuration; } 获取图片属性字典 NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties; //从图片属性中获取gif属性字典 NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary]; //从git属性中获取当前帧的显示时间 NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; //如果当前帧的显示时间不为nil,赋值给frameDuration if (delayTimeUnclampedProp != nil) { frameDuration = [delayTimeUnclampedProp floatValue]; } else { //如果通过key:kCGImagePropertyGIFUnclampedDelayTime 从gifProperties字典中获取不到当前帧的显示时间,则通过另一个key:kCGImagePropertyGIFDelayTime获取 NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; //如果不为空,赋值给赋值给frameDuration if (delayTimeProp != nil) { frameDuration = [delayTimeProp floatValue]; } } //许多烦人的广告指定0持续时间,以使图像尽快闪现。 我们遵循Firefox的行为,并为指定持续时间<= 10 ms的任何帧使用100 ms的持续时间。 如果当前帧显示的时间实现小于11ms,就重新设置为100ms if (frameDuration < 0.011f) { frameDuration = 0.100f; } //释放cfFrameProperties CFRelease(cfFrameProperties); //返回当前帧显示时间 return frameDuration; }
|